java.lang.Object 是所有 Java 類別的隱性父類別,定義了 toString()、equals()、hashCode() 三大核心方法。正確覆寫這三者,是編寫健壯、可預期行為類別的基本功。
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'}
System.out.println(obj) → obj.toString()"User: " + user → user.toString()log.info("User: {}", user) (SLF4J 自動呼叫)| 運算子/方法 | 比較對象 | 基本型別 | 參考型別 |
|---|---|---|---|
| == | 位元模式 | 數值相等 | **記憶體位址**相等 |
| Object.equals() | 參考位址 | N/A | 同 == (預設) |
| String.equals() | 字元序列 | N/A | **內容相等** |
x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) → x.equals(z)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 允許子類別相等 (需謹慎)
@Override
public int hashCode() {
return Objects.hash(id, name, email); // Java 7+ 輔助方法
// 等價於: 31 * (31 * id.hashCode() + name.hashCode()) + email.hashCode()
}
Objects.hash() 自動處理 null (回傳 0)
System.identityHashCode(obj); // JVM 根據記憶體位址產生,不可變
obj.hashCode(); // 覆寫後為邏輯雜湊碼
若需判斷「是否同一物件實例」,用 == 或 System.identityHashCode()。
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 元素。
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 不同 → 不同桶位
List list = new ArrayList<>();
list.add(new User(1, "Tom"));
list.contains(new User(1, "Tom")); // true (若 equals 正確)
Alt+Insert → toString()/equals()/hashCode()@Data (含 toString、equals、hashCode、Getter/Setter) 或 @EqualsAndHashCodeEqualsBuilder, HashCodeBuilder, ToStringBuilder| 方法 | 用途 | 覆寫必要性 | 核心原則 |
|---|---|---|---|
| toString() | 除錯、日誌、顯示 | 強烈建議 | 包含關鍵欄位、格式清晰 |
| equals() | 邏輯相等判斷 | 值物件/DTO/Entity 必須 | 滿足 5 大契約、用 Objects.equals |
| hashCode() | 雜湊容器索引 | 覆寫 equals 時**必須**覆寫 | equals 用欄位全用、Objects.hash、不可變 |
三者緊密相依:**覆寫 equals 必覆寫 hashCode,兩者使用相同欄位集合,欄位最好不可變**。掌握此鐵律,避開 90% 集合與比較相關 Bug。