String 是 java.lang.Object 的子類,默認值 null。但它擁有特殊語法支持(字面量、+ 拼接)且被 JVM 深度優化。
編譯期確定的字符串字面量(如 "kvill")存入 .class 文件的常量池,類加載時進入 運行時常量池(方法區/元空間)。JVM 保證全局唯一:相同字面量只有一份拷貝。
String s0 = "kvill";
String s1 = "kvill";
String s2 = "kv" + "ill"; // 編譯期常量折疊為 "kvill"
System.out.println(s0 == s1); // true
System.out.println(s0 == s2); // true
new String("kvill") 在運行時於堆中創建新對象,不進入常量池,每次調用都分配新內存:
String s3 = new String("kvill");
System.out.println(s0 == s3); // false
String.intern() 是 native 方法,邏輯如下:
equals())的字符串;String s1 = new String("kvill");
String s2 = s1.intern(); // 常量池無 "kvill",存入 s1 引用
System.out.println(s1 == s2); // false!s1 仍指向堆對象
System.out.println(s1.intern() == s2); // true,intern() 返回常量池引用
誤區澄清:intern() 不會把堆對象「變成」常量池對象,只是讓常量池持有對堆對象的引用(或複製,視 JDK 版本)。原堆對象依然存在。
== 比較引用地址(是否同一對象)。equals() 比較字符序列(Unicode 碼點逐個比對)。== 優化。String 一旦創建不可修改。String s = "a" + "b" + "c"; 編譯器優化為 "abc";但 String s = ""; for (...) s += x; 循環中每次拼接都產生新 StringBuilder → toString(),產生大量臨時對象,拖垮 GC。
最佳實踐:
+,編譯器自動優化為 StringBuilder。StringBuilder(單線程)或 StringBuffer(多線程)。CharSequence、StringJoiner、TextBlock(Java 13+)。理解常量池、堆、intern() 三者關係,是寫出高性能、無 Bug 字符串代碼的關鍵。記住:內容比較用 equals,引用比較用 ==,拼接用 StringBuilder,池化用 intern()(謹慎使用,防止元空間 OOM)。