不可變物件:建立後狀態永不改變。Java String 設計為不可變,帶來多重效益:
hashCode() 計算一次後快取,提升 HashMap/HashSet 效能| 版本 | 核心欄位 | 備註 |
|---|---|---|
| JDK 6/7 | char[] value, int offset, int count, int hash | 支援 substring 共享底層陣列(記憶體洩漏風險) |
| JDK 7u6+ | char[] value, int hash | substring 複製新陣列,修復記憶體洩漏 |
| JDK 9+ | byte[] value, byte coder (LATIN1/UTF16), int hash | Compact Strings,節省 50% 記憶體 |
String s = "ABC";
s = "123"; // 參考 s 指向新物件,原 "ABC" 物件未變
所有「修改」方法(replace、substring、toLowerCase 等)實際上都是建立新 String 物件回傳,原物件保持不變。
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; // 欄位為 final,不可重新指派
while (++i < len) {
if (val[i] == oldChar) break;
}
if (i < len) {
char[] buf = new char[len];
for (int j = 0; j < i; j++) buf[j] = val[j];
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true); // 建立新物件
}
}
return this; // 無變化回傳自身
}
public static void hackString() throws Exception {
String s = "Hello World";
System.out.println("修改前: " + s); // Hello World
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
char[] value = (char[]) valueField.get(s);
value[5] = '_'; // 直接修改底層陣列
System.out.println("修改後: " + s); // Hello_World
}
警告:此行為違反 String 契約,會導致字串常量池污染、HashMap 鍵值查找失效、安全檢查繞過等嚴重後果。
StringBuilder(非執行緒安全)或 StringBuffer(執行緒安全)+ 拼接長字串String.join()、String.format()、Collectors.joining() 簡化代碼String.repeat()、String.strip() 等新方法