原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
所谓原型模式,就是 Java 中的克隆技术,以某个对象为原型,复制出新的对象。显然新的对象具备原型对象的特点,效率高(避免了重新执行构造过程步骤)。
我们在设计后端代码的工程结构的时候会定义分层领域模型(VO、DTO、DO等),Service 层调 Dao 层代码获取数据库数据,返回的对象模型是 DO,在 Service 需要把 DO 对象转换为 VO 对象,这个时候就需要使用到原型模式。
原型模式主要适用于以下场景:
- 类初始化消耗资源较多;
- 使用 new 产生一个对象需要非常繁琐的过程(数据准备、访问权限等);
- 构造函数比较复杂;
- 在循环体中生产大量对象。
对象克隆有如下实现方式:
继承 Cloneable 接口,实现 clone 方法
反射实现克隆,例子有 Apache BeanUtils、PropertyUtils,Spring BeanUtils
序列化实现克隆(字节流、JSON)
浅克隆
对值类型的成员变量进行值的复制,对引用类型的成员变量仅仅复制引用,不复制引用的对象。对应的实现方式有 Java clone、反射。
浅克隆的写法如下所示:
/**
* 学校对象
*/
public class School {
private String schoolName;
public School() {
schoolName = new String("苏州小学");
}
// get set
}
/**
* 学生对象
*/
public class Student implements Cloneable {
private String studentName;
private School school;
public Student() {
studentName = new String("张三");
school = new School();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// get set
}
测试代码如下:
private static void shallowClone() throws Exception {
System.out.println("*************浅克隆*************");
Student s1 = new Student();
Student s2 = (Student) s1.clone();
System.out.println(s1 == s2);
System.out.println(s1.getSchool() == s2.getSchool());
System.out.println(s1.getSchool());
System.out.println(s2.getSchool());
}
运行结果如下图所示。
从测试结果可以看出,school 的引用地址是相同的,这意味着复制的不是值,而是引用的地址。这样的话,如果我们修改任意一个对象的 school 的值,另外一个对象中的 school 的值也会改变,这就是我们常说的浅克隆。
深克隆
对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制。对应的实现方式有序列化。
深克隆的写法如下所示:
public class School implements Serializable {
private static final long serialVersionUID = 1L;
private String schoolName;
public School() {
schoolName = new String("苏州小学");
}
// get set
}
public class Student implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private String studentName;
private School school;
public Student() {
studentName = new String("张三");
school = new School();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* 利用序列化、反序列化实现深拷贝
*
* @return
* @throws Exception
*/
public Student deepClone() throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Student)ois.readObject();
}
// get set
}
测试代码如下:
private static void deepClone() throws Exception {
System.out.println("*************深克隆*************");
Student s1 = new Student();
Student s2 = s1.deepClone();
System.out.println(s1 == s2);
System.out.println(s1.getSchool() == s2.getSchool());
System.out.println(s1.getSchool());
System.out.println(s2.getSchool());
}
运行结果如下图所示。
深克隆也可以通过 JSON 工具来实现。
摘录:《Spring 5 核心原理与30个类手写实战》来自文艺界的Tom老师的书籍。
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/bo7icd 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。