Java Object類方法解析

Java Object類方法解析

在Java中Object是所有類的父類,任何類都默認繼承Object,其提供的方法主要有以下幾種:

  • registerNatives()

  • hashCode和equale函數用來判斷對象是否相同,

  • wait(),wait(long),wait(long,int),notify(),notifyAll()

  • toString()和getClass

  • clone()

  • finalize()用於在垃圾回收

下面介紹下各個方法:

registerNatives()

registerNatives函數前面有native關鍵字修飾,Java中,用native關鍵字修飾的函數表明該方法的實現並不是在Java中去完成,而是由C/C++ 去完成,並被編譯成了.dll,由Java去調用。方法的具體實現體在dll文件中,對於不同平臺,其具體實現應該有所不同。用native修飾,即表示操作系統,需要提供此方法,Java本身需要使用。具體到registerNatives()方法本身,其主要作用是將C/C++中的方法映射到Java中的native方法,實現方法命名的解耦。
在Java源碼中,此方法在靜態代碼塊中被調用:

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

equales

equales經常跟“= =”做比較,兩者區別是:
“= =”表示的是變量值完全相同(對於基礎類型,地址中存儲的是值,引用類型則存儲指向實際對象的地址,所以基本數據類型:“= =”比較的是他們的值。引用類型(類、接口、數組) :用 “= =” 進行比較的時候,比較的是他們在內存中的存放地址,除非是同一個new出來的對象,他們的比較後的結果爲true,否則比較後結果爲false。)
我們知道對象在JVM中是存放放在Java堆中的,Java虛擬機棧中存放的是對象的引用(地址)。由此可見 “= =” 是對Java虛擬機棧中的值進行比較的。如果要比較Java堆中對象的內容是否相同,那麼就需要使用equals方法了。
equals表示的是對象的內容完全相同,此處的內容多指對象的特徵/屬性。 Object類中關於equals()方法的定義如下:

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

Object原生的equals()方法內部調用的正是= =,與= =具有相同的含義。既然如此,爲什麼還要定義此equals()方法?
equlas()方法的正確理解應該是:判斷兩個對象是否相等。那麼判斷對象相等的標尺又是什麼?
如上,在object類中,此標尺即爲==。當然,這個標尺不是固定的,其他類中可以按照實際的需要對此標尺含義進行重定義。如String類中則是依據字符串內容是否相等來重定義了此標尺含義。如此可以增加類的功能性和實際編碼的靈活性。當然了,如果自定義的類沒有重寫equals()方法來重新定義此標尺,那麼默認的將是其父類的equals(),直到object基類。
String 類的equals方法:

    public boolean equals(Object var1) {
        if (this == var1) {
            return true;
        } else {
            if (var1 instanceof String) {
                String var2 = (String)var1;
                int var3 = this.value.length;
                if (var3 == var2.value.length) {
                    char[] var4 = this.value;
                    char[] var5 = var2.value;

                    for(int var6 = 0; var3-- != 0; ++var6) {
                        if (var4[var6] != var5[var6]) {
                            return false;
                        }
                    }

                    return true;
                }
            }

            return false;
        }
    }

String中equals方法判斷相等的步驟是:
1).若A==B 即是同一個String對象 返回true
2).若對比對象是String類型則繼續,否則返回false
3).判斷A、B長度是否一樣,不一樣的話返回false
4).逐個字符比較,若有不相等字符,返回false
注:Java中約定重寫equals()方法必須重寫hasCode()方法。

最後總結下:

==比較的是“值”(引用數據類型是比較其內存地址值,基本數據類型是比較變量值)

equals比較的是對象的內容,對內容比較的標尺可以由子類自己定義。

hashCode

