概念
原型模式就是以一个已创建实例为原型,通过复制该原型对象来创建一个和原型相同或者相似的新对象。
优点:在需要大量创建相似对象的场景下可以提高性能,因为不需要new对象,消耗的资源少。
缺点:通过克隆方式实现复制时,要注意是否有引用对象。
在java中通常都是通过克隆或者序列化来实现原型模式。
克隆
浅克隆
首先我们创建User和Work两个类,作为克隆的实例。
public class User implements Cloneable {
public String name;
public int age;
public Work work;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Work implements Cloneable {
public String workName;
public Long salary;
public Date entryDate;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试
public class PrototypeTest {
public static void main(String[] args) {
User user = new User();
user.name = "老王";
user.age = 28;
Work work = new Work();
work.workName = "程序猿";
work.salary = 40000L;
work.entryDate = new Date();
user.work = work;
try {
User other = (User) user.clone();
System.out.println(user == other);
System.out.println(other.name);
System.out.println(other.age);
System.out.println(user.work == other.work);
System.out.println(other.work.workName);
System.out.println(other.work.salary);
System.out.println(other.work.entryDate);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
测试结果,我们可以看到对于引用对象work,是没有复制对象里面的内容,而是将对象的地址拷贝了一份。
false
老王
28
true // 两个work实例是相等的
程序猿
40000
Tue Dec 03 09:51:02 CST 2019
因此我们可以知道浅克隆只拷贝按值传递的数据(基本数据类型),但对String类型,数组,引用对象都不拷贝,也就是说内存中原型实例和拷贝实例都指向同一个引用对象的地址。对于String类型虽然也指向同一个地址,不过由于String类型是不可变类型,所以即使克隆出来的对象改变了也不会影响到原对象。
深克隆
如何实现引用对象、数组指向不同的地址,我们可以通过重写clone方法来实现。
public class User implements Cloneable, Serializable {
public String name;
public int age;
public Work work;
@Override
protected Object clone() throws CloneNotSupportedException {
User object = (User) super.clone();
object.work = (Work) object.work.clone();
return object;
}
}
在代码中我们通过手动调用work实例的clone方法从而实现深克隆。
测试结果:
false
老王
28
false // 两个work实例是不相等的
程序猿
40000
Tue Dec 03 10:01:09 CST 2019
序列化
除了重写clone方法手动调用引用对象的clone方法之外,我们还可以通过Serializable接口实现深克隆。
public class User implements Cloneable, Serializable {
public String name;
public int age;
public Work work;
@Override
protected Object clone() throws CloneNotSupportedException {
return serialize();
}
private Object serialize() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
Object object = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
object = ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return object;
}
}
public class Work implements Cloneable, Serializable {
public String workName;
public Long salary;
public Date entryDate;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试结果:
false
老王
28
false // 两个work实例是不相等的
程序猿
40000
Tue Dec 03 10:01:09 CST 2019