本文節選自《編寫高質量代碼:改善 Java 程序的 151 個建議》第 4 章,詳細解析字串字面值、replaceAll 陷阱、String/StringBuilder/StringBuffer 選型,以及字串連接運算子優先級的關鍵知識點。
錯誤示範:String s = new String("詹姆斯");
正確做法:String s = "詹姆斯";
核心原因:Java 維護一個字串常量池。字面值賦值會檢查池中是否已存在相同內容的字串,若有則直接返回引用,避免重複建立物件;new String() 則總在堆上建立新物件,不進入常量池,導致 == 比較失敗。
String str1 = "詹姆斯";
String str2 = "詹姆斯";
String str3 = new String("詹姆斯");
String str4 = str3.intern(); // 手動放入常量池
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
System.out.println(str1 == str4); // true
intern() 可將堆上字串放入常量池並返回池中引用。字串常量池位於方法區(JDK 7 後移至堆),不被 GC 回收,極大提升重複字串的記憶體效率。
replaceAll 參數為正則表達式String.replaceAll(String regex, String replacement) 的第一個參數是正則表達式,而非純字串。若需單純字串替換,請使用 replace(CharSequence target, CharSequence replacement)(JDK 1.5+)。
// 錯誤:$ 在正則中代表行尾
"$是$".replaceAll("$", ""); // 結果仍為 "$是$"
// 正確:使用 replace
"$是$".replace("$", ""); // 結果為 "是"
許多開發者因忽略此點導致難以排查的 Bug,尤其處理含特殊字元($、.、* 等)的字串時。
| 類別 | 可變性 | 執行緒安全 | 效能 | 適用場景 |
|---|---|---|---|---|
| String | 不可變 | - | 最低(頻繁修改產生大量物件) | 常量、少量拼接、Map Key |
| StringBuffer | 可變 | 是(方法加 synchronized) | 中等 | 多執行緒環境下的字串緩衝 |
| StringBuilder | 可變 | 否 | 最高 | 單執行緒大量拼接、建構複雜字串 |
原理:String 每次修改產生新物件;StringBuilder/Buffer 內部維護 char[] 擴容複製。StringBuffer 同步開銷導致效能遠低於 StringBuilder。
Java 編譯器將 + 優化為 StringBuilder.append(),但運算順序由左至右,首個遇到的字串會觸發後續所有運算元轉為字串拼接。
String str1 = 1 + 2 + "apples"; // (1+2) → 3 → "3apples" String str2 = "apples" + 1 + 2; // "apples1" → "apples12"
口訣:+ 運算式中,字串擁有最高優先級,一旦出現字串,後續全變字串拼接。
"text",避免 new String()。replace(),正則替換才用 replaceAll()。StringBuilder,多執行緒選 StringBuffer,簡單拼接 + 即可。+ 運算順序,避免非預期的字串拼接結果。