Appearance
原型模式 (Prototype Pattern)
一、定义
一句话概括:用原型实例指定创建对象的种类,并通过拷贝这些原型来创建新的对象。
官方定义(GoF):Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
原型模式的核心思想是:通过克隆(Clone)已有对象来创建新对象,而不是通过 new。当对象的创建成本很高(如需要大量数据库查询或网络请求)时,克隆一个已有对象比重新创建高效得多。
二、解决的问题
2.1 什么场景下需要原型模式?
- 对象创建成本高:创建对象需要大量资源(数据库查询、网络请求、复杂计算)
- 需要动态创建对象:运行时才知道要创建什么类型的对象
- 需要避免复杂的工厂层次:相比工厂模式,原型模式只需一个
clone()方法 - 需要保存对象状态快照:如撤销操作、状态回滚
- 需要大量相似对象:如游戏中的怪物、子弹,只需克隆模板然后微调
2.2 不用原型模式会有什么问题?
java
// 问题场景:创建一份报表需要从数据库查询大量数据
public class Report {
private List<SalesData> salesData; // 从数据库查询的结果
private List<UserBehavior> userBehavior; // 从数据库查询的结果
private Map<String, Config> configs; // 从配置中心加载
// 创建一份报表需要 5 秒(数据库查询 + 网络请求)
public Report() {
loadFromDatabase(); // 耗时 3 秒
loadFromConfigCenter(); // 耗时 2 秒
}
}
// 如果需要创建 100 份相似的报表(只修改标题),需要 500 秒!
// 使用原型模式:先创建一份,然后克隆 99 份,只需 5 秒 + 克隆时间三、结构
3.1 文字描述
原型模式包含三个角色:
- Prototype(抽象原型):声明克隆方法,通常是
Cloneable接口或自定义接口 - ConcretePrototype(具体原型):实现克隆方法,返回自身的副本
- Client(客户端):通过调用原型的
clone()方法创建新对象
3.2 ASCII 类图
┌──────────────────────────┐
│ <<interface>> │
│ Prototype │
├──────────────────────────┤
│ + clone(): Prototype │
└──────────────────────────┘
△
│
┌───────────────────────┐ ┌───────────────────────┐
│ ConcretePrototypeA │ │ ConcretePrototypeB │
├───────────────────────┤ ├───────────────────────┤
│ - field1: String │ │ - field1: int │
│ - field2: List<String> │ │ - field2: Date │
├───────────────────────┤ ├───────────────────────┤
│ + clone(): Prototype │ │ + clone(): Prototype │
│ (深拷贝/浅拷贝) │ │ (深拷贝/浅拷贝) │
└───────────────────────┘ └───────────────────────┘3.3 原型管理器(Prototype Registry)
┌──────────────────────────────┐
│ PrototypeRegistry │
├──────────────────────────────┤
│ - prototypes: Map<String, Proto>│
├──────────────────────────────┤
│ + register(key, proto): void │
│ + unregister(key): void │
│ + clone(key): Prototype │
└──────────────────────────────┘四、代码实现
4.1 基础实现
4.1.1 浅克隆(Shallow Clone)
java
/**
* 浅克隆 —— 实现 Cloneable 接口
*
* 浅克隆特点:
* - 基本类型字段:复制值
* - 引用类型字段:复制引用(指向同一个对象)
* - 修改克隆对象的引用字段会影响原对象!
*/
public class ShallowCloneDemo {
public static class Document implements Cloneable {
private String title;
private List<String> paragraphs; // 引用类型
private Date createdAt;
public Document(String title, List<String> paragraphs) {
this.title = title;
this.paragraphs = paragraphs;
this.createdAt = new Date();
}
// ========== 浅克隆实现 ==========
@Override
public Document clone() {
try {
// Object.clone() 是浅克隆,逐字段复制
return (Document) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public List<String> getParagraphs() { return paragraphs; }
public Date getCreatedAt() { return createdAt; }
@Override
public String toString() {
return "Document{title='" + title + "', paragraphs=" + paragraphs +
", createdAt=" + createdAt + "}";
}
}
public static void main(String[] args) {
List<String> originalParagraphs = new ArrayList<>();
originalParagraphs.add("这是第一段内容");
originalParagraphs.add("这是第二段内容");
Document original = new Document("原始文档", originalParagraphs);
Document cloned = original.clone();
System.out.println("===== 克隆后 =====");
System.out.println("原始: " + original);
System.out.println("克隆: " + cloned);
// 修改克隆对象的字段
cloned.setTitle("克隆文档");
cloned.getParagraphs().add("这是克隆添加的段落");
System.out.println("\n===== 修改克隆对象后 =====");
System.out.println("原始: " + original);
System.out.println("克隆: " + cloned);
// 注意:paragraphs 被共享了!修改克隆对象影响了原对象
System.out.println("\n【浅克隆问题】paragraphs 引用是否相同?"
+ (original.getParagraphs() == cloned.getParagraphs()));
}
}4.1.2 深克隆(Deep Clone)
java
/**
* 深克隆 —— 递归克隆所有引用类型字段
*
* 深克隆特点:
* - 基本类型字段:复制值
* - 引用类型字段:递归复制,创建全新的对象
* - 克隆对象和原对象完全独立,互不影响
*/
public class DeepCloneDemo {
public static class Document implements Cloneable {
private String title;
private List<String> paragraphs;
private Date createdAt;
private Author author; // 嵌套引用类型
public Document(String title, List<String> paragraphs, Author author) {
this.title = title;
this.paragraphs = paragraphs;
this.createdAt = new Date();
this.author = author;
}
// ========== 深克隆实现 ==========
@Override
public Document clone() {
try {
// 1. 先进行浅克隆
Document cloned = (Document) super.clone();
// 2. 对每个引用类型字段进行深拷贝
cloned.paragraphs = new ArrayList<>(this.paragraphs); // 新建 List
cloned.createdAt = (Date) this.createdAt.clone(); // 克隆 Date
cloned.author = this.author.clone(); // 克隆 Author
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
// Getters & Setters
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public List<String> getParagraphs() { return paragraphs; }
public Date getCreatedAt() { return createdAt; }
public Author getAuthor() { return author; }
@Override
public String toString() {
return "Document{title='" + title + "', paragraphs=" + paragraphs +
", createdAt=" + createdAt + ", author=" + author + "}";
}
}
// ========== 嵌套对象(也需要实现深克隆) ==========
public static class Author implements Cloneable {
private String name;
private String email;
public Author(String name, String email) {
this.name = name;
this.email = email;
}
@Override
public Author clone() {
try {
return (Author) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "Author{name='" + name + "', email='" + email + "'}";
}
}
public static void main(String[] args) {
List<String> originalParagraphs = new ArrayList<>();
originalParagraphs.add("第一段内容");
originalParagraphs.add("第二段内容");
Author originalAuthor = new Author("张三", "zhangsan@example.com");
Document original = new Document("原始文档", originalParagraphs, originalAuthor);
Document cloned = original.clone();
System.out.println("===== 克隆后 =====");
System.out.println("原始: " + original);
System.out.println("克隆: " + cloned);
// 修改克隆对象
cloned.setTitle("克隆文档");
cloned.getParagraphs().add("克隆添加的段落");
cloned.getAuthor().setName("李四");
System.out.println("\n===== 修改克隆对象后 =====");
System.out.println("原始: " + original);
System.out.println("克隆: " + cloned);
// 验证独立性
System.out.println("\n【深克隆验证】");
System.out.println("paragraphs 引用是否相同? "
+ (original.getParagraphs() == cloned.getParagraphs()));
System.out.println("author 引用是否相同? "
+ (original.getAuthor() == cloned.getAuthor()));
}
}4.2 进阶实现
4.2.1 序列化深克隆(通用方案)
java
import java.io.*;
/**
* 通过序列化实现深克隆 —— 最通用的深克隆方案
*
* 优点:
* - 不需要手动处理每个引用字段
* - 自动处理嵌套对象、集合、Map 等复杂结构
* - 适用于任何实现了 Serializable 接口的对象
*
* 缺点:
* - 性能较低(涉及序列化/反序列化 I/O 操作)
* - 所有字段必须实现 Serializable
* - transient 字段不会被克隆
*/
public class SerializationDeepClone {
public static class ComplexObject implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private List<String> tags;
private Map<String, Object> metadata;
private NestedObject nested;
public ComplexObject(String name) {
this.name = name;
this.tags = new ArrayList<>();
this.metadata = new HashMap<>();
this.nested = new NestedObject("nested-" + name);
}
// ========== 使用序列化实现深克隆 ==========
@SuppressWarnings("unchecked")
public ComplexObject deepClone() {
try {
// 序列化到字节数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
oos.close();
// 从字节数组反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ComplexObject) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("序列化深克隆失败", e);
}
}
// ========== 通用深克隆工具方法 ==========
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T object) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.close();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("序列化深克隆失败", e);
}
}
// Getters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<String> getTags() { return tags; }
public Map<String, Object> getMetadata() { return metadata; }
public NestedObject getNested() { return nested; }
@Override
public String toString() {
return "ComplexObject{name='" + name + "', tags=" + tags +
", metadata=" + metadata + ", nested=" + nested + "}";
}
}
public static class NestedObject implements Serializable {
private static final long serialVersionUID = 1L;
private String value;
public NestedObject(String value) { this.value = value; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
@Override
public String toString() {
return "NestedObject{value='" + value + "'}";
}
}
public static void main(String[] args) {
ComplexObject original = new ComplexObject("原型");
original.getTags().add("tag1");
original.getMetadata().put("key1", "value1");
// 使用序列化深克隆
ComplexObject cloned = SerializationDeepClone.deepClone(original);
System.out.println("原始: " + original);
System.out.println("克隆: " + cloned);
// 修改克隆对象,验证独立性
cloned.setName("克隆副本");
cloned.getTags().add("tag2");
cloned.getNested().setValue("modified");
System.out.println("\n修改后 - 原始: " + original);
System.out.println("修改后 - 克隆: " + cloned);
System.out.println("\nnested 引用是否相同? "
+ (original.getNested() == cloned.getNested()));
}
}4.2.2 原型管理器(Prototype Registry)
java
/**
* 原型管理器 —— 维护一个原型注册表,通过 key 获取克隆副本
*
* 场景:Spring 中 prototype 作用域的 Bean 创建
*/
public class PrototypeRegistry {
// ========== 原型接口 ==========
public interface Prototype extends Cloneable {
Prototype clone();
String getType();
}
// ========== 具体原型 ==========
public static class Circle implements Prototype {
private int radius;
private String color;
public Circle(int radius, String color) {
this.radius = radius;
this.color = color;
}
@Override
public Circle clone() {
try {
return (Circle) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String getType() { return "CIRCLE"; }
public void setRadius(int radius) { this.radius = radius; }
public void setColor(String color) { this.color = color; }
@Override
public String toString() {
return "Circle{radius=" + radius + ", color='" + color + "'}";
}
}
public static class Rectangle implements Prototype {
private int width;
private int height;
private String color;
public Rectangle(int width, int height, String color) {
this.width = width;
this.height = height;
this.color = color;
}
@Override
public Rectangle clone() {
try {
return (Rectangle) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String getType() { return "RECTANGLE"; }
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
@Override
public String toString() {
return "Rectangle{width=" + width + ", height=" + height + ", color='" + color + "'}";
}
}
// ========== 原型注册表 ==========
public static class Registry {
private final Map<String, Prototype> prototypes = new HashMap<>();
public void register(Prototype prototype) {
prototypes.put(prototype.getType(), prototype);
}
public void unregister(String type) {
prototypes.remove(type);
}
public Prototype create(String type) {
Prototype prototype = prototypes.get(type);
if (prototype == null) {
throw new IllegalArgumentException("未注册的原型类型: " + type);
}
return prototype.clone();
}
}
// ========== 测试 ==========
public static void main(String[] args) {
Registry registry = new Registry();
// 注册原型模板
registry.register(new Circle(10, "红色"));
registry.register(new Rectangle(20, 10, "蓝色"));
// 通过克隆创建新对象
Circle circle1 = (Circle) registry.create("CIRCLE");
Circle circle2 = (Circle) registry.create("CIRCLE");
Rectangle rect1 = (Rectangle) registry.create("RECTANGLE");
// 修改克隆对象的属性
circle2.setRadius(20);
circle2.setColor("绿色");
System.out.println("Circle 1: " + circle1);
System.out.println("Circle 2: " + circle2);
System.out.println("Rectangle 1: " + rect1);
System.out.println("\ncircle1 和 circle2 是同一个对象吗? " + (circle1 == circle2));
}
}4.3 生产级实现(Spring Boot 场景)
java
import java.util.concurrent.ConcurrentHashMap;
/**
* 生产级报表模板管理器 —— 原型模式 + Spring Boot
*
* 业务场景:报表系统需要生成大量相似报表,每个报表从数据库查询数据成本很高
* 解决方案:先创建一份模板报表(加载所有数据),后续报表通过克隆模板生成
*/
public class ReportTemplateManager {
// ========== 报表产品 ==========
public static class Report implements Cloneable {
private String reportId;
private String title;
private String author;
private List<SalesData> salesData; // 从数据库查询(成本高)
private Map<String, Object> configs; // 从配置中心加载(成本高)
private Date generatedAt;
public Report(String reportId, String title, String author) {
this.reportId = reportId;
this.title = title;
this.author = author;
this.generatedAt = new Date();
}
public void loadSalesData() {
// 模拟从数据库加载大量数据(耗时操作)
System.out.println(" [Report] 从数据库加载销售数据...");
this.salesData = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
salesData.add(new SalesData("PROD-" + i, Math.random() * 1000));
}
}
public void loadConfigs() {
// 模拟从配置中心加载配置
System.out.println(" [Report] 从配置中心加载配置...");
this.configs = new ConcurrentHashMap<>();
configs.put("company", "ABC Corp");
configs.put("currency", "CNY");
configs.put("timezone", "Asia/Shanghai");
}
// ========== 深克隆 ==========
@Override
public Report clone() {
try {
Report cloned = (Report) super.clone();
// 深拷贝引用类型
cloned.salesData = new ArrayList<>(this.salesData);
cloned.configs = new ConcurrentHashMap<>(this.configs);
cloned.generatedAt = new Date();
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
public void setTitle(String title) { this.title = title; }
public void setReportId(String reportId) { this.reportId = reportId; }
@Override
public String toString() {
return "Report{id='" + reportId + "', title='" + title +
"', author='" + author + "', dataSize=" +
(salesData != null ? salesData.size() : 0) +
", configs=" + configs + "}";
}
}
public static class SalesData {
private String productId;
private double amount;
public SalesData(String productId, double amount) {
this.productId = productId;
this.amount = amount;
}
}
// ========== 报表模板管理器 ==========
public static class ReportTemplateRegistry {
// 缓存已创建的模板报表
private final Map<String, Report> templates = new ConcurrentHashMap<>();
/**
* 创建并缓存模板报表(只执行一次高成本的数据加载)
*/
public Report createTemplate(String templateKey, String author) {
return templates.computeIfAbsent(templateKey, key -> {
System.out.println("===== 创建模板报表: " + key + " =====");
Report template = new Report("TEMPLATE-" + key, "模板报表", author);
template.loadSalesData(); // 高成本操作,只执行一次
template.loadConfigs(); // 高成本操作,只执行一次
System.out.println("===== 模板报表创建完成 =====\n");
return template;
});
}
/**
* 基于模板生成新报表(克隆 + 微调)
*/
public Report generateReport(String templateKey, String reportId, String title) {
Report template = templates.get(templateKey);
if (template == null) {
throw new IllegalArgumentException("模板不存在: " + templateKey);
}
// 克隆模板(避免了重复的数据加载)
Report report = template.clone();
report.setReportId(reportId);
report.setTitle(title);
return report;
}
}
// ========== 测试 ==========
public static void main(String[] args) {
ReportTemplateRegistry registry = new ReportTemplateRegistry();
// 第一次创建模板(耗时较长)
long start = System.currentTimeMillis();
registry.createTemplate("monthly_sales", "张三");
System.out.println("模板创建耗时: " + (System.currentTimeMillis() - start) + "ms\n");
// 基于模板快速生成多份报表(几乎瞬间完成)
start = System.currentTimeMillis();
Report report1 = registry.generateReport("monthly_sales", "RPT-001", "2024年1月销售报表");
Report report2 = registry.generateReport("monthly_sales", "RPT-002", "2024年2月销售报表");
Report report3 = registry.generateReport("monthly_sales", "RPT-003", "2024年3月销售报表");
System.out.println("3份报表生成耗时: " + (System.currentTimeMillis() - start) + "ms\n");
System.out.println(report1);
System.out.println(report2);
System.out.println(report3);
}
}五、优缺点
优点
| 优点 | 说明 |
|---|---|
| 性能提升 | 克隆比 new 创建代价高的对象快得多,尤其是涉及 I/O 操作时 |
| 简化对象创建 | 避免复杂的初始化过程,直接克隆已有对象 |
| 动态创建 | 运行时动态决定创建哪种类型的对象,比工厂模式更灵活 |
| 避免工厂层次 | 不需要为每种产品创建对应的工厂类 |
| 保存状态快照 | 可用于实现撤销/重做功能 |
缺点
| 缺点 | 说明 |
|---|---|
| 深克隆复杂 | 每个类都需要实现 clone(),且需处理嵌套对象的深拷贝 |
| Cloneable 接口问题 | Java 的 Cloneable 接口没有定义 clone() 方法,使用不便 |
| 循环引用问题 | 存在循环引用时,深克隆可能导致无限递归或栈溢出 |
| 克隆与构造器的区别 | 克隆不调用构造器,可能导致某些初始化逻辑被跳过 |
六、适用场景
- 对象创建成本高:如包含大量数据库查询结果、网络请求结果的对象
- 需要大量相似对象:如游戏中的敌人、子弹、粒子效果
- 需要保存对象状态:如撤销/重做(Undo/Redo)、事务回滚
- 动态配置:从配置文件中读取模板,运行时克隆生成具体配置
- 避免工厂层次爆炸:当产品种类很多时,用原型模式替代工厂模式
- Spring Prototype Bean:每次请求获取 prototype Bean 时,Spring 会创建新实例
- 缓存系统:缓存序列化/反序列化后的对象副本
七、JDK / Spring 框架中的实际应用
7.1 JDK 中的原型模式
1. java.lang.Object.clone()
- 所有对象的克隆能力都源于此方法
- 是浅克隆,需要深克隆时需手动实现
2. java.lang.Cloneable
- 标记接口,标识该类支持克隆
- 不实现 Cloneable 调用 clone() 会抛 CloneNotSupportedException
3. java.util.ArrayList.clone()
- ArrayList 实现了深克隆(元素本身是浅克隆)
- 新 ArrayList 和原 ArrayList 的 elementData 是不同的数组
4. java.util.HashMap.clone()
- HashMap 实现了深克隆
- 新的 table 数组 + 新的 entry 节点
5. java.util.Date.clone()
- Date 实现了克隆,返回新的 Date 对象7.2 Spring 框架中的原型模式
1. @Scope("prototype")
- Spring 的 prototype 作用域 Bean
- 每次 getBean() 都会创建新实例(本质是原型模式)
2. AbstractBeanDefinition
- Spring 内部使用 BeanDefinition 作为原型来描述 Bean 的元数据
- 真正创建 Bean 时,根据 BeanDefinition 原型 + 依赖注入创建实例
3. ObjectMapper (Jackson)
- 虽然 ObjectMapper 不是原型模式的标准实现,但 Spring 中常将其作为单例
- 通过 readValue() 反序列化时,本质是根据类模板(原型)创建对象
4. PrototypeBeanNameGenerator
- Spring 的 Bean 命名生成器,与原型模式概念相关7.3 源码示例:Spring Prototype Bean
java
// Spring 源码中 AbstractBeanFactory 的 doGetBean 方法(简化)
protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) {
// 获取 BeanDefinition(原型元数据)
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 如果是 prototype 作用域
if (mbd.isPrototype()) {
// 每次都会创建新的 Bean 实例(原型模式)
Object prototypeInstance = createBean(beanName, mbd, args);
return (T) prototypeInstance;
}
// 如果是 singleton 作用域
else {
// 从单例缓存中获取
Object singletonInstance = getSingleton(beanName);
return (T) singletonInstance;
}
}八、与其他模式的关系
| 相关模式 | 关系说明 |
|---|---|
| 工厂方法模式 | 工厂方法创建新对象,原型模式克隆已有对象。工厂方法通常用于创建层次结构中的对象,原型模式用于创建相似对象 |
| 抽象工厂模式 | 抽象工厂可以用原型模式实现:工厂维护原型对象,通过克隆创建新实例 |
| 单例模式 | 原型模式与单例模式相反:单例限制唯一实例,原型模式鼓励克隆创建多个实例 |
| 组合模式 | 组合模式的树形结构常使用原型模式来克隆复杂节点 |
| 备忘录模式 | 备忘录模式使用原型模式保存对象状态快照,用于撤销操作 |
| 建造者模式 | 建造者分步构建,原型一步克隆。两者可以组合:先克隆原型,再用建造者修改 |
九、面试常见问题
Q1:Java 中浅克隆和深克隆的区别?
答案:
| 对比维度 | 浅克隆(Shallow Clone) | 深克隆(Deep Clone) |
|---|---|---|
| 基本类型字段 | 复制值 | 复制值 |
| 引用类型字段 | 复制引用(指向同一个对象) | 递归复制(创建新对象) |
| 内存独立性 | 克隆对象和原对象共享引用类型 | 完全独立,互不影响 |
| 实现方式 | super.clone() | 手动递归克隆 / 序列化 |
| 性能 | 快 | 慢 |
| 风险 | 修改克隆对象的引用字段会影响原对象 | 无 |
Q2:Java 中 Cloneable 接口为什么不包含 clone() 方法?
答案: 这是一个经典的 Java 设计缺陷(Joshua Bloch 在《Effective Java》中也有提及):
Cloneable是一个标记接口(Marker Interface),不包含任何方法clone()方法定义在Object类中,且是protected的- 如果
Cloneable包含了clone()方法,那么所有实现类都必须实现它,但这种设计在 Java 1.0 时没有采用 - 这种设计导致:如果不实现
Cloneable就调用clone(),会抛出CloneNotSupportedException
最佳实践:使用拷贝构造器或序列化深克隆来替代 Cloneable。
Q3:为什么《Effective Java》建议使用拷贝构造器替代 clone()?
答案: Joshua Bloch 认为 Cloneable 接口存在以下问题:
- 接口不包含方法:无法通过接口调用
clone() - 实现约束不明确:不实现
Cloneable会抛异常,但编译期无法检查 - final 字段问题:
clone()与final字段不兼容 - 浅克隆默认行为:
Object.clone()默认是浅克隆,容易出错 - 不调用构造器:克隆不调用构造器,可能导致初始化逻辑被跳过
推荐的替代方案:
java
// 方式1:拷贝构造器
public class Document {
private String title;
private List<String> paragraphs;
// 拷贝构造器
public Document(Document other) {
this.title = other.title;
this.paragraphs = new ArrayList<>(other.paragraphs); // 深拷贝
}
}
// 方式2:静态工厂方法
public static Document copyOf(Document other) {
Document copy = new Document();
copy.setTitle(other.getTitle());
copy.setParagraphs(new ArrayList<>(other.getParagraphs()));
return copy;
}Q4:原型模式和 Spring 的 prototype scope 有什么关系?
答案: Spring 的 prototype scope 本质就是原型模式的应用:
- 每次调用
getBean()时,Spring 会根据BeanDefinition(原型元数据)创建新的 Bean 实例 - 这与原型模式中"通过克隆原型创建新对象"的思想一致
- 区别在于:Spring 通过反射 + 依赖注入创建新实例,而不是通过
clone()方法 - Spring 的
singletonscope 是单例模式,prototypescope 是原型模式
Q5:原型模式在什么情况下比工厂模式更好?
答案:
- 产品种类极多:工厂模式需要为每种产品创建工厂类,原型模式只需一个原型对象
- 对象创建成本高:克隆比
new+ 初始化快得多 - 运行时动态决定:工厂模式在编译期确定产品类型,原型模式在运行时动态克隆
- 需要保存状态:原型可以保存当前状态,工厂只能创建初始状态的对象
- 避免类层次爆炸:当产品类数量庞大时,工厂类数量也会爆炸
总结
原型模式通过克隆已有对象来创建新对象,核心要点:
- 浅克隆 vs 深克隆:根据是否需要独立修改引用类型字段来选择
- 序列化深克隆是最通用的深克隆方案,但性能较差
- 拷贝构造器是
Cloneable的推荐替代方案 - 原型管理器维护一个原型对象注册表,按需克隆
- Spring 的 prototype scope 是原型模式在生产环境中的典型应用
- 原型模式特别适合创建成本高、需要大量相似对象的场景
