首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Java字符串常量池深度解析:字符串拼接與對象創建的底層機制

     編輯:程式設計疑難解答

經典面試題

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"` 指向哪個?

核心結論

  • 常量池中只有一個 `"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)

常量池 vs 堆 內存圖解

常量池 (元空間/方法區)          堆
+------------------+            +------------------+
| "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常量池將堆對象內容入池,返回池中引用

JDK 版本差異

  • JDK 6: 常量池在永久代,`intern()` 會複製對象到永久代。
  • JDK 7/8: 常量池移至堆,`intern()` 在池中記錄引用,不複製對象,節省內存。
  • JDK 9+: 字符串底層改為 `byte[]` + `coder` (Latin1/UTF-16),常量池機制不變。

最佳實踐建議

  1. ✅ 比較內容用 `equals()`,絕不信賴 `==`。
  2. ✅ 大量拼接用 `StringBuilder`/`StringJoiner`,避免產生大量臨時對象。
  3. ✅ 需要入池去重時顯式調用 `intern()`(慎用,池大小有限)。
  4. ✅ 理解編譯期常量折疊:`"a" + "b"` ≠ `s1 + s2`。

總結

字符串常量池是 JVM 優化內存的關鍵機制。編譯期確定的字面量進池,運行期拼接在堆。掌握 `ldc`、`StringBuilder`、`intern()` 三者關係,是寫出高性能、無 Bug Java 代碼的基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved