一、什么是不可变模式?
不可变,顾名思义,就是对象创建之后就不能够变化嘛!更具体地说,就是对象创建之后它的属性值
不能够发生变化!所有对原对象的操作都会返回原对象的拷贝。那么在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、适合在多线程环境中进行同步而不需要考虑线程同步