不可變類__不可變類

來源:互聯網
上載者:User


不可變類

先來科普2個概念,可變類和不可變類。

1),不可變類的意思就是建立該類的執行個體後,該執行個體的執行個體變數是不可改變的。Java提供的8個封裝類和String類都是不可變類,當建立他們的執行個體後,其執行個體的執行個體變數是不

可改變的。

2),與不可變類對應的是可變類,可變類的含義是該類的執行個體變數是可變的。大部分時候所建立的類都是可變類,特別是JavaBean,因為總是在其執行個體變數提供了setter和

getter方法。

看下面的代碼:

Double d = new Double(6.5);String linkin = "LinkinPark";
上面的程式建立了一個Double對象和一個String對象,並為這兩個對象傳入了6.5和"LinkinPark"字串作為參數,那麼Double類和String類肯定需要提供執行個體變數來儲存這兩個

參數,但程式無法修改這兩個執行個體變數的值,因此Double類和String類沒有提供修改它們的方法。


如果需要建立自訂的不可變類,要遵守以下的規則:

1),使用private和final修飾該類的成員變數

2),提供帶參數的構造器,用於根據傳入參數來初始化該類的成員變數

3),僅為該類提供getter方法,不要提供setter方法,因為普通的方法不能改變這個類的屬性

4),如果有必要,重寫equals和hashcode方法。

/** * 不可變類 *  * @author LinkinPark *  *         <pre> *         1,屬性使用private final修飾 *         2,構造器對屬性賦值 *         3,只提供get方法,不提供set方法 *         4,如果有需要就重寫equals和hashCode方法 *         </pre> */public class LinkinPark{private final String name;private final Integer age;public LinkinPark(String name, Integer age){super();this.name = name;this.age = age;}public String getName(){return name;}public Integer getAge(){return age;}public static void main(String[] args){new LinkinPark("LinkinPark", 25);}}

與可變類相比,不可變類的執行個體在整個生命週期中永遠出於初始化階段,它的執行個體變數不可改變,因此對不可變類的執行個體的控制將更加簡單。

前面介紹final關鍵字時提到,當使用final修飾參考型別變數時,僅表示這個參考型別變數不可被重新賦值,但參考型別變數所指向的對象依然可以改變。

這就產生了一個問題,當建立一個不可變類時,如果它包含成員變數的類型是可變的,那麼其對象的成員變數的值依然是可變的,那這個不可變類其實是失敗的。

看下面的例子:

