程式碼對比
String a = "aaa"; // 方式一:字面值
String b = "aaa"; // 指向同一常量池物件
String a1 = new String("aaa"); // 方式二:new 建構式
String b1 = new String("aaa"); // 每次 new 都在堆建立新物件
記憶體模型圖解
| 特性 | String s = "aaa" | String s = new String("aaa") |
| 儲存區域 | **字串常量池** (String Constant Pool),屬於方法區/Metaspace | **堆** (Heap),受 GC 管理 |
| 物件複用 | 相同字面值**共用同一物件 | 每次 new **建立新物件** |
| 生命週期 | 類別卸載前常駐(通常伴隨 JVM 生命週期) | 無引用時可被 GC 回收 |
== 比較 | a == b 為 true | a1 == b1 為 false |
equals() 比較 | true | true (比較內容) |
JVM 內部運作
ldc 指令載入常量池字面值,若池中已存在則直接返回引用。
new 指令在堆分配記憶體,呼叫 String(byte[]) 建構式複製字元陣列。
String.intern() 可手動將堆上字串放入常量池(或返回池中現有引用)。
效能與最佳實踐
- **優先使用字面值**
String s = "text":避免不必要的物件建立,利用常量池複用。
- **僅在需動態構建/頻繁修改時** 使用
new StringBuilder() 或 new String(char[])。
- **比較內容永遠用
equals()**,除非確定為同一常量池引用才用 ==。
- JDK 7+ 常量池移至堆,GC 可回收無引用常量,但複用機制不變。
常見面試陷阱
String s1 = "a" + "b"; // 編譯期常量折疊 → "ab" 在常量池
String s2 = "ab"; // s1 == s2 → true
String s3 = new String("a") + new String("b"); // 執行期 new → 堆物件
String s4 = "ab"; // s3 == s4 → false
s3.intern() == s4 // true (JDK 7+)