JDK源码阅读:Object类阅读笔记

Object

1. @HotSpotIntrinsicCandidate

@HotSpotIntrinsicCandidate
public final native Class<?> getClass();

使用@HotSpotIntrinsicCandidate注解标注的方法,表示JVM可能为该方法提供了一些基于CPU指令的高效实现,而非使用Java的实现。

2. native方法

getClass()hashCode()clone()notify()等方法的默认实现都是native方法

3. equals方法

equals()方法默认实现

public boolean equals(Object obj) {
    return (this == obj);
}

4. clone方法

clone()方法默认是native实现,且默认是一个浅拷贝。但要调用这个方法,要求类实现Clonable接口,否则会抛出CloneNotSupportedException异常。即使Clonable没有定义任何方法。

public interface Cloneable {
}

下面是一个简单的示例

public class Test implements Cloneable{
    public int a = 10;

    public static void main(String[] args) {
        try {
            Test test = new Test();
            Object clone = test.clone();
            System.out.println(clone.getClass());
            System.out.println(test.getClass());
            System.out.println(test.a);
            Test toClone = (Test)clone;
            System.out.println(toClone.a);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}
// 输出
class Test
class Test
10
10

6. toString方法

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

默认实现返回类名+十六进制哈希码

7. notify和notifyAll方法

唤醒在对象监视器上等待的线程。

有下面这些方法令当前线程获得一个对象的监视器

  • 执行synchronized方法
  • 执行synchronized块
  • 执行synchronized的静态方法(获得的是当前类的class对象的监视器)

8. wait方法

下面是一个带纳秒参数nano的wait方法

public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
    if (timeoutMillis < 0) {
        throw new IllegalArgumentException("timeoutMillis value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
            "nanosecond timeout value out of range");
    }

    if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {
        timeoutMillis++;
    }

    wait(timeoutMillis);
}

我们看到第三个if判断,假如nano参数大于0且timeoutMillis参数未满,所做的直接是令timeoutMillis参数自增。最后直接调用wait(timeoutMillis),这简直是对程序员的欺骗。注:1ms = 1000000ms

下面的这段代码是一段示例代码。推荐的等待方法是在调用wait的while循环中检查等待的条件,如下例所示。 这种方法避免了可能由虚假唤醒引起的问题。

虚假唤醒:线程可以在没有被通知、中断或超时的情况下唤醒

synchronized (obj) {
    while (<condition does not hold> and <timeout not exceeded>) {
        long timeoutMillis = ... ; // recompute timeout values
        int nanos = ... ;
        obj.wait(timeoutMillis, nanos);
    }
    ... // Perform action appropriate to condition or timeout
}

9. finalize方法

@Deprecated(since="9")
protected void finalize() throws Throwable { }

显然我们看到,通过@Deprecated(since="9")注解,标记了finalize()方法自JDK9开始被弃用。

不推荐使用的原因

  • finalize()方法不能保证执行。
    还有另外一个方法能够回收对象,Runtime.getRuntime().runFinalization(); 但是这只能保证GC做出最大的努力,但是我们也不能finalize方法都能执行。我们还有一种方式能够保证执行finalize()方法,Runtime.runFinalizersOnExit(true),这个方法已经被JDK弃用,因为这种方法本质上是不安全的,可能导致finalizers方法被活对象调用而其他线程正在并行操作这个对象,从而导致不正确的行为或者死锁。

  • finalize()方法不像构造方法在链中工作,意味着像当你调用构造方法的时候,超类中的构造方法也会被隐含的调用,但是在finalize()方法的这种情况中,这种隐含的调用不会发生。超类中的finalize()方法需要显示的调用。假设,你创建了一个类并且小心翼翼的写了finalize()方法。一些人来extend了你的类,但是在子类中的finalize()块中没有调用super.finalize()方法。然后超类中finalize()方法将永远都不会被调用。

  • 任何有finalize()方法抛出的异常都会被GC线程忽略而且不会被进一步传播,事实上也不会在日志文件上记录下来。

正确的姿势

  • 要在finalize()方法中一直调用super.finalize()。
  • 考虑到不可预测预测性,不要在时间要求高的应用中使用finalize()。
  • 不要使用Runtime.runFinalizersOnExit(true);方法,因为你可能将你的系统置于危险之中。
  • 尝试遵循下边的模板使用finalize()方法。
@Override
protected void finalize() throws Throwable{
    try{
        //release resources here
    }catch(Throwable t){
        throw t;
    }finally{
        super.finalize();
}
Tags: