每日一学:设计模式之原型模式
一、什么是原型模式原型模式是创建型设计模式核心思想通过复制拷贝一个已存在的实例原型来创建新的对象而不是通过new关键字重新创建。核心特点隐藏对象创建的复杂过程性能比new创建对象更高尤其是大对象 / 复杂初始化可以在运行时动态创建对象Java 实现核心Cloneable接口 clone()方法二、Java 实现方式Java 提供了原生支持分两种拷贝浅拷贝默认只复制基本类型引用类型共用同一个对象深拷贝完全复制包括引用类型的对象相互独立1. 浅拷贝步骤类实现Cloneable接口标记接口无方法重写Object类的clone()方法代码示例package prototype; // 原型类实现 Cloneable 接口 class User implements Cloneable { private String name; private int age; public User(String name, int age) { this.name name; this.age age; } // 正常重写代码但是返回的是Object为了方便使用。我们自己重写下 // Override // protected Object clone() throws CloneNotSupportedException { // return super.clone(); // } Override protected User clone() { try{ return (User) super.clone(); }catch (CloneNotSupportedException e){ throw new RuntimeException(); } } Override public String toString() { return name:name-age:age; } }测试2、浅拷贝存在的问题无法拷贝引用类型举例我们给User中再创建一个对象Role(省略get、set)public class Role { private String roleName; public Role(String roleName) { this.roleName roleName; } }每个用户一个角色(省略get、set)// 原型类实现 Cloneable 接口 class User implements Cloneable { private String name; private int age; private Role role; public User(String name, int age, Role role) { this.name name; this.age age; this.role role; } Override protected User clone() { try{ return (User) super.clone(); }catch (CloneNotSupportedException e){ throw new RuntimeException(); } } Override public String toString() { return name:name-age:age; } }测试结果package prototype; public class Test { public static void main(String[] args) { // 1. 创建原型对象 Role role new Role(超级管理员); User prototype new User(张三, 20,role); // 2. 拷贝新对象 User cloneUser prototype.clone(); System.out.println(prototype); // name:张三-age:20-roleName:超级管理员 System.out.println(cloneUser); // name:张三-age:20-roleName:超级管理员 // 注意这里我修改拷贝对象的角色 cloneUser.getRole().setRoleName(普通用户); // 再来输出一次 System.out.println(prototype); // name:张三-age:20-roleName:普通用户 System.out.println(cloneUser); // name:张三-age:20-roleName:普通用户 } }这里我们可以看到我修改的是拷贝对象但是原对象也被修改了为什么出现这样的结果因为在我们使用浅拷贝时拷贝的对象的引用类型实际指向的还是原对对象的引用类型的地址他们共用这个引用类型所以修改一个会导致都被修改3、 深拷贝解决引用类型共用问题浅拷贝缺陷如果对象里有引用类型如对象、集合拷贝后会共用同一个引用修改一个会影响另一个。深拷贝目标所有层级都完全复制互不影响。Java 深拷贝两种实现手动递归拷贝序列化 / 反序列化前拷贝后手动处理引用类型示例序列化深拷贝class Role2 implements Serializable { private String roleName; // 构造、get/set 省略 } class User2 implements Serializable { private String name; private Role2 role; // 引用类型 // 深拷贝方法序列化 public User2 deepClone() { try { // 写入流 ByteArrayOutputStream bos new ByteArrayOutputStream(); ObjectOutputStream oos new ObjectOutputStream(bos); oos.writeObject(this); // 读取流 ByteArrayInputStream bis new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois new ObjectInputStream(bis); return (User2) ois.readObject(); } catch (Exception e) { throw new RuntimeException(e); } } }测试public class Test { public static void main(String[] args) { User2 prototype new User2(张三, new Role2(超级管理员)); User2 cloneUser prototype.deepClone(); // 修改拷贝对象的引用类型 cloneUser.getRole().setRoleName(普通用户); // 原对象不受影响深拷贝成功 System.out.println(prototype.getRole().getRoleName()); System.out.println(cloneUser.getRole().getRoleName()); } }三、浅拷贝 vs 深拷贝特性浅拷贝深拷贝实现难度简单稍复杂引用类型共用同一个对象完全复制独立对象性能高较低使用场景无引用类型 / 允许共享引用类型需要独立绝大多数业务场景四、优缺点优点性能极高比new快尤其复杂对象简化创建不用关心对象初始化细节运行时创建可动态拷贝不同状态的对象不用依赖具体类松耦合缺点每一个类都要考虑是否支持拷贝深拷贝代码复杂循环引用的对象拷贝会出问题五、经典使用场景Java 中1、ArrayList / HashMap等集合的拷贝方法2、Spring 中的scopeprototypebeanprototype 多例 Bean 本身是新建的但它里面 Autowired 注入的对象默认全是单例永远共用同一个引用3、大量重复对象创建如报表、配置对象4、多线程环境下避免共享对象总结原型模式 拷贝对象创建新实例Java 实现Cloneableclone()浅拷贝快但引用类型共享深拷贝完全独立推荐序列化实现适合创建成本高、需要大量相似对象的场景