原型模式(Prototype)
当我需要创建多个相同的对象时,若通过new 创建对象比较费时费力,那么可以通过克隆来创建一个一样的新对象,即通过原型创建新对象。
克隆方式:
实现Cloneable接口(声明接口:声明此方法可以克隆),重写Object类的clone方法。
浅克隆:被Clone的对象的所有变量都含有原来对象相同的值,而引用变量还是原来对用的引用。
深克隆:被克隆对象的所有变量都含有原来的对象相同的值,引用变量也重新复制了一份。
实现方式:
1、在clone对象内对引用属性进行克隆。2、使用反序列化进行克隆。
1、原型模式,但是浅克隆:
无视引用变量可能造成的影响。
例子:
import java.util.Date; /** * 克隆猪猪,它支持被克隆。 */ public class ClonePig implements Cloneable{ private String name;//姓名 private Date birthday;//生日 @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); return obj;//调用父类的克隆方法。 } //此处的birthday指向的是传过来的Date对象的地址,那个对象改变会导致birthday改变 public ClonePig(String name, Date birthday) { this.name = name; this.birthday = birthday; } public String getName() { return name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public void setName(String name) { this.name = name; } }
测试代码:
import java.util.Date; public class Client { public static void main(String[] args) throws Exception { //准备一个Daye,等会儿把date作为猪猪的属性传进去,修改date会导致猪猪的属性更改 Date date = new Date(111111111l); //我有一个22年的叫做佩奇的猪猪 ClonePig pig = new ClonePig("佩奇", date); //通过克隆获取一直一模一样的猪猪 ClonePig clonePig1= (ClonePig) pig.clone(); System.out.println("-----------------刚刚克隆出来,使用的同一个时间对象:data"); System.out.println("pig birthday:"+pig.getBirthday().getTime()); System.out.println("clonePig1 birthday:"+clonePig1.getBirthday().getTime()); System.out.println("-------------------修改共同的引用对象,data"); //通过修改date,直接改变佩奇的出生时间 date.setTime(222222222l); //佩奇二号的出生日也变了!因为他们的Date对象是用的同一个。 System.out.println("pig birthday:"+pig.getBirthday().getTime()); System.out.println("clonePig1 birthday:"+clonePig1.getBirthday().getTime()); System.out.println("-------------------通过set方法给pig新的Date对象"); //通过set方法改变佩奇的出生时间 pig.setBirthday(new Date(333333333333l)); //佩奇二号的出生日不会变,此时pig的birthday为新创建的对象。 System.out.println("pig birthday:"+pig.getBirthday().getTime()); System.out.println("clonePig1 birthday:"+clonePig1.getBirthday().getTime()); System.out.println("--------------------再次修改date,只有clonePig改变"); date.setTime(444444444444l); //只有克隆佩奇会变。 System.out.println("修改之后pig:"+pig.getBirthday().getTime()); System.out.println("修改之后:"+clonePig1.getBirthday().getTime()); } }
运行结果:
2、原型模式,但是深克隆(修改clone方法):
把引用类型的属性也克隆一份,修改clone方法为:
@Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); //解决浅克隆使用同一个引用地址的情况,把属性也克隆一份。 ClonePig clonePig = (ClonePig) obj; clonePig.birthday = (Date) this.birthday.clone(); return clonePig;//调用父类的克隆方法。 }
再次运行测试代码:此时克隆出来的clonePig丝毫不受影响,完全独立。
3、原型模式,但是使用反序列化实现深克隆
总所周知,反序列化的时候会重新创建一个对象,也包括属性对象(除非你序列化的类重写了readResolve方法)。
实现:ClonePig实现可序列号接口。
import java.io.Serializable; import java.util.Date; /** * 克隆猪猪,它支持被序列化 */ public class ClonePig implements Serializable { private String name;//姓名 private Date birthday;//生日 //此处的birthday指向的是传过来的Date对象的地址,那个对象改变会导致birthday改变 public ClonePig(String name, Date birthday) { this.name = name; this.birthday = birthday; } public String getName() { return name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public void setName(String name) { this.name = name; } }
对其进行序列号,然后反序列化克隆出新的对象(深克隆)。
import java.io.*; import java.util.Date; public class Client { public static void main(String[] args) throws Exception { Date date = new Date(1111111L); ClonePig pig = new ClonePig("佩奇",date); //序列化,把pig写入字节数组中 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(pig); byte[] obj = baos.toByteArray(); //反序列化,把字节数组内容还原为一个ClonePig对象 ByteArrayInputStream bais = new ByteArrayInputStream(obj); ObjectInputStream ois = new ObjectInputStream(bais); ClonePig clonePig = (ClonePig) ois.readObject(); //测试 date.setTime(2222222222L); System.out.println("pig:"+pig.getBirthday().getTime()); System.out.println("clonePig:"+clonePig.getBirthday().getTime()); } }
运行结果: