java.lang.Object 類詳解

簡述

Object 方法包括 5 個(gè)非 final 類型的方法,分別是:clone、hashCode、equals、toString、finalize 方法;包括 4 個(gè) final 方法,分別是 getClass、wait、notify、notifyAll 方法。其中 clone 方法是 protected 方法,finalize 方法自 Java 9 之后被廢棄。

1. clone 方法

1.1 源碼

@HotSpotIntrinsicCandidate protected native Object clone() throws CloneNotSupportedException;

說明:調(diào)用該方法實(shí)現(xiàn)一個(gè)對(duì)象的淺復(fù)制,創(chuàng)建并且返回此對(duì)象的副本(“副本”的準(zhǔn)確含義可能依賴于對(duì)象的類)。 Object.clone() 方法是一個(gè) protected 方法,類只有實(shí)現(xiàn) java.lang.Cloneable 接口,并重寫 Object.clone() 方法才能使用該 clone 方法,否則拋出 CloneNotSupportedException 。

1.2 clone 與 copy 的區(qū)別

假設(shè)我們有一個(gè) Person 對(duì)象,并假設(shè) Person 類實(shí)現(xiàn)了Cloneable 接口并重寫了 clone 方法。

Person a = new Person();
//copy 的做法通常為:
Person b = a;
//clone 的做法通常為:
Person c = a.clone();

說明:
(1)copy 是將對(duì)象 a 的引用賦值給對(duì)象 b,賦值之后對(duì)象 a 和對(duì)象 b 都指向同一個(gè)引用 a。
(2)clone 是實(shí)現(xiàn)對(duì)象的淺拷貝,產(chǎn)生一個(gè)新的對(duì)象,對(duì)象 c 與 對(duì)象 a 不指向同一個(gè)引用。

clone 在內(nèi)存中實(shí)際操作是:將對(duì)象 a 的內(nèi)存,拷貝一個(gè)副本,并重新分配一塊內(nèi)存區(qū)域用于保存副本。

1.3 淺拷貝和深拷貝

前面我們提到,clone 在內(nèi)存中的實(shí)際操作時(shí)將一個(gè)對(duì)象的內(nèi)存拷貝出一個(gè)副本、并重新分配一個(gè)內(nèi)存用于保存副本。由于原對(duì)象(被拷貝的對(duì)象)屬性可能存在兩種值傳遞類型,分別是值傳遞和引用傳遞,對(duì)副本的操作可能對(duì)原對(duì)象造成影響(改變副本的引用傳遞屬性的值,由于引用傳遞導(dǎo)致原對(duì)象的響應(yīng)值同樣改變)。

如果一個(gè)對(duì)象包含引用傳遞類型的屬性,直接拷貝對(duì)象,不做特殊處理,這種拷貝稱為淺拷貝。若一個(gè)對(duì)象不存在引用傳遞類型的數(shù)據(jù),那也就不區(qū)別什么淺拷貝和深拷貝,可以稱為淺拷貝也可以稱為深拷貝。

示例:

定義2個(gè)類:Person類 包含 Book屬性

public class Person implements Cloneable {

    private String name;
    private Book book;
    
    //省略 constructor 、 getter 、 setter 方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", book=" + book +
                '}';
    }
}
public class Book {

    private String bookName;
    private String author;
    
    //省略 constructor 、 getter 、 setter 方法

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}
public class Main {
    //主方法:
    public static void main(String[] args) throws Exception {
        Book effectiveJava = new Book("Effective Java", "Joshua Bloch");
        Person sungm = new Person("sungm", effectiveJava);
        Person sunhw = (Person) sungm.clone();

        System.out.println("對(duì)象 sungm 的信息:" + sungm);
        System.out.println("對(duì)象 sunhw 的信息:" + sunhw);
        System.out.println("對(duì)象 sungm 與對(duì)象 sunhw 是否相等:" + (sungm == sunhw));

        System.out.println("-----------------------------------------");

        //改變對(duì)象 sunhw 的 book 屬性
        sunhw.getBook().setBookName("Vue.js");
        sunhw.getBook().setAuthor("尤雨溪");
        System.out.println("對(duì)象 sunhw 的信息:" + sunhw);
        System.out.println("對(duì)象 sungm 的信息:" + sungm);
    }
}
/* 輸出結(jié)果 */
對(duì)象 sungm 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
對(duì)象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
對(duì)象 sungm 與對(duì)象 sunhw 是否相等:false
-----------------------------------------
對(duì)象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Vue.js', author='尤雨溪'}}
對(duì)象 sungm 的信息:Person{name='sungm', book=Book{bookName='Vue.js', author='尤雨溪'}}

