== 與 equals() 是 Java 比較操作的雙子星,混淆二者是新手高頻 Bug 來源。本文從記憶體模型、源碼實現、String/包裝類特例三個維度徹底釐清。
| 操作符/方法 | 比較對象 | 語義 |
|---|---|---|
== | 引用地址 | 是否為同一個物件實例(堆中同一塊記憶體) |
equals() | 物件內容 | 是否代表邏輯上「相等」的實體(需類覆寫實現) |
// java.lang.Object
public boolean equals(Object obj) {
return (this == obj);
}
若類未覆寫 equals(),則等同於 ==。
String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2; // false:兩個 new 出來的不同實例
s1.equals(s2); // true:String 覆寫 equals 比較字符序列
java.lang.String 覆寫 equals() 逐字符比較值,而非引用。
String s3 = "abc";
String s4 = "abc";
s3 == s4; // true!編譯期常量池優化,指向同一實例
s3.equals(s4); // true
字面量 "abc" 編譯期進入常量池,同值只存一份。new String("abc") 強制在堆創建新實例。
==。equals(),前提是該類正確覆寫(如 String、包裝類、集合、自定義 Entity)。==(如單例檢查、Enum 比較、同步鎖對象)。hashCode(),否則作為 Map Key/Set 元素會失效。
String s1 = new String("s1");
String s2 = new String("s1");
// 創建了幾個 String 物件?
// 答:3 個。常量池 1 個("s1"),堆 2 個(new 出來的)。
String s1 = "s1";
String s2 = s1;
s2 = "s2";
// s1 指向的物件內容是?
// 答:"s1"。字符串不可變,s2 重新賦值只改變引用指向,不影響原物件。
記住:== 問「是不是同一個」,equals() 問「值不值一樣」。分清身份與價值,是寫出正確 Java 代碼的起點。