String 對象一旦創建,內容永不可變。所有「修改」操作(replace、substring、concat、toUpperCase 等)都會返回新 String 對象,原對象不變。
String str3 = "LOVO JAVA";
str3.replaceAll("JAVA", "J2EE"); // 返回新對象,未賦值則丟棄
System.out.println(str3); // 輸出:LOVO JAVA (原對象未變)
String str4 = str3.replaceAll("JAVA", "J2EE");
System.out.println(str4); // 輸出:LOVO J2EE (新對象)
String s1 = "abc";
String s2 = s1.replace('a', 'x');
Heap:
┌─────────────┐ ┌─────────────┐
│ "abc" ← s1 │ │ "xbc" ← s2 │
└─────────────┘ └─────────────┘
原對象不變 新對象
public String replace(char oldChar, char newChar) {
if (oldChar == newChar) return this; // 無實際變化返回自身
char[] val = value.clone(); // 複製底層 char[]/byte[]
for (int i = 0; i < val.length; i++) {
if (val[i] == oldChar) val[i] = newChar;
}
return new String(val); // 封裝新數組返回新 String
}
// ❌ 違反:每次循環創建新 StringBuilder + 新 String,O(n²)
String s = "";
for (int i = 0; i < 10000; i++) s += "a";
// ✅ 正確:單一 StringBuilder,O(n)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) sb.append("a");
String s = sb.toString();
// Java 9+ 編譯器優化:+ 會自動轉為 StringBuilder,但顯式更可控
hash 欄位懶加載,Map/Set 鍵高效| 類 | 可變性 | 線程安全 | 適用場景 |
|---|---|---|---|
String | 不可變 | 是 | 常量、鍵值、少量拼接 |
StringBuilder | 可變 | 否 | 單線程大量拼接 |
StringBuffer | 可變 | 是 | 多線程大量拼接 (較慢) |