一、什麼是不可變模式?
不可變,顧名思義,就是物件建立之後就不能夠變化嘛!更具體地說,就是物件建立之後它的屬性值
不能夠發生變化!所有對原物件的操作都會返回原物件的拷貝。那麼在java中怎麼做到這一點呢?答案就是使用final
關鍵字。下面我將講講如何設計出一個`immutable``物件。
設計一個不可變類應該遵循以下幾點:
- 1、類的所有屬性宣告為
private
,去除掉所有的setter
方法,防止外界直接對其進行修改 - 2、類的宣告採用
final
進行修飾,保證沒有父類對其修改 - 3、類的屬性宣告為
final
,如果物件型別為可變型別,應對其重新包裝,重新new
一個物件返回
下面是一個不可變類例項:
package com.wokao66;
/**
* 不可變類
* @author: huangjiawei
* @since: 2018年4月2日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
//採用fianl修飾,防止子類繼承
public final class Immutable {
/**
* 所有的屬性private且final
*/
private final String name;
private final int age;
/**
* 構造方法
* @param name
* @param age
*/
public Immutable(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 去除所有的setter方法
*/
public String getName() {
return name;
}
public int getAge() {
return age;
}
/**
* 將年齡增加10歲
* @param newAge
* @return
*/
public Immutable addAge(int newAge) {
/**
* 重新返回一個物件
*/
return new Immutable(this.getName(), newAge + this.getAge());
}
public static void main(String[] args) {
Immutable immutable = new Immutable("a", 12);
System.err.println(immutable.getAge());
Immutable newImmutable = immutable.addAge(10);
System.err.println(immutable.getAge());
System.err.println(newImmutable.getAge());
}
}
複製程式碼
執行結果:
12
12
22
複製程式碼
二、一不小心就設計成可變物件了!
如果上面的不可變類這樣設計,那麼就變成可變的了!
package com.wokao66;
import java.util.Date;
/**
* 人生處處有驚喜,一不小心就掉進陷阱裡
* @author: huangjiawei
* @since: 2018年4月2日
* @version: $Revision$ $Date$ $LastChangedBy$
*/
public final class Mutable {
/**
* 所有的屬性private且final
*/
private final String name;
private final int age;
private final Date birthday;
/**
* 構造方法
* @param name
* @param age
*/
public Mutable(String name, int age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
/**
* 去除所有的setter方法
*/
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Date getBirthday() {
return birthday;
}
/**
* 將年齡增加10歲
* @param newAge
* @return
*/
public Mutable addAge(int newAge) {
/**
* 重新返回一個物件
*/
return new Mutable(this.getName(), newAge + this.getAge(), this.birthday);
}
public static void main(String[] args) {
Date birthday = new Date();
Mutable xiaoming = new Mutable("小明", 21, birthday);
System.err.println("小明的生日為 : " + xiaoming.getBirthday());
//我設定下我的生日,你會發現我的生日居然可以改變
birthday.setTime(System.currentTimeMillis() + 1000000000);
System.err.println("小明的生日為 : " + xiaoming.getBirthday());
}
}
複製程式碼
輸出如下:
小明的生日為 : Mon Apr 02 15:33:44 CST 2018
小明的生日為 : Sat Apr 14 05:20:24 CST 2018
複製程式碼
可見結果發生了變化,因為Date
是可變型別的。將構造方法修改如下:
/**
* 構造方法
* @param name
* @param age
*/
public Mutable(String name, int age, Date birthday) {
this.name = name;
this.age = age;
//對於可變型別的屬性,初始化的時候應該重新生成一個
//this.birthday = new Date(birthday.getTime());
this.birthday = birthday;
}
複製程式碼
輸出如下:
小明的生日為 : Mon Apr 02 15:36:24 CST 2018
小明的生日為 : Mon Apr 02 15:36:24 CST 2018
複製程式碼
二、不可變模式優缺點及應用場景
優點:
- 1、因為是不可變的,所以不允許程式對其進行修改,避免了程式中修改資料帶來的異常產生
- 2、由於物件是不可變的,減少了執行緒同步帶來的開銷
缺點:
- 1、每次返回都建立新的物件,記憶體會有一定的開銷,不容易被垃圾回收器回收,造成資源的浪費
應用場景:
- 1、不適合大物件、且建立頻繁的場景,因為物件大且建立頻繁會容易導致記憶體洩漏
- 2、適合表示抽象資料型別(如數字、列舉型別或顏色)的值
- 3、適合在多執行緒環境中進行同步而不需要考慮執行緒同步