克隆对象的艺术:原型模式与深浅拷贝的完美结合 目录一、原型模式的定义与理解一基本定义二基本理解分析三在Spring中的具体体现二、具体简单实现方式(一)实现原型类具备的三个条件二具体举例三、深拷贝、浅拷贝与原型模式的解决方案一理解复制的含义去除原有的误解二浅拷贝及其带来的问题1.新建一个上司类作为员工类的某个属性2.测试浅拷贝带来的问题三通过深拷贝来解决问题四、原型模式的应用场景分析五、总结参考书籍、文献和资料干货分享感谢您的阅读在软件开发的世界里创建对象似乎是每天的“常规操作”。但你有没有遇到过这样的时刻每次创建对象时都要重新初始化一堆数据当对象的构建过程复杂又繁琐如何才能避免每次都从头开始这时候原型模式Prototype Pattern就像一位聪明的助手悄悄地用克隆的方式帮你复制对象节省了不少时间和精力。你只需要一个“原型”然后通过复制这个原型就可以迅速生成更多对象避免了重复劳动。不过原型模式可不仅仅是简单的“复制粘贴”这么简单。背后涉及的深拷贝和浅拷贝可能让你在实践中一不小心就踩雷。而且原型模式的应用场景可广泛到不只是在创建对象时还可以用在框架设计、对象管理等多个领域。那么如何在项目中正确地运用原型模式避免深浅拷贝的坑呢今天我们将一起揭开原型模式的神秘面纱探索如何实现这个模式并分析它在实际开发中的应用和潜在问题。准备好了吗让我们开始这场“对象复制”的奇妙旅程吧一、原型模式的定义与理解一基本定义在软件系统中有些对象的创建过程较为复杂而且有时候需要频繁创建原型模式通过给出一个原型对象来指明所要创建的对象的类型然后用复制这个原型对象的办法创建出更多同类型的对象这就是原型模式。具体GOF给出的原型模式定义如下使用原型实例指定将要创建的对象类型通过复制这个实例创建新的对象。Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.二基本理解分析原型模式是通过给出一个原型对象来指明所创建的对象的类型然后使用自身实现的克隆接口来复制这个原型对象该模式就是用这种方式来创建出更多同类型的对象。使用这种方式创建新的对象的话就无需再通过 new 实例化来创建对象了。这是因为Object 类的 clone 方法是一个本地方法它可以直接操作内存中的二进制流所以性能相对 new 实例化来说更佳。三在Spring中的具体体现Spring 中Service 默认都是单例的。用了私有全局变量若不想影响下次注入或每次上下文获取 bean就需要用到原型模式我们可以通过以下注解来实现Scope(“prototype”)。二、具体简单实现方式(一)实现原型类具备的三个条件要实现一个原型类需要具备三个条件实现 Cloneable 接口Cloneable 接口与序列化接口的作用类似它只是告诉虚拟机可以安全地在实现了这个接口的类上使用 clone 方法。在 JVM 中只有实现了 Cloneable 接口的类才可以被拷贝否则会抛出 CloneNotSupportedException 异常。重写 Object 类中的 clone 方法在 Java 中所有类的父类都是 Object 类而 Object 类中有一个 clone 方法作用是返回对象的一个拷贝。在重写的 clone 方法中调用 super.clone()默认情况下类不具备复制对象的能力需要调用 super.clone() 来实现。二具体举例此处简单实现一个员工类的原型模式复杂问题及深拷贝、浅拷贝问题暂时不考虑具体实现如下/** * 描述原型模式具体案例 * * author yanfengzhang * date 2020-05-26 10:39 */ Data public class EmployeePrototype implements Cloneable { private String name; /** * 功能描述原型抽象类需要实现Cloneable接口,重写clone方法 * * author yanfengzhang * date 2020-05-26 10:49 */ Override public EmployeePrototype clone() { EmployeePrototype employeePrototype null; try { employeePrototype (EmployeePrototype) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return employeePrototype; } public static void main(String[] args) { EmployeePrototype employeePrototype1 new EmployeePrototype(); employeePrototype1.setName(zhangyanfeng); EmployeePrototype employeePrototype2 employeePrototype1.clone(); employeePrototype2.setName(zhouyi); System.out.println(原型模式测验当前员工1姓名为 employeePrototype1.getName()); System.out.println(原型模式测验当前员工2姓名为 employeePrototype2.getName()); } }通过 clone 方法复制的对象才是真正的对象复制clone 方法赋值的对象完全是一个独立的对象这块涉及深拷贝、浅拷贝的理解。Object 类的 clone 方法是一个本地方法它直接操作内存中的二进制流特别是复制大对象时性能的差别非常明显。三、深拷贝、浅拷贝与原型模式的解决方案一理解复制的含义去除原有的误解有时我们会误以为 Object anew Object();Object ba; 这种形式就是一种对象复制的过程然而这种复制只是对象引用的复制也就是 a 和 b 对象指向了同一个内存地址如果 b 修改了a 的值也就跟着被修改了。与前面的简单实现相对应实现如下/** * 描述对复制的理解分析 * * author yanfengzhang * date 2020-05-26 11:06 */ Data public class EmployeePrototypeTest { private String name; public static void main(String[] args) { EmployeePrototypeTest employeePrototypeTest1 new EmployeePrototypeTest(); employeePrototypeTest1.setName(zhangyanfeng); EmployeePrototypeTest employeePrototypeTest2 employeePrototypeTest1; employeePrototypeTest2.setName(zhouyi); System.out.println(未使用clone方法实现的复制只是对象引用的复制测试结果如下); System.out.println(原型模式测验当前员工1姓名为 employeePrototypeTest1.getName()); System.out.println(原型模式测验当前员工2姓名为 employeePrototypeTest2.getName()); EmployeePrototype employeePrototype1 new EmployeePrototype(); employeePrototype1.setName(zhangyanfeng); EmployeePrototype employeePrototype2 employeePrototype1.clone(); employeePrototype2.setName(zhouyi); System.out.println(使用clone方法实现的复制测试结果如下); System.out.println(原型模式测验当前员工1姓名为 employeePrototype1.getName()); System.out.println(原型模式测验当前员工2姓名为 employeePrototype2.getName()); } }具体输入如下未使用clone方法实现的复制只是对象引用的复制测试结果如下 原型模式测验当前员工1姓名为zhouyi 原型模式测验当前员工2姓名为zhouyi 使用clone方法实现的复制测试结果如下 原型模式测验当前员工1姓名为zhangyanfeng 原型模式测验当前员工2姓名为zhouyi二浅拷贝及其带来的问题在调用 super.clone() 方法之后首先会检查当前对象所属的类是否支持 clone也就是看该类是否实现了 Cloneable 接口。如果支持则创建当前对象所属类的一个新对象并对该对象进行初始化使得新对象的成员变量的值与当前对象的成员变量的值一模一样但对于其它对象的引用以及 List 等类型的成员属性则只能复制这些对象的引用了。简单调用 super.clone() 这种克隆对象方式就是一种浅拷贝。1.新建一个上司类作为员工类的某个属性/** * 描述复合形势下的原型模式对员工增加对应的上司 * * author yanfengzhang * date 2020-05-26 11:20 */ Data public class SupervisorPrototype implements Cloneable { private String name; /** * 功能描述原型抽象类需要实现Cloneable接口,重写clone方法 * * author yanfengzhang * date 2020-05-26 10:49 */ Override public SupervisorPrototype clone() { SupervisorPrototype supervisorPrototype null; try { supervisorPrototype (SupervisorPrototype) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return supervisorPrototype; } }2.测试浅拷贝带来的问题/** * 描述需要注意浅拷贝带来的问题 * * author yanfengzhang * date 2020-05-26 11:19 */ Data public class EmploteeSupervisorPrototype implements Cloneable { private String name; private SupervisorPrototype supervisorPrototype; Override public EmploteeSupervisorPrototype clone() { EmploteeSupervisorPrototype emploteeSupervisorPrototype null; try { emploteeSupervisorPrototype (EmploteeSupervisorPrototype) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return emploteeSupervisorPrototype; } public static void main(String[] args) { SupervisorPrototype supervisorPrototype new SupervisorPrototype(); supervisorPrototype.setName(zhangyanfeng 1); EmploteeSupervisorPrototype emploteeSupervisorPrototype1 new EmploteeSupervisorPrototype(); emploteeSupervisorPrototype1.setName(zhouyi 1); emploteeSupervisorPrototype1.setSupervisorPrototype(supervisorPrototype); EmploteeSupervisorPrototype emploteeSupervisorPrototype2 emploteeSupervisorPrototype1.clone(); emploteeSupervisorPrototype2.setName(zhouyi 2); emploteeSupervisorPrototype2.getSupervisorPrototype().setName(zhangyanfeng 2); System.out.println(员工 emploteeSupervisorPrototype1.getName() 的上司是 emploteeSupervisorPrototype1.getSupervisorPrototype().getName()); System.out.println(员工 emploteeSupervisorPrototype2.getName() 的上司是 emploteeSupervisorPrototype2.getSupervisorPrototype().getName()); } }打印结果如下员工zhouyi 1的上司是zhangyanfeng 2 员工zhouyi 2的上司是zhangyanfeng 2可以发现在我们给员工 zhouyi 2 修改对应上司的时候员工 zhouyi 1的对应上司也跟着被修改了。这就是浅拷贝带来的问题。三通过深拷贝来解决问题我们可以通过深拷贝来解决这种问题其实深拷贝就是基于浅拷贝来递归实现具体的每个对象代码如下/** * 描述通过深拷贝解决浅拷贝所带来的问题 * * author yanfengzhang * date 2020-05-26 13:44 */ Data public class EmploteeSupervisorWithDeepPrototype implements Cloneable { private String name; private SupervisorPrototype supervisorPrototype; Override public EmploteeSupervisorWithDeepPrototype clone() { EmploteeSupervisorWithDeepPrototype emploteeSupervisorWithDeepPrototype null; try { emploteeSupervisorWithDeepPrototype (EmploteeSupervisorWithDeepPrototype) super.clone(); SupervisorPrototype supervisorPrototype this.supervisorPrototype.clone(); emploteeSupervisorWithDeepPrototype.setSupervisorPrototype(supervisorPrototype); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return emploteeSupervisorWithDeepPrototype; } public static void main(String[] args) { SupervisorPrototype supervisorPrototype new SupervisorPrototype(); supervisorPrototype.setName(zhangyanfeng 1); EmploteeSupervisorWithDeepPrototype emploteeSupervisorWithDeepPrototype1 new EmploteeSupervisorWithDeepPrototype(); emploteeSupervisorWithDeepPrototype1.setName(zhouyi 1); emploteeSupervisorWithDeepPrototype1.setSupervisorPrototype(supervisorPrototype); EmploteeSupervisorWithDeepPrototype emploteeSupervisorWithDeepPrototype2 emploteeSupervisorWithDeepPrototype1.clone(); emploteeSupervisorWithDeepPrototype2.setName(zhouyi 2); emploteeSupervisorWithDeepPrototype2.getSupervisorPrototype().setName(zhangyanfeng 2); System.out.println(员工 emploteeSupervisorWithDeepPrototype1.getName() 的上司是 emploteeSupervisorWithDeepPrototype1.getSupervisorPrototype().getName()); System.out.println(员工 emploteeSupervisorWithDeepPrototype2.getName() 的上司是 emploteeSupervisorWithDeepPrototype2.getSupervisorPrototype().getName()); } }运行结果为员工zhouyi 1的上司是zhangyanfeng 1 员工zhouyi 2的上司是zhangyanfeng 2四、原型模式的应用场景分析使用new关键字创建类的时候必须指定类名但是在开发过程中也会有“在不指定类名的前提下生成实例”的需求。例如在下面这些情况下就需要根据现有的实例来生成新的实例。1) 对象种类繁多无法将他们整合到一个类的时候2) 难以根据类生成实例时3) 想解耦框架与生成的实例时。如果想要让生成实例的框架不再依赖于具体的类这时不能指定类名来生成实例而要事先“注册”一个“原型”实例然后通过复制该实例来生成新的实例。五、总结通过对原型模式的深入探讨我们可以看到它不仅仅是一个简单的对象复制工具而是通过克隆现有对象来避免重复创建的一个设计模式。原型模式能够提高对象创建的效率尤其在那些对象构造复杂或者需要频繁创建的场景中能够显著提升性能。然而要实现原型模式的正确使用我们不仅要理解其基本概念还需要小心浅拷贝和深拷贝之间的差异并确保在复杂对象中避免数据共享的副作用。本文通过简单的员工类示例以及深入分析了浅拷贝与深拷贝的问题展示了如何通过克隆接口实现原型模式。此外我们还在实际应用场景中探讨了原型模式的优势尤其在构建灵活、可扩展的系统时原型模式能够有效地解耦系统与具体的类实现简化对象的创建过程。总之原型模式是一种强大且灵活的设计模式适用于各种复杂对象创建的场景。但使用时也要谨慎处理深浅拷贝的问题确保对象之间的数据隔离避免意外的副作用。在今后的项目中合理应用原型模式将使我们的代码更加高效、可维护并且能够应对不断变化的需求。希望本文能为你深入理解原型模式提供帮助并鼓励你在实际项目中尝试和应用这一模式发挥它的最大效能参考书籍、文献和资料1.《Sring 5 核心原理与30个类手写实战》谭勇徳中国公信出版社2019.2.极客时间课程《Java性能调优实战》刘超2019.3.原型模式_川峰的博客-CSDN博客4.https://blog.csdn.net/qq_34337272/article/details/80706444?ops_request_miscrequest_idbiz_id102utm_term%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%AE%9A%E4%B9%89utm_mediumdistribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-1-80706444