在java中String類為什么要設(shè)計(jì)成final?

String很多實(shí)用的特性褪子,比如說(shuō)“不可變性”量淌,是工程師精心設(shè)計(jì)的藝術(shù)品!藝術(shù)品易碎嫌褪!用final就是拒絕繼承呀枢,防止世界被熊孩子破壞,維護(hù)世界和平笼痛!

1. 什么是不可變裙秋?

String不可變很簡(jiǎn)單,如下圖缨伊,給一個(gè)已有字符串"abcd"第二次賦值成"abcedl"摘刑,不是在原內(nèi)存地址上修改數(shù)據(jù),而是重新指向一個(gè)新對(duì)象刻坊,新地址枷恕。

image

2. String為什么不可變?

翻開(kāi)JDK源碼谭胚,java.lang.String類起手前三行徐块,是這樣寫(xiě)的:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {/** String本質(zhì)是個(gè)char數(shù)組. 而且用final關(guān)鍵字修飾.*/private final char value[];  ... ...}

首先String類是用final關(guān)鍵字修飾,這說(shuō)明String不可繼承灾而。再看下面胡控,String類的主力成員字段value是個(gè)char[ ]數(shù)組,而且是用final修飾的绰疤。final修飾的字段創(chuàng)建以后就不可改變铜犬。 有的人以為故事就這樣完了,其實(shí)沒(méi)有轻庆。因?yàn)殡m然value是不可變癣猾,也只是value這個(gè)引用地址不可變。擋不住Array數(shù)組是可變的事實(shí)余爆。Array的數(shù)據(jù)結(jié)構(gòu)看下圖

image

也就是說(shuō)Array變量只是stack上的一個(gè)引用纷宇,數(shù)組的本體結(jié)構(gòu)在heap堆。String類里的value用final修飾蛾方,只是說(shuō)stack里的這個(gè)叫value的引用地址不可變像捶。沒(méi)有說(shuō)堆里array本身數(shù)據(jù)不可變上陕。看下面這個(gè)例子拓春,

final int[] value={1,2,3}int[] another={4,5,6};value=another;    //編譯器報(bào)錯(cuò)释簿,final不可變

value用final修飾,編譯器不允許我把value指向堆區(qū)另一個(gè)地址硼莽。但如果我直接對(duì)數(shù)組元素動(dòng)手庶溶,分分鐘搞定。

final int[] value={1,2,3};value[2]=100;  //這時(shí)候數(shù)組里已經(jīng)是{1,2,100}

或者更粗暴的反射直接改懂鸵,也是可以的偏螺。

final int[] array={1,2,3};Array.set(array,2,100); //數(shù)組也被改成{1,2,100}

所以String是不可變,關(guān)鍵是因?yàn)镾UN公司的工程師匆光,在后面所有String的方法里很小心的沒(méi)有去動(dòng)Array里的元素套像,沒(méi)有暴露內(nèi)部成員字段。

private final char value[]這一句里终息,private的私有訪問(wèn)權(quán)限的作用都比f(wàn)inal大夺巩。而且設(shè)計(jì)師還很小心地把整個(gè)String設(shè)成final禁止繼承,避免被其他人繼承后破壞采幌。所以String是不可變的關(guān)鍵都在底層的實(shí)現(xiàn)劲够,而不是一個(gè)final⌒莅考驗(yàn)的是工程師構(gòu)造數(shù)據(jù)類型征绎,封裝數(shù)據(jù)的功力。

3. 不可變有什么好處磨取?

這個(gè)最簡(jiǎn)單的原因人柿,就是為了安全。

示例1

package _12_01字符串;public class 為什么String要設(shè)計(jì)成不可變類你 { public static void main(String[] args) {        String a, b, c;     a = "test";     b = a;      c = b;      String processA = processA(a);      String processB = processB(b);      String processC = processC(c);      System.out.println(processA);       System.out.println(processB);       System.out.println(processC);   }   static String processA(String str){     return str + "A";   }   static String processB(String str){     return str + "B";   }   static String processC(String str){     return str + "C";   }}//OUTPUT// testA//testB//testC

當(dāng)String支持非可變性的時(shí)候忙厌,它們的值很好確定凫岖,不管調(diào)用哪個(gè)方法,都互不影響逢净。

如果String是可變的哥放,就可能如下例,我們使用StringBuffer來(lái)模擬String是可變的

