原型模式-是指原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节,不调用构造函数!属于创建型模式。
它不通过new,而是通过内存中已有的对象为模板,不使用构造函数的方式创造出来其他对象。
已经有很多原型模式的实现方案,想beanutils、json、guava、实现JDK的cloneable接口(该方式为深克隆)等等,不需要关心他们的实现方式,直接用就好了。
主要分为浅克隆和深克隆!
浅克隆:复制的是这个对象的引用,也就是说原来对象的值改变的话,复制的对象的值也会跟着改变。
深克隆:复制的是这个对象的值,通过字节码的构建,把这个对象创建出来。
其中,Spring中是通过配置实现的,一般都是浅克隆,平时用的也大部分都是浅克隆。
它和单例模式相互冲突。如果你的对象每次使用的时候都要创建一下,就可以使用原型模式,如果每次都可以使用同一个对象,就不需要使用原型 模式!
浅克隆示例——
先创建原型Prototype接口:
package com.pansoft.com.prototype; public interface prototype { prototype clone(); }
创建具体需要克隆的对象ConcretePrototype:
package com.pansoft.com.prototype; import java.util.List; 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; } public prototype clone(){ ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA(); concretePrototypeA.setAge(this.age); concretePrototypeA.setName(this.name); concretePrototypeA.setHobbies(this.hobbies); return null; } }
它继承了Prototype接口,big实现了clone()方法,在clone方法里new了一个ConcretePrototypeA类的对象。
创建Client对象:
package com.pansoft.com.prototype; public class Client { /*private prototype pro; public Client(prototype pro){ this.pro = pro; }*/ public prototype startClone(prototype concretePrototype){ return concretePrototype.clone(); } }
在它里面通过prototype接口的对象调用clone方法。
测试:
package com.pansoft.com.prototype; import java.util.ArrayList; import java.util.List; public class prototypeTest { public static void main(String[] args){ ConcretePrototypeA concretePrototype = new ConcretePrototypeA(); concretePrototype.setAge(18); concretePrototype.setName("Tom"); List hobbies = new ArrayList<String>(); concretePrototype.setHobbies(hobbies); Client client = new Client(); ConcretePrototypeA copy = (ConcretePrototypeA)client.startClone(concretePrototype); System.out.println(copy); System.out.println("克隆对象中引用类型地址的值:"+copy.getHobbies()); System.out.println("原对象中引用类型地址的值:"+concretePrototype.getHobbies()); //相等,说明copy的并不是值,而是地址,这就是浅克隆。 //这种方式存在一定风险 System.out.println("对象地址比较:"+(copy.getHobbies() == concretePrototype.getHobbies())); } }
但是这里有个问题,就是clone()方法里仍然是通过new方法和构造函数来获得的对象!是我没理解对吗?
深克隆示例——
创建原型猴子Monkey类:
package com.pansoft.com.prototype.deep; import java.util.Date; public class Monkey { public int height; public int weight; public Date birthday; }
它有三个属性,高度,宽度和生日。
创建引用对象金箍棒JinGuBang类:
package com.pansoft.com.prototype.deep; import java.io.Serializable; 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; } }
它继承了Serializable接口,定义了两个变量并初始化,还定义了两个方法,一个变大,一个变小。
创建具体的对象齐天大圣QiTianDaSheng类:
package com.pansoft.com.prototype.deep; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Date; 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); QiTianDaSheng copy = (QiTianDaSheng)ois.readObject(); copy.birthday = new Date(); return copy; }catch(Exception e){ e.printStackTrace(); return null; } } public QiTianDaSheng shallowColne(QiTianDaSheng target){ QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); qiTianDaSheng.height = target.height; qiTianDaSheng.weight = target.weight; qiTianDaSheng.jinGuBang = target.jinGuBang; qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } }
它继承了Monkey类和Cloneable、Serializable接口,其中,里面的clone()方法是深克隆,shallowClone(QiTianDaSheng target)方法是浅克隆。
在深克隆里通过字节码的方式获得到了QiTianDaSheng的对象。
测试:
package com.pansoft.com.prototype.deep; public class DeepCloneTest { 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.shallowColne(q); System.out.println("浅克隆:"+(q.jinGuBang == n.jinGuBang)); } }
发现深克隆出来的两个对象不相等,浅克隆相等。折旧意味着如果克隆的目标的对象是单例对象,那么深克隆就会破坏单例,那么如何防止深克隆破坏单例么?
要么单例类不实现Cloneable接口,要么跟上节一样,重写clone方法,在clone方法中返回单例对象即可——即return INSTANCE;
我们常用的ArrayList就实现了Cloneable接口!
原文:https://www.cnblogs.com/yinyj/p/11266305.html