原型模式 (Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需依赖于昂贵的实例化过程。该模式基于原型实例生成新的对象,并且可以根据需要进行修改和定制。
直接对目标对象进行浅拷贝,复制基本数据类型的值和引用类型的地址。
- import java.util.ArrayList;
- import java.util.List;
-
- class Prototype implements Cloneable {
- private int id;
- private String name;
- private List
list; -
- // 构造函数
-
- public Prototype(int id, String name, List
list) { - this.id = id;
- this.name = name;
- this.list = list;
- }
-
- // Getter 和 Setter
-
-
- public void setList(List
list) { - this.list = list;
- }
-
- public List
getList(){ - return this.list;
- }
-
- @Override
- public Prototype clone() throws CloneNotSupportedException {
- return (Prototype) super.clone();
- }
- }
-
-
- public class Main {
- public static void main(String[] args) {
- List
originalList = new ArrayList<>(); - originalList.add("item1");
- originalList.add("item2");
-
- Prototype originalObject = new Prototype(1, "Original", originalList);
-
- try {
- Prototype clonedObject = originalObject.clone();
-
- System.out.println(originalObject == clonedObject); // 输出 false
- System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 true
-
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- }
- }
在上述示例中,Prototype
类实现了 Cloneable
接口,并重写了 clone()
方法。通过调用 super.clone()
进行浅拷贝操作,返回一个新的克隆对象。
接下来我们创建了一个原始对象 originalObject
,并进行浅拷贝得到克隆对象 clonedObject
。可以观察到两个对象不相等(因为引用地址不同),但是它们共享相同的引用类型字段(即列表),所以对列表的修改会同时影响两个对象。
存在问题
由于浅拷贝仅仅是对引用进行了简单地复制操作,并没有创建全新独立的副本,因此可能会导致以下问题:
对目标对象进行深拷贝,复制基本数据类型的值和引用类型的整个对象。
- import java.util.ArrayList;
- import java.util.List;
-
- class Prototype implements Cloneable {
- private int id;
- private String name;
- private List
list; -
- // 构造函数
-
- public Prototype(int id, String name, List
list) { - this.id = id;
- this.name = name;
- this.list = list;
- }
-
- // Getter 和 Setter
-
-
- public List
getList() { - return list;
- }
-
- public void setList(List
list) { - this.list = list;
- }
-
- @Override
- public Prototype clone() throws CloneNotSupportedException {
- Prototype clonedObject = (Prototype) super.clone();
- clonedObject.list = new ArrayList<>(this.list); // 创建全新副本
- return clonedObject;
- }
- }
-
-
- public class Main {
- public static void main(String[] args) {
- List
originalList = new ArrayList<>(); - originalList.add("item1");
- originalList.add("item2");
-
- Prototype originalObject = new Prototype(1, "Original", originalList);
-
- try {
- Prototype clonedObject = originalObject.clone();
-
- System.out.println(originalObject == clonedObject); // 输出 false
- System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false
-
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- }
- }
在上述示例中,Prototype
类实现了 Cloneable
接口,并重写了 clone()
方法。通过调用 super.clone()
进行浅拷贝操作,然后对引用类型字段进行深层次克隆。
接下来我们创建了一个原始对象 originalObject
,并进行深拷贝得到克隆对象 clonedObject
。可以观察到两个对象完全独立(因为引用地址不同),它们的列表也是独立的副本,所以对列表的修改不会相互影响。
深拷贝虽然解决了浅拷贝可能带来的问题,但也存在以下问题:
使用原型管理器来存储和获取原型实例,可以通过名称或标识符查找并克隆相应的原型。
- import java.util.ArrayList;
- import java.util.List;
- import java.util.HashMap;
- import java.util.Map;
-
- class Prototype implements Cloneable {
- private int id;
- private String name;
- private List
list; -
- // 构造函数
-
- public Prototype(int id, String name, List
list) { - this.id = id;
- this.name = name;
- this.list = list;
- }
-
- // Getter 和 Setter
- public void setName(String name){
- this.name = name;
- }
-
- public String getName() {
- return this.name;
- }
- public List
getList() { - return list;
- }
-
- public void setList(List
list) { - this.list = list;
- }
-
- @Override
- public Prototype clone() throws CloneNotSupportedException {
- Prototype clonedObject = (Prototype) super.clone();
- clonedObject.list = new ArrayList<>(this.list); // 创建全新副本
- return clonedObject;
- }
- }
-
- class PrototypeRegistry {
- private static PrototypeRegistry instance;
- private Map
prototypes; -
- private PrototypeRegistry() {
- prototypes = new HashMap<>();
- }
-
- public static synchronized PrototypeRegistry getInstance() {
- if (instance == null) {
- instance = new PrototypeRegistry();
- }
- return instance;
- }
-
- public void addPrototype(String name, Prototype prototype) {
- prototypes.put(name, prototype);
- }
-
- public void removePrototype(String name) {
- prototypes.remove(name);
- }
-
- public Prototype getPrototype(String name) throws CloneNotSupportedException {
- Prototype prototype = prototypes.get(name);
- if (prototype != null) {
- return prototype.clone();
- }
- return null;
- }
- }
-
- public class Main {
- public static void main(String[] args) {
- // 创建原始对象并添加到注册表
- List
originalList = new ArrayList<>(); - originalList.add("item1");
- originalList.add("item2");
-
- Prototype originalObject = new Prototype(1, "Original", originalList);
-
- PrototypeRegistry registry = PrototypeRegistry.getInstance();
- registry.addPrototype("object1", originalObject);
-
- // 从注册表获取副本并进行修改
- try {
- Prototype clonedObject = registry.getPrototype("object1");
-
- if (clonedObject != null) {
- clonedObject.setName("Cloned");
-
- System.out.println(originalObject.getName()); // 输出 "Original"
- System.out.println(clonedObject.getName()); // 输出 "Cloned"
-
- System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false
- }
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
-
- }
- }
在深拷贝实现代码的基础上,我们增加了PrototypeRegistry
类实现了原型注册表的功能。通过使用单例模式保证只有一个注册表实例,并使用 HashMap
存储原型对象。然后创建了一个原始对象 originalObject
并将其添加到注册表中。然后通过调用 getPrototype()
方法从注册表中获取副本,并对副本进行修改。可以观察到两个对象是独立的,对一个对象的修改不会影响到另一个对象。
将对象序列化为字节流,然后再反序列化为新对象。
- import java.io.*;
- import java.util.ArrayList;
- import java.util.List;
-
- class Prototype implements Serializable {
- private int id;
- private String name;
- private List
list; -
- // 构造函数
-
- public Prototype(int id, String name, List
list) { - this.id = id;
- this.name = name;
- this.list = list;
- }
-
- // Getter 和 Setter
-
-
- public List
getList() { - return list;
- }
-
- public Prototype deepClone() {
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(this);
-
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bais);
- return (Prototype) ois.readObject();
-
- } catch (IOException | ClassNotFoundException e) {
- e.printStackTrace();
- }
-
- return null;
- }
- }
-
- public class Main {
- public static void main(String[] args) {
- List
originalList = new ArrayList<>(); - originalList.add("item1");
- originalList.add("item2");
-
- Prototype originalObject = new Prototype(1, "Original", originalList);
-
- Prototype clonedObject = originalObject.deepClone();
-
- System.out.println(originalObject == clonedObject); // 输出 false
- System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false
-
- }
- }
在上述示例中,Prototype
类实现了 Serializable
接口,并提供了一个 deepClone()
方法来进行深拷贝。在该方法中,首先将对象写入输出流并转换为字节数组,然后再从输入流中读取字节数组并转换回对象。然后我们创建了一个原始对象 originalObject
并通过调用 deepClone()
方法进行深拷贝得到副本对象 clonedObject
。可以观察到两个对象是独立的(因为引用地址不同),它们的列表也是独立的副本,所以对列表的修改不会相互影响。需要注意的是当被克隆的类包含不可被序列化或反序列化的字段(如线程、文件句柄等),则需要采取额外措施来处理这些字段。