現象
String str1 = "aa", str2 = "bb";
String str3 = "aa" + "bb"; // 編譯期常量折疊 -> "aabb" 進入常量池
String str4 = str1 + str2; // 執行期 StringBuilder -> 新物件
System.out.println(str3 == str4); // false
原理解析
- 編譯期常量折疊:
"aa" + "bb" 在編譯時即計算為 "aabb",存入 字串常量池(String Constant Pool),str3 直接引用池中物件。
- 執行期字串連接:
str1 + str2 編譯為 new StringBuilder().append(str1).append(str2).toString(),在堆上建立新 String 物件,str4 指向該物件。
== 比較參考位址,故為 false。
驗證:查看位元碼
javap -c -verbose YourClass.class
// 會看到 str3 為 ldc #常量池索引,str4 為 StringBuilder 建構
最佳實踐
- 比較字串內容**永遠使用
equals()**。
- 需確保共用常量池物件時,呼叫
str4.intern() 將執行期物件放入/取得常量池引用。
- 大量拼接使用
StringBuilder(非執行緒安全)或 StringBuffer(執行緒安全)。
圖解記憶體
常量池 (Perm/Metaspace) 堆
+------------------+ +------------------+
| "aabb" <------- | str3 | |
+------------------+ | StringBuilder |
| .toString() |
| "aabb" <------- | str4
+------------------+