1.介绍
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。属于创建型模式。
UML图:
1)浅拷贝: 指创建一个新的对象,然后将原始对象的字段值复制到新对象中。如果字段是基本类型,直接复制其值;如果字段是引用类型,则复制其引用,新对象和原对象将共享同一份引用指向相同的内存地址。一般实现Cloneable接口,重写clone()方法。
2)深拷贝: 指创建一个新的对象,然后将原始对象的字段值复制到新对象中。但与浅拷贝不同的是,对于引用类型的字段,深拷贝会递归地复制其所指向的对象,而不是复制引用本身。一般实现Serializable接口进行序列化再反序列化。
2.示例
一个学校的学生信息有着许多可以复用的,因此可以使用原型模式进行设计,快速创建复用的信息。
1)学生对象:Student
public class Student implements Cloneable, Serializable {
private String name;
private String sex;
/**
* 年级
*/
private String grade;
/**
* 学校
*/
private String schoolName;
/**
* 学科
*/
private List<String> subjects;
/**
* 浅拷贝 ,调用顶级父类Object的方法
*
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
/**
* 深拷贝
*
* @return
*/
public Student deepClone() {
try {
// 转换二进制输出流,序列化
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
// 输入流转换,反序列化,拷贝形成新的对象
ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bai);
return (Student) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Student() {
}
public Student(String name, String sex, String grade, String schoolName, List<String> subjects) {
this.name = name;
this.sex = sex;
this.grade = grade;
this.schoolName = schoolName;
this.subjects = subjects;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
public List<String> getSubjects() {
return subjects;
}
public void setSubjects(List<String> subjects) {
this.subjects = subjects;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", grade='" + grade + '\'' +
", schoolName='" + schoolName + '\'' +
", subjects=" + subjects +
'}';
}
}
2)运行:
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student("大美", "女", "一年级", "大大小学", Arrays.asList("数学", "语文", "英语"));
// 浅拷贝
Student student2 = student1.clone();
student2.setName("小美");
student2.getSubjects().set(1,"da");
// 深拷贝
Student student3 = student1.deepClone();
student3.setName("小庄");
student3.setSex("男");
student3.getSubjects().set(1,"hh");
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
// 浅拷贝,引用类型数据指向共同的地址
System.out.println(student2.getSubjects() == student1.getSubjects());
System.out.println(student3.getSubjects() == student1.getSubjects());
}
}
3.总结
1)优点:
a. 当创建新的对象实例较为复杂时,可以简化对象的创建过程,提高新实例的创建效率;
b. 可以辅助实现撤销操作,采取深克隆的方式保存对象的状态,将对象复制⼀份并将其状态保存起来,在需要的时候使其恢复到历史状态。
2)缺点:
a. 每个类都需要重写克隆方法,比较繁琐且不符合开闭原则;
b. 深克隆的实现编写较为复杂,且对象间存在多重嵌套引用时,其中的每个必须支持深克隆。