一、Java 的原型模式是什么?
当我们拥有一个对象 obj_01 的时候,我们想通过拷贝这个对象来得到另一个对象 obj_02,类似于复制粘贴功能。这个过程叫做【克隆】,这个模型就叫做原型模式。
二、原型模式分为两种:浅克隆 和 深克隆。
代码说明:ProtoType01.java 为待克隆的 Java Bean,Student.java 为ProtoType01 的一个引用类型的成员变量,ProtoType01Test 为测试用例。
1、浅克隆。java 中每个对象都是 Object.java 的子类,Object 有一个方法 clone(),所以每个类都可以通过 super.clone()方法来实现浅克隆。浅克隆会给新对象一个新的内存地址,但是新对象引用类型的成员变量的地址仍然不会改变。待克隆的类必须实现接口 Cloneable,否则会抛出异常。参见 ProtoType01.java 的 clone()方法。
在 ProtoType01Test.java 中我们通过调用 test01来克隆,并将内存地址打印出来。(我们通过System.identityHashCode() 方法来获取对象的内存地址,即使重写了 hasCode()方法,该值也不会改变。)
打印结果为:
427451634
846813423
1019071692
1019071692
mmm
mmm
通过观察打印结果我们可以发现,克隆前后的内存地址发生了改变(从 427451634 变为了 846813423),但是其引用类型的成员变量内存地址并没有发生任何变化(一直都是 1019071692 ),并且我们在浅克隆之后给 Student 的 name 设置了值,它覆盖了原对象的name值,变成了 mmm。
2、深克隆。深克隆是相较浅克隆而言的,它不仅会改变克隆后新对象的内存地址,它还会给新对象的每一个成员变量重新分配内存地址(即使该成员变量为引用类型)。也就是说新对象和老对象在内存的占用上已经完全不同了,没有任何共用的地方。
我们通过流的转化实现深克隆,实现流的转化就必须将对象序列化,所以待克隆的类以及它属性中的对象都必须实现 Serializable 接口,当我们将一个对象转化成流的时候,JVM 会保留原有对象,并重新 "复制" 一个对象,所以我们通过将一个对象转化成对象流,再将该对象流转化回对象来实现深克隆。参见 ProtoType01.java 的 deepClone()方法。
在 ProtoType01Test.java 中我们通过调用 test02来深克隆,并将内存地址打印出来。打印结果为:
9964338
373773844
2114387947
627328633
nnn
mmm
通过观察打印结果我们可以发现,无论是新的克隆后的新对象,还是它的引用类型 Student 的内存地址都发生了改变,所以,原对象 的 student().getName() 值依然为 nnn,没有改变。
public class Student implements Serializable{
private static final long serializableVersionId = 1L;
private String name;
public void setName(String name) {
this.name = name;
}
public String getName(){
return this.name;
}
}
public class ProtoType01 implements Cloneable, Serializable{
private static final long serializableVersionId = 1L;
private String attr;
private Student student;
//浅克隆
public ProtoType01 clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return (ProtoType01)obj;
}
//深克隆
public ProtoType01 deepClone() {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objectOut = new ObjectOutputStream(out);
objectOut.writeObject(this);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream objectIn = new ObjectInputStream(in);
return (ProtoType01)objectIn.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
public class ProtoType01Test {
@Test
public void test01() {
ProtoType01 pt01 = new ProtoType01();
Student student = new Student();
student.setName("nnn");
pt01.setStudent(student);
ProtoType01 pt02 = pt01.clone();
pt02.getStudent().setName("mmm");
//打印克隆对象的内存地址
System.out.println(System.identityHashCode(pt01));
System.out.println(System.identityHashCode(pt02));
//打印克隆对象引用对象类型Student的内存地址
System.out.println(System.identityHashCode(pt01.getStudent()));
System.out.println(System.identityHashCode(pt02.getStudent()));
System.out.println((pt01.getStudent().getName()));
System.out.println((pt02.getStudent().getName()));
}
@Test
public void test02() {
ProtoType01 pt01 = new ProtoType01();
Student student = new Student();
student.setName("nnn");
pt01.setStudent(student);
ProtoType01 pt02 = pt01.deepClone();
pt02.getStudent().setName("mmm");
//打印克隆对象的内存地址
System.out.println(System.identityHashCode(pt01));
System.out.println(System.identityHashCode(pt02));
//打印克隆对象引用对象类型Student的内存地址
System.out.println(System.identityHashCode(pt01.getStudent()));
System.out.println(System.identityHashCode(pt02.getStudent()));
System.out.println((pt01.getStudent().getName()));
System.out.println((pt02.getStudent().getName()));
}
}