從輸出結(jié)果可以看出:對(duì)象 sunhw 改變了的屬性 book 的內(nèi)容, 對(duì)對(duì)象 sungm 造成了影響。因?yàn)閮蓚€(gè)對(duì)象的 book 屬性保存的是同一個(gè)引用,造成這種差異是由于對(duì)象進(jìn)行了淺拷貝。

進(jìn)行深拷貝示例:

Person implements Cloneable {

    private String name;
    private Book book;

    //省略 constructor 、 getter 、 setter 方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.book = (Book) book.clone();
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", book=" + book +
                '}';
    }
}
class Book implements Cloneable{

    private String bookName;
    private String author;

    //省略 constructor 、 getter 、 setter 方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}
public class Main {
    public static void main(String[] args) throws Exception {
        Book effectiveJava = new Book("Effective Java", "Joshua Bloch");
        Person sungm = new Person("sungm", effectiveJava);
        Person sunhw = (Person) sungm.clone();

        System.out.println("對(duì)象 sungm 的信息:" + sungm);
        System.out.println("對(duì)象 sunhw 的信息:" + sunhw);
        System.out.println("對(duì)象 sungm 與對(duì)象 sunhw 是否相等:" + (sungm == sunhw));

        System.out.println("-----------------------------------------");

        //改變對(duì)象 sunhw 的 book 屬性
        sunhw.getBook().setBookName("Vue.js");
        sunhw.getBook().setAuthor("尤雨溪");
        System.out.println("對(duì)象 sunhw 的信息:" + sunhw);
        System.out.println("對(duì)象 sungm 的信息:" + sungm);
    }
}
/*運(yùn)行結(jié)果*/
對(duì)象 sungm 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
對(duì)象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}
對(duì)象 sungm 與對(duì)象 sunhw 是否相等:false 
-----------------------------------------
對(duì)象 sunhw 的信息:Person{name='sungm', book=Book{bookName='Vue.js', author='尤雨溪'}}
對(duì)象 sungm 的信息:Person{name='sungm', book=Book{bookName='Effective Java', author='Joshua Bloch'}}

說明:進(jìn)行深拷貝一般有2中方式
(1)將屬性對(duì)象實(shí)現(xiàn) java.lang.Cloneable 接口并重寫 clone 方法,然后在原始類中修改 clone方法。
(2)實(shí)現(xiàn) java.io.Serializable 接口,通過序列化和反序列號(hào)拷貝對(duì)象。

2. hashCode 方法

返回對(duì)象的 Hash 值 (也稱散列碼)。對(duì)象的散列碼是為了更好的支持基于哈希機(jī)制的 Java 集合類,例如:HashMap、HashSet、HashTable。

2.1 通用約定

(1)在 Java 程序執(zhí)行期間,多次調(diào)用該方法應(yīng)該返回相同的值,前提是未修改在 equals 方法中使用的信息。
(2)如果 2 個(gè)對(duì)象通過 equals 方法判定為 2 個(gè)對(duì)象相等、那么他們返回的 Hash 值也應(yīng)該相等。
(3)對(duì)于 2 個(gè)對(duì)象來說,如果使用 equals 方法返回 false,那么這兩個(gè)對(duì)象的 hashCode 值不要求一定不同(可以相同,可以不同),但是如果不同則可以提高應(yīng)用的性能。
(4)對(duì)于 Object 類來說,不同 Object 對(duì)象的 hash 值是不同的、其 hash 值返回的是內(nèi)存地址。

