原型模式 (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
。可以观察到两个对象是独立的(因为引用地址不同),它们的列表也是独立的副本,所以对列表的修改不会相互影响。需要注意的是当被克隆的类包含不可被序列化或反序列化的字段(如线程、文件句柄等),则需要采取额外措施来处理这些字段。