字串在日常開發中極為常見,用法看似簡單,卻蘊含深層機制。面試官偏愛考察 String 相關題目,不僅測試 Java 基礎掌握程度,更考驗候選人對技術細節的探究態度。本文將通過反編譯字節碼,結合 JVM 內存模型演進,徹底剖析 String 與 intern() 的核心邏輯,助你攻克經典面試難題。

字面量賦值:優先檢查字串常量池,若存在則直接引用;否則在常量池創建並引用。
new 關鍵字:直接在堆內存創建新對象,引用指向堆地址。JDK 7 起 HotSpot 將常量池從永久代移至堆中(詳見《JDK8 內存模型——消失的 PermGen》)。
intern() 方法可將堆中字串對象加入常量池,返回常量池引用。
String str1 = "string";
String str2 = new String("string");
String str3 = str2.intern();
System.out.println(str1 == str2); // #1
System.out.println(str1 == str3); // #2
結果分析:

通過 javap -c -verbose StringTest01.class 反編譯觀察字節碼指令:


關鍵指令解讀:
ldc:從運行時常量池加載引用至棧。astore_n:將引用存入第 n 個局部變量。執行流程關鍵點:
str1、str2 均為字面量 "baseStr01",編譯期合併為常量池同一項 → #3 輸出 true。str3 = baseStr + "01":非 final 變量,運行期通過 StringBuilder.append() 拼接,生成新堆對象 → #4 輸出 false。str4 = baseFinalStr + "01":final 變量編譯期常量折疊,直接引用常量池 "baseStr01" → #5 輸出 true。str5 = new String("baseStr01").intern():堆對象調用 intern(),常量池已存在 "baseStr01",返回常量池引用 → #6 輸出 true。
// 情況 1:先 intern 再字面量賦值
String str2 = new String("str") + new String("01");
str2.intern();
String str1 = "str01";
System.out.println(str2 == str1); // #7
// 情況 2:先字面量賦值再 intern
String str1 = "str01";
String str2 = new String("str") + new String("01");
str2.intern();
System.out.println(str2 == str1); // #8


核心差異:JDK 1.7 起常量池移至堆,intern() 不再複製字串,而是在常量池記錄堆對象引用。
| 題目 | 答案 | 核心原理 |
|---|---|---|
String s = new String("xyz") 創建幾個對象? | 兩個 | 常量池 "xyz" + 堆中新對象 |
String s1="abc"; String s2="abc"; s1==s2 | true | 均指向常量池同一對象 |
String s1=new String("abc"); String s2=new String("abc"); s1==s2 | false | 兩個獨立堆對象 |
| 非 final 變量拼接 vs final 變量拼接 | false / true | 編譯期常量折疊 vs 運行期 StringBuilder |
| true (JDK 1.7+) | intern 返回常量池引用 |
掌握 String 常量池、堆內存交互、intern() 語義演進、編譯期優化(final 常量折疊、字面量合併)及字節碼指令(ldc、invokespecial、invokevirtual),即可洞悉所有字串面試題本質。建議結合 javap 實際反編譯驗證,深化理解。