首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Object 類別三大核心方法深度解析:toString、equals、hashCode

     編輯:Java 相關知識

前言

java.lang.Object 是所有 Java 類別的隱性父類別,定義了 toString()、equals()、hashCode() 三大核心方法。正確覆寫這三者,是編寫健壯、可預期行為類別的基本功。

1. toString():物件的自我描述

預設行為

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// 例: com.example.User@1a2b3c4d

資訊量極低,除錯時幾乎無用。

覆寫最佳實務


@Override
public String toString() {
    return "User{" +
           "id=" + id +
           ", name='" + name + '\'' +
           ", email='" + email + '\'' +
           '}';
}
// 輸出: User{id=1, name='Tom', email='tom@example.com'}
  • 包含所有關鍵欄位
  • 格式清晰、可讀、可作為日誌輸出
  • IDE (IntelliJ/Eclipse) 可自動產生

隱性呼叫時機

  • System.out.println(obj) → obj.toString()
  • 字串串接:"User: " + user → user.toString()
  • 日誌框架:log.info("User: {}", user) (SLF4J 自動呼叫)

2. equals():邏輯相等性判斷

== vs equals()

運算子/方法比較對象基本型別參考型別
==位元模式數值相等**記憶體位址**相等
Object.equals()參考位址N/A同 == (預設)
String.equals()字元序列N/A**內容相等**

覆寫契約 (JLS)

  1. 自反性:x.equals(x) == true
  2. 對稱性:x.equals(y) == y.equals(x)
  3. 傳遞性:x.equals(y) && y.equals(z) → x.equals(z)
  4. 一致性:多次呼叫結果不變 (除非欄位被修改)
  5. 非空性:x.equals(null) == false

標準覆寫模板


@Override
public boolean equals(Object obj) {
    if (this == obj) return true;                    // 同一參考
    if (obj == null || getClass() != obj.getClass()) return null; // 型別檢查
    User other = (User) obj;
    return Objects.equals(id, other.id) &&           // 使用 Objects.equals 處理 null
           Objects.equals(name, other.name) &&
           Objects.equals(email, other.email);
}

關鍵點:

  • 參數型別為 Object (非 User),否則是多載而非覆寫
  • 用 getClass() 嚴格檢查型別,或 instanceof 允許子類別相等 (需謹慎)
  • 所有用於比較的欄位必須為 **不可變** 或 **最終狀態**

3. hashCode():雜湊碼與集合效能

核心契約

  1. equals 相等 → hashCode 必相等 (違反會導致 HashMap/HashSet 失效)
  2. equals 不相等 → hashCode 應盡量分散 (減少碰撞、提升效能)
  3. 同一物件多次呼叫 → 回傳值一致 (除非 equals 用欄位被修改)

標準實作


@Override
public int hashCode() {
    return Objects.hash(id, name, email); // Java 7+ 輔助方法
    // 等價於: 31 * (31 * id.hashCode() + name.hashCode()) + email.hashCode()
}
  • 使用 Objects.hash() 自動處理 null (回傳 0)
  • 乘質數 31 可減少碰撞 (JVM 內部優化乘法為位移+減法)
  • **equals 用到的欄位,hashCode 必須同樣使用**

身份雜湊碼 vs 邏輯雜湊碼


System.identityHashCode(obj); // JVM 根據記憶體位址產生,不可變
obj.hashCode();               // 覆寫後為邏輯雜湊碼

若需判斷「是否同一物件實例」,用 == 或 System.identityHashCode()。

實戰陷阱與案例

陷阱 1:可變物件放入 HashSet


User u = new User(1, "Tom");
Set set = new HashSet<>();
set.add(u);
u.setName("Jerry"); // 修改 equals/hashCode 依賴欄位!
set.contains(u);    // false!雜湊碼變了,找不到桶位

鐵律:不可變物件才適合作為 HashMap Key / HashSet 元素。

陷阱 2:忘記覆寫 hashCode


class BadUser { String name; /* 只有 equals */ }
BadUser a = new BadUser("Tom");
BadUser b = new BadUser("Tom");
Map map = new HashMap<>();
map.put(a, "A");
map.get(b); // null!預設 hashCode 依位址,a/b 不同 → 不同桶位

陷阱 3:ArrayList.contains() 依賴 equals


List list = new ArrayList<>();
list.add(new User(1, "Tom"));
list.contains(new User(1, "Tom")); // true (若 equals 正確)

IDE 自動產生與庫支援

  • IntelliJ: Alt+Insert → toString()/equals()/hashCode()
  • Lombok: @Data (含 toString、equals、hashCode、Getter/Setter) 或 @EqualsAndHashCode
  • Apache Commons Lang: EqualsBuilder, HashCodeBuilder, ToStringBuilder

總結速查表

方法用途覆寫必要性核心原則
toString()除錯、日誌、顯示強烈建議包含關鍵欄位、格式清晰
equals()邏輯相等判斷值物件/DTO/Entity 必須滿足 5 大契約、用 Objects.equals
hashCode()雜湊容器索引覆寫 equals 時**必須**覆寫equals 用欄位全用、Objects.hash、不可變

三者緊密相依:**覆寫 equals 必覆寫 hashCode,兩者使用相同欄位集合,欄位最好不可變**。掌握此鐵律,避開 90% 集合與比較相關 Bug。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved