在Java中,字符串是一個不可變的類,一個不可變的類指的是它的實例對象不能被修改。所有關於這個對象的信息在這個對象被創建時已初始化,且它們不能被修改。不可變類有很多優勢,這篇文章總結了字符串被設計成不可變類的原因。一個合理的解釋依賴於對內存模型,同步,數據結構等的深度理解。
字符串常量池是方法區中的一塊特別存儲區域。當需要創建一個字符串時,如果它的值在字符串常量池中已存在,那麼常量池中的該字符串引用將被直接返回,而不會創建一個新的字符串對象返回其引用。
String string1 = "abcd"; String string2 = "abcd";
如果字符串是可變的,那麼通過一個引用改變字符串的值,其它該字符串對象的引用將得到錯誤的值。
字符串的hashcode在java中經常被使用,如HashMap。不可變保證了hashcode會一直相同,所以它可以緩存起來而不需要擔心改變。這也意味著,每次使用String的時候,不需要重新計算hashcode,這使得性能更加有效。
在String類中,有一個hash字段用於緩存hash code。
private int hash;//this is used to cache hash code.
為了更加具體的說明,看下下面的程序:
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";
在這個例子中,如果字符串是可變的,那麼它的value會被改變,這將違背Set集合的設計(不允許重復元素)。當然,這僅僅是一個示例,實際情況,String是沒有value字段的。
字符串作為參數被廣泛使用,如網絡連接,文件打開操作等。如果字符串是可變的,那麼網絡連接或者文件將會被改變,這將引起嚴重的安全威脅。某個方法被認為連接到某一台機器,但是卻被改變了,連接到其它機器。在反射中,可變的字符串同樣會引起安全問題,因為參數同樣是字符串。
如下代碼是一個例子:
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); }
因為不可變對象不能被修改,它們可以被多線程共享,這消除了同步操作。
總的來說,字符串被設計成不可變是出於性能和安全考慮,這也是為什麼通常不可變類比較受歡迎。
譯文鏈接:http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/