| 場景 | JDK 1.6 | JDK 1.7+ |
|---|---|---|
String s = new String("abc") 創建對象數 | 2(常量池 "abc" + 堆對象) | 2 |
字面量 "abc" 多次出現 | 指向常量池同一引用 | 同 |
new String("abc").intern() | 常量池無則複製引用入池,有則返回池中引用 | 常量池無則在池中記錄堆對象引用(不複製),有則返回池中引用 |
final 變量參與 + 拼接 | 編譯期常量折疊為單一字面量 | 同 |
非常量變量 + 拼接 | 運行期 StringBuilder 生成新對象 | 同 |
new String("string") vs intern()String str1 = "string";
String str2 = new String("string");
String str3 = str2.intern();
System.out.println(str1 == str2); // false:str1指常量池,str2指堆
System.out.println(str1 == str3); // true:intern()返回常量池引用
final 語義final String baseFinal = "baseStr";
String str1 = "baseStr01";
String str2 = "baseStr" + "01"; // 編譯期合併 → "baseStr01"
String str3 = baseStr + "01"; // 運行期 StringBuilder
String str4 = baseFinal + "01"; // 編譯期常量折疊 → "baseStr01"
String str5 = new String("baseStr01").intern();
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
System.out.println(str1 == str4); // true
System.out.println(str1 == str5); // true
反編譯關鍵字節碼:
str2:ldc #常量池索引 直接加載合併後字面量。str3:new StringBuilder → dup → invokespecial → aload → invokevirtual append → ldc "01" → invokevirtual append → invokevirtual toString。str4:ldc #常量池索引(final 導致編譯期替換)。str5:new String → dup → ldc "baseStr01" → invokespecial → invokevirtual intern。intern() 執行順序對 JDK 1.6/1.7 行為差異的影響// 版本 A
String str2 = new String("str") + new String("01");
str2.intern(); // 先 intern
String str1 = "str01"; // 後字面量
System.out.println(str2 == str1); // JDK6: true, JDK7+: false
// 版本 B
String str1 = "str01"; // 先字面量
String str2 = new String("str") + new String("01");
str2.intern(); // 後 intern
System.out.println(str2 == str1); // JDK6: false, JDK7+: false
原理:
intern() 會將字符串複製到常量池。版本 A 先 intern() 將堆對象引用複製入池,後字面量直接複用池中引用 → true。intern() 僅在常量池記錄堆對象引用(不複製)。版本 A 先 intern() 記錄堆引用,後字面量在常量池創建新引用指向新字符串對象 → 兩引用指向不同對象 → false。String s = new String("xyz") → 2 個對象(常量池 "xyz" + 堆對象)。String s1="abc", s2="abc" → true(同常量池引用)。new String("abc") 兩次 → false(兩個堆對象)。s2+s3 非 final 變量拼接 → false(StringBuilder 生成新對象)。final String s2="a", s3="bc"; s2+s3 → true(編譯期折疊為 "abc")。掌握 String、常量池、intern()、final 編譯期優化、JDK 版本差異,是攻克字符串面試題與避免生產 Bug 的利器。