JDK原始碼閱讀:Object類閱讀筆記

pedro7發表於2021-09-18

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();
}

相關文章