為什麼Java中的String是不可變的?
String
是Java中的一個不可變類。所謂不可變,簡單來說就是其物件不能被修改。例項中的所有資訊在初始化的時候就已經具備,並且不能被修改(老外好囉嗦…)。不可變類有很多優點。這篇文章簡要說明了為什麼String
被設計為不可變類。關於其好的回答應該建立在對記憶體模型、同步和資料結構等的理解之上。
1. 字串池的需求
字串池是一個位於方法區的特殊區域。當一個字串被建立的時候,如果該字串已經存在於字串池中,那麼直接返回該字串的引用,而不是建立一個新的字串。
下邊的程式碼將只會建立一個字串物件:
String s1 = "abcd";
String s2 = "abcd";
就不上圖了,這塊的內容s1
和s2
都指向同一個字串物件。
如果String
不是不可變的,那麼修改s1
的字串物件同樣也會導致s2
的內容發生變化。
2. 快取Hashcode
字串的hashcode在Java中經常被用到。例如,在一個HashMap
中。其不可變性保證了hashcode(雜湊值)總是保持不變,從而不用擔心因hashcode變化導致的快取問題。那就意味著,不用每次在其使用的時候計算其hashcode,從而更加高效。
在String
類中,有如下程式碼:
private int hash; //用來快取hash code
3. 簡化其他物件的使用
為了理解這一點,請看下邊的程式碼:
HashSet<String> set = new HashSet<String>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));
for (String a : set)
a.value = "a";
這個例子中,如果String
是可變的,也就是說set中的值是可變的,這會影響到set的設計(set包含不重複的元素)。當然這個例子是有問題的,在String
類中是不存在value
這個屬性的。
4.安全性
字串在許多的java類中都用作引數,例如網路連線,開啟檔案等等。如果字串是可變的,一個連線或檔案就會被修改從而導致嚴重的錯誤。可變的字串也會導致在使用反射時導致嚴重的問題,因為引數是字串形式的。
舉例如下:
boolean connect(String s) {
if (!isSecure(s)) {
throw new SecurityException();
}
// 如果s內的值被修改,則會導致出現問題
doSomethind(s);
}
(雖然略牽強,但是也有一定道理)
5. 不可變的物件本身就是執行緒安全的
不可變的物件,可以在多個執行緒間自由共享。從而免除了進行同步的麻煩。
總之, String
被設計為不可變的類,是出於效能和安全性的考慮,這也是其他所有不可變類應用的初衷。