JDK1.8源碼閱讀——Object類

1、Object類的結構java

    上圖爲Object類的結構樹,由此能夠清晰的看到整個Object的架構。其中我的通過搜索、平常開發的總結,認爲Object、clone、equals(Object)、hashCode、getClass、toString這幾個方法相對重要(僅屬我的意見,若有不一樣之見,歡迎討論)。可能有人認爲notify、wait等線程有關的方法也很重要,可是從我的角度出發,我認爲這些方法更應該放在線程裏去研究和討論。程序員

2、native方法介紹面試

    咱們都知道,Java的底層是經過C、C++等語言實現的,那麼Java是如何區分這些方法並能準確地去調用的呢?架構

2.1 native關鍵字ide

    在Java中,若是一個方法使用native關鍵字來修飾,即代表該方法並非由Java實現的,它是由non-java即C、C++負責實現。其具體的實現方法被編譯在了dll文件中,由Java調用。由native修飾的方法有:測試


2.2  registerNatives()this

    registerNatives(),顧名思義,註冊native修飾的方法,其做用是將C、C++等方法映射到Java中由native修飾的方法,這也是Java爲什麼能作到準確地去調用non-java方法。其源碼以下:.net

private static native void registerNatives();
    能夠看到,這裏並無執行此方法。Java使用的是靜態代碼塊去執行registerNatives(),源碼以下:線程

static {
    registerNatives();
}
2.3   clone()code

protected native Object clone() throws CloneNotSupportedException;
    經過上述源碼,咱們知道,clone不是Java原生的方法,且Object提供的複製是淺複製,不是深度複製。

    淺複製是指只複製對象的引用,而深度複製則是將原來複制的對象完徹底全的複製出來,此時被複制的對象與複製出來的對象已經沒有任何關係,歸納起來就是,淺複製複製引用與值,引用與值都不變;深度複製複製值,引用變值不變。

    能夠看到,clone方法顯式拋出不支持複製的異常,這說明,實現對象的複製是有條件的。

    1)  方法由protected修飾,說明若要實現複製,須要繼承Object(默認都是繼承Object的...)

    2)返回類型爲Object,代表若要獲得咱們想要複製的結果須要進行類型轉換

    3)實現Cloneable接口,不然會拋出不支持複製的異常

    如下爲一段測試clone的代碼:

class Son { 
    
    private Integer sonAge;
    
    private String sonName;
 
    public Son(Integer sonAge, String sonName) {
        super();
        this.sonAge = sonAge;
        this.sonName = sonName;
    }
 
    public Son() {
        super();
    }
 
    public Integer getSonAge() {
        return sonAge;
    }
 
    public void setSonAge(Integer sonAge) {
        this.sonAge = sonAge;
    }
 
    public String getSonName() {
        return sonName;
    }
 
    public void setSonName(String sonName) {
        this.sonName = sonName;
    }
}
public class Parent {
 
    public static void main(String[] args) throws CloneNotSupportedException {
        
        Son son = new Son(10,"清然");
        
        Parent parent = new Parent(20,"安然",son);
        Parent temp1 = parent.clone();
    }
    
    private Integer age;
    
    private String name;
    
    private Son son;
    
    public Parent(Integer age, String name, Son son) {
        super();
        this.age = age;
        this.name = name;
        this.son = son;
    }
 
    public Parent() {
        super();
    }
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Son getSon() {
        return son;
    }
 
    public void setSon(Son son) {
        this.son = son;
    }
    
}
   此時編譯器提示須要進行類型轉換:

   完成類型轉換後,提示須要對異常進行處理,即CloneNotSupportedException:

    將異常拋出後,沒有發現任何編譯錯誤,咱們運行main方法,控制檯出現以下錯誤:

Exception in thread "main" java.lang.CloneNotSupportedException: jdkreader.java.lang.object.Parent
    at java.lang.Object.clone(Native Method)
    at jdkreader.java.lang.object.Parent.main(Parent.java:16)
   異常顯示Parent並不支持clone,此時咱們須要實現Cloneable接口:

public class Parent implements Cloneable
   咱們能夠看一下複製後的兩個對象的關係:

Son son = new Son(10,"清然");
        
Parent parent = new Parent(20,"安然",son);
Parent temp1 = (Parent) parent.clone();
Parent temp2 = (Parent) parent.clone();
        
System.out.println("temp1 == temp2 : " + (temp1 == temp2));
    運行結果爲:

temp1 == temp2 : false
    很明顯,結果爲false,由於複製出來是一個新的對象,引用不一樣。咱們再對他們的各個變量進行一一比較:

