1. 程式人生 > >為什麼Java中的String是不可變的?

為什麼Java中的String是不可變的?

String是Java中的一個不可變類。所謂不可變,簡單來說就是其物件不能被修改。例項中的所有資訊在初始化的時候就已經具備,並且不能被修改(老外好囉嗦…)。不可變類有很多優點。這篇文章簡要說明了為什麼String被設計為不可變類。關於其好的回答應該建立在對記憶體模型、同步和資料結構等的理解之上。

1. 字串池的需求

字串池是一個位於方法區的特殊區域。當一個字串被建立的時候,如果該字串已經存在於字串池中,那麼直接返回該字串的引用,而不是建立一個新的字串。
下邊的程式碼將只會建立一個字串物件:

String s1 = "abcd";
String s2 = "abcd";

就不上圖了,這塊的內容

前一篇翻譯的文章已經解釋過了。也就是s1s2都指向同一個字串物件。
如果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被設計為不可變的類,是出於效能和安全性的考慮,這也是其他所有不可變類應用的初衷。