Java中==和equals()的區別
- java
一、概述
在本教程中,我們將描述 Java 中的兩個基本相等檢查——引用相等和值相等。我們將比較它們,展示示例,並突出它們之間的主要區別。
此外,我們將專注於null
檢查,並理解為什麼在處理對象時應該使用引用相等而不是值相等。
2. 引用相等
我們將從理解引用比較開始,它由相等運算符 ( ==
) 表示。當兩個引用指向內存中的同一個對象時,就會發生引用相等。
2.1 基本類型的比較
我們知道 Java 中的原始類型是簡單的、非類的原始值。當我們對原始類型使用相等運算符時,我們只是比較它們的值:
int a = 10;
int b = 15;
assertFalse(a == b);
int c = 10;
assertTrue(a == c);
int d = a;
assertTrue(a == d);
如上所示,對於原語,相等性和引用檢查的工作方式相同。當我們用相同的值初始化一個新的原語時,檢查返回true.
此外,如果我們將原始值重新分配給新變量並進行比較,則運算符返回相同的結果。
現在讓我們執行null
檢查:
int e = null; // compilation error
assertFalse(10 == null); // compilation error
assertFalse(a == null); // compilation error
Java 禁止將null
分配給原語。通常,我們不能使用相等運算符對原始變量或值執行任何null
檢查。
2.2.對像類型的相等比較
對於 Java 中的對像類型,相等運算符只執行引用相等比較,而忽略對象值。在我們實現測試之前,讓我們創建一個簡單的自定義類:
public class Person {
private String name;
private int age;
// constructor, getters, setters...
}
現在,讓我們初始化一些類對象並檢查相等運算符的結果:
Person a = new Person("Bob", 20);
Person b = new Person("Mike", 40);
assertFalse(a == b);
Person c = new Person("Bob", 20);
assertFalse(a == c);
Person d = a;
assertTrue(a == d);
結果與以前大不相同。第二次檢查返回false
,而我們為原語得到了true
。正如我們前面提到的,相等運算符在比較時忽略了對象的內部值。它只檢查兩個變量是否引用了相同的內存地址。
與原語不同,我們可以在處理對象時使用null
:
assertFalse(a == null); Person e = null; assertTrue(e == null);
通過使用相等運算符並比較null,
我們檢查分配給變量的對像是否已經初始化。
3.相同的值比較
現在讓我們關注價值平等測試。當兩個獨立的對象碰巧具有相同的值或狀態時,就會發生值相等。
這會比較值,並且與Object's equals()
方法密切相關。和以前一樣,讓我們將其與原語和對像類型的使用進行比較,看看關鍵的區別。
3.1 具有原始類型的equals()
方法
眾所周知,原語是具有單個值的基本類型,不實現任何方法。因此,不可能直接使用原語調用equals()
方法:
int a = 10; assertTrue(a.equals(10)); // compilation error
然而,由於每個原語都有自己的包裝類,我們可以使用boxing mechanism
將其轉換為它的對象表示。然後,我們可以像使用對像類型一樣輕鬆調用equals()
方法:
int a = 10;
Integer b = a;
assertTrue(b.equals(10));
3.2 具有對像類型equals()
方法
讓我們回到我們的Person
類。為了讓equals()
方法正常工作,我們需要通過考慮類中包含的字段來覆蓋自定義類中的方法:
public class Person {
// other fields and methods omitted
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}
首先,如果給定值具有相同的引用, equals()
方法返回true
,這由引用運算符檢查。如果不是,我們開始平等測試。
此外,我們測試兩個值的Class
對象的相等性。如果它們不同,我們返回false
。否則,我們繼續檢查是否相等。最後,我們返回分別比較每個屬性的組合結果。
現在,讓我們修改之前的測試並檢查結果:
Person a = new Person("Bob", 20);
Person b = new Person("Mike", 40);
assertFalse(a.equals(b));
Person c = new Person("Bob", 20);
assertTrue(a.equals(c));
Person d = a;
assertTrue(a.equals(d));
正如我們所見,第二個檢查返回true
而不是引用相等。我們重寫的equals()
方法比較對象的內部值。
如果我們不重寫equals()
方法,則使用父類Object
中的方法。由於Object.equals()
方法只進行引用相等性檢查,因此在比較Person
對象時,行為可能不是我們所期望的。
雖然我們沒有在上面展示hashCode()
方法,但我們應該注意,每當我們覆蓋equals()
方法時,覆蓋它是很重要的 以確保這些方法之間的一致性。
4. 處理Null值
最後,讓我們檢查一下equals()
方法如何處理null
值:
Person a = new Person("Bob", 20);
Person e = null;
assertFalse(a.equals(e));
assertThrows(NullPointerException.class, () -> e.equals(a));
當我們使用equals()
方法對另一個對象進行檢查時,我們會根據這些變量的順序得到兩個不同的結果。最後一條語句引發異常,因為我們在null
引用上調用了equals()
方法。要修復最後一條語句,我們應該首先調用相等運算符檢查:
assertFalse(e != null && e.equals(a));
現在,條件的左側返回false
,使整個語句等於false
,從而防止了NullPointerException
被拋出。因此,我們必須記住首先檢查我們調用equals()
方法的值不是null
,否則會導致煩人的錯誤。
此外,從 Java 7 開始,我們可以使用 null 安全的Objects#equals()
static
方法來執行相等檢查:
assertFalse(Objects.equals(e, a));
assertTrue(Objects.equals(null, e));
此輔助方法執行額外的檢查以防止拋出NullPointerException
,當兩個參數都為null
時返回true
。
5. 結論
在本文中,我們討論了引用相等和值相等 檢查原始值和對象值。
為了測試引用相等性,我們使用==
運算符。該運算符對原始值和對象的工作方式略有不同。當我們將相等運算符與基元一起使用時,它會比較值。另一方面,當我們將它與對像一起使用時,它會檢查內存引用。通過將其與null
值進行比較,我們只需檢查對像是否已在內存中初始化。
為了在 Java 中執行值相等測試,我們使用從Object
繼承的equals()
方法。 Primitives 是簡單的非類值,所以這個方法不能在沒有包裝的情況下被調用。
我們還需要記住只在實例化對像上調用equals()
方法。否則會拋出異常。為了防止這種情況發生,如果我們懷疑null
值,我們應該使用==
運算符檢查該值。