📝 原型模式
🟢 概述
- 📌 原型模式的核心思想是通过复制现有对象来创建新的对象,而不是通过实例化的方式。
- 📌 这种模式特别适用于对象的创建成本较高、复杂或者系统需要保护某些对象的信息而使其不被修改时。
🟢 特点
- 📌 性能和资源: 可以解决构建复杂对象的资源消耗问题,能在某些场景中提升构建对象的效率。
- 📌 保护性拷贝: 返回一个拷贝对象,实现只读的限制。
🟢 结构
-
抽象原型类:
- 📌 定义了一个
clone
接口,规定了具体原型对象必须实现的方法。 - ⚠️ 注意: 在Java中,可以通过实现
Cloneable
接口并重写clone
方法来达到这一要求。
- 📌 定义了一个
-
具体原型类:
- 📌 实现或继承抽象原型类,并重写
clone
方法。 - 📌 该类的对象是可被复制的。
- 📌 实现或继承抽象原型类,并重写
-
对象访问类:
- 📌 使用具体原型类中的
clone
方法来复制新的对象。
- 📌 使用具体原型类中的
⚠️ 注意: 使用原型模式时要处理深拷贝和浅拷贝的问题。浅拷贝只复制对象的基本类型,对象类型仍然指向原来的地址。深拷贝则会复制对象内的所有属性,包括对象。
📝 深克隆和浅克隆
📌 浅克隆 (Shallow Clone)
🟢 特点
-
基本数据类型:
- 📌 在浅克隆中,当对象被复制后,所有基本数据类型(如
int
,float
,char
等)的变量都会重新创建。
- 📌 在浅克隆中,当对象被复制后,所有基本数据类型(如
-
引用类型:
- 📌 对于引用类型(如数组、对象引用等),浅克隆只复制引用,不复制引用指向的对象。因此,原对象及其复制体中的这些引用类型变量仍然指向相同的对象。
🟢 例子
假设有一个对象 A
,该对象有一个基本数据类型的变量 x
和一个引用类型的变量 y
(指向对象 B
)。
当对 A
进行浅克隆后:
- 新对象
A'
的x
是新创建的,与原对象A
中的x
不共享存储空间。 - 新对象
A'
的y
仍然指向对象B
,与原对象A
中的y
指向的是同一个对象B
。
🟢 实现方式
-
使用
clone
方法:- 📌 Java提供的默认
clone
方法就是浅克隆。 - 📌 基本数据类型会被克隆,而引用类型的变量则只是复制了引用。
- 📌 Java提供的默认
-
使用工具类:
- 📌 如
BeanUtil.copyProperties
可以进行浅克隆,但要确保不进行深度复制。 - ⚠️ 注意: 需要留意该方法的具体行为和配置。
- 📌 如
⚠️ 注意: 浅克隆可能会导致意外的问题,特别是当你希望复制的对象与原对象是完全独立的时候。在这种情况下,可能需要使用深克隆。
package com.study.notes.design.patterns.pattern.create.prototype.test3;
import java.io.Serializable;
/**
* @version v1.0
* @ClassName: Student
* @Description: TODO(一句话描述该类的功能)
* @Author: lzq
*/
public class Student implements Serializable {
//学生的姓名
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
package com.study.notes.design.patterns.pattern.create.prototype.test3;
import java.io.Serializable;
/**
* @version v1.0
* @ClassName: Citation
* @Description: TODO(一句话描述该类的功能)
* @Author: lzq
*/
public class Citation implements Cloneable, Serializable {
private Student stu;
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu;
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
public void show() {
System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
}
package com.study.notes.design.patterns.pattern.create.prototype.test2;
/**
* @version v1.0
* @ClassName: CitaionTest
* @Description: TODO(一句话描述该类的功能)
* @Author: lzq
*/
public class CitaionTest {
public static void main(String[] args) throws CloneNotSupportedException {
//1,创建原型对象
Citation citation = new Citation();
//创建张三学生对象
Student stu = new Student();
stu.setName("张三");
citation.setStu(stu);
//2,克隆奖状对象
Citation citation1 = citation.clone();
Student stu1 = citation1.getStu();
stu1.setName("李四");
//3,调用show方法展示
citation.show();
citation1.show();
}
}
📌 深克隆 (Deep Clone)
🟢 特点
-
基本数据类型:
- 📌 在深克隆中, 所有基本数据类型(如
int
,float
,char
等)的变量都会重新创建。
- 📌 在深克隆中, 所有基本数据类型(如
-
引用类型:
- 📌 对于引用类型(如数组、对象引用等),深克隆会复制引用及引用指向的所有对象,直到该对象可达的所有对象。这意味着复制出的对象与原对象是完全独立的,它们不共享任何引用类型的成员变量。
🟢 例子
假设有一个对象 A
,该对象有一个基本数据类型的变量 x
和一个引用类型的变量 y
(指向对象 B
)。
当对 A
进行深克隆后:
- 新对象
A'
的x
是新创建的,与原对象A
中的x
不共享存储空间。 - 新对象
A'
的y
指向一个新的对象B'
,这个对象B'
是对象B
的深克隆。B'
与B
是完全独立的。
🟢 实现
-
使用
clone
方法:- 📌 可以实现深克隆,但需要确保所有的成员变量都被正确克隆。
- ⚠️ 注意: 需要递归地对所有引用类型的成员变量进行克隆。
-
使用工具类:
- 📌 如
BeanUtil.copyProperties
可以进行深克隆。 - ⚠️ 注意: 该方法的限制和要求,以及是否真正满足深克隆的需求。
- 📌 如
-
序列化与反序列化:
- 📌 通过对象的序列化和随后的反序列化来实现深克隆。
- 📌 这种方法的好处是不需要为每个类编写复杂的克隆逻辑。
⚠️ 注意: 深克隆可能相对耗时和耗资源。只有当确实需要对象与其克隆体之间完全隔离时,才应该使用深克隆。
package com.study.notes.design.patterns.pattern.create.prototype.test3;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @version v1.0
* @ClassName: CitaionTest
* @Description: TODO(一句话描述该类的功能)
* @Author: lzq
*/
public class CitaionTest {
public static void main(String[] args) throws Exception {
//1,创建原型对象
Citation citation = new Citation();
//创建张三学生对象
Student stu = new Student();
stu.setName("张三");
citation.setStu(stu);
//创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/robin/a.txt"));
//写对象
oos.writeObject(citation);
//释放资源
oos.close();
//创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/robin/a.txt"));
//读取对象
Citation citation1 = (Citation) ois.readObject();
//释放资源
ois.close();
Student stu1 = citation1.getStu();
stu1.setName("李四");
citation.show();
citation1.show();
}
}
⚠️ 注意: 无论是浅克隆还是深克隆,都需要确保对象的 clone
方法已被正确覆盖,并且该对象实现了 Cloneable
接口。否则,调用 clone
方法时会抛出 CloneNotSupportedException
异常。