1 什么是原型模式
原型模式就是将一个对象A克隆一份得到对象B,对象B与对象A完全一样,并且对象B的修改不会影响到对象A。说白了就是对象的克隆。
2 原型模式的实现
要实现原型模式,对于Java语言,只需要完成如下两步就可以:
(1)让对象实现Cloneable接口
(2)重写clone()方法
假如我们有一个Dog类,让它实现Cloneable接口:
public class Dog implements Cloneable {
private String name;
private Date birthday;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Dog() {
}
public Dog(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
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
}
现在我们要实现克隆:
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象dog1
Date date = new Date();
Dog dog1 = new Dog("二哈", date);
System.out.println("dog1:" + dog1);
System.out.println("dog1 hashcode:" + dog1.hashCode());
// dog2克隆dog1
Dog dog2 = (Dog) dog1.clone();
System.out.println("dog2:" + dog2);
System.out.println("dog2 hashcode:" + dog2.hashCode());
// 修改dog2不影响dog1
dog2.setName("克隆的二哈");
System.out.println("dog1:" + dog1);
System.out.println("dog2:" + dog2);
}
输出结果:
dog1:Dog{name='二哈', birthday=Thu May 28 22:38:17 CST 2020}
dog1 hashcode:329611835
dog2:Dog{name='二哈', birthday=Thu May 28 22:38:17 CST 2020}
dog2 hashcode:1347870667
dog1:Dog{name='二哈', birthday=Thu May 28 22:38:17 CST 2020}
dog2:Dog{name='克隆的二哈', birthday=Thu May 28 22:38:17 CST 2020}
可以看到dog1和dog2的name和birthday都是相同的,但是hashcode不同,说明是两个对象,并且修改dog2的name不会影响到dog1的值。
3 深克隆和浅克隆
然而上面的克隆方式如果修改的是引用类型对象,那么原型对象和克隆对象都会受影响:
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象dog1
Date birthday = new Date();
Dog dog1 = new Dog("二哈", birthday);
Dog dog2 = (Dog) dog1.clone();
System.out.println("dog1:" + dog1);
System.out.println("dog2:" + dog2);
// 修改birthday的值
birthday.setTime(12121212);
System.out.println("----------------");
System.out.println("dog1:" + dog1);
System.out.println("dog2:" + dog2);
}
输出结果:
dog1:Dog{name='二哈', birthday=Thu May 28 22:56:29 CST 2020}
dog2:Dog{name='二哈', birthday=Thu May 28 22:56:29 CST 2020}
----------------
dog1:Dog{name='二哈', birthday=Thu Jan 01 11:22:01 CST 1970}
dog2:Dog{name='二哈', birthday=Thu Jan 01 11:22:01 CST 1970}
可以看到修改birthday的值之后,dog1和dog2的值都变化了。这是因为clone只是把引用对象的地址拷贝了一份,并没有重新创建个对象,所以dog1和dog2的birthday指向的是同一个对象,这就是浅克隆。
为了解决这个问题,我们需要对Dog的clone方法做一些修改:
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = super.clone();
// 将对象的属性也克隆
Dog dog = (Dog) object;
dog.birthday = (Date) this.birthday.clone();
return dog;
}
这时我们在运行上面的代码的结果如下:
dog1:Dog{name='二哈', birthday=Thu May 28 23:05:03 CST 2020}
dog2:Dog{name='二哈', birthday=Thu May 28 23:05:03 CST 2020}
----------------
dog1:Dog{name='二哈', birthday=Thu Jan 01 11:22:01 CST 1970}
dog2:Dog{name='二哈', birthday=Thu May 28 23:05:03 CST 2020}
我们发现修改birthday的值只会影响dog1,而不会影响到dog2,这样就能保证Dog的克隆都是深克隆。当然,上述代码中对属性的克隆也是一次浅克隆,如果想实现完全的深克隆,可以是使用序列化和反序列化来实现,但是序列化和反序列化需要和IO流打交道,效率自然比不过Cloneable接口,所以在具体实现原型模式时,需要按照具体场景选择合适的实现方法。