1. 程式人生 > 程式設計 >Java 基礎 | Object 原始碼解析

Java 基礎 | Object 原始碼解析

Object原始碼解析

Java 是一門面向物件的語言,在 Java 裡面一切都可以看作是一個物件,而 Java 裡面所有的物件都預設繼承於 Object 類,所以狗哥今天就複習了一遍這個類。

Object 方法概覽

上圖看出 Object 一共有 12 個方法,其中 registerNatives() 是由 C 語言實現的,這個不在研究範圍內。

1、getClass

/**
     * Returns the runtime class of this {@code Object}. The returned
     * {@code Class} object is the object that is locked by {@code
     * static synchronized} methods of the represented class.
*/
public final native Class<?> getClass();
複製程式碼

這個方法的作用就是返回某個物件的執行時類,它的返回值是 Class 型別,Class c = obj.getClass();通過物件 c ,我們可以獲取該物件的所有成員方法,每個成員方法都是一個 Method 物件;我們也可以獲取該物件的所有成員變數,每個成員變數都是一個 Field 物件;同樣的,我們也可以獲取該物件的建構函式,建構函式則是一個 Constructor 物件。這個方法在反射時會常用到。

2、hashCode

/**
     * Returns a hash code value for the object. This method is
     * supported for
the benefit of hash tables such as those provided by * {@link java.util.HashMap}. */ public native int hashCode(); 複製程式碼

這個方法的註釋比較長,就不放出來了。註釋指出:

  • hashCode 方法返回雜湊值。
  • 返回值預設是由物件的地址轉換而來的。
  • 同一個物件呼叫 hashCode 的返回值是相等的。
  • 兩個物件的 equals 相等,那 hashCode 一定相等。
  • 兩個物件的 equals 不相等,那 hashCode 也不一定相等。

3、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 ?

這個問題之前分享過舊文:mp.weixin.qq.com/s/iIoAnneae…

4、clone

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

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

  • 克隆的物件必須要實現 Cloneable 介面並重寫 clone 方法,否則會報 CloneNotSupportedException 異常
  • clone() 方法並不是 Cloneable 介面的方法,而是 Object 的一個 protected 方法。Cloneable 介面只是規定,如果一個類沒有實現 Cloneable 介面又呼叫了 clone() 方法,就會丟擲 CloneNotSupportedException。
  • 淺拷貝:拷貝物件和原始物件的引用型別引用同一個物件。
  • 深拷貝:拷貝物件和原始物件的引用型別引用不同物件。

關於淺拷貝與深拷貝的詳解,請看這篇舊文: mp.weixin.qq.com/s/I6Gq1shyf…

5、toString

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

這個方法應該沒什麼好講的,原生的 toString 方法僅僅返回,物件名 + 它的 hashCode ,但做過開發的都知道,原生的 toString 作用不大。我們需要重寫 toString 一般是因為方便除錯,需要知道物件的屬性值,而不僅僅是 hashCode 。所以,應該像下面這樣重寫:

public class Student {

    private int age;

    private String name;

    // 省略 get、set

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ",name='" + name + '\'' +
                '}';
    }
}
複製程式碼

6、notify 和 wait

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

首先是 notify ,註釋就不貼出來了,notify 的作用就是隨機喚醒在等待佇列的某個執行緒,而 notifyAll 就是喚醒在等待佇列的所有執行緒。

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 的作用是讓當前執行緒進入等待狀態,同時,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) 效果一樣。

除此之外,notify 和 wait 的註釋中還有這麼一段:

* <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. A thread becomes the owner of the
     * object's monitor in one of three ways:
     * <ul>
     * <li>By executing a synchronized instance method of that object.
     * <li>By executing the body of a {@code synchronized} statement
     *     that synchronizes on the object.
     * <li>For objects of type {@code Class,} by executing a
     *     synchronized static method of that class.
     * </ul>
* <p>
複製程式碼

看到這英文,剛過四級的我瑟瑟發抖。以上註釋主要就是描述了,notify 和 wait 方法的使用規範。意思就是這二者必須在 synchronized 修飾的同步方法或同步程式碼中使用。

  • 為什麼 wait() 必須在同步 (Synchronized) 方法/程式碼塊中呼叫?

答:呼叫 wait() 就是釋放鎖,釋放鎖的前提是必須要先獲得鎖,先獲得鎖才能釋放鎖。

  • 為什麼 notify()、notifyAll() 必須在同步 (Synchronized) 方法/程式碼塊中呼叫?

答:notify()、notifyAll() 是將鎖交給含有 wait() 方法的執行緒,讓其繼續執行下去,如果自身沒有鎖,怎麼叫把鎖交給其他執行緒呢?(本質是讓處於入口佇列的執行緒競爭鎖)

詳細解釋請參考這篇博文:blog.csdn.net/qq_42145871…

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

首先,二者都可以暫停當前執行緒,釋放 CPU 控制權。主要的區別在於 Object.wait()在釋放 CPU 同時,釋放了物件鎖的控制。而 Thread.sleep() 沒有對鎖釋放。換句話說 sleep 就是耍流氓,佔著茅坑不拉屎。

推薦閱讀:

1、java | 什麼是動態代理

2、SpringBoot | 啟動原理

3、SpringBoot | 自動配置原理

一個優秀的廢人