String 是 Java 中最基礎、使用頻率最高的類之一,其 不可變 特性是設計者深思熟慮的結果。不可變類的實例一旦創建,其成員變量值永不改變。這一設計不僅關乎 String 自身,更深刻影響了 Java 內存模型、並發安全、集合框架、安全機制等核心體系。本文將從字符串池、哈希緩存、集合契約、安全性、線程安全五大維度,深度解析 String 不可變的設計動機與收益。
字符串池位於方法區,是一塊特殊的存儲區域。當創建字符串字面量(如 String s = "abcd")時,JVM 會先檢查池中是否存在內容相同的字符串:
因此,String s1 = "abcd"; String s2 = "abcd"; 僅在堆中創建 一個 String 對象,s1 和 s2 指向同一引用。

若 String 可變,當多個引用指向池中同一字符串時,任一引用修改內容都會影響其他引用,導致不可預期的行為,池化機制將失效。不可變性保證了池中對象狀態恆定,使得引用共享安全可靠。
Java 中頻繁使用字符串的哈希碼,如 HashMap、HashSet 的鍵操作。String 內部緩存了哈希碼:
private int hash; // 默認為 0,表示未計算
由於 String 不可變,其哈希碼一經計算永不改變。hashCode() 方法邏輯為:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
hash = h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
}
return h;
}
首次調用計算並緩存,後續直接返回,避免重複計算,大幅提升哈希集合操作效能。若可變,哈希碼會隨內容改變而失效,導致集合定位錯誤,破壞契約。
HashSet 依賴元素的 equals() 和 hashCode() 保證唯一性。若 String 可變,考慮如下場景:
HashSet set = new HashSet<>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));
// 假設 String 有可變字段 value
for (String s : set) {
s.value = "a"; // 修改所有元素內容為 "a"
}
修改後,Set 中三個元素內容均為 "a",嚴重違反 Set 不允許重複元素的契約。不可變性從根本上杜絕了此類邏輯破壞。
String 廣泛用作關鍵操作的參數:網絡連接地址、文件路徑、反射類名、數據庫 URL 等。若可變,惡意代碼可在安全檢查通過後、實際操作前修改字符串內容,導致嚴重安全漏洞:
boolean connect(String host) {
if (!isSecure(host)) {
throw new SecurityException();
}
// 若 host 在此處被其他引用修改,將連接到非預期機器
causeProblem(host);
}
不可變性確保參數在傳遞過程中 不可被篡改,構建了可信的安全邊界。同理,反射機制中類名字符串若可變,將導致任意類加載風險。
不可變對象一經構建,狀態永不改變,天然線程安全。多線程可自由共享同一 String 實例,無需任何同步機制(鎖、volatile、原子類等),消除了併發編程中的鎖開銷、死鎖風險、可見性問題。這在高併發系統中是巨大的效能與簡潔性紅利。
String 設計為不可變,核心動機是 安全 與 高效:
這是一個教科書級的不可變模式最佳實踐,值得我們在自定義值對象、DTO、配置類等場景中借鑑。