說明:鑒于第 (2) 條約定,如果重寫了 equals 方法,那就要求重寫 hashCode 方法。

3. equals 方法

判斷兩個(gè)對(duì)象是否相等。僅當(dāng)兩個(gè)對(duì)象引用的是同一個(gè)內(nèi)存地址,即同一個(gè)對(duì)象,該方法返回 true。若不滿足指向同一個(gè)內(nèi)存地址、即使兩個(gè)對(duì)象的內(nèi)容相同,也會(huì)返回 false。

源碼:

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

規(guī)則:
(1)自反性:對(duì)于任意非空對(duì)象, x.equals(x) 應(yīng)該返回 true
(2)對(duì)稱性:對(duì)于任意非空對(duì)象,若 x.equals(y) 返回 true,則 y.equals(x) 也應(yīng)該返回 true
(3)傳遞性:對(duì)于任意非空對(duì)象,若 x.equals(y) 返回 true、y.equals(z) 也返回 true ,則 x.equals(z) 也應(yīng)該返回 true
(4)一致性:若 x.equals(y) 返回 true,那第二次、第三次調(diào)用也應(yīng)該返回 true,前提是未修改兩個(gè)對(duì)象。

4. toString 方法

返回對(duì)象的字符串表現(xiàn)形式(類全名及無符號(hào)十六進(jìn)制的 Hash 值)。API 建議所有的子類都重寫該方法。

源碼:

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

5. finalize 方法

該方法自 Java 9 之后被廢棄。

源碼:

@Deprecated(since="9") protected void finalize() throws Throwable { }

說明:該方法并非一兩句話能解釋清楚,這里引入一篇博客、可供參考學(xué)習(xí)。http://www.itdecent.cn/p/9d2788fffd5f

6. getClass 方法

返回運(yùn)行時(shí)該對(duì)象的 class 對(duì)象,返回的 class 對(duì)象是被表示對(duì)象的類的 static synchronized 方法鎖定的對(duì)象。

該方法一般常見于反射技術(shù)。

7. wait 方法

導(dǎo)致當(dāng)前線程等待,可設(shè)置等待的毫秒數(shù),知道其他線程調(diào)用 notify 方法或者調(diào)用該對(duì)象的 notifyAll 方法喚醒該線程。

源碼:

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

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

8. notify 方法

喚醒正在此對(duì)象的監(jiān)聽器上等待的單個(gè)線程。如果該對(duì)象的監(jiān)聽器等待的線程存在多個(gè)、則喚醒其中一個(gè)線程,該線程的喚醒是隨機(jī)的。

源碼:

@HotSpotIntrinsicCandidate public final native void notify();

9. notifyAll 方法

喚醒正在此對(duì)象的監(jiān)聽器上等待的所有線程。

源碼:

@HotSpotIntrinsicCandidate public final native void notifyAll();
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 九種基本數(shù)據(jù)類型的大小,以及他們的封裝類。(1)九種基本數(shù)據(jù)類型和封裝類 (2)自動(dòng)裝箱和自動(dòng)拆箱 什么是自動(dòng)裝箱...
    關(guān)瑋琳linSir閱讀 2,049評(píng)論 0 47
  • 一、基礎(chǔ)知識(shí):1、JVM、JRE和JDK的區(qū)別:JVM(Java Virtual Machine):java虛擬機(jī)...
    殺小賊閱讀 2,553評(píng)論 0 4
  • 所有知識(shí)點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,700評(píng)論 1 4
  • 本文源碼都基于JDK1.8 概述 Java是一門面向?qū)ο蟮木幊陶Z言,在Java的世界里,萬物皆對(duì)象。而Object...
    長大后簡單很幸福_f63e閱讀 1,350評(píng)論 0 2
  • 對(duì)象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對(duì)象:僅僅是創(chuàng)建對(duì)象的方法,并非Fa...
    孫小磊閱讀 2,177評(píng)論 0 3

友情鏈接更多精彩內(nèi)容