前言
原型模式核心在于实例的拷贝。以系统内已存在的实例,直接基于二进制流进行拷贝,无需经过耗时的对象初始化过程。不调用构造函数。
实现方法十分简单,是需要实现Cloneable
接口,并覆盖clone
方法即可。
public class PersonPrototype implements Cloneable{
private Integer age;
private String name;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected PersonPrototype clone() {
try {
return (PersonPrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "PersonPrototype{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
测试类
public static void main(String[] args) {
PersonPrototype p1 = new PersonPrototype();
p1.setAge(11);
p1.setName("a");
PersonPrototype p2 = p1.clone();
System.out.println(p1);
System.out.println(p2);
}
输出结果:
接下来,我们添加一个爱好属性hobbies
public class PersonPrototype implements Cloneable{
private Integer age;
private String name;
private List<String> hobbies;
.....(省略其余代码)
}
测试代码
PersonPrototype p1 = new PersonPrototype();
p1.setAge(11);
p1.setName("a");
List<String> list = new ArrayList<>();
list.add("画画");
list.add("书法");
p1.setHobbies(list);
PersonPrototype p2 = p1.clone();
p2.setName("b");
System.out.println(p1);
System.out.println(p2);
p1.getHobbies().add("钢琴");
System.out.println(p1);
System.out.println(p2);
运行结果:
我们给复制后的克隆对象新增一项爱好,发现原型对象也被修改了。这显然不符合我们的预期。以为我们希望克隆出来的对象应该和原型对象是两个相互独立的对象,不应该再有任何联系。从结果我们分析,hobbies
共用了相同的内存地址。这意味着复制的不是值,而是引用地址。这就是我们常说的浅克隆
。
浅克隆
浅克隆只是完整的复制值类型的数据,没有赋值引用对象。也就是说,所有引用对象仍只想原来的对象。这显然不是我们想要的。下面我们来探讨如果进行深度克隆。
深克隆
在原来的基础上,增加deepClone
方法,同时实现Serializable
接口。
public class PersonDeepPrototype implements Cloneable, Serializable {
....
public PersonDeepPrototype deepClone(){
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 (PersonDeepPrototype) ois.readObject();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
....
}
测试代码:
PersonDeepPrototype p1 = new PersonDeepPrototype();
p1.setAge(11);
p1.setName("a");
List<String> list = new ArrayList<>();
list.add("画画");
list.add("书法");
p1.setHobbies(list);
PersonDeepPrototype p2 = p1.deepClone();
p2.setName("b");
System.out.println(p1);
System.out.println(p2);
p1.getHobbies().add("钢琴");
System.out.println(p1);
System.out.println(p2);
运行结果:
通过序列化,这次克隆的对象已经完全独立出来。得到了我们预期的结果。
克隆破坏单例
为了防止我们的单例被克隆,简单的有两种方式。
- 对象不实现
Cloneable
接口 - 重写
clone
方法,方法中直接返回当前实例。
原型模式的优缺点
优点
- 性能优良
- 简化对象创建流程
缺点
- 为每个类配置一个克隆方法
- 对已有的类改造需要修改代码
- 深度克隆编写起来比较复杂