Java爲什麼把String設計成不可變的?

原文來自:Why String is immutable in Java?

這是來自Java和Android面試中經常被問的一道題目。在Java語言中,String是字符串常量,StringBuilder是字符串變量,爲什麼將String設計成不可變的(immutable)?

在Java裏String類是不可變的,不可變類是一個不能被修改實例的類,實例創建時所有的信息都被初始化,並且不可被修改。這樣的設計也有很多優點,本文從內存、同步和數據結構方面總結了爲什麼把String類設計成不可變的原因。

1、字符串常量池的需要

字符串常量池是一個特殊的存儲區域。當創建字符串時,如果字符串已經存在於池中,則將返回現有字符串的引用,而不會創建新的對象。
下面的代碼只在堆中創建一個字符串對象:

String string1 = "abcd";
String string2 = "abcd";

示意圖如下:
這裏寫圖片描述
如果String是可變的,用一個引用改變字符串將導致其他引用的值錯誤。

2、HashCode緩存

在Java裏存儲一個字符串通常需要使用到HashCode值,例如,在一個HashMap或一個HashSet。一成不變可以保證HashCode的唯一性,不用考慮他的變化。這意味着不需要每次使用它的時候都計算HashCode,提高操作效率。
在String類中有如下代碼:

private int hash;//this is used to cache 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";

在這個實例中,如果字符串是可變的,他將違背集合的設計(集合包含非重複元素)。當然,上面的示例僅僅用作演示用,在真正的字符串類中沒有值字段。

4、安全

字符串被廣泛用於許多Java類的參數,比如網絡連接、打開文件等等。如果字符串不是不可變的,連接或者文件的更改將會導致嚴重的安全威脅。他認爲連接到一個機器,並不是。因爲參數是字符串的,字符串可變會導致在反射中的安全問題。(不可變性使之能夠在不同的線程間共享,同時確保線程安全,幫助使用者減少線程同步的開發工作)
示例如下:

boolean connect(string s){
    if (!isSecure(s)) { 
        throw new SecurityException(); 
    }
    //here will cause problem, if s is changed before this by using other references.    
    causeProblem(s);
}

5、不可變對象自然是線程安全的

因爲不可變對象是不能被改變的,所以他們可以自由的共享多線程。這就消除了他們進行同步的要求。

總結

總之,String被設計成不可變的是出於效率和安全的因素。這也是爲什麼很多情況下首選類是不可變類的原因。

當然,不同的使用場景體現出不可變特性的優勢也不相同。大家可以回憶自己項目中的使用場景,多加思考這類問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章