使可變性最小

第十五條:使可變性最小


1.什么是不可變類?

(1)需要的所有參數必須在實例化的時候都傳進去。
(2)對象中所有信息在對象的整個生命周期中都保持不變。

2.使類不可變的原則

(1)不要提供任何修改對象狀態的方法。
(2)保證類不會被繼承。
(3)使所有的域都是final類型的。
(4)使所有的域都是私有類型的。
(5)確保對于任何可變組件的互斥性。意思就是,確保在該類的外部不會獲取到該類中可變對象的引用。比如下面這個例子:

public class MyObject{
  private final List<String> list = new ArrayList<>;//可變對象
  public List<String> getList() {
    return new ArrayList(list);
  }
  public void setList(List<String> list) {
    this.list = new ArrayList(list);
  }
}

3.不可變類的優點

(1)不可變類簡單。
不可變類只有一種狀態,就是它被創建出來時候的狀態,如果你要根據這個類進行一系列復雜操作,那么這個操作無論在什么時候結果都是相同的,所以你可以直接將結果緩存起來,在下一次執行同樣操作的時候取出來,而不必再進行下一次操作。
(2)不可變類本質上是線程安全的,它不需要同步鎖。
(3)對于不可變類,你永遠都不需要實現拷貝方法。拷貝方法對它來說是沒有意義的。
(4)不可變類可以被自由的共享。

4.不可變對象的缺點

(1)對于每一個不同的值都需要創建一個新的對象。

5.缺點的彌補辦法

(1)先猜測一下經常用到哪些多步驟的操作,然后將它們作為基本數據類型提供。比如Integer,它將值為-128到127的對象緩存起來,但調用valueOf(int i)的時候,直接從緩存中拿,不用再重復創建提高效率。

(2)我們可以創建一個可變配套類。例如String是一個不可變類,它的可變配套類為StirngBuilder和StringBuffer。下面是一個例子,我們現在實現一個復數類,對外提供一個相加的方法如下:

public class Complex {
  private final double re;//實部
  private final double im;//虛部

  private Complex(double re, double im) {
    this.re = re;
    this.im = im;
  }

  public static Complex valueOf(double re, double im) {
    return new Complex(re, im);
  }

  public double realPart() {
    return re;
  }
  public double imaginaryPart() {
    return im;
  }
  
  //復數相加
  public Complex add(Complex c) {
    return new Complex(re + c.re, im + c.im);
  }
}

我們可以為他創建一個配套類:

public class ComplexBuilder {
  private double re;
  private double im;
  
  private ComplexBuider(double re, double im) {
    this.re = re;
    this.im = im;
  }
  public static ComplexBuider newInstance(Complex c) {
    return new ComplexBuilder(c.realPart(), c.imaginaryPart());
  }

  public void add(Complex c) {
    this.re = this.re + c.realPart();
    this.im = this.im + c.imaginaryPart();
  }

  public Complex toComplex() {
    return Complex.valueOf(this.re, this.im);
  }
}

在客戶端中我們如果需要用一個復數和另一個復數相加100次,我們如果不用ComplexBuilder的話就像下面這樣,算上最開始穿件的兩個實例,我們將會創建102個實例:

public class Test {
  @Test
  public void addNoBuiderTest() throws Exception{
    Complex c1 = Complex.valueOf(1, 2);
    Complex c2 = Complex.valueOf(2, 3);
    for (int i = 0 ; i < 100 ; i++) {
      c1 = c1.add(c2);
    }
  }
}

現在改用ComplexBuilder,現在我們只會創建4個實例:

public class Test {
  @Test
  public void addNoBuiderTest() throws Exception{
    Complex c1 = Complex.valueOf(1, 2);
    Complex c2 = Complex.valueOf(2, 3);
    ComplexBuilder cb = ComplexBuider.newInstance(c1);
    for (int i = 0 ; i < 100 ; i++) {
      cb.add(c2);
    }
    c1 = cb.toComplex();
  }
}

6.總結

(1)堅決不要為每個getter都生成setter。
(2)能將類做成不可變的就做成不可變的。
(3)一般比較小的值類都是需要做成不可變的。
(4)對于一些比較大的值類盡量考慮實現成不可變類。
(5)性能方面很有必要的時候才需提供配套類。
(6)如果真的不能作為不可變類,那就盡量限制其可變性
(7)對于一個類的初始化只能在構造器或是靜態工廠中完成。也就是說類的初始化操作(賦值之類的)只能執行一次,就是在構造器或是靜態工廠中。這一點參考java類庫中的TimerTask類。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 不可變類是指:其實例不能被修改的類。每個實例中包含的所有信息都必須在創建該實例的時候就提供,并在對象的整個生命周期...
    慧執行閱讀 629評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,462評論 25 708
  • 目錄 第二章 創建和銷毀對象 1 考慮用靜態工廠方法替代構造器 對于代碼來說, 清晰和簡潔是最重要的. 代碼應該被...
    高廣超閱讀 1,472評論 0 12
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,767評論 18 399
  • 公元1085年二月,宋神宗趙頊駕崩,其年僅十歲的兒子趙煦即位,即宋哲宗。由于年幼,由神宗母親宣仁高太后垂簾聽政。高...
    青年小圣閱讀 318評論 0 3