中年从原型模式到浅拷贝和深拷贝( 二 )


static List sheepList = new ArrayList&lt&gt() public static void main(String[] args) { Goat goat = new Goat() goat.setName("山羊") goat.setAge(3) goat.setColor("灰色") for (int i = 0 i &lt 5 i++) { sheepList.add(goat.clone()) }
Lamb lamb = new Lamb() lamb.setName("羔羊") lamb.setAge(2) lamb.setColor("白色") for (int i = 0 i &lt 5 i++) { sheepList.add(lamb.clone()) System.out.println(lamb.hashCode()+","+lamb.clone().hashCode()) }
for (Sheep sheep : sheepList) { System.out.println(sheep.toString()) }}
原型模式将克隆过程委派给被克隆的实际对象 。 模式为所有支持克隆的对象声明了一个通用接口 ,该接口让你能够克隆对象 , 同时又无需将代码和对象所属类耦合 。 通常情况下 , 这样的接口中仅包含一个 克隆方法 。
所有的类对 克隆方法的实现都非常相似 。 该方法会创建一个当前类的对象 ,然后将原始对象所有的成员变量值复制到新建的类中 。 你甚至可以复制私有成员变量 ,因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量 。
支持克隆的对象即为原型 。 当你的对象有几十个成员变量和几百种类型时 ,对其进行克隆甚至可以代替子类的构造 。
优势
使用原型模式创建对象比直接 new 一个对象在性能上要好的多 , 因为 Object 类的 clone 方法是一个本地方法 , 它直接操作内存中的二进制流 , 特别是复制大对象时 , 性能的差别非常明显 。
使用原型模式的另一个好处是简化对象的创建 , 使得创建对象就像我们在编辑文档时的复制粘贴一样简单 。
因为以上优点 , 所以在需要重复地创建相似对象时可以考虑使用原型模式 。 比如需要在一个循环体内创建对象 , 假如对象创建过程比较复杂或者循环次数很多的话 , 使用原型模式不但可以简化创建过程 , 而且可以使系统的整体性能提高很多 。
适用场景
《Head First 设计模式》是这么形容原型模式的:当创建给定类的实例的过程很昂贵或很复杂时 , 就是用原型模式 。
如果你需要复制一些对象 , 同时又希望代码独立于这些对象所属的具体类 , 可以使用原型模式 。
如果子类的区别仅在于其对象的初始化方式 ,那么你可以使用该模式来减少子类的数量 。 别人创建这些子类的目的可能是为了创建特定类型的对象
原型模式在 Spring 中的应用
我们都知道 Spring bean 默认是单例的 , 但是有些场景可能需要原型范围 , 如下
bean&gt
同样 , 王二小还是有 10 只羊 , 感兴趣的也可以看下他们创建的对象是不是同一个
public class Client { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml") for (int i = 0 i &lt 10 i++) { Object bean = context.getBean("sheep") System.out.println(bean) } }}
感兴趣的同学可以深入源码看下具体的实现 , 在 AbstractBeanFactory 的 doGetBean() 方法中
中年从原型模式到浅拷贝和深拷贝
本文插图

原型模式的注意事项
【中年从原型模式到浅拷贝和深拷贝】使用原型模式复制对象不会调用类的构造方法 。 因为对象的复制是通过调用 Object 类的 clone 方法来完成的 , 它直接在内存中复制数据 , 因此不会调用到类的构造方法 。 不但构造方法中的代码不会执行 , 甚至连访问权限都对原型模式无效 。 还记得单例模式吗?单例模式中 , 只要将构造方法的访问权限设置为 private 型 , 就可以实现单例 。 但是 clone 方法直接无视构造方法的权限 , 所以 , 单例模式与原型模式是冲突的 , 在使用时要特别注意 。


推荐阅读