设计模式-原型模式
1、原型模式简介
定义:属于创建型模式,用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象,这种方式创建对象非常高效,根本无须知道对象创建的细节
目标:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
解决问题:运行期建立和删除实例
优点:性能提高
逃避构造函数的约束
缺点:需要为每一个类都配置一个 clone 方法
clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦
说明:原型模式的克隆分为两种,一种叫深克隆,一种叫浅克隆
2、浅克隆
定义:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
实现:必须得实现实现 Cloneable 接口,只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化
//原型类 public class User { private String name; private int date; private int age; public void setName(String name) { this.name = name; } public void setDate(int date) { this.date = date; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getDate() { return date; } public int getAge() { return age; } @Override public String toString() { return getName()+"&&"+getDate()+"&&"+getAge(); } } //具体原型类 public class CloneablePrototype implements Cloneable { private User user = new User(); public CloneablePrototype(){ user.setAge(25); user.setDate(1995); user.setName("CC"); System.out.println("具体原型创建成功"); } public Object clone() throws CloneNotSupportedException { System.out.println("具体原型复制成功"); return (CloneablePrototype)super.clone(); } public User show(){ return user; } } //测试方法 public static void main(String[] args) throws CloneNotSupportedException { CloneablePrototype cloneablePrototype1 = new CloneablePrototype(); CloneablePrototype cloneablePrototype2 = (CloneablePrototype) cloneablePrototype1.clone(); System.out.println("clone1:"+cloneablePrototype1.show().toString()); System.out.println("clone2:"+cloneablePrototype2.show().toString()); System.out.println(cloneablePrototype1 == cloneablePrototype2); System.out.println("==========================="); cloneablePrototype2.show().setAge(30); System.out.println("clone1:"+cloneablePrototype1.show().toString()); System.out.println("clone2:"+cloneablePrototype2.show().toString()); System.out.println(cloneablePrototype1 == cloneablePrototype2); } //测试结果 具体原型创建成功 具体原型复制成功 clone1:CC&&1995&&25 clone2:CC&&1995&&25 false =========================== clone1:CC&&1995&&30 clone2:CC&&1995&&30 false
3、深克隆
定义:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址
实现:实现 Serializable 接口,通过字节流来对数据进行复制
//原型类,实现Serializable接口 public class User extends Object implements Serializable { private static final long serialVersionUID = 1L; private String name; private int date; private int age; public void setName(String name) { this.name = name; } public void setDate(int date) { this.date = date; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getDate() { return date; } public int getAge() { return age; } @Override public String toString() { return getName()+"&&"+getDate()+"&&"+getAge(); } } //具体原型类 public class SerializablePrototype implements Serializable { private User user = new User(); public SerializablePrototype(){ user.setAge(21); user.setName("NN"); user.setDate(1998); System.out.println("具体原型创建成功"); } public User show(){ return user; } public SerializablePrototype sClone(SerializablePrototype serializablePrototype){ try(ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ObjectOutputStream oOut = new ObjectOutputStream(bOut)){ //oOut对象中指向写入数据存储的内存地址与bOut对象中指向数据存储的内存地址是一样的 oOut.writeObject(serializablePrototype); try(ByteArrayInputStream bIn= new ByteArrayInputStream(bOut.toByteArray()); ObjectInputStream oIn = new ObjectInputStream(bIn)){ Object o = oIn.readObject(); System.out.println("具体原型复制成功"); return (SerializablePrototype) o; }catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } return null; } } //测试方法 public static void main(String[] args) throws CloneNotSupportedException { SerializablePrototype s1 = new SerializablePrototype(); SerializablePrototype s2 = s1.sClone(s1); System.out.println(s1.show().toString()); System.out.println(s2.show().toString()); System.out.println(s1 == s2); System.out.println("============================"); s2.show().setName("QQ"); System.out.println(s1.show().toString()); System.out.println(s2.show().toString()); System.out.println(s1 == s2); } //测试结果 具体原型创建成功 具体原型复制成功 NN&&1998&&21 NN&&1998&&21 false ============================ NN&&1998&&21 QQ&&1998&&21 false 由此可见,s2改变之后s1的不变,说明通过字节流将s1对象指向的实际内存地址中的数据复制到另一块实际内存中,s2指向的这块新的内存地址