1. 程式人生 > >為什麼要把Java字串設計為不可變的

為什麼要把Java字串設計為不可變的

String是Java中一個不可變的類,所以它一旦被例項化就無法被修改。不可變類一旦被建立就不可以被修改。本文將從記憶體、同步和資料結構相關知識簡單說明一下將String設計為不可變類的好處。

(1)字串池:

字串池是方法區中一部分特殊儲存。當一個字串被建立的時候,首先會去字串池中查詢,如果找到,直接返回對該字串的引用。

String str1 = "abcd";
String str2 = "abcd";

這裡寫圖片描述
這兩個建立的引用都指向了同一個記憶體地址,在大量使用的情況下可以節省空間提高效率。最重要的是如果String是可變的話那麼修改str2就會使得str1的值也發生改變,這不是我們希望看到的。

(2)String為什麼不可變

在JDK原始碼中String類是被final修飾的,這就說明了String類不可以被繼承。String本質上是一個char陣列,而且也是用final修飾的。

//JDK部分原始碼
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

雖然value是不可變的,但也只是它的引用地址不可變,並不能改變它是一個Array陣列的事實。也就是說雖然Array的引用地址不可變,但是Array中的資料是可以改變的。

final int[] value={1,2,3}  
int[] another={4,5,6};  
value=another;    //編譯器報錯,final不可變 
//因為value使用final修飾的,不允許我就value的引用指向另一個堆區
//但是如果直接修改裡面的數值就是另外一回事了
final int[] value={1,2,3};  
value[2]=100;  //這時候數組裡已經是{1,2,100}
//或者直接用反射直接修改
final int[] array={1,2,3};  
Array.set(array,2,100); //陣列也被改成{1,2,100}  

(3)不可變有什麼好處

最直接的好處就是安全。String被廣泛應用在其他Java類中充當引數。比如網路連線、開啟檔案等操作。如果字串可變,那麼類似操作可能會導致安全問題。因為某個方法在呼叫連線操作的時候,它認為會連線到某個機器上,但實際上沒有(其它引用同一字串的值修改會直接導致該字串中內容的修改)。可變字串也可能導致反射安全問題,因為他的引數也是字串。

    class Test{  
        public static void main(String[] args){  
            HashSet<StringBuilder> hs=new HashSet<StringBuilder>();  
            StringBuilder sb1=new StringBuilder("aaa");  
            StringBuilder sb2=new StringBuilder("aaabbb");  
            hs.add(sb1);  
            hs.add(sb2);    //這時候HashSet裡是{"aaa","aaabbb"}  

            StringBuilder sb3=sb1;  
            sb3.append("bbb");  //這時候HashSet裡是{"aaabbb","aaabbb"}  
            System.out.println(hs);  
        }  
    }  
    //Output:  
    //[aaabbb, aaabbb]  

StringBuilder型變數sb1和sb2分別指向了堆內的字面量”aaa”和”aaabbb”。把他們都插入一個HashSet。到這一步沒問題。但如果後面我把變數sb3也指向sb1的地址,再改變sb3的值,因為StringBuilder沒有不可變性的保護,sb3直接在原先”aaa”的地址上改。導致sb1的值也變了。這時候,HashSet上就出現了兩個相等的鍵值”aaabbb”。破壞了HashSet鍵值的唯一性。所以千萬不要用可變型別做HashMap和HashSet鍵值。
不可變物件天生就是執行緒安全的。在併發的場景下,多個執行緒去讀一個資源時,不會出現什麼問題,但是多個執行緒去寫一個資源時就有危險。不可變物件不能被重寫所以是安全的。

本內容部分參考原址