字符串比較是 Java 基礎中最高頻考點,也是新手最易踩坑的地方。本文從內存模型、字符串池、編譯器優化三個維度,徹底講透 == 與 equals() 的區別。
| 操作符 | 比較內容 | 適用場景 |
|---|---|---|
== | 引用地址(內存地址) | 判斷是否為同一對象 |
equals() | 字符序列內容 | 判斷字符串值是否相等 |
public class StringCompareDemo {
public static void main(String[] args) {
String a = "test"; // 常量池
String b = "test"; // 常量池(同一引用)
String c = new String("test"); // 堆對象
String d = new String("test"); // 堆對象(不同引用)
String e = a; // 同引用
String f = new String(a); // 堆對象
String g = a + ""; // 拼接產生新對象
System.out.println("a==b: " + (a == b)); // true
System.out.println("a==c: " + (a == c)); // false
System.out.println("c==d: " + (c == d)); // false
System.out.println("a==e: " + (a == e)); // true
System.out.println("a==f: " + (a == f)); // false
System.out.println("a==g: " + (a == g)); // false
// equals 全部 true
System.out.println("equals: " + (a.equals(b) && b.equals(c) && c.equals(d)
&& d.equals(e) && e.equals(f) && f.equals(g)));
}
}
String a = "test":編譯期確定,存入常量池String b = "test":編譯期發現常量池已有 "test",直接復用引用a == b 為 truenew String("test"):運行期在堆創建新對象,即使常量池有相同內容c、d、f 互為不同對象,c == d 為 falseString e = a:引用傳遞,指向同一對象 → a == e 為 trueString f = new String(a):顯式 new,新對象 → falsea + "":編譯器優化為 new StringBuilder().append(a).append("").toString()a == g 為 false
// String.equals() 核心邏輯
public boolean equals(Object anObject) {
if (this == anObject) return true; // 同引用快速返回
if (anObject instanceof String) {
String another = (String) anObject;
// 比較字符數組長度與內容
return value.length == another.value.length
&& Arrays.equals(value, another.value);
}
return false;
}
Object.equals() 默認就是 ==,String 重寫實現內容比較。
"test".equals(input) 或 Objects.equals(a, b)(防空指針)"CONST".equals(var) 避免 var 為 null 拋 NPEString s = new String("test").intern() 返回常量池引用,s == "test" 為 truefinal String s = "a" + "b" → "ab" 入池intern() 性能提升== 比地址,equals 比內容。字面量自動入池,new 強制新對象。拼接隱式 new,intern 手動入池。業務比較永遠用 equals,防空用 Objects.equals。