1. 比較Java中的對象
在 Java 中,對象可以通過引用和值進行比較。
比較參考資料
如果兩個變量在內存中指向同一個對象,那麼這些變量中存儲的引用是相等的。如果使用相等運算符 ( ==) 比較這些變量,結果為真,並且該結果有意義。這裡的一切都很簡單。
| 代碼 | 控制台輸出 |
|---|---|
|
|
按價值比較
但是您經常會遇到兩個變量引用兩個相同的不同對象的情況。例如,包含相同文本的兩個不同字符串對象。
要確定不同的對像是否相同,請使用equals()方法。例如:
| 代碼 | 控制台輸出 |
|---|---|
|
|
該equals方法不限於String類。每個班級都有。
甚至是您自己編寫的類,這就是原因。
2.Object類
Java 中的所有類都繼承該類Object。Java 的創建者想出了這種方法。
而如果一個類繼承了該類Object,那麼它就獲得了該類的所有方法Object。這是繼承的主要結果。
換句話說,每個類都有Object類的方法,即使他們的代碼沒有提到它們。
這些繼承的方法包括與對像比較相關的方法。這些是equals()和hashCode()方法。
| 代碼 | 實際上,這就是我們將擁有的: |
|---|---|
|
|
在上面的示例中,我們創建了一個Person帶有 name 和 age 參數的簡單類,但沒有一個方法。但是因為所有的類都繼承了這個Object類,所以這個Person類自動有兩個方法:
| 方法 | 描述 |
|---|---|
|
比較當前對象和傳遞的對象 |
|
返回當前對象的哈希碼 |
事實證明,絕對每個對像都有equals方法,不同類型的對象可以相互比較。這樣的代碼將編譯並完美運行。
| 代碼 | 控制台輸出 |
|---|---|
|
|
|
|
3.equals()方法
該equals()方法繼承自Object該類,實現了比較當前對象與傳遞的對象的最簡單算法:它只是比較對對象的引用。
Person如果您只是比較變量而不是調用方法,您會得到相同的結果equals()。例子:
| 代碼 | 控制台輸出 |
|---|---|
|
|
當equals調用該方法時a,它只是將存儲在變量中的引用a與存儲在b變量中的引用進行比較。
但是,類的比較工作不同String。為什麼?
因為創建該類的人String編寫了他們自己的方法實現equals()。
equals()方法的實現
現在讓我們在類中編寫我們自己的 equals 方法的實現Person。我們將考慮 4 個主要案例。
equals方法,它總是以一個
Object對像作為參數
場景 1:調用該方法的同一對equals像也傳遞給該equals方法。如果當前對象和傳遞對象的引用相等,則該方法必須返回true。一個對像等於它自己。
在代碼中它看起來像這樣:
| 代碼 | 描述 |
|---|---|
|
比較參考資料 |
場景 2:null傳遞給equals方法——我們沒有什麼可比較的。調用該方法的對象equals肯定不為null,所以我們需要false在這種情況下返回。
在代碼中它看起來像這樣:
| 代碼 | 描述 |
|---|---|
|
比較引用 是傳遞過來的對象 null嗎? |
場景 3:將對不是 a 的對象的引用傳遞Person給該equals方法。對像是否Person等於非Person對象?這是一個由課程開發者Person決定的問題,無論他或她想要什麼。
但通常對象必須屬於同一類才能被視為相等。因此,如果類的對像以外的東西Person被傳遞給我們的 equals 方法,那麼我們將始終返回false。如何檢查對象的類型?沒錯——通過使用instanceof運算符。
這是我們的新代碼的樣子:
| 代碼 | 描述 |
|---|---|
|
比較引用 是傳遞過來的對象 null嗎? 如果傳遞的對像不是 Person |
4.比較兩個Person對象
我們最終得到了什麼?如果我們已經到達方法的末尾,那麼我們有一個Person不是的對象引用null。所以我們將其轉換為a Person,並比較兩個對象的相關內部數據。這是我們的第四個場景。
| 代碼 | 描述 |
|---|---|
|
比較引用 是傳遞過來的對象 null嗎? 如果傳遞的對像不是 Person 類型轉換 |
你如何比較兩個Person對象?如果他們具有相同的名字 ( name) 和年齡 ( age),則它們是相等的。最終代碼將如下所示:
| 代碼 | 描述 |
|---|---|
|
比較引用 是傳遞過來的對象 null嗎? 如果傳遞的對像不是 Person 類型轉換 |
但這還不是全部。
首先,name字段是a String,所以需要通過調用equals方法來比較name字段。
this.name.equals(person.name)
其次,該name字段可能是null:在這種情況下,您不能調用equals它。您需要額外檢查null:
this.name != null && this.name.equals(person.name)
也就是說,如果名稱字段null在兩個Person對像中,那麼名稱仍然相同。
第四種情況的代碼可能如下所示:
|
如果年齡不相等, 則立即 return false If this.name等於null,使用該方法進行比較沒有意義equals。這裡要么第二個name字段等於null,要么不等於。 使用 方法比較兩個名稱字段 equals。 |
5.hashCode()方法
除了equals旨在對兩個對象的所有字段執行詳細比較的方法之外,還有另一種方法可用於不精確但非常快速的比較:hashCode().
想像一下,您正在按字母順序對包含數千個單詞的列表進行排序,並且您需要反复比較單詞對。單詞很長,由很多字母組成。一般來說,這樣的比較會耗費很長時間。
但它可以加速。假設我們有以不同字母開頭的單詞——很明顯它們是不同的。但如果它們以相同的字母開頭,那麼我們還不能說結果會是什麼:這些詞可能相同或不同。
該hashCode()方法使用類似的原理工作。如果你在一個對像上調用它,它會返回一些數字——類似於單詞中的第一個字母。該數字具有以下屬性:
- 相同的對象總是具有相同的哈希碼
- 不同的對象可以有相同的哈希碼,或者它們的哈希碼可以不同
- 如果對像有不同的哈希碼,那麼對象肯定是不同的
為了更清楚地說明這一點,讓我們用文字重新定義這些屬性:
- 相同的單詞總是有相同的首字母。
- 不同單詞的首字母可以相同,也可以不同
- 如果單詞的首字母不同,則單詞肯定不同
最後一個屬性用於加速對像比較:
首先,計算兩個對象的哈希碼。如果這些hashcode不一樣,那麼對象肯定是不一樣的,沒必要再去比較了。
但是如果哈希碼相同,那麼我們仍然需要使用 equals 方法來比較對象。
6. 代碼契約
上述行為必須由 Java 中的所有類實現。在編譯期間,無法檢查對像是否被正確比較。
Java 程序員有一個普遍的共識,如果他們自己編寫 equals() 方法的實現並因此覆蓋標準實現(在類中),他們也必須按照上述規則編寫自己的方法Object實現hashCode()使滿意。
這種安排稱為合同。
如果您只在您的班級中實施該方法equals()或僅實施該hashCode()方法,那麼您就嚴重違反了合同(您違反了協議)。不要這樣做。
如果其他程序員使用您的代碼,它可能無法正常工作。此外,您將使用依賴於遵守上述合同的代碼。
所有Java集合在查找元素時,都是先比較對象的hashcode,然後才使用方法進行比較equals。
這意味著,如果您為自己的類提供了一個equals方法,但您沒有編寫自己的hashCode()方法或未正確實現它,那麼集合可能無法與您的對像一起正常工作。
例如,您可能將一個對象添加到列表中,然後使用該方法搜索它contains(),但集合可能找不到您的對象。
GO TO FULL VERSION