首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 字符串內存模型深度解析:String、StringBuilder、intern() 與面試必考題

     編輯:Java 綜合教學

String 核心特性回顧

  • 不可變性: `final char[] value`,任何修改操作都產生新對象
  • 常量池 (String Pool): 堆中專區,存儲字面量與 `intern()` 進入的字符串,**JDK 7+ 移至堆**,受 GC 管理
  • 創建機制: 字面量 (`"abc"`) → 檢查常量池,有則引用,無則創建入池;`new String("abc")` → **堆中新建對象**,內容複製自常量池字面量

經典面試題逐題破解

題 1.1:`new String("abc")` 創建幾個對象?

String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2); // false

答案: 每行創建 **2 個對象** (堆對象 + 常量池字面量 "abc",但字面量只會在類加載時創建/入池一次)。

圖解:

String 內存結構圖

`s1`、`s2` 指向不同堆對象,`==` 比較地址 → `false`。

題 1.2:`String.equals(StringBuffer)` 返回什麼?

String s1 = "abc";
StringBuffer s2 = new StringBuffer(s1);
System.out.println(s1.equals(s2)); // false

答案:false。`String.equals(Object)` 源碼關鍵:

public boolean equals(Object anObject) {
    if (this == anObject) return true;
    if (anObject instanceof String) {  // 關鍵:必須是 String 實例
        String anotherString = (String) anObject;
        // 逐字符比較...
    }
    return false;
}

`StringBuffer` 不是 `String`,直接返回 `false`。教訓:**equals 契約要求類型一致**。

題 1.3:`String s1 = new String("abc"); String s2 = new String("abc");` 內存幾個對象?

答案:3 個 —— 2 個堆對象 (s1、s2) + 1 個常量池字面量 "abc"。

題 1.4:`intern()` 陷阱題

String s1 = "abc";
String s2 = new String("abc");
s2.intern();
System.out.println(s1 == s2); // false

答案:false。`intern()` 返回常量池引用,**但未賦值給 s2**,s2 仍指向堆對象。

修正版:

String s1 = "abc";
String s2 = new String("abc");
s2 = s2.intern();  // 關鍵:重新賦值
System.out.println(s1 == s2); // true

補充題:JDK 6 vs JDK 7+ `intern()` 行為差異

String s = new String("a") + new String("b"); // "ab" 堆對象
String s2 = s.intern(); // JDK 6: 複製到永久代常量池,返回新對象地址 ≠ s
                        // JDK 7+: 在堆常量池記錄引用,返回 s 自己的地址 == s
System.out.println(s == s2); // JDK 6: false, JDK 7+: true

**根因:** JDK 7 將常量池從永久代移至堆,`intern()` 不再複製對象,僅在常量池登記引用。

String、StringBuilder、StringBuffer 選型指南

類可變性線程安全性能適用場景
String不可變不可變即線程安全拼接產生大量臨時對象常量、少量拼接、鍵值、傳遞參數
StringBuilder可變非線程安全最快 (無同步開銷)**單線程** 大量拼接、構建 SQL/JSON/日誌
StringBuffer可變線程安全 (synchronized)較慢多線程共享字符串構建器 (較少見)

常量池與堆的演進歷程

  • JDK 6: 永久代,`intern()` 複製對象到永久代
  • JDK 7: 常量池移至堆,`intern()` 登記引用,永久代開始淘汰
  • JDK 8: 永久代移除,元空間 存類元數據,字符串常量池在堆

實戰避坑指南

  1. 避免循環中 `+=` 拼接字符串:使用 `StringBuilder`,或 Java 8+ `String.join()`、`Collectors.joining()`
  2. 慎用 `intern()`:大量調用會擴大常量池,導致 Full GC、甚至 OOM,僅用於**有限值集合** (如狀態碼、枚舉值) 去重
  3. 字符串作為 Map 鍵:利用不可變性保證哈希碼穩定,但注意長字符串 `hashCode()` 計算開銷 (JDK 13+ 緩存哈希碼)
  4. 正則分割/替換:`split("|")` 需轉義 `split("\\|")`,頻繁操作預編譯 `Pattern`

深入理解 String 內存模型,不僅是面試必備,更是寫出高性能、低 GC 壓力 Java 代碼的基石。記住:**不可變是特性,常量池是優化,工具類選型是藝術**。

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