一、Immutable介绍
不可变对象永远不会发生改变,其字段的值只在构造函数运行时设置一次,其后就不会再改变。例如JDK中常见的两种基本数据类型String和Integer,它们都是不可变对象。
二、Immutable的设计原则(需要满足的条件)
①.确保类不会被覆写,即该类不会被继承,实现这一点要加上修饰符final;或者使用静态工厂创建方法,确保构造函数私有的。
②.所有的字段必须是私有的,并且加上修饰符final。
③.所有的设置只需简单的一个构造函数即可,不要使用空构造函数,不要使用Javabean风格的构造函数。
④.不要提供任何可以改变对象的方法,即取消setter方法。
三、适用场景
①.被建模对象的状态变化不频繁:设置一个专门的线程用于被建模对象状态发生变化时创建新的不可变对象。而其他线程只是读取不可变对象的状态。
②.同时对一组相关的数据进行写操作,需要保证原子性,通常的做法是使用显示锁,但若采用Immutable Object模式,将这一组相关的数据组合成一个不可变对象,则对这一组数据的操作就可以无需加显示锁也能保证原子性,这样既简化了编程,又提高了代码运行效率
四、代码实例
①.Person类:不可变类,必须满足不可变对象的四个条件
package chapter2.immutable;
/**
* @author czd
* 不可变对象类必须满足的条件:1.只能存在getter方法,不能存在setter方法
* 2.变量必须定义为final和private
* 3.这个类是不可被继承的,即定义此类为final
*/
final public class Person {
private final String name;
private final String address;
public Person(String name , String address){
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
②.UsePeronThread类
package chapter2.immutable;
/**
* @author czd
*/
public class UsePersonThread extends Thread{
private Person person;
public UsePersonThread(Person person){
this.person = person;
}
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName() + " person >>>>>" +person.toString());
}
}
}
③.ImutableClient 类
package chapter2.immutable;
/**
* @author czd
*/
public class ImutableClient {
public static void main(String[] args) {
Person person = new Person("Guang" , "GuangZhou");
for (int i = 0; i < 5; i++){
new UsePersonThread(person).start();
}
}
}
运行结果
可以看出,在没有使用synchronized关键字类修饰方法的情况下,无论多少个线程使用这个Person.class都不会出现数据错误的问题。
五、Immutable的优点
①.不可变对象易于构造,使用和测试。
②.自动地为线程安全型,避免了一些繁杂的同步问题。
③.不需要一个复制构造函数(copy constructor)。
④.不需要一个clone的实现。
⑤.一旦构造以后,具有类型不变性,不用检查。
六、需要注意的问题
①.被建模对象的变更比较频繁的时候尽量不要使用
②.使用等效或者近似的不可变对象
③.防御性复制