原文來自: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被設計成不可變的是出於效率和安全的因素。這也是爲什麼很多情況下首選類是不可變類的原因。
當然,不同的使用場景體現出不可變特性的優勢也不相同。大家可以回憶自己項目中的使用場景,多加思考這類問題。