目录java
现实世界中山寨这种行为每每意味着假冒伪劣,备受批判。可是在软件开发中,山寨却又很多可取之处。首先其“成分”和“质量”和原创不相上下;其次相比原创一个东西的时间开销,山寨一个出来总归是省时省力的,毕竟对于计算机,克隆一个对象要比建立一个对象性能好得多(拷贝对象不会执行构造方法)。若是在开发中咱们须要一个类的多个实例,这些实例只在某些属性细节上不一样,相比直接new出它们的时间开销,从一个实例原型拷贝出其余的实例或许是更可取的方法。而原型模式,或者说克隆模式就可以帮咱们作到这点。框架
用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。ide
原型模式中的主要角色是Prototype类,含有clone方法供客户端调用,从而建立它的拷贝对象。类结构简单清晰。性能
public class Prototype implements Cloneable { @Override protected Prototype clone() throws CloneNotSupportedException { return(Prototype)super.clone(); } }
public class TestCase { public static void main(String[] args) throws CloneNotSupportedException { Prototype pObj = new Prototype(); Prototype cObj = pObj.clone(); System.out.println(pObj); System.out.println(cObj); } }
首先建立了一个原型类的实例,其次调用该实例的clone方法得到了该实例的拷贝,分别打印出两个对象的哈希值测试
当原对象内部有一个非基本类型的引用变量时,浅拷贝意味着拷贝对象内部该变量和原对象内部的引用变量指向同一个对象,通俗的说,它只对外部对象进行拷贝,对内部引用变量指向的对象不进行真实拷贝,只是指向该对象而已。3d
深拷贝,除了拷贝外部对象外,其引用变量指向的对象也要拷贝,同时若是该对象还有引用变量,其指向的对象一样须要拷贝,一直递归进行指导拷贝完成。能够看出,深拷贝是深度的拷贝。若是对象间的组合关系十分复杂的话,深度拷贝过程就相似于树的遍历了。code
对此,咱们能够验证以下,首先修改原型类,添加一个引用变量obj指向Object对象。对象
public class Prototype implements Cloneable { public Object obj=new Object(); @Override protected Prototype clone() throws CloneNotSupportedException { return(Prototype)super.clone(); } }
测试比较原型对象和拷贝对象的obj变量是否指向同一Object对象blog
public class TestCase { public static void main(String[] args) throws CloneNotSupportedException { Prototype pObj = new Prototype(); Prototype cObj = pObj.clone(); System.out.println(pObj.obj.equals(cObj.obj)); } }
运行结果以下
继承
实现深拷贝的方式不少,好比递归实现浅拷贝,或者工做中可使用开源类库来作。下面介绍一种使用对象序列化和反序列化实现深拷贝的方式。原型类必须实现另外一个标记接口Serializable,且其内部引用变量指向的对象也必须实现该序列化接口,因为Object没有实现该接口,因此会报出NotSerializableException异常。下面给出关键的序列化和反序列化代码,就不修改原型类进行测试了。
import java.io.*; public class TestCase { public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException { Prototype pObj = new Prototype(); //序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(pObj); byte[] bytes = bos.toByteArray(); //反序列化 ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); Prototype cObj = (Prototype) ois.readObject(); System.out.println(pObj.obj.equals(cObj.obj)); } }
原型模式经过对象拷贝的方式得到类的多个实例。在不少状况下这样能得到更好的性能。原型模式一般和其余模式结合使用,好比Spring框架中经过和工厂模式结合使得咱们能够得到相同Bean的多个实例,这样帮咱们咱们屏蔽了建立新实例的复杂细节。