原文 Why String is immutable in Java?
在java里String是不可變的亭姥。一個(gè)不可變的對象是一個(gè)簡單的類挖胃,它的實(shí)例不會(huì)被修改臊恋。當(dāng)一個(gè)不可變的類的實(shí)例被創(chuàng)建時(shí)冈钦,所有的信息已經(jīng)在實(shí)例中被初始化了居凶,而且這些信息不能被修改。 不可變的類有很多優(yōu)勢躯喇。本文總結(jié)了為什么String被設(shè)計(jì)為不可變的辫封,分別從內(nèi)存,同步和數(shù)據(jù)結(jié)構(gòu)三個(gè)方面進(jìn)行了說明廉丽。
1. 字符串池
推薦這篇文章 Java字符串池(String Pool)深度解析
字符串池是方法區(qū)中的一個(gè)特殊存儲區(qū)倦微。當(dāng)一個(gè)字符串被創(chuàng)建的時(shí)候,如果字符串池中已經(jīng)存在這個(gè)字符串值正压,就直接返回已存在字符串的引用欣福,否則,就創(chuàng)建一個(gè)新的字符串到字符串池中焦履。
下面代碼將只會(huì)在堆中創(chuàng)建一個(gè)字符串對象:
String string1 = "abcd";
String string2 = "abcd";
如圖所示:
如果字符串是可變的拓劝,改變引用的字符串將會(huì)導(dǎo)致其他引用此字符串是錯(cuò)誤的雏逾。
2. 緩存Hashcode
在java中經(jīng)常使用字符串的哈希碼。例如:在HashMap 或HashSet 中郑临,字符串的不可變性校套,保證了哈希碼是一致的,從而不必?fù)?dān)心哈希碼會(huì)改變牧抵。這意味著,每次使用哈希碼都不必重新計(jì)算一次侨把。這樣犀变,會(huì)更加高效。
在String類中秋柄,有如下代碼:
private int hash;//this is used to cache hash code.
以上代碼中hash變量中就保存了一個(gè)String對象的hashcode获枝,因?yàn)镾tring類不可變,所以一旦對象被創(chuàng)建骇笔,該hash值也無法改變省店。所以,每次想要使用該對象的hashcode的時(shí)候笨触,直接返回即可懦傍。
3. 使其他類的使用更加方便
為了說明這一點(diǎn),請看以下代碼:
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";
在這個(gè)例子中芦劣,如果String是可變的粗俱,它的值改變將會(huì)違反set的設(shè)計(jì)(set中包含了不重復(fù)的元素)。當(dāng)然虚吟,以上代碼僅僅是個(gè)演示寸认,實(shí)際String類中,并沒有value字段串慰。
4.安全
在許多java類中偏塞,字符串被廣泛使用為參數(shù)。比如:網(wǎng)絡(luò)連接邦鲫,打開文件等灸叼。如果字符串是可變的,則一個(gè)連接或文件將被更改掂碱,這可能會(huì)導(dǎo)致嚴(yán)重的安全威脅怜姿。該方法認(rèn)為它連接到一臺機(jī)器,但可能并沒有疼燥〔茁可變的字符串可能在反射中也會(huì)造成安全問題,因?yàn)樗膮?shù)是字符串醉者。
代碼示例:
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.不可變的對象但狭,自然是線性安全的
因?yàn)椴豢勺儗ο蟛荒鼙桓呐虼丝梢栽诙鄠€(gè)線程之間自由共享。不需要任何同步處理立磁。
總之呈队,把字符串設(shè)計(jì)成不可變的,主要目的是為了高效和安全唱歧。這也是為什么許多情況下更偏愛選擇不可變的類的原因宪摧。