現象
String a = "a";
String b = "b";
String c = "ab";
String d = a + "b"; // new StringBuilder().append(a).append("b").toString()
System.out.println(c == d); // false
原因
- 字符串字面量(`"a"`、`"b"`、`"ab"`)編譯期進入常量池。
- `a + "b"` 運行期由 `StringBuilder` 生成**新對象**,堆上分配,**不自動進入常量池**。
- 因此 `c` 指向常量池 `"ab"`,`d` 指向堆上新對象,`==` 比較引用地址,結果為 `false`。
解決:使用 intern()
String c = "ab".intern(); // 確保在常量池
String d = (a + "b").intern(); // 手動放入常量池並返回引用
System.out.println(c == d); // true
intern() 官方語義
若常量池已有 equals 相等的字符串,返回池中引用;否則將此字符串加入池並返回其引用。
最佳實踐
- 僅在需要極致性能、大量重複字符串去重時使用 `intern()`。
- 日常比較內容請用 `equals()`。
- JDK 7+ 常量池移至堆,`intern()` 不再導致 PermGen OOM,但過度使用仍會增加堆壓力。