原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的.
原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据.
原型模式中的拷贝分为"浅拷贝"和"深拷贝":
浅拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.
深拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制.
类图:
查看api object的clone()方法,这里面是这样说明的:
此方法返回的对象应该独立于该对象(正被复制的对象)。要获得此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被复制对象的内部“深层结构”的所有可变对象,并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用,那么通常不需要修改 super.clone 返回的对象中的字段。
Object 类的 clone 方法执行特定的复制操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。注意,所有的数组都被视为实现接口 Cloneable。否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被自我复制。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。
实例一:浅拷贝
public class Prototype implements Cloneable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } } public class TestMain { public static void main(String[] args) { testPrototype(); } private static void testPrototype(){ Prototype pro = new Prototype(); pro.setName("original object"); Prototype pro1 = (Prototype)pro.clone(); pro.setName("changed object1"); System.out.println("original object:" + pro.getName()); System.out.println("cloned object:" + pro1.getName()); } }
结果:
original object:changed object1
cloned object:original object
克隆的对象复制了String类型的name的引用,没有复制常量池中的对象,我们知道String是引用数据类型,在常量池中他的值只有一份,而且String的定义是,public final class String{},所以无法更改,当给克隆对象的name赋值时,会在常量池中重新建立一个新的字符串,对该name的赋值,指向新的字符串对象的地址。不能复制引用的对象。
原name的地址仍然指向原字符串对象。
浅拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.
既然String特性不能更改对象的值,再来一个自己写的小例子仔细说明:
package com.java.simpleclone; public class ConcretePrototype implements Cloneable{ //引用类型 String name; //引用类型 int[] array;//数组对象 public int[] getArray() { return array; } public void setArray(int[] array) { this.array = array; } public String getName() { return name; } public void setName(String name) { this.name = name; } public ConcretePrototype() { } @Override protected Object clone(){ // TODO Auto-generated method stub try { return super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
客户端
package com.java.simpleclone; public class CloneTest { public static void main(String[] args) { ConcretePrototype cp=new ConcretePrototype(); cp.setName("Orginal Prototype"); cp.setArray(new int[]{1000,200}); int[] b=cp.getArray(); System.out.println("克隆前cp "+cp.getName()+"==数组=="+cp.getArray()[0]); ConcretePrototype cp2=(ConcretePrototype)cp.clone(); cp2.setName("Copy Prototype"); int[] a=cp2.getArray();// a[0]=500; cp2.setArray(a); System.out.print("是否同属于一个数组:"); System.out.println(a==b); System.out.println("克隆后cp "+cp.getName()+"==数组=="+cp.getArray()[0]); System.out.println("克隆后cp2 "+cp2.getName()+"==数组=="+cp2.getArray()[0]); /** * 运行结果: * 克隆前cp Orginal Prototype==数组==1000 是否同属于一个数组:true 克隆后cp Orginal Prototype==数组==500 克隆后cp2 Copy Prototype==数组==500 */ //克隆之后给cp2的数组重新定义,cp的数组的值改变了,这说明cp2.getArray() 获取的数组对象,跟cp里面的数组对象是同一个, // 非常明白的说明了浅克隆的 只复制对象的引用,不复制引用对象 } }
运行结果:
克隆前cp Orginal Prototype==数组==1000
是否同属于一个数组:true
克隆后cp Orginal Prototype==数组==500
克隆后cp2 Copy Prototype==数组==500
下面对上面这个例子进行深克隆,深复制
package com.java.simpleclone; public class ConcretePrototype implements Cloneable{ //引用类型 String name; //引用类型 int[] array;//数组对象 public int[] getArray() { return array; } public void setArray(int[] array) { this.array = array; } public String getName() { return name; } public void setName(String name) { this.name = name; } public ConcretePrototype() { } @Override protected Object clone(){ ConcretePrototype cn=null; try { cn=(ConcretePrototype) super.clone(); cn.array=this.array.clone();//api的说明,所有的数组都实现了clonable 接口,复制一个新的数组副本 return cn; } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }客户端
package com.java.simpleclone; public class CloneTest { public static void main(String[] args) { ConcretePrototype cp=new ConcretePrototype(); cp.setName("Orginal Prototype"); cp.setArray(new int[]{1000,200}); int[] b=cp.getArray(); System.out.println("克隆前cp "+cp.getName()+"==数组=="+cp.getArray()[0]); ConcretePrototype cp2=(ConcretePrototype)cp.clone(); cp2.setName("Copy Prototype"); int[] a=cp2.getArray();// a[0]=500; cp2.setArray(a); System.out.print("是否同属于一个数组:"); System.out.println(a==b); System.out.println("克隆后cp "+cp.getName()+"==数组=="+cp.getArray()[0]); System.out.println("克隆后cp2 "+cp2.getName()+"==数组=="+cp2.getArray()[0]); /** * 运行结果: * 克隆前cp Orginal Prototype==数组==1000 是否同属于一个数组:false 克隆后cp Orginal Prototype==数组==1000 克隆后cp2 Copy Prototype==数组==500 */ // 克隆之后给cp2的数组重新定义,cp的数组的值没有改变,这说明cp2.getArray() 获取的数组对象,跟cp里面的数组对象不是同一个, // 说明我们的深复制成功了 } }
public class Prototype{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class NewPrototype implements Cloneable { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } private Prototype prototype; public Prototype getPrototype() { return prototype; } public void setPrototype(Prototype prototype) { this.prototype = prototype; } public Object clone(){ try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } } public class TestMain { public static void main(String[] args) { // TODO Auto-generated method stub testPrototype(); } private static void testPrototype(){ Prototype pro = new Prototype(); pro.setName("original object"); NewPrototype newObj = new NewPrototype(); newObj.setId("test1"); newObj.setPrototype(pro); NewPrototype copyObj = (NewPrototype)newObj.clone(); copyObj.setId("testCopy"); copyObj.getPrototype().setName("changed object"); System.out.println("original object id:" + newObj.getId()); System.out.println("original object name:" + newObj.getPrototype().getName()); System.out.println("cloned object id:" + copyObj.getId()); System.out.println("cloned object name:" + copyObj.getPrototype().getName()); } }
结果:
original object id:test1
original object name:changed object
cloned object id:testCopy
cloned object name:changed object
实例三: 深拷贝
public class Prototype implements Cloneable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } } public class NewPrototype implements Cloneable { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } private Prototype prototype; public Prototype getPrototype() { return prototype; } public void setPrototype(Prototype prototype) { this.prototype = prototype; } public Object clone(){ NewPrototype ret = null; try { ret = (NewPrototype)super.clone(); ret.prototype = (Prototype)this.prototype.clone(); return ret; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } } public class TestMain { /** * @param args */ public static void main(String[] args) { testDeepCopy(); } private static void testDeepCopy(){ Prototype pro = new Prototype(); pro.setName("original object"); NewPrototype newObj = new NewPrototype(); newObj.setId("test1"); newObj.setPrototype(pro); NewPrototype copyObj = (NewPrototype)newObj.clone(); copyObj.setId("testCopy"); copyObj.getPrototype().setName("changed object"); System.out.println("original object id:" + newObj.getId()); System.out.println("original object name:" + newObj.getPrototype().getName()); System.out.println("cloned object id:" + copyObj.getId()); System.out.println("cloned object name:" + copyObj.getPrototype().getName()); } }
结果:
original object id:test1
original object name:original object
cloned object id:testCopy
cloned object name:changed object
实例四: 利用串行化来做深复制
把对象写道流里的过程是串行化(Serilization)过程;把对象从流中读出来是并行化(Deserialization)过程. 写在流里的是对象的一个拷贝,然后再从流里读出来重建对象.
public class PrototypeSe implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class NewPrototypeSe implements Serializable { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } private PrototypeSe prototype; public PrototypeSe getPrototype() { return prototype; } public void setPrototype(PrototypeSe prototype) { this.prototype = prototype; } public Object deepClone(){ try { ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return oi.readObject(); } catch (IOException | ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } } public class TestDeepClone { public static void main(String[] args) { // TODO Auto-generated method stub PrototypeSe po = new PrototypeSe(); po.setName("test1"); NewPrototypeSe se = new NewPrototypeSe(); se.setPrototype(po); NewPrototypeSe deepClone = (NewPrototypeSe)se.deepClone(); deepClone.getPrototype().setName("test2"); System.out.println("original name:" + se.getPrototype().getName()); System.out.println("cloned name:" + deepClone.getPrototype().getName()); } }