Java 基礎 | Object 源碼解析

點贊再看,養成習慣,公衆號搜一搜【一角錢技術】關注更多原創技術文章。本文 GitHub org_hejianhui/JavaStudy 已收錄,有個人系列文章。java

前言

Java 是一門面向對象的語言,在 Java 裏面一切均可以看做是一個對象,而 Java 裏面全部的對象都默認繼承於 Object 類。git

本文基於JDK1.8程序員

能夠看到一共12個方法,其中 registerNatives() 是 native 修飾的,一個本地方法,具體是用C(C++)在DLL中實現的,而後經過JNI調用。github

getClass

public final native Class<?> getClass();
複製代碼

getClass 也是個 native 方法,這個方法的做用就是返回某個對象的運行時類,它的返回值是 Class 類型,Class c = obj.getClass();經過對象 c ,咱們能夠獲取該對象的全部成員方法,每一個成員方法都是一個 Method 對象;咱們也能夠獲取該對象的全部成員變量,每一個成員變量都是一個 Field 對象;一樣的,咱們也能夠獲取該對象的構造函數,構造函數則是一個 Constructor 對象。這個方法在反射時會經常使用到。web

hashCode

public native int hashCode();
複製代碼
  • hashCode 方法返回散列值。
  • 返回值默認是由對象的地址轉換而來的。
  • 同一個對象調用 hashCode 的返回值是相等的。
  • 兩個對象的 equals 相等,那 hashCode 必定相等。
  • 兩個對象的 equals 不相等,那 hashCode 也不必定相等。

equals

public boolean equals(Object obj) {
    return (this == obj);
}
複製代碼

equals 的實現很是簡單,它的做用就是比較兩個對象是否相等,而比較的依據就是兩者的內存地址。除此以外,equals 還遵循如下幾個原則:編程

1、自反性:x.equals(x);  // true
2、對稱性:x.equals(y) == y.equals(x);  // true
3、傳遞性:if (x.equals(y) && y.equals(z))
            x.equals(z); // true;
4、一致性,只要對象沒有被修改,屢次調用 equals() 方法結果不變:
x.equals(y) == x.equals(y); // true 
5、非空性,對任何不是 null 的對象 x 調用 x.equals(null) 結果都爲 false :
x.equals(null); // false;
複製代碼

爲何要重寫 hashcode 和 equals ?

由於這兩個方法都跟對象的比較有關,因此若是在程序中要作對象比較,那大機率要重寫這兩個方法了。由於equals默認的比較邏輯就是對象的地址進行比較,兩個對象內存地址確定不一樣,因此不管如何兩個對象經過eqals比較確定返回false。markdown

但在實際編程中,咱們常常會遇到去重,或者將對象放到有序集合中,或者將對象存入無重複的集合中,這時若是沒有重寫equals和hashCode,則沒法達到需求。svg

舉個例子,系統中同時存在兩個對象,對象A和對象B,其姓名和身份證號如出一轍。此時,在系統內存中是兩個對象,但其內容一致分明是一我的同時產生了兩條重複信息。若是使用默認的equals方法比較,則這兩個對象永遠不相等,永遠不能比出來是一條相同的重複信息。因此,要重寫equals和hashCode方法來達到以上需求效果。函數

clone

protected native Object clone() throws CloneNotSupportedException;
複製代碼

clone() 是 Object 的 protected 方法,它不是 public,一個類不顯式去重寫 clone(),其它類就不能直接去調用該類實例的 clone() 方法。此外,Clone 的註釋中還提到比較重要的幾點:oop

  • 克隆的對象必需要實現 Cloneable 接口並重寫 clone 方法,不然會報 CloneNotSupportedException 異常
  • clone() 方法並非 Cloneable 接口的方法,而是 Object 的一個 protected 方法。Cloneable 接口只是規定,若是一個類沒有實現 Cloneable 接口又調用了 clone() 方法,就會拋出 CloneNotSupportedException。
  • 淺拷貝:拷貝對象和原始對象的引用類型引用同一個對象。
  • 深拷貝:拷貝對象和原始對象的引用類型引用不一樣對象。

關於淺拷貝與深拷貝咱們後面再討論。

toString

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

返回該對象的字符串表示,很是重要的方法

  • getClass().getName(); 獲取字節碼文件的對應全路徑名例如java.lang.Object;
  • Integer.toHexString(hashCode()); 將哈希值轉成16進制數格式的字符串。

 

wait 和 notify

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

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);
}
複製代碼

wait 的做用是讓當前線程進入等待狀態,同時,wait() 也會讓當前線程釋放它所持有的鎖。直到其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,當前線程被喚醒進入就緒狀態。

