一什么是不可变模式?
不可变, 顾名思义, 就是对象创建之后就不能够变化嘛! 更具体地说, 就是对象创建之后它的属性值不能够发生变化! 所有对原对象的操作都会返回原对象的拷贝那么在 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 10
二一不小心就设计成可变对象了!
如果上面的不可变类这样设计, 那么就变成可变的了!
- 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 适合在多线程环境中进行同步而不需要考虑线程同步
来源: https://juejin.im/post/5ac1cbde518825558723b6e9