核心結論先行
==:比較引用地址(基本類型比較值)。
equals():默認比較引用地址,但常被重寫以比較內容(String、包裝類、集合等)。
基本類型 vs 引用類型
int a = 1, b = 1;
System.out.println(a == b); // true,比較值
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i1 == i2); // false,不同對象地址
System.out.println(i1.equals(i2)); // true,Integer 重寫 equals 比較值
Object 源碼:equals 默認行為
public boolean equals(Object obj) {
return (this == obj); // 直接比較引用
}
常見類重寫 equals 的邏輯(JDK 8 源碼節選)
| 類 | 比較邏輯 |
| String | 先比引用,再比長度,最後逐字符比對 char[] value |
| Integer/Long/Short/Byte | value == ((Integer)obj).intValue() 比較內部 int value |
| Double/Float | 用 doubleToLongBits 處理 NaN、-0.0 等特殊值 |
| Boolean | value == ((Boolean)obj).booleanValue() |
| Date | getTime() == ((Date)obj).getTime() 比較毫秒時間戳 |
實測代碼與輸出
public class EqualsDemo {
public static void main(String[] args) {
Integer i1 = new Integer(1), i2 = new Integer(1);
String s1 = new String("123"), s2 = new String("123");
Date d1 = new Date(), d2 = new Date(); // 不同毫秒數
System.out.println("Integer: == " + (i1 == i2) + ", equals " + i1.equals(i2));
System.out.println("String: == " + (s1 == s2) + ", equals " + s1.equals(s2));
System.out.println("Date: == " + (d1 == d2) + ", equals " + d1.equals(d2));
}
}
Integer: == false, equals true
String: == false, equals true
Date: == false, equals true // 若 d1/d2 毫秒不同
最佳實踐清單
- 基本類型:直接用
==。
- 引用類型比較內容:永遠用 equals(),並注意防空(
Objects.equals(a,b) 或 "constant".equals(str))。
- 自定義類:必須重寫 equals() 和 hashCode(),遵循對稱性、傳遞性、一致性契約。
- 枚舉:可直接用
==,因為枚舉實例在 JVM 中唯一。
常見坑點
List.contains()、Set、Map key 內部均依賴 equals(),未重寫會導致邏輯錯誤。
- 自動裝箱緩存範圍(-128~127)會讓
Integer a = 100; Integer b = 100; a==b 為 true,切勿依賴此行為。