定义
原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
原型模式是直接基于内存二进制流进行复制,不调用构造函数,新对象的创建时间大大缩短。
Jdk浅克隆
jdk中,不需要手动创建原型接口,因为jdk中已经内置了Cloneable原型接口,自定义的原型类只需要实现该接口并重写clone方法即可完成类的复制。
//setter getter tostring省略
public class Prototype implements Cloneable{
private String name;
private Integer age;
private Date date;
//注意clone需要改成public修饰
@Override
public Prototype clone() throws CloneNotSupportedException {
return (Prototype)super.clone();
}
}
浅克隆测试:
public static void main(String[] args) throws CloneNotSupportedException {
Prototype prototype = new Prototype();
prototype.setAge(1);
prototype.setName("wj");
prototype.setDate(new Date());
Prototype clone = prototype.clone();
System.out.println(clone);
System.out.println(prototype);
clone.getDate().setTime(0);
System.out.println(clone);
System.out.println(prototype);
}
我们发现:修改了克隆类的引用对象后,原对象也发生修改。
原因:类中存在引用对象属性,则原型对象与克隆对象的该属性会只想同一对象的引用。
Jdk浅克隆升级版(深克隆)
public class Prototype implements Cloneable{
private String name;
private Integer age;
private Date date;
@Override
public Prototype clone() throws CloneNotSupportedException {
Prototype clone = (Prototype)super.clone();
Date dateClone = (Date)clone.getDate().clone();
clone.setDate(dateClone);
return clone;
}
}
再使用上述方法进行测试:
发现修改了clone对象,原对象没有修改。这是因为再super.clone()后,手动给克隆对象的相关属性分配给另一块内存。
不过有一个弊端:当原型对象中有很多引用属性的时候,手动分配十分麻烦,并且引用属性也要重写clone接口。
序列化实现深克隆
需要实现Cloneable,Serializable
public class Prototype implements Cloneable,Serializable {
private String name;
private Integer age;
private Date date;
@Override
public Prototype clone() throws CloneNotSupportedException {
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Prototype)ois.readObject();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
从结果上看,实现了深克隆。
克隆破坏单例模式
如果单例对象实现了克隆接口,然后我们重写了clone方法并调用,这样会创建出一个新的单例对象,破坏了单例模式。
解决方案如下:
-
单例对象不实现Cloneable接口
-
重写clone方法:
@Override public Prototype clone(){ return instance;//instance 就是单例对象,只需要直接返回即可 }