hashCode()方法返回一個整形數值,表示該對象的哈希碼值。
hashCode()具有如下約定:
1).在Java應用程序程序執行期間,對於同一對象多次調用hashCode()方法時,其返回的哈希碼是相同的,前提是將對象進行equals比較時所用的標尺信息未做修改。在Java應用程序的一次執行到另外一次執行,同一對象的hashCode()返回的哈希碼無須保持一致;
2).如果兩個對象相等(依據:調用equals()方法),那麼這兩個對象調用hashCode()返回的哈希碼也必須相等;
3).反之,兩個對象調用hasCode()返回的哈希碼相等,這兩個對象不一定相等。
即嚴格的數學邏輯表示爲: 兩個對象相等 <=> equals()相等 => hashCode()相等。因此,重寫equlas()方法必須重寫hashCode()方法,以保證此邏輯嚴格成立,同時可以推理出:hasCode()不相等 => equals()不相等 <=> 兩個對象不相等。
可能有人在此產生疑問:既然比較兩個對象是否相等的唯一條件(也是充要條件)是equals,那麼爲什麼還要弄出一個hashCode(),並且進行如此約定,弄得這麼麻煩?
其實,這主要體現在hashCode()方法的作用上,其主要用於增強哈希表的性能。
以集合類中的Set爲例,當新加一個對象時,需要判斷現有集合中是否已經存在與此對象相等的對象,如果沒有hashCode()方法,需要將Set進行一次遍歷,並逐一用equals()方法判斷兩個對象是否相等,此種算法時間複雜度爲o(n)。通過藉助於hasCode方法,先計算出即將新加入對象的哈希碼,然後根據哈希算法計算出此對象的位置,直接判斷此位置上是否已有對象即可。(注:Set的底層用的是Map的原理實現)
在此需要糾正一個理解上的誤區:對象的hashCode()返回的不一定是對象所在的物理內存地址。甚至也不一定是對象的邏輯地址,hashCode()相同的兩個對象,不一定相等,換言之,不相等的兩個對象,hashCode()返回的哈希碼可能相同。
小結:
1).如果兩個對象equals,Java運行時環境會認爲他們的hashcode一定相等。
2).如果兩個對象不equals,他們的hashcode有可能相等。
3).如果兩個對象hashcode相等,他們不一定equals。
4).如果兩個對象hashcode不相等,他們一定不equals。
爲了比較兩個對象時更高效,判斷兩個對象是否相等可以採用以下規則:
第一步,如果hashCode()相等,則查看第二步,否則不相等;
第二步,查看equals()是否相等,如果相等,則兩obj相等,否則還是不相等。

wait(),wait(long),wait(long,int),notify(),notifyAll()

一說到wait(…) / notify() /notifyAll()這幾個方法,首先想到的是線程。確實,這幾個方法主要用於java線程之間的通信協作。先具體看下這幾個方法的主要含義:
1、wait(…)方法調用後當前線程將立即阻塞,且釋放其所持有的同步代碼塊中的鎖,直到被喚醒或超時或打斷後且重新獲取到鎖後才能繼續執行;
2、notify()/notifyAll()方法調用後,其所在線程不會立即釋放所持有的鎖,直到其所在同步代碼塊中的代碼執行完畢,此時釋放鎖,因此,如果其同步代碼塊後還有代碼,其執行則依賴於JVM的線程調度。

注:wait(…) / notify() / notifyAll()一般情況下都是配套使用。且wait(…)/notify()|notifyAll()方法只能在同步代碼塊中才能使用。

爲什麼Object這個基類會提供這類方法?因爲定義在Object中就可以實現將任何對象視爲線程同步中的監聽器。

對於wait和notify方法我們在Java線程部分還會再次分析。

toString()

toString()方法返回該對象的字符串表示。先看一下Object中的具體方法體:

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

toString()方法相信大家都經常用到,即使沒有顯式調用,但當我們使用System.out.println(obj)時,其內部也是通過toString()來實現的。

Object類的toString()方法返回的是對象的類型和其哈希碼,子類可以重寫該方法以打印自己想要打印的內容。

getClass

getClass()是一個native方法,返回的是此Object對象的類對象/運行時類對象Class<?>。
首先解釋下”類對象”的概念:在Java中,類是是對具有一組相同特徵或行爲的實例的抽象描述,對象則是此類所描述的特徵或行爲的具體實例。作爲概念層次的類,其本身也具有某些共同的特性,如都具有類名稱、由類加載器去加載,都具有包,具有父類,屬性和方法等。於是,Java中有專門定義了一個類,Class類,去描述其他類所具有的這些特性,從此角度去看,類本身是屬於Class類的對象,我們稱之爲”類對象”。

clone()

clone()方法同樣是native方法,因此,我們知道了clone()方法並不是Java的原生方法,具體的實現是有C/C++完成的。clone英文翻譯爲”克隆”,其目的是創建並返回此對象的一個副本。Java術語表述爲:clone函數返回的是一個引用,該引用指向的是新的clone出來的對象,此對象與原對象分別佔用不同的堆空間。
同時Java中的語法規定:
clone()的正確調用是需要實現Cloneable接口,如果沒有實現Cloneable接口,並且子類直接調用Object類的clone()方法,會拋出CloneNotSupportedException異常。
Cloneable接口僅是一個表示接口,接口本身不包含任何方法,用來指示Object.clone()可以合法的被子類引用所調用。

finalize()

finalize方法主要與Java垃圾回收機制有關。首先我們看一下finalized方法在Object中的具體定義:

    protected void finalize() throws Throwable {
    }

我們發現Object類中finalize方法被定義成一個空方法,爲什麼要如此定義呢?finalize方法的調用時機是怎麼樣的呢?
首先,Object中定義finalize方法表明Java中每一個對象都將具有finalize這種行爲,其具體調用時機在:JVM準備對此對象所佔用的內存空間進行垃圾回收前,將會調用其finalize。由此可以看出,此方法並不是由我們主動去調用的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章