在开发过程中,我们经常会遇到“不可变对象”的需求,所谓不可变对象(Immutable Object),顾名思义就是:对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化。
比如业务中有一个关于员工的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class Employee{ private String SN; private String name; private Date birthday;
public Employee(String SN, String name, Date birthday){ this.SN = SN; this.name = name; this.birthday = birthday; }
public String getSN(){ return SN; }
public String getName(){ return name; }
public Date getBirthday(){ return birthday; } public static void main(String[] args) throws InterruptedException { Date birth = new Date(); Employee emp = new Employee("123", "John", birth); System.out.println(emp.getBirthday()); Thread.sleep(1000); birth.setTime(System.currentTimeMillis()); System.out.println(emp.getBirthday()); Thread.sleep(1000); birth.setTime(System.currentTimeMillis()); System.out.println(emp.getBirthday()); } }
|
运行结果:
1 2 3
| Wed Aug 08 21:03:37 CST 2018 Wed Aug 08 21:03:38 CST 2018 Wed Aug 08 21:03:39 CST 2018
|
通常遵照以下几点来定义不可变对象:
- 所有成员变量必须是
private
- 最好同时用
final
修饰(非必须)
- 不提供能够修改原有对象状态的方法
- 最常见的方式是不提供
setter
方法
- 如果提供修改方法,需要新创建一个对象,并在新创建的对象上进行修改
- 通过构造器初始化所有成员变量,引用类型的成员变量必须进行深拷贝(deep copy)
- getter方法不能对外泄露this引用以及成员变量的引用
- 最好不允许类被继承(非必须)
上面Employee
类中的birthday
属性,传入和传出的都是Date
对象的引用,所以依然可以在类的外部通过引用对birthday
的值进行修改。所以不可变类应该改成下面的样子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class Employee{ private String SN; private String name; private Date birthday;
public Employee(String SN, String name, Date birthday){ this.SN = SN; this.name = name; this.birthday = (Date) birthday.clone(); }
public String getSN(){ return SN; }
public String getName(){ return name; } public Date getBirthday(){ return(Date) birthday.clone(); } public static void main(String[] args) throws InterruptedException { Date birth = new Date(); Employee emp = new Employee("123", "John", birth); System.out.println(emp.getBirthday()); Thread.sleep(1000); birth.setTime(System.currentTimeMillis()); System.out.println(emp.getBirthday()); Thread.sleep(1000); birth.setTime(System.currentTimeMillis()); System.out.println(emp.getBirthday()); } }
|
运行结果:
1 2 3
| Wed Aug 08 21:05:37 CST 2018 Wed Aug 08 21:05:37 CST 2018 Wed Aug 08 21:05:37 CST 2018
|
注:如果类中存在集合对象,可以通过JDK或Guava提供的方法创建不可变集合
Collections.unmodifiableList(List<? extends T> list)
ImmutableList.copyOf(list)