/** * 參考型別的變數導致不可變類失敗 *  * @author LinkinPark */public class LinkinPark{private final String name;private final Linkin linkin;public LinkinPark(String name, Linkin linkin){super();this.name = name;this.linkin = linkin;}public String getName(){return name;}public Linkin getLinkin(){return linkin;}@Overridepublic String toString(){return getClass().getSimpleName() + " [name=" + name + ", linkin=" + linkin + "]";}public static void main(String[] args){Linkin linkin = new Linkin();linkin.setName("NightWish1");linkin.setAge(25);LinkinPark linkinPark = new LinkinPark("LinkinPark", linkin);System.out.println(linkinPark);linkin.setAge(24);linkin.setName("NightWish2");System.out.println(linkinPark);// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish2, age=24]]}}class Linkin{private String name;private Integer age;public String getName(){return name;}public void setName(String name){this.name = name;}public Integer getAge(){return age;}public void setAge(Integer age){this.age = age;}@Overridepublic String toString(){return getClass().getSimpleName() + " [name=" + name + ", age=" + age + "]";}}
運行上面的代碼,我們也看到了,參考型別的變數導致我建立了一個失敗的不可變類。那應該要怎麼做呢。看下面代碼:

/** * 參考型別的變數導致不可變類失敗 * 所以要針對參考型別的變數做專門的處理 *  * <pre> * 1,構造器中不要直接使用傳入的參考型別變數,自己取值然後重新new一次 * 2,參考型別的變數用來儲存剛才那個初始化的對象 * 3,防止get方法直接返回剛才那個變數從而改變引用的那個對象,同樣的方式處理 * </pre> *  * @author LinkinPark */public class LinkinPark{private final String name;private final Linkin linkin;/** * @param name * @param linkin */public LinkinPark(String name, Linkin linkin){super();this.name = name;this.linkin = new Linkin(linkin.getName(), linkin.getAge());}public String getName(){return name;}public Linkin getLinkin(){return new Linkin(linkin.getName(), linkin.getAge());}@Overridepublic String toString(){return getClass().getSimpleName() + " [name=" + name + ", linkin=" + linkin + "]";}public static void main(String[] args){Linkin linkin = new Linkin("NightWish1", 25);LinkinPark linkinPark = new LinkinPark("LinkinPark", linkin);System.out.println(linkinPark);linkin.setAge(24);linkin.setName("NightWish2");System.out.println(linkinPark);// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish2, age=24]]}}class Linkin{private String name;private Integer age;public Linkin(String name, Integer age){super();this.name = name;this.age = age;}public String getName(){return name;}public void setName(String name){this.name = name;}public Integer getAge(){return age;}public void setAge(Integer age){this.age = age;}@Overridepublic String toString(){return getClass().getSimpleName() + " [name=" + name + ", age=" + age + "]";}}


緩衝執行個體的不可變類

不可變類的執行個體狀態不可改變,可以很方便的被多個對象所共用。如果程式經常需要使用相同的不可變執行個體,則應該考慮緩衝這種不可變類的執行個體。畢竟重複建立相同的對象沒

有太大的意義,而且加大系統開銷。如果可能,應該將已經建立的不可變類的執行個體進行緩衝。

緩衝是軟體設計中一個非常有用的模式,緩衝的實現方式也有很多種,不同的實現方式可能存在較大的效能差別,關於緩衝的效能問題和實現方式我會在後面的部落格中整理一個

分類,此處不做贅述。

OK,前面我已經使用了不可變類LinkinPark,現在我自己用一個數組寫一個緩衝池,從而實現一個緩衝LinkinPark執行個體的緩衝池。

當然也可以直接在LinkinPark類中寫緩衝,這樣子將實現一個緩衝自己執行個體的不可變類。

public class LinkinParkCache{// 定義一個數組+一個下標+數組最大容量private static int POS_INDEX = 0;private static final int MAX_SIZE = 10;private static final LinkinPark[] cache = new LinkinPark[MAX_SIZE];// 定義一個name標示用來重寫hashCode方法private final String name;private LinkinParkCache(String name){this.name = name;}public String getName(){return name;}public static LinkinPark valueOf(String name){// 1,迴圈擷取緩衝的執行個體for (int i = 0; i < cache.length; i++){if (cache[i] != null && cache[i].getName().equals(name)){return cache[i];}}// 2,迴圈結束後沒有找見執行個體,則向緩衝中添加if (POS_INDEX == MAX_SIZE){cache[0] = new LinkinPark(name, new Linkin("LinkinPark", 25));POS_INDEX = 1;}else{cache[POS_INDEX++] = new LinkinPark(name, new Linkin("LinkinPark", 25));}return cache[POS_INDEX - 1];}@Overridepublic boolean equals(Object obj){if (this == obj){return true;}if (obj != null && obj.getClass() == this.getClass()){LinkinPark linkinPark = (LinkinPark) obj;return linkinPark.getName().equals(this.getName());}return false;}@Overridepublic int hashCode(){return name.hashCode();}public static void main(String[] args){LinkinPark linkin = LinkinParkCache.valueOf("林肯的緩衝池");LinkinPark linkinPark = LinkinParkCache.valueOf("林肯的緩衝池");// 下面代碼輸出true,使用了緩衝System.out.println(linkin == linkinPark);}}/** * 不可變類 *  * @author LinkinPark */class LinkinPark{private final String name;private final Linkin linkin;public LinkinPark(String name, Linkin linkin){super();this.name = name;this.linkin = new Linkin(linkin.getName(), linkin.getAge());}public String getName(){return name;}public Linkin getLinkin(){return new Linkin(linkin.getName(), linkin.getAge());}@Overridepublic String toString(){return getClass().getSimpleName() + " [name=" + name + ", linkin=" + linkin + "]";}public static void main(String[] args){Linkin linkin = new Linkin();linkin.setName("NightWish1");linkin.setAge(25);LinkinPark linkinPark = new LinkinPark("LinkinPark", linkin);System.out.println(linkinPark);linkin.setAge(24);linkin.setName("NightWish2");System.out.println(linkinPark);// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]}}/** * 不可變類中的參考型別變數的定義 *  * @author LinkinPark */class Linkin{private String name;private Integer age;public Linkin(){super();}public Linkin(String name, Integer age){super();this.name = name;this.age = age;}public String getName(){return name;}public void setName(String name){this.name = name;}public Integer getAge(){return age;}public void setAge(Integer age){this.age = age;}@Overridepublic String toString(){return getClass().getSimpleName() + " [name=" + name + ", age=" + age + "]";}}


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.