解讀不可變的String

在JDK API的對String的描述中,有以下對String的介紹:

String 類代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作為此類的實例實現(xiàn)。</br>
字符串是常量;它們的值在創(chuàng)建之后不能更改飒箭。字符串緩沖區(qū)支持可變的字符串。因為 String 對象是不可變的蜒灰,所以可以共享弦蹂。

在這里指出了String對象時不可變的,那么下面我們來具體分析為什么java中的String是不可變的强窖。

不可變對象

不可變對象是指一個對象的狀態(tài)在對象被創(chuàng)建之后就不再變化凸椿。不可改變的意思就是說:不能改變對象內(nèi)的成員變量,包括基本數(shù)據(jù)類型的值不能改變翅溺,引用類型的變量不能指向其他的對象脑漫,引用類型指向的對象的狀態(tài)也不能改變髓抑。

那么會有人說,以下的代碼不就改變了String嗎:

public static void main(String[] args) {
    String s = "ABCDEF";
    System.out.println("s = " + s);
    
    s = "123456";
    System.out.println("s = " + s);
}

打印的結(jié)果:</br>
s = ABCDEF</br>
s = 123456

  • 你看窿撬,從結(jié)果上看启昧,確實是如此叙凡,s的值改變了劈伴,這不是和我們說的不可變對象String完全不搭邊。
  • 其實握爷,這里存在一個誤區(qū)跛璧,s呢 ,僅僅是一個String對象的引用新啼,并不是對象本身追城。對象在內(nèi)存中是一塊內(nèi)存區(qū),而s只是一個引用燥撞,它指向了一個具體的對象座柱。
  • 在創(chuàng)建String對象的時候,s指向的是內(nèi)存中的"ABDCEF",當(dāng)執(zhí)行語句s = "123456"后物舒,其實又創(chuàng)建了一個新的對象"123456",而s重新指向了這個新的對象色洞,同時原來的"ABCDEF"并沒有發(fā)生改變,仍保存在內(nèi)存中冠胯。

在這里火诸,對象引用和C語言中的指針其實很像,它們都是存放的對象在內(nèi)存中的地址值荠察,只是在java中的引用喪失了部分靈活性置蜀,比如就不能像C中的指針那樣進(jìn)行運算。

那么又會有朋友說啦悉盆,String類中有修改自身的方法又是怎樣呢盯荤,比如不是有一個replaced(替換)方法么

public static void main(String[] args) {
    String s = "ABCDEF";
    System.out.println("s = " + s);
    
    s.replace("A", "a");
    System.out.println("s = " + s);
}

打印結(jié)果:</br>
s = ABCDEF</br>
s = ABCDEF</br>

結(jié)果是不是沒有改變,是否出乎你的意料呢焕盟,現(xiàn)在改一下代碼:

public static void main(String[] args) {
    String s = "ABCDEF";
    System.out.println("s = " + s);
    
    s = s.replace("A", "a");
    System.out.println("s = " + s);
}

打印結(jié)果:</br>
s = ABCDEF</br>
s = aBCDEF</br>

這時候你會發(fā)現(xiàn)秋秤,結(jié)果確實是替換了,但是并不是說String對象發(fā)生了改變京髓,而是執(zhí)行語句s = s.replace("A", "a")將引用s指向了一個新的String對象("aBCDEF")航缀,原來的"ABCDEF"還在內(nèi)存并沒有被改變。

如何實現(xiàn)不可變

要了解String的不可變性堰怨,首先看一下String類中都有哪些成員變量芥玉,

/** The value is used for character storage. */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0
  • 從注釋中,可以看出value變量是String用來存放字符的备图,還有一個hash成員變量是該String對象的哈希值緩存灿巧。
  • value赶袄,hash這兩個成員變量都是private final修飾的,而String類中并沒有提供set/get等公共方法來修改這些值抠藕,所以String類的外部無法修改String饿肺,同時final保證了在String內(nèi)部,只要值初始化了盾似,也不能被改變敬辣。
  • 所以認(rèn)為String對象是不可變的。
  • 而在java中零院,數(shù)據(jù)也是對象溉跃,那么value也僅僅是一個引用,指向真正的數(shù)組對象告抄,這將在下面的文章中用到撰茎。

