1. 程式人生 > >如何讓Java類不可變

如何讓Java類不可變

不可變類:一旦建立,狀態無法改變
關於建立不可變類有很多規則,下面一一介紹這些規則:
目錄

  1. 定義不可變類的益處
  2. 定義不可變類指南

定義不可變的益處

  1. 構造簡單,便於測試和使用
  2. 不可變類自然是執行緒安全的,無需關心多執行緒和同步問題
  3. 不需要實現clone
  4. 可以延遲載入,快取它的返回值
  5. 由於不可變可以用於Map的key和Set的元素(set元素不能重複)
  6. 當作為屬性時,不需要深度clone

如何讓類不可變

在Java文件中,有關於如何定義不可變類指南: click here

  1. 不提供setter方法,setter方法用於修改屬性和物件引用
    這個原則闡述了在你類定義的所有可變屬性中,不提供setter方法,setter方法意味著你能夠改變這個屬性的狀態。必須阻止提供setter方法
  2. 所有的屬性修飾新增private和final
    這是另外一種增加不可變的方式,屬性宣告為private為了在類之外不能夠被訪問到,final修飾符為了讓你不能隨便的改變它們
  3. 不允許子類重寫方法
    最簡單的方式宣告類為final,final類不允許被重寫
  4. 當屬性中存在可變物件變數時,要特別留意
    永遠銘記你的物件變數,不是可變的就是不可變的(這句好像是廢話。。),識別出來可變物件,對可變物件的內容進行copy,並建立一個新物件賦值給它,這樣保證可變物件的不可變,通過直接copy物件內容的形式,保持資料不可變
  5. 來點優雅的,定義一個private的構造方法,通過 工廠方法構造物件

    只說太抽象,還是來點例項痛快


import java.util.Date;

/**
* Always remember that your instance variables will be either mutable or immutable.
* Identify them and return new objects with copied content for all mutable objects.
* Immutable variables can be returned safely without extra effort.
* */
public final class ImmutableClass
{
/** * Integer class is immutable as it does not provide any setter to change its content * */ private final Integer immutableField1; /** * String class is immutable as it also does not provide setter to change its content * */ private final String immutableField2; /** * Date class is mutable as it provide setters to change various date/time parts * */ private final Date mutableField; //Default private constructor will ensure no unplanned construction of class private ImmutableClass(Integer fld1, String fld2, Date date) { this.immutableField1 = fld1; this.immutableField2 = fld2; this.mutableField = new Date(date.getTime()); } //Factory method to store object creation logic in single place public static ImmutableClass createNewInstance(Integer fld1, String fld2, Date date) { return new ImmutableClass(fld1, fld2, date); } //Provide no setter methods /** * Integer class is immutable so we can return the instance variable as it is * */ public Integer getImmutableField1() { return immutableField1; } /** * String class is also immutable so we can return the instance variable as it is * */ public String getImmutableField2() { return immutableField2; } /** * Date class is mutable so we need a little care here. * We should not return the reference of original instance variable. * Instead a new Date object, with content copied to it, should be returned. * */ public Date getMutableField() { return new Date(mutableField.getTime()); } @Override public String toString() { return immutableField1 +" - "+ immutableField2 +" - "+ mutableField; } }

驗證以上不可變類:


import java.util.Date;

public class MainTest
{
    public static void main(String[] args)
    {
        ImmutableClass im = ImmutableClass.createNewInstance(100,"test", new Date());
        System.out.println(im);
        tryModification(im.getImmutableField1(),im.getImmutableField2(),im.getMutableField());
        System.out.println(im);
    }

    private static void tryModification(Integer immutableField1, String immutableField2, Date mutableField)
    {
        immutableField1 = 10000;
        immutableField2 = "test changed";
        mutableField.setDate(10);
    }
}

輸出結果如下:
100 - test - Tue Jun 09 23:14:01 CST 2015
100 - test - Tue Jun 09 23:14:01 CST 2015

從輸出中可以看出:即使通過物件引用改變物件變數,值依然不改變,因此類是不可變類