JDK1.8原始碼閱讀筆記(1)Object類

aixueforever發表於2021-09-07

JDK1.8原始碼閱讀筆記(1)Object類

​ Object 類屬於 java.lang 包,此包下的所有類在使⽤時⽆需⼿動導⼊,系統會在程式編譯期間⾃動 導⼊。Object 類是所有類的基類,當⼀個類沒有直接繼承某個類時,預設繼承Object類,也就是說任何 類都直接或間接繼承此類,Object 類中能訪問的⽅法在所有類中都可以調⽤。

Object類原始碼:

native關鍵字

​ Java有兩種方法:Java方法和本地方法。Java方法是由Java語言編寫,編譯成位元組碼,儲存在class檔案中。本地方法是由其他語言(比如C,C++,或者彙編)編寫的,編譯成和處理器相關的機器程式碼。本地方法儲存在動態連線庫中,格式是各個平臺專有的。Java方法是平臺無關的,單本地方法卻不是。執行中的Java程式呼叫本地方法時,虛擬機器裝載包含這個本地方法的動態庫,並呼叫這個方法。本地方法是聯絡Java程式和底層主機作業系統的連線方法

​ 被native關鍵字修飾的方法叫做本地方法,簡單地講,一個native方法就是一個java呼叫非java程式碼的介面。一個Native Method是這樣一個java的方法:該方法的實現由非java語言實現,比如C。另外native方法在JVM中執行時資料區也和其它方法不一樣,它有專門的本地方法棧。

Object類原始碼

package java.lang;

public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    public final native Class<?> getClass();

    public native int hashCode();

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

    protected native Object clone() throws CloneNotSupportedException;

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

    public final native void notify();

    public final native void notifyAll();

    public final native void wait(long timeout) throws InterruptedException;

    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }
        if (nanos > 0) {
            timeout++;
        }
        wait(timeout);
    }

    public final void wait() throws InterruptedException {
        wait(0);
    }

    protected void finalize() throws Throwable { }
}

Object類中的12個方法

clone

​ clone方法可以完成物件的淺克隆。所謂淺克隆就是說被克隆的物件的各個屬性都是基本型別,而不是引用型別(介面、類、陣列),如果存在引用型別的屬性,則需要進行深克隆。要想實現 Address的深克隆,首先讓Address類實現 Cloneable 介面,重寫clone方法

之前的文章中寫過clone方法與深拷貝淺拷貝:Java clone() 方法克隆物件——深拷貝與淺拷貝

  protected native Object clone() throws CloneNotSupportedException;

equals

在引用型別中,"=="是比較兩個引用是否指向堆記憶體裡的同一個地址(同一個物件),而equals是一個普通的方法,該方法返回的結果依賴於自身的實現。要比較兩個物件的內容是否相同,需要重寫該方法。

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

finalize

​ finalize()是Object的protected方法,子類可以覆蓋該方法以實現資源清理工作,GC在回收物件之前呼叫該方法,當物件變成(GC Roots)不可達時,GC會判斷該物件是否覆蓋了finalize方法,若未覆蓋,則直接將其回收。否則,若物件未執行過finalize方法,將其放入F-Queue佇列,由一低優先順序執行緒執行該佇列中物件的finalize方法。執行finalize方法完畢後,GC會再次判斷該物件是否可達,若不可達,則進行回收,否則,物件“復活”。

protected void finalize() throws Throwable { }

getClass

​ Class是一個實實在在的類,在包 java.lang 下,有一個Class.java檔案,它跟我們自己定義的類一樣,是一個實實在在的類,Class物件就是這個Class類的例項。在Java裡,所有的類的根源都是Object類,而Class也不例外,它是繼承自Object的一個特殊的類,它內部可以記錄類的成員、介面等資訊,也就是在Java裡,Class是一個用來表示類的類。

public final native Class<?> getClass();

registerNatives

​ 在Object類中,除了有registerNatives這個本地方法之外,還有hashCode()、clone()等本地方法,而在Class類中有forName()這樣的本地方法等等。也就是說,凡是包含registerNatives()本地方法的類,同時也包含了其他本地方法。所以,顯然,當包含registerNatives()方法的類被載入的時候,註冊的方法就是該類所包含的除了registerNatives()方法以外的所有本地方法。使用native方法的好處:

  • 通過registerNatives方法在類被載入的時候就主動將本地方法連結到呼叫方,比當方法被使用時再由虛擬機器來定位和連結更方便有效;
  • 如果本地方法在程式執行中更新了,可以通過呼叫registerNative方法進行更新;
  • Java程式需要呼叫一個本地應用提供的方法時,因為虛擬機器只會檢索本地動態庫,因而虛擬機器是無法定位到本地方法實現的,這個時候就只能使用registerNatives()方法進行主動連結。

registerNatives是⽤ private 關鍵字宣告的,在類外⾯根本調⽤不了。靜態程式碼塊就是⼀個類在初始化過程中必定會執⾏的內容,所以在類載入的時候是會執⾏該⽅法的,通過該⽅法來註冊本地⽅法。

private static native void registerNatives();
static {
    registerNatives();
}

hashCode

​ hashCode就是物件的雜湊碼,是根據物件的某些資訊推匯出的一個整數值,預設情況下表示是物件的儲存地址。通過雜湊碼,可以提高檢索的效率,主要用於在雜湊儲存結構中快速確定物件的儲存地址,如Hashtable、hashMap中。

wait notify notifyAll

這三個方法最終呼叫的都是jvm級的final native方法

  • 如果物件呼叫了wait方法就會使持有該物件的執行緒把該物件的控制權交出去,然後處於等待狀態。
  • 如果物件呼叫了notify方法就會通知某個正在等待這個物件的控制權的執行緒可以繼續執行。
  • 如果物件呼叫了notifyAll方法就會通知所有等待這個物件控制權的執行緒繼續執行。

其中wait方法有三個over load方法:

  • wait()
  • wait(long)
  • wait(long,int)

wait方法通過引數可以指定等待的時長。如果沒有指定引數,預設一直等待直到被通知。

toString()

​ getClass().getName()是返回物件的全類名(包含包名),Integer.toHexString(hashCode()) 是以16進位制⽆符號整數形式返回此雜湊碼的字串表示形式。列印某個物件時,預設是調⽤ toString ⽅法,⽐如 System.out.println(object),等價於System.out.println(object.toString())。

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

相關文章