不可變對象的好處

  1. 不可變對象更容易構(gòu)造,測試與使用打洼;
  2. 真正不可變對象都是線程安全的龄糊;
  3. 不可變對象的使用沒有副作用(沒有保護(hù)性拷貝);
  4. 對象變化的問題得到了避免募疮;
  5. 不可變對象的失敗都是原子性的炫惩;
  6. 不可變對象更容易緩存,且可以避免null引用酝锅;
  7. 不可變對象可以避免時間上的耦合诡必;

String,StringBuffer搔扁,StringBuilder爸舒,都是final類,不允許被繼承稿蹲,在本質(zhì)上都是字符數(shù)組扭勉,不同的是,String的長度是不可變的而后兩者長度可變苛聘,在進(jìn)行連接操作時涂炎,String每次返回一個新的String實例,而StringBuffer和StringBuilder的append方法直接返回this设哗,所以當(dāng)進(jìn)行大量的字符串連接操作時唱捣,不推薦使用String,因為它會產(chǎn)生大量的中間String對象网梢。

StringBuffer和StringBuilder的一個區(qū)別是震缭,StringBuffer在append方法前增加了一個synchronized修飾符,以起到同步的作用战虏,為此也降低了執(zhí)行效率拣宰;若要在toString方法中使用循環(huán)党涕,使用StringBuilder

那么,真的不可變么

現(xiàn)在我們已經(jīng)知道了String的成員變量是private final 的巡社,也就是初始化之后不可改變的膛堤。同時也提到value這個成員變量其實也是一個引用,指向真正的數(shù)組內(nèi)存地址晌该,不能改變它的引用指向肥荔,我們能不能直接改變內(nèi)存數(shù)組中的數(shù)據(jù)呢,那么就需要獲取到value气笙,而value是私有的次企,我們怎么獲取呢?對潜圃!就是用反射。

public static void reflectString() throws Exception{
    
    String s = "ABCDEF";
    System.out.println("s = " + s);
    
    Field valueField = s.getClass().getDeclaredField("value");
    valueField.setAccessible(true);
    
    char[] value = (char[]) valueField.get(s);
    value[0] = 'a';
    value[2] = 'c';
    value[4] = 'e';
    
    System.out.println("s = " + s);
}

打印結(jié)果:</br>
s = ABCDEF</br>
s = aBcDeF</br>
結(jié)果顯而易見舟茶!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谭期,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吧凉,更是在濱河造成了極大的恐慌隧出,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阀捅,死亡現(xiàn)場離奇詭異,居然都是意外死亡饲鄙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門帆谍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人轴咱,你說我怎么就攤上這事汛蝙。” “怎么了朴肺?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長西土。 經(jīng)常有香客問我,道長器瘪,這世上最難降的妖魔是什么翠储? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任绘雁,我火速辦了婚禮,結(jié)果婚禮上庐舟,老公的妹妹穿的比我還像新娘。我一直安慰自己挪略,他們只是感情好滔岳,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谱煤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刘离。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天茧痕,我揣著相機與錄音,去河邊找鬼踪旷。 笑死豁辉,一個胖子當(dāng)著我的面吹牛令野,可吹牛的內(nèi)容都是我干的秋忙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼堵幽,長吁一口氣:“原來是場噩夢啊……” “哼弹澎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起苦蒿,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后团滥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竿屹,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡拱燃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年力惯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片父晶。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖尝苇,靈堂內(nèi)的尸體忽然破棺而出俺猿,到底是詐尸還是另有隱情茎匠,我是刑警寧澤押袍,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布凯肋,位于F島的核電站,受9級特大地震影響圈盔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驱敲,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一宽闲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧容诬,春花似錦、人聲如沸览徒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芦缰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饺藤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工罗丰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留再姑,地道東北人萌抵。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓绍填,卻偏偏與公主長得像,于是被迫代替她去往敵國和親讨永。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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