String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
System.out.println(s3 == (s1 + s2)); // false
System.out.println(s3.equals(s1 + s2)); // true
問題: 常量池中存在幾個 `"helloworld"`?若 `String s5 = "helloworld"` 指向哪個?
s1 + s2 在運行期創建新對象(底層 `StringBuilder`),堆內存中新建 `"helloworld"`。s3 == (s1+s2) 比較引用地址 → false(常量池 vs 堆)。s3.equals(s1+s2) 比較內容 → true。String s5 = "helloworld" 直接指向常量池中的那個字面量(同 `s3`)。// s1 = "hello" → ldc #hello (常量池)
// s2 = "world" → ldc #world (常量池)
// s3 = "helloworld" → ldc #helloworld (常量池,編譯期常量折疊)
// s1 + s2 → new StringBuilder → append → toString() (運行期堆分配)
// String s5 = "helloworld" → ldc #helloworld (同 s3)
常量池 (元空間/方法區) 堆
+------------------+ +------------------+
| "hello" ← s1 | | "helloworld" ← s1+s2 (臨時對象) |
| "world" ← s2 | +------------------+
| "helloworld" ← s3, s5 (若 intern() 會入池)
+------------------+
| 場景 | 創建對象數 | 存儲位置 | 說明 |
|---|---|---|---|
| 字面量直接賦值 | 0 或 1 | 常量池 | 編譯期去重,`"a" + "b"` 編譯期優化為 `"ab"` |
| 變量拼接 `s1+s2` | 1 | 堆 | 運行期 `new StringBuilder().append().toString()` |
| `new String("abc")` | 1 或 2 | 堆 (+常量池) | 堆新建,字面量 `"abc"` 進常量池 |
| `intern()` | 0 或 1 | 常量池 | 將堆對象內容入池,返回池中引用 |
字符串常量池是 JVM 優化內存的關鍵機制。編譯期確定的字面量進池,運行期拼接在堆。掌握 `ldc`、`StringBuilder`、`intern()` 三者關係,是寫出高性能、無 Bug Java 代碼的基礎。