equals() 是 Java 對象比較的核心方法,但其默認行為常被誤解。本文從源碼層面剖析 Object.equals() 與 String.equals() 的差異,並總結自定義類重寫的關鍵要點。
public boolean equals(Object obj) {
return (this == obj); // 僅比較引用地址
}
結論:默認 equals() 與 == 行為完全一致,均為引用比較,而非內容比較。
public boolean equals(Object anObject) {
// 1. 引用相同直接返回 true
if (this == anObject) {
return true;
}
// 2. 類型檢查
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = count; // 字符數量
// 3. 長度快速失敗
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
// 4. 逐字符比較
while (n-- != 0) {
if (v1[i++] != v2[j++]) {
return false;
}
}
return true;
}
}
return false;
}
instanceof 確保比較對象為 Stringchar[] 逐個比對字符值| 誤區 | 正確認知 |
|---|---|
| equals() 默認比較內容 | 默認比較引用,只有重寫後才比較內容 |
| 所有類的 equals() 都比較內容 | 只有重寫過 equals() 的類(如 String、包裝類、集合類)才比較內容 |
| == 和 equals() 可互換 | == 比較引用/基本類型值;equals() 比較邏輯相等性 |
根據 Java 語言規範,重寫 equals() 必須滿足五大特性:
x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) → x.equals(z)x.equals(null) == false
public class User {
private Long id;
private String username;
private String email;
@Override
public boolean equals(Object o) {
// 1. 引用相同
if (this == o) return true;
// 2. 空值或類型不匹配
if (o == null || getClass() != o.getClass()) return false;
// 3. 轉型並比較核心字段
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(username, user.username) &&
Objects.equals(email, user.email);
}
@Override
public int hashCode() {
// 必須同時重寫 hashCode(),保持 equals-hashCode 契約
return Objects.hash(id, username, email);
}
}
equals() 相等的對象,hashCode() 必須相等,否則在 HashMap/HashSet 中會出現異常行為getClass() 而非 instanceof 保證對稱性(Effective Java 建議)現代 IDE(IntelliJ IDEA、Eclipse)均支援根據字段自動生成規範的 equals()/hashCode(),建議使用並審查生成代碼,而非手寫。
總結:理解
equals()默認行為與重寫規範,是正確使用集合框架、避免業務邏輯 Bug 的基礎。自定義實體類必須配對重寫equals()和hashCode()。