System.out.println("temp1Age == temp2Age : " + (temp1.getAge() == temp2.getAge()));
System.out.println("temp1Name == temp2Name : " + (temp1.getName() == temp2.getName()));
System.out.println("temp1Son == temp2Son : " + (temp1.getSon() == temp2.getSon()));
    運行結果爲:

temp1Age == temp2Age : true
temp1Name == temp2Name : true
temp1Son == temp2Son : true
   很奇怪,他們的內部屬性卻所有是相同的,這是爲何呢?

   由於Object提供的clone是淺複製,若是是基本類型,則複製其值,若是是引用內容,則複製其引用。因此二者指向的地址是相同的,故相等。

    2.4  getClass

public final native Class<?> getClass();
    此方法返回的是運行時類對象,這點從註釋可明顯看出:

Returns the runtime class of this {@code Object}.
    什麼是類對象?咱們知道,在Java中,一切皆對象。在Java中,類是是對具備一組相同特徵或行爲的實例的抽象並進行描述,對象則是此類所描述的特徵或行爲的具體實例。做爲概念層次的類,其自己也具備某些共同的特性,如都具備類名稱、由類加載器去加載,都具備包,具備父類,屬性和方法等。因而,Java中有專門定義了一個類,Class,去描述其餘類所具備的這些特性。

  2.5  hashCode

public native int hashCode();
    這也是由non-java實現的方法,返回的是一個對象的散列值,類型爲int。通常狀況下,在當前程序的運行期間,一個對象屢次調用hashCode方法,其返回的散列值是相同的。這裏須要注意的是:

    若兩個對象相等,則它們的散列值必定相等,反之,散列值相等,兩個對象不必定相等;

    若兩個對象不相等,則它們的散列值不必定相等,反之,散列值不一樣,兩個對象必定不相等。

    在Java中,有許多地方都應用到了hash,如集合set、map,equals等,這裏不作過多研究。

2.6  equals

public boolean equals(Object obj) {
     return (this == obj);
}
    關於「==」與equals的區別,面試的時候多數人都被問到過。咱們知道「==」比較基本數據類型時,比較的是值,當比較對象時,比較的是其引用;而equals比較的是兩個對象是否相等。

    在Object類中,equals與「==」實際上是等價的,可是咱們在不少狀況下是須要重寫equals方法的,例如比較兩個學生是否爲同一我的,咱們直接使用equals方法確定是不可行的。一般狀況下,咱們認爲若是學號相同,那麼這兩個學生就是同一我的。重寫的示例以下:

    Student類

/**
 * 用於測試重寫Object類equals方法
 * 
 * @author xuyong
 *
 */
public class Student {
 
    private String no;        //學號
    
    private String name;    //姓名
 
    public Student(String no, String name) {
        super();
        this.no = no;
        this.name = name;
    }
 
    public Student() {
        super();
    }
 
    public String getNo() {
        return no;
    }
 
    public void setNo(String no) {
        this.no = no;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
    測試類

Student stu1 = new Student("1", "路人甲");
Student stu2 = new Student("1", "路人乙");
System.out.println(stu1.equals(stu2));
    運行,控制檯打印false。此時,咱們重寫一下Student類的equals方法:

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Student) {
            Student stu = (Student)obj;
            return no.equals(stu.getNo());
        }
        return super.equals(obj);    
    }
    再次運行剛剛的代碼,控制檯打印true。因而,咱們便實現了經過學號判斷是否爲同一我的的業務。

    不過,騷年們,覺得這就結束了嗎?NO!

    因爲Java須要維護hash的通常規律,沒錯就是剛剛標紅的內容:兩個對象相等,那麼他們的散列值必須相等。

    可是此時,咱們測試一下他們的hash是否相等,能夠明顯發現,他們是不相等的。

System.out.println(stu1.hashCode() == stu2.hashCode());
    因此,咱們在重寫equals方法時,必需要重寫hashCode方法,因爲咱們是經過學號判斷的,因此最好也是使用學號的散列值替代原有的散列值:

    @Override
    public int hashCode() {
        return no.hashCode();
    }
    此時再運行方法,就會發現返回的是true了。

2.7  finalize

protected void finalize() throws Throwable { } 該方法用於垃圾回收,通常由 JVM 自動調用,通常不須要程序員去手動調用該方法。 ———————————————— 版權聲明:本文爲CSDN博主「t1heluosh1」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/PostersXu/article/details/81947715

相關文章
相關標籤/搜索