<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手?jǐn)]了一個Java的不可變對象,很哇塞!

          共 4423字,需瀏覽 9分鐘

           ·

          2021-11-04 19:03

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

          收到讀者小 R 的私信后,我就總感覺自己有一種義不容辭的責(zé)任,非要把 immutable 類說明白不可!

          PS:star 這種事,只能求,不求沒效果,鐵子們,《Java 程序員進(jìn)階之路》在 GitHub 上已經(jīng)收獲了 523 枚星標(biāo),鐵子們趕緊去點點了,沖 600 star!

          https://github.com/itwanger/toBeBetterJavaer

          01、什么是不可變類

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

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

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

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

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

          02、常見的不可變類

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

          1)常量池的需要

          字符串常量池是 Java 堆內(nèi)存中一個特殊的存儲區(qū)域,當(dāng)創(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 是不可變的,就可以在多個線程之間共享,不需要同步處理。

          因此,當(dāng)我們調(diào)用 String 類的任何方法(比如說 trim()、substring()toLowerCase())時,總會返回一個新的對象,而不影響之前的值。

          String?cmower?=?"沉默王二,一枚有趣的程序員";
          cmower.substring(0,4);
          System.out.println(cmower);//?沉默王二,一枚有趣的程序員

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

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

          03、手?jǐn)]不可變類

          看懂一個不可變類也許容易,但要創(chuàng)建一個自定義的不可變類恐怕就有點難了。但知難而進(jìn)是我們作為一名優(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ā)進(jìn)階之路");
          ????????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ā)進(jìn)階之路',?price=79}
          促銷價:Book{name='Web全棧開發(fā)進(jìn)階之路',?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 對象就不會再被修改了。此時,運(yùn)行 WriterDemo,就會發(fā)現(xiàn)價格不再發(fā)生變化了。

          定價:Book{name='Web全棧開發(fā)進(jìn)階之路',?price=79}
          促銷價:Book{name='Web全棧開發(fā)進(jìn)階之路',?price=79}

          04、總結(jié)

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

          這是《Java 程序員進(jìn)階之路》專欄的第 66 篇。Java 程序員進(jìn)階之路,風(fēng)趣幽默、通俗易懂,對 Java 初學(xué)者極度友好和舒適??,內(nèi)容包括但不限于 Java 語法、Java 集合框架、Java IO、Java 并發(fā)編程、Java 虛擬機(jī)等核心知識點。

          點擊上方名片,發(fā)送消息「03」 就可以獲取《Java 程序員進(jìn)階之路》的 PDF 版了,一起成為更好的 Java 工程師。

          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产在线一区视频 | 91精品国产乱码久久 - | 免费观看一区二区三区 | 翔田千里在线一区二区三区 | 麻豆国产一区二区三区四区 |