自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

[[433249]]

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

https://github.com/itwanger/toBeBetterJavaer

01、什么是不可變類

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

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

自從有了多線程,生產(chǎn)力就被無(wú)限地放大了,所有的程序員都愛(ài)它,因?yàn)閺?qiáng)大的硬件能力被充分地利用了。但與此同時(shí),所有的程序員都對(duì)它心生忌憚,因?yàn)橐徊恍⌒?,多線程就會(huì)把對(duì)象的狀態(tài)變得混亂不堪。

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

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

02、常見(jiàn)的不可變類

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

1)常量池的需要

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

2)hashCode 的需要

因?yàn)樽址遣豢勺兊?,所以在它?chuàng)建的時(shí)候,其 hashCode 就被緩存了,因此非常適合作為哈希值(比如說(shuō)作為 HashMap 的鍵),多次調(diào)用只返回同一個(gè)值,來(lái)提高效率。

3)線程安全

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

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

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

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

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

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

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

接下來(lái),就請(qǐng)和我一起,來(lái)自定義一個(gè)不可變類吧。一個(gè)不可變誒,必須要滿足以下 4 個(gè)條件:

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

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

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

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

按照以上條件,我們來(lái)自定義一個(gè)簡(jiǎn)單的不可變類 Writer。

  1. public final class Writer { 
  2.     private final String name
  3.     private final int age; 
  4.  
  5.     public Writer(String nameint age) { 
  6.         this.name = name
  7.         this.age = age; 
  8.     } 
  9.  
  10.     public int getAge() { 
  11.         return age; 
  12.     } 
  13.  
  14.     public String getName() { 
  15.         return name
  16.     } 

Writer 類是 final 的,name 和 age 也是 final 的,沒(méi)有 setter 方法。

OK,據(jù)說(shuō)這個(gè)作者分享了很多博客,廣受讀者的喜愛(ài),因此某某出版社找他寫(xiě)了一本書(shū)(Book)。Book 類是這樣定義的:

  1. public class Book { 
  2.     private String name
  3.     private int price; 
  4.  
  5.     public String getName() { 
  6.         return name
  7.     } 
  8.  
  9.     public void setName(String name) { 
  10.         this.name = name
  11.     } 
  12.  
  13.     public int getPrice() { 
  14.         return price; 
  15.     } 
  16.  
  17.     public void setPrice(int price) { 
  18.         this.price = price; 
  19.     } 
  20.  
  21.     @Override 
  22.     public String toString() { 
  23.         return "Book{" + 
  24.                 "name='" + name + '\'' + 
  25.                 ", price=" + price + 
  26.                 '}'
  27.     } 

2 個(gè)字段,分別是 name 和 price,以及 getter 和 setter,重寫(xiě)后的 toString() 方法。然后,在 Writer 類中追加一個(gè)可變對(duì)象字段 book。

  1. public final class Writer { 
  2.     private final String name
  3.     private final int age; 
  4.     private final Book book; 
  5.  
  6.     public Writer(String nameint age, Book book) { 
  7.         this.name = name
  8.         this.age = age; 
  9.         this.book = book; 
  10.     } 
  11.  
  12.     public int getAge() { 
  13.         return age; 
  14.     } 
  15.  
  16.     public String getName() { 
  17.         return name
  18.     } 
  19.  
  20.     public Book getBook() { 
  21.         return book; 
  22.     } 

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

完成以上工作后,我們來(lái)新建一個(gè)測(cè)試類,看看 Writer 類的狀態(tài)是否真的不可變。

  1. public class WriterDemo { 
  2.     public static void main(String[] args) { 
  3.         Book book = new Book(); 
  4.         book.setName("Web全棧開(kāi)發(fā)進(jìn)階之路"); 
  5.         book.setPrice(79); 
  6.  
  7.         Writer writer = new Writer("沉默王二",18, book); 
  8.         System.out.println("定價(jià):" + writer.getBook()); 
  9.         writer.getBook().setPrice(59); 
  10.         System.out.println("促銷(xiāo)價(jià):" + writer.getBook()); 
  11.     } 

程序輸出的結(jié)果如下所示:

  1. 定價(jià):Book{name='Web全棧開(kāi)發(fā)進(jìn)階之路', price=79} 
  2. 促銷(xiāo)價(jià):Book{name='Web全棧開(kāi)發(fā)進(jìn)階之路', price=59} 

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

如果一個(gè)不可變類中包含了可變類的對(duì)象,那么就需要確保返回的是可變對(duì)象的副本。也就是說(shuō),Writer 類中的 getBook() 方法應(yīng)該修改為:

  1. public Book getBook() { 
  2.     Book clone = new Book(); 
  3.     clone.setPrice(this.book.getPrice()); 
  4.     clone.setName(this.book.getName()); 
  5.     return clone; 

這樣的話,構(gòu)造方法初始化后的 Book 對(duì)象就不會(huì)再被修改了。此時(shí),運(yùn)行 WriterDemo,就會(huì)發(fā)現(xiàn)價(jià)格不再發(fā)生變化了。

  1. 定價(jià):Book{name='Web全棧開(kāi)發(fā)進(jìn)階之路', price=79} 
  2. 促銷(xiāo)價(jià):Book{name='Web全棧開(kāi)發(fā)進(jìn)階之路', price=79} 

04、總結(jié)

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

 

責(zé)任編輯:武曉燕 來(lái)源: 沉默王二
相關(guān)推薦

2021-10-04 09:29:41

對(duì)象池線程池

2022-01-21 07:35:06

LRU緩存java

2022-03-01 11:38:51

RPC框架后端

2021-05-14 13:30:17

Mybatis分表插件

2021-10-27 06:49:34

線程池Core函數(shù)

2020-11-04 07:56:19

工具Linux 翻譯

2022-02-14 07:34:23

工具類GET、POST

2022-03-01 08:21:32

工具類代碼封裝網(wǎng)絡(luò)請(qǐng)求

2022-02-08 09:09:45

智能指針C++

2024-04-08 07:58:11

Python數(shù)據(jù)類型字符串

2022-11-26 08:03:57

StringJava

2022-04-22 08:22:50

MVCCMySQLC++

2021-11-29 07:47:57

gRPCGUI客戶端

2021-02-03 07:56:08

版本游戲邏輯

2021-04-27 07:52:19

StarterSpring Boot配置

2023-11-27 09:33:22

2015-03-19 15:04:06

2020-12-09 08:34:24

css生成器設(shè)計(jì)師

2021-06-14 09:34:23

對(duì)象存儲(chǔ)存儲(chǔ)

2022-12-20 08:32:02

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)