package _12_01字符串;public class 為什么String要設(shè)計(jì)成不可變類2 { public static void main(String[] args) {        StringBuffer a, b, c;       a = new StringBuffer("test");       b = a;      c = b;      String processA = processA(a);      String processB = processB(b);      String processC = processC(c);      System.out.println(processA);       System.out.println(processB);       System.out.println(processC);   }   static String processA(StringBuffer str){       return str.append("A").toString();  }   static String processB(StringBuffer str){       return str.append("B").toString();  }   static String processC(StringBuffer str){       return str.append("C").toString();  }}//OUTPUT// testA//testAB//testABC

能看出b=a,c=b;程序員的本意是希望變量是不變的爹土。所以String不可變的安全性就體現(xiàn)在這里甥雕。實(shí)際上StringBuffer的作用就是起到了String的可變配套類角色。

示例2

再看下面這個(gè)HashSet用StringBuilder做元素的場(chǎng)景胀茵,問(wèn)題就更嚴(yán)重了社露,而且更隱蔽。

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);    //這時(shí)候HashSet里是{"aaa","aaabbb"}StringBuilder sb3=sb1;sb3.append("bbb");  //這時(shí)候HashSet里是{"aaabbb","aaabbb"}System.out.println(hs);}}//Output://[aaabbb, aaabbb]

StringBuilder型變量sb1和sb2分別指向了堆內(nèi)的字面量"aaa"和"aaabbb"琼娘。把他們都插入一個(gè)HashSet峭弟。到這一步?jīng)]問(wèn)題附鸽。但如果后面我把變量sb3也指向sb1的地址,再改變sb3的值瞒瘸,因?yàn)镾tringBuilder沒(méi)有不可變性的保護(hù)坷备,sb3直接在原先"aaa"的地址上改。導(dǎo)致sb1的值也變了挨务。這時(shí)候击你,HashSet上就出現(xiàn)了兩個(gè)相等的鍵值"aaabbb"。破壞了HashSet鍵值的唯一性谎柄。所以千萬(wàn)不要用可變類型做HashMap和HashSet鍵值。

不可變性支持線程安全

還有一個(gè)大家都知道惯雳,就是在并發(fā)場(chǎng)景下朝巫,多個(gè)線程同時(shí)讀一個(gè)資源,是不會(huì)引發(fā)竟態(tài)條件的石景。只有對(duì)資源做寫(xiě)操作才有危險(xiǎn)劈猿。不可變對(duì)象不能被寫(xiě),所以線程安全潮孽。

不可變性支持字符串常量池

最后別忘了String另外一個(gè)字符串常量池的屬性揪荣。像下面這樣字符串one和two都用字面量"something"賦值。它們其實(shí)都指向同一個(gè)內(nèi)存地址往史。

String one = "someString";String two = "someString";
image

這樣在大量使用字符串的情況下仗颈,可以節(jié)省內(nèi)存空間,提高效率椎例。但之所以能實(shí)現(xiàn)這個(gè)特性挨决,String的不可變性是最基本的一個(gè)必要條件。要是內(nèi)存里字符串內(nèi)容能改來(lái)改去订歪,這么做就完全沒(méi)有意義了脖祈。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市刷晋,隨后出現(xiàn)的幾起案子盖高,更是在濱河造成了極大的恐慌,老刑警劉巖眼虱,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喻奥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蒙幻,警方通過(guò)查閱死者的電腦和手機(jī)映凳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)邮破,“玉大人诈豌,你說(shuō)我怎么就攤上這事仆救。” “怎么了矫渔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵彤蔽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我庙洼,道長(zhǎng)顿痪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任油够,我火速辦了婚禮蚁袭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘石咬。我一直安慰自己揩悄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布鬼悠。 她就那樣靜靜地躺著删性,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焕窝。 梳的紋絲不亂的頭發(fā)上蹬挺,一...
    開(kāi)封第一講書(shū)人閱讀 51,775評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音它掂,去河邊找鬼巴帮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛群发,可吹牛的內(nèi)容都是我干的晰韵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼熟妓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雪猪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起起愈,我...
    開(kāi)封第一講書(shū)人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤只恨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后抬虽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體官觅,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年阐污,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了休涤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖功氨,靈堂內(nèi)的尸體忽然破棺而出序苏,到底是詐尸還是另有隱情,我是刑警寧澤捷凄,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布忱详,位于F島的核電站,受9級(jí)特大地震影響跺涤,放射性物質(zhì)發(fā)生泄漏匈睁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一桶错、第九天 我趴在偏房一處隱蔽的房頂上張望航唆。 院中可真熱鬧,春花似錦牛曹、人聲如沸佛点。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至鸳玩,卻和暖如春阅虫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背不跟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工颓帝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窝革。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓购城,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親虐译。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘪板,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容