二哥,你能給我說說為什么 String 是 immutable
          類(不可變對象)嗎?我想研究它,想知道為什么它就不可變了,這種強烈的愿望就像想研究浩瀚的星空一樣。但無奈自身功力有限,始終覺得霧里看花終隔一層。二哥你的文章總是充滿趣味性,我想一定能夠說明白,我也一定能夠看明白,能在接下來寫一寫嗎?

          收到讀者小 R 的私信后,我就總感覺自己有一種義不容辭的責任,非要把 immutable 類說明白,否則我就怎么地——你說了算!

          01、什么是不可變類

          一個類的對象在通過構(gòu)造方法創(chuàng)建后如果狀態(tài)不會再被改變,那么它就是一個不可變(immutable)類。它的所有成員變量的賦值僅在構(gòu)造方法中完成,不會提供任何
          setter 方法供外部類去修改。


          還記得《神雕俠侶》中小龍女的古墓嗎?隨著那一聲巨響,僅有的通道就被無情地關(guān)閉了。別較真那個密道,我這么說只是為了打開你的想象力,讓你對不可變類有一個更直觀的印象。


          自從有了多線程,生產(chǎn)力就被無限地放大了,所有的程序員都愛它,因為強大的硬件能力被充分地利用了。但與此同時,所有的程序員都對它心生忌憚,因為一不小心,多線程就會把對象的狀態(tài)變得混亂不堪。

          為了保護狀態(tài)的原子性、可見性、有序性,我們程序員可以說是竭盡所能。其中,synchronized(同步)關(guān)鍵字是最簡單最入門的一種解決方案。

          假如說類是不可變的,那么對象的狀態(tài)就也是不可變的。這樣的話,每次修改對象的狀態(tài),就會產(chǎn)生一個新的對象供不同的線程使用,我們程序員就不必再擔心并發(fā)問題了。

          02、常見的不可變類

          提到不可變類,幾乎所有的程序員第一個想到的,就是 String 類。那為什么 String 類要被設(shè)計成不可變的呢?

          1)常量池的需要

          字符串常量池是 Java 堆內(nèi)存中一個特殊的存儲區(qū)域,當創(chuàng)建一個 String
          對象時,假如此字符串在常量池中不存在,那么就創(chuàng)建一個;假如已經(jīng)存,就不會再創(chuàng)建了,而是直接引用已經(jīng)存在的對象。這樣做能夠減少 JVM 的內(nèi)存開銷,提高效率。

          2)hashCode 的需要

          因為字符串是不可變的,所以在它創(chuàng)建的時候,其 hashCode 就被緩存了,因此非常適合作為哈希值(比如說作為 HashMap
          的鍵),多次調(diào)用只返回同一個值,來提高效率。

          3)線程安全

          就像之前說的那樣,如果對象的狀態(tài)是可變的,那么在多線程環(huán)境下,就很容易造成不可預(yù)期的結(jié)果。而 String
          是不可變的,就可以在多個線程之間共享,不需要同步處理。

          因此,當我們調(diào)用 String 類的任何方法(比如說 trim()、substring()、toLowerCase()
          )時,總會返回一個新的對象,而不影響之前的值。
          String?cmower?=?"沉默王二,一枚有趣的程序員";
          cmower.substring(0,4);
          System.out.println(cmower);//?沉默王二,一枚有趣的程序員

          雖然調(diào)用 substring() 方法對 cmower 進行了截取,但 cmower 的值沒有改變。

          除了 String 類,包裝器類 Integer、Long 等也是不可變類。

          03、自定義不可變類

          看懂一個不可變類也許容易,但要創(chuàng)建一個自定義的不可變類恐怕就有點難了。但知難而進是我們作為一名優(yōu)秀的程序員不可或缺的品質(zhì),正因為不容易,我們才能真正地掌握它。

          接下來,就請和我一起,來自定義一個不可變類吧。一個不可變誒,必須要滿足以下 4 個條件:

          1)確保類是 final 的,不允許被其他類繼承。

          2)確保所有的成員變量(字段)是 final 的,這樣的話,它們就只能在構(gòu)造方法中初始化值,并且不會在隨后被修改。

          3)不要提供任何 setter 方法。

          4)如果要修改類的狀態(tài),必須返回一個新的對象。

          按照以上條件,我們來自定義一個簡單的不可變類 Writer。
          public?final?class?Writer?{
          ????private?final?String?name;
          ????private?final?int?age;

          ????public?Writer(String?name,?int?age)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????}

          ????public?int?getAge()?{
          ????????return?age;
          ????}

          ????public?String?getName()?{
          ????????return?name;
          ????}
          }

          Writer 類是 final 的,name 和 age 也是 final 的,沒有 setter 方法。

          OK,據(jù)說這個作者分享了很多博客,廣受讀者的喜愛,因此某某出版社找他寫了一本書(Book)。Book 類是這樣定義的:
          public?class?Book?{
          ????private?String?name;
          ????private?int?price;

          ????public?String?getName()?{
          ????????return?name;
          ????}

          ????public?void?setName(String?name)?{
          ????????this.name?=?name;
          ????}

          ????public?int?getPrice()?{
          ????????return?price;
          ????}

          ????public?void?setPrice(int?price)?{
          ????????this.price?=?price;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"Book{"?+
          ????????????????"name='"?+?name?+?'\''?+
          ????????????????",?price="?+?price?+
          ????????????????'}';
          ????}
          }

          2 個字段,分別是 name 和 price,以及 getter 和 setter,重寫后的 toString() 方法。然后,在 Writer
          類中追加一個可變對象字段 book。
          public?final?class?Writer?{
          ????private?final?String?name;
          ????private?final?int?age;
          ????private?final?Book?book;

          ????public?Writer(String?name,?int?age,?Book?book)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????????this.book?=?book;
          ????}

          ????public?int?getAge()?{
          ????????return?age;
          ????}

          ????public?String?getName()?{
          ????????return?name;
          ????}

          ????public?Book?getBook()?{
          ????????return?book;
          ????}
          }

          并在構(gòu)造方法中追加了 Book 參數(shù),以及 Book 的 getter 方法。

          完成以上工作后,我們來新建一個測試類,看看 Writer 類的狀態(tài)是否真的不可變。
          public?class?WriterDemo?{
          ????public?static?void?main(String[]?args)?{
          ????????Book?book?=?new?Book();
          ????????book.setName("Web全棧開發(fā)進階之路");
          ????????book.setPrice(79);

          ????????Writer?writer?=?new?Writer("沉默王二",18,?book);
          ????????System.out.println("定價:"?+?writer.getBook());
          ????????writer.getBook().setPrice(59);
          ????????System.out.println("促銷價:"?+?writer.getBook());
          ????}
          }

          程序輸出的結(jié)果如下所示:
          定價:Book{name='Web全棧開發(fā)進階之路',?price=79}
          促銷價:Book{name='Web全棧開發(fā)進階之路',?price=59}

          糟糕,Writer 類的不可變性被破壞了,價格發(fā)生了變化。為了解決這個問題,我們需要為不可變類的定義規(guī)則追加一條內(nèi)容:

          如果一個不可變類中包含了可變類的對象,那么就需要確保返回的是可變對象的副本。也就是說,Writer 類中的 getBook() 方法應(yīng)該修改為:
          public?Book?getBook()?{
          ????Book?clone?=?new?Book();
          ????clone.setPrice(this.book.getPrice());
          ????clone.setName(this.book.getName());
          ????return?clone;
          }

          這樣的話,構(gòu)造方法初始化后的 Book 對象就不會再被修改了。此時,運行 WriterDemo,就會發(fā)現(xiàn)價格不再發(fā)生變化了。
          定價:Book{name='Web全棧開發(fā)進階之路',?price=79}
          促銷價:Book{name='Web全棧開發(fā)進階之路',?price=79}

          04、總結(jié)

          不可變類有很多優(yōu)點,就像之前提到的 String
          類那樣,尤其是在多線程環(huán)境下,它非常的安全。盡管每次修改都會創(chuàng)建一個新的對象,增加了內(nèi)存的消耗,但這個缺點相比它帶來的優(yōu)點,顯然是微不足道的——無非就是撿了西瓜,丟了芝麻。

          如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回復(fù)【666】【1024】更有我為你精心準備的 500G
          高清教學視頻(已分門別類),以及大廠技術(shù)牛人整理的面經(jīng)一份。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                中国一级淫片 | 美女被艹视频网站 | 麻豆精品免费观看 | 国精产品一区一区三区mba下载 | 青久草视频在线 |