一、基本介绍
原型模式(创建型):用一个已经创建好的对象作为原型,通过复制该原型对象创建出相同或相似的对象。
二、包含角色
1.抽象原型类:规定了具体原型对象必须实现的接口。
2.具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
三、案例及UML类图
案例说明:
当做试卷的时候,我们需要抄别人的试卷,则需要复制对原来的试卷进行复制操作。
UML类图:
方式一:浅克隆
类TestPaper1:
public class TestPaper1 implements Cloneable{
private String name;
private int clazz;
private String content;
private Integer rank;
private Date date;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getClazz() {
return clazz;
}
public void setClazz(int clazz) {
this.clazz = clazz;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
@Override
public TestPaper1 clone() throws CloneNotSupportedException {
return (TestPaper1) super.clone();
}
}
说明:试卷类,具体原型类,需要实现抽象原型接口Cloneable。
类Cloneable:
public interface Cloneable {
}
说明:克隆接口,抽象原型类,该类并无clone方法,因为在java中clone是Object的,所以就不必再有clone方法,但使用clone必须实现该接口。
类PrototypeTest1:
public class PrototypeTest1 {
public static void main(String[] args) throws CloneNotSupportedException, InterruptedException {
TestPaper1 testPaper = new TestPaper1();
testPaper.setClazz(654);
testPaper.setName("xuye");
testPaper.setContent("题目内容");
testPaper.setDate(new Date());
testPaper.setRank(1);
TestPaper1 testPaper1 = testPaper.clone();
System.out.println(testPaper.getDate() == testPaper1.getDate());
}
}
说明:测试类及客户端,浅克隆输出的结果为true。
方式二:深克隆
类TestPaper2:
public class TestPaper2 implements Cloneable, Serializable {
private String name;
private int clazz;
private String content;
private Integer rank;
private Date date;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getClazz() {
return clazz;
}
public void setClazz(int clazz) {
this.clazz = clazz;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
@Override
public TestPaper2 clone() {
TestPaper2 testPaper2 = null;
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ObjectInputStream objectInputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
byte[] bytes = byteArrayOutputStream.toByteArray();
byteArrayInputStream = new ByteArrayInputStream(bytes);
objectInputStream = new ObjectInputStream(byteArrayInputStream);
testPaper2 = (TestPaper2) objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
objectInputStream.close();
byteArrayInputStream.close();
objectOutputStream.close();
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return testPaper2;
}
}
说明:试卷类,具体原型类,需要实现抽象原型接口Cloneable。
类Cloneable:
public interface Cloneable {
}
说明:克隆接口,抽象原型类,该类并无clone方法,因为在java中clone是Object的,所以就不必再有clone方法,但使用clone必须实现该接口。
类PrototypeTest2:
public class PrototypeTest2 {
public static void main(String[] args) throws CloneNotSupportedException, InterruptedException {
TestPaper2 testPaper = new TestPaper2();
testPaper.setClazz(654);
testPaper.setName("xuye");
testPaper.setContent("题目内容");
testPaper.setDate(new Date());
testPaper.setRank(1);
TestPaper2 testPaper1 = testPaper.clone();
System.out.println(testPaper.getDate() == testPaper1.getDate());
}
}
说明:测试类及客户端,浅克隆输出的结果为false。
四、适用场景
1.适用于对模板进行重复创建的场景,如抄作业,抄试卷,打印机打印等,深克隆模式用在需要对引用类型的成员变量进行修改时使用。
五、其它
深克隆和浅克隆区别:
浅克隆:对于基础数据类型的成员变量,直接赋值,对于引用类型的成员变量则是把原型中该成员变量的地址拷贝至克隆对象的成员变量中,即引用类型的成员变量地址和原型成员变量一致。
深克隆:对于基础数据类型的成员变量,直接赋值,对于引用类型的成员变量是在堆中开辟一个空间,把原型对象中该成员变量的值拷贝过去,其地址已经发生改变。即引用类型的成员变量地址和原型成员变量不一致,但值相同。
深克隆的方式:
在java中深克隆有多种方式,本文使用的是序列化,该方式要求牵扯到原型对象的类都需要实现序列化接口,即Serializable接口,当然还可以使用例如json进行深克隆,不需要实现任何接口,但效率不如序列化。
注意事项:
在java中clone方法是Objcet类的方法,所有的类都默认是继承了Objcet类,所以Cloneable接口是空方法,是一个标记接口,如果需要浅克隆,直接调用Objcet的clone方法实现Cloneable接口即可,虚拟机会进行其对应的操作。