wait(long timeout) (以毫秒爲單位)讓當前線程處於等待(阻塞)狀態,直到其餘線程調用此對象的notify() 方法或 notifyAll() 方法,或者超過指定的時間量,當前線程被喚醒進入就緒狀態。

wait(long timeout, int nanos) 和 wait(long timeout) 功能同樣,惟一的區別是這個能夠提供更高的精度。總超時時間(以納秒爲單位)計算爲 1000000 *timeout+ nanos。By the way ,wait(0,0) 和 wait(0) 效果同樣。

public final native void notify();
public final native void notifyAll();
複製代碼

首先是 notify ,notify 的做用就是隨機喚醒在等待隊列的某個線程,而 notifyAll 就是喚醒在等待隊列的全部線程。

注意:notify 和 wait 方法的使用規範。意思就是這兩者必須在 synchronized 修飾的同步方法或同步代碼中使用。

Thread.sleep() 和 Object.wait() 有什麼區別?

首先,兩者均可以暫停當前線程,釋放 CPU 控制權。主要的區別在於 Object.wait()在釋放 CPU 同時,釋放了對象鎖的控制。而 Thread.sleep() 沒有對鎖釋放。換句話說 sleep 就是耍流氓,佔着茅坑不拉屎。

完整代碼

package java.lang;


public class Object {

    /** * 一個本地方法,具體是用C(C++)在DLL中實現的,而後經過JNI調用 */
    private static native void registerNatives();

    /** * 對象初始化時自動調用此方法 */
    static {
        registerNatives();
    }

    /** * 返回此Object的運行時類 */
    public final native Class<?> getClass();

    /** * hashCode的常規協定是: * 1.在java應用程序執行期間,在對同一對象屢次調用hashCode()方法時,必須一致地返回相同的整數,前提是將對象進行equals比較時所用的信息沒有被修改。 * 從某一應用程序的一次執行到同一應用程序的另外一次執行,該整數無需保持一致。 * 2.若是根據equals(object)方法,兩個對象是相等的,那麼對這兩個對象中的每一個對象調用hashCode方法都必須生成相同的整數結果。 * 3.若是根據equals(java.lang.Object)方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用hashCode()方法不要求必定生成不一樣的整數結果。 * 可是,程序員應該意識到,爲不相等的對象生成不一樣整數結果能夠提升哈希表的性能。 */
    public native int hashCode();

    /** * 這裏比較的是對象的內存地址 */
    public boolean equals(Object obj) {
        return (this == obj);
    }

    /** * 本地clone方法,用於對象的複製 */
    protected native Object clone() throws CloneNotSupportedException;

    /** * 返回該對象的字符串表示,很是重要的方法 * getClass().getName();獲取字節碼文件的對應全路徑名例如java.lang.Object * Integer.toHexString(hashCode());將哈希值轉成16進制數格式的字符串。 */
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    /** * 不能被重寫,用於喚醒一個在因等待該對象(調用了wait方法)被處於等待狀態(waiting 或 time_wait)的線程,該方法只能同步方法或同步塊中調用 */
    public final native void notify();

    /** * 不能被重寫,用於喚醒全部在因等待該對象(調用wait方法)被處於等待狀態(waiting或time_waiting)的線程,該方法只能同步方法或同步塊中調用 */
    public final native void notifyAll();

    /** * 不能被重寫,用於在線程調用中,致使當前線程進入等待狀態(time_waiting),timeout單位爲毫秒,該方法只能同步方法或同步塊中調用,超過設置時間後線程從新進入可運行狀態 */
    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);
    }

    /** * 在其餘線程調用此對象的notify()方法或notifyAll()方法前,致使當前線程等待。換句話說,此方法的行爲就好像它僅執行wait(0)調用同樣。 * 當前線程必須擁有此對象監視器。 * 該線程發佈對此監視器的全部權並等待,直到其餘線程經過調用notify方法或notifyAll方法通知在此對象的監視器上等待的線程醒來, * 而後該線程將等到從新得到對監視器的全部權後才能繼續執行。 */
    public final void wait() throws InterruptedException {
        wait(0);
    }

    /** * 這個方法用於當對象被回收時調用,這個由JVM支持,Object的finalize方法默認是什麼都沒有作,若是子類須要在對象被回收時執行一些邏輯處理,則能夠重寫finalize方法。 */
    protected void finalize() throws Throwable {
    }
}
複製代碼

PS:這裏有一個技術交流羣(扣扣羣:1158819530),方便你們一塊兒交流,持續學習,共同進步,有須要的能夠加一下。

文章持續更新,能夠公衆號搜一搜「 一角錢技術 」第一時間閱讀, 本文 GitHub org_hejianhui/JavaStudy 已經收錄,歡迎 Star。

相關文章
相關標籤/搜索