什么是原型模式
原型模式(
Prototype Pattern
) 就是Java中的克隆技术。这种设计模式属于创建型模式,是以某个对象为原型,复制生成出新的对象,这种方式效率较高。原型模式类似于new
,但是不同于new
。new
创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。
原型模式的三个角色
- 抽象原型类(prototype):生明一个克隆自身的接口;
- 具体原型类(ConcretePrototype):实现一个克隆自身的操作;
- 客户端(Client):让一个原型克隆自身从而创建一个新的对象
优点
- 效率高;
- 创建较为复杂的对象时,使用原型模式可以简化对象的创建过程;
缺点
- 必须实现 Cloneable 接口;
- 当对象存在多层引用嵌套时,实现深克隆较为复杂;
- 对已知类进行改造时,需修改源代码,违背了
开闭原则
;
常见的应用场景
- Java中的Object clone()方法;
浅克隆与深克隆
在原型模式中还区分浅克隆与深克隆:
浅克隆:原型对象的成员变量如果是基本类型(byte,short,int,long,char,double,float,boolean
)的话,直接复制变量的值,而如果是引用类型的话则只复制内存地址值,也就是说原型对象的引用类型的成员变量与克隆新生成的对象的引用类型成员变量的内存地址指向的是同一个内存地址值。这样带来的问题就是当修改原型对象的引用类型变量时,克隆新生成对象的引用类型成员变量也会跟着改变。
深克隆:不管原型对象的成员变量是任何类型,都会完全复制,各自独立,任何修改都不会改变原型对象属性值。
下面我将一一演示如何实现浅克隆与深克隆
浅克隆
public class SheepShallowClone implements Cloneable{
private String name;
private Date birthday;
public SheepShallowClone(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 直接调用Object类的克隆方法
}
}
测试:
public class Test {
public static void main(String[] args) throws Exception{
Date date = new Date(100000000L);
SheepShallowClone sheep = new SheepShallowClone("老羊", date);
System.out.println(sheep.getName() + "&" + sheep.getBirthday() + "&" + sheep);
SheepShallowClone cloneSheep = (SheepShallowClone) sheep.clone();
System.out.println(cloneSheep.getName() + "&" + cloneSheep.getBirthday() + "&" + cloneSheep);
}
}
输出结果:
老羊&Fri Jan 02 11:46:40 CST 1970&com.chaytech.example.prototype.SheepShallowClone@1c2c22f3
老羊&Fri Jan 02 11:46:40 CST 1970&com.chaytech.example.prototype.SheepShallowClone@18e8568
可以看到克隆后新生成对象的属性值与原型对象一致。
下面我们在克隆方法执行之后修改一下原型对象的birthday
属性,看看克隆后的结果是否一致
public class Test {
public static void main(String[] args) throws Exception{
Date date = new Date(100000000L);
SheepShallowClone sheep = new SheepShallowClone("老羊", date);
System.out.println(sheep.getName() + "&" + sheep.getBirthday() + "&" + sheep);
SheepShallowClone cloneSheep = (SheepShallowClone) sheep.clone();
cloneSheep.setName("老羊");
date.setTime(120000001L);
System.out.println(cloneSheep.getName() + "&" + cloneSheep.getBirthday() + "&" + cloneSheep);
}
}
修改后输出结果:
老羊&Fri Jan 02 11:46:40 CST 1970&com.chaytech.example.prototype.SheepShallowClone@1c2c22f3
小羊&Fri Jan 02 17:20:00 CST 1970&com.chaytech.example.prototype.SheepShallowClone@18e8568
可以看到结果是不一致的,这个也就是浅克隆。
深克隆
将原型对象中的引用类型属性也实现Clonable接口
public class SheepDeepClone implements Cloneable{
private String name;
private Date birthday;
public SheepDeepClone(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = super.clone(); // 直接调用Object类的克隆方法
// 深克隆实现
SheepDeepClone sheep = (SheepDeepClone) object;
sheep.birthday = (Date) this.birthday.clone();
return object;
}
}
测试:
public class Test {
public static void main(String[] args) throws Exception{
Date date = new Date(100000000L);
SheepDeepClone sheep = new SheepDeepClone("老羊", date);
System.out.println(sheep.getName() + "&" + sheep.getBirthday() + "&" + sheep);
SheepDeepClone cloneSheep = (SheepDeepClone) sheep.clone();
cloneSheep.setName("小羊");
date.setTime(120000001L);
System.out.println(cloneSheep.getName() + "&" + cloneSheep.getBirthday() + "&" + cloneSheep);
}
}
老羊&Fri Jan 02 11:46:40 CST 1970&com.chaytech.example.prototype.SheepDeepClone@1c2c22f3
小羊&Fri Jan 02 11:46:40 CST 1970&com.chaytech.example.prototype.SheepDeepClone@18e8568
可以看到输出的结果是一致的
下面将介绍基于反序列化来实现深克隆
深克隆(基于反序列化实现)
public class SheepSerializeClone implements Serializable{
private String name;
private Date birthday;
public SheepSerializeClone(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
测试:
public class Test {
public static void main(String[] args) throws Exception{
Date date = new Date(100000000L);
SheepSerializeClone s1 = new SheepSerializeClone("老羊", date);;
FileOutputStream outputStream = new FileOutputStream("E:/test.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(s1);
objectOutputStream.close();
outputStream.close();
System.out.println(s1.getName() + "&" + s1.getBirthday() + "&" + s1);
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("E:/test.txt"));
SheepSerializeClone obj1 = (SheepSerializeClone) inputStream.readObject();
date.setTime(120000001L);
s1.setBirthday(s1.getBirthday());
System.out.println(obj1.getName() + "&" + obj1.getBirthday() + "&" + obj1);
}
}
老羊&Fri Jan 02 11:46:40 CST 1970&com.chaytech.example.prototype.SheepSerializeClone@6979e8cb
老羊&Fri Jan 02 11:46:40 CST 1970&com.chaytech.example.prototype.SheepSerializeClone@19bb089b
可以看到结果也是一致的。