原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
调用者不需要知道任何创建细节,不调用构造函数
属于创建型模式
适用场景: 1 类初始化消耗资源较多
2 new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
3 构造函数比较复杂
4 循环体中生产大量对象时,可读性下降
原型模式就是如果快速构建对象的方法总结,简单工厂将getter、setter封装到某个方法中,JDK提供的实现Cloneable接口,在 Spring 中,原型模式应用得非常广泛。例如 scope=“prototype”,通常与 scope=”singleton” 配合适用。
在我们经常用的 JSON.parseObject()也是一种原型模式, BeanUtils.copy()可以设置深克隆, Guava的Copy工具类都是原型模式。
优点: 1 性能比直接new一个对象性能高 2 简化了创建过程
缺点: 1 必须配备克隆(或者可拷贝)方法
2 对克隆复杂对象或对克隆出的对象进行复杂改造时,易带来风险
3 深拷贝、浅拷贝要运用得当
浅拷贝
public interface Prototype{ Prototype clone(); }
public class ConcretePrototypeA implements Prototype { private int age; private String name; private List hobbies; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getHobbies() { return hobbies; } public void setHobbies(List hobbies) { this.hobbies = hobbies; } @Override public ConcretePrototypeA clone() { ConcretePrototypeA concretePrototype = new ConcretePrototypeA(); concretePrototype.setAge(this.age); concretePrototype.setName(this.name); concretePrototype.setHobbies(this.hobbies); return concretePrototype; } }
// 利用了简单工厂和静态代理 public class Client { private Prototype prototype; public Client(Prototype prototype){ this.prototype = prototype; } public Prototype startClone(Prototype concretePrototype){ return (Prototype)concretePrototype.clone(); } }
public static void main(String[] args) { // 创建一个具体的需要克隆的对象 ConcretePrototypeA concretePrototype = new ConcretePrototypeA(); // 填充属性,方便测试 concretePrototype.setAge(18); concretePrototype.setName("prototype"); List hobbies = new ArrayList<String>(); concretePrototype.setHobbies(hobbies); System.out.println(concretePrototype); // 创建Client对象,准备开始克隆 Client client = new Client(concretePrototype); ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA) client.startClone(concretePrototype); System.out.println(concretePrototypeClone); System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies()); System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies()); System.out.println("对象地址比较:"+(concretePrototypeClone.getHobbies() == concretePrototype.getHobbies())); }
深克隆
public class Monkey { public int height; public int weight; public Date birthday; }
public class JinGuBang implements Serializable { public float h = 100; public float d = 10; public void big(){ this.d *= 2; this.h *= 2; } public void small(){ this.d /= 2; this.h /= 2; } }
public class QiTianDaSheng extends Monkey implements Cloneable,Serializable { public JinGuBang jinGuBang; public QiTianDaSheng(){ //只是初始化 this.birthday = new Date(); this.jinGuBang = new JinGuBang(); } @Override protected Object clone() throws CloneNotSupportedException { return this.deepClone(); } public Object 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); // 完整的新的对象, new出一个对象 QiTianDaSheng copy = (QiTianDaSheng)ois.readObject(); copy.birthday = new Date(); return copy; }catch (Exception e){ e.printStackTrace(); return null; } } public QiTianDaSheng shallowClone(QiTianDaSheng target){ QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); qiTianDaSheng.height = target.height; qiTianDaSheng.weight = target.height; qiTianDaSheng.jinGuBang = target.jinGuBang; qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } }
public static void main(String[] args) { QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); try { QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone(); System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang)); } catch (Exception e) { e.printStackTrace(); } QiTianDaSheng q = new QiTianDaSheng(); QiTianDaSheng n = q.shallowClone(q); System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang)); }
ArrayList实现了Cloneable接口, 实现了深度克隆,重写了clone()方法