🚀 Java 巩固进阶 · 第 32 天
主题:设计模式(建造者、原型)------ 复杂对象创建方案
📅 进度概览 :继昨天的单例/工厂模式之后,今天学习 另外两种创建型设计模式。建造者模式解决"复杂对象构建",原型模式解决"对象克隆复制",两者在实际开发中应用广泛。
💡 核心价值:
- 构建优雅:建造者模式用链式调用替代"伸缩构造器",代码可读性提升 10 倍。
- 克隆高效:原型模式避免重复初始化,适合"模板复制"场景(如简历克隆、订单复制)。
- 框架关联:理解 StringBuilder、AlertDialog、Spring Bean 克隆的底层思想。
- 面试加分:深浅克隆区别、Builder 模式适用场景是高级开发常考题。
一、建造者模式(Builder):链式构建复杂对象 🔨
1. 为什么需要建造者模式?
java
// ❌ 问题:伸缩构造器反模式(参数多、易出错、可读性差)
public class Computer {
// 10 个参数,调用时根本不知道每个参数含义!
public Computer(String cpu, String ram, String disk, String gpu,
String motherboard, String power, String case_,
String cooling, String monitor, String keyboard) { ... }
}
// 调用:
new Computer("i7", "16G", "512G", "RTX3060", null, null, "ATX", null, null, null);
// 😵 哪个是 null?参数顺序记错怎么办?
// ✅ 解决:建造者模式,链式调用,语义清晰
Computer computer = new Computer.Builder()
.cpu("i7")
.ram("16G")
.disk("512G")
.gpu("RTX3060")
.case_("ATX")
.build(); // 最后一步构建,中间参数顺序随意,未设置用默认值
2. 标准实现模板(⭐ 背下来!)
java
public class Computer {
// 1. 私有字段
private final String cpu;
private final String ram;
private final String disk;
private final String gpu;
// ... 其他字段
// 2. 私有构造器,只允许 Builder 访问
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
// ... 赋值
}
// 3. 静态内部类 Builder
public static class Builder {
// 与外部类相同的字段
private String cpu = "i3"; // 默认值
private String ram = "8G";
private String disk = "256G";
private String gpu = "集成显卡";
// 链式设置方法(返回 this)
public Builder cpu(String cpu) { this.cpu = cpu; return this; }
public Builder ram(String ram) { this.ram = ram; return this; }
public Builder disk(String disk) { this.disk = disk; return this; }
public Builder gpu(String gpu) { this.gpu = gpu; return this; }
// 构建方法(校验 + 创建)
public Computer build() {
// 可选:参数校验
if (cpu == null) throw new IllegalArgumentException("CPU 不能为空");
return new Computer(this);
}
}
// getter 方法(省略)
}
3. 🔍 关键细节解析
| 设计点 | 原因 | 注意事项 |
|---|---|---|
| 静态内部类 | 避免外部类膨胀,隔离构建逻辑 | Builder 可访问外部类私有字段 |
| 字段 final | 保证对象不可变,线程安全 | 构建完成后不能修改 |
| 链式返回 this | 支持流畅调用 | 每个 setter 必须 return this |
| build() 校验 | 确保对象状态合法 | 在构建最后一步集中校验 |
💡 生产实践:
- Lombok 的
@Builder注解可自动生成 Builder 代码- 但建议手动写一次,理解原理后再用注解
二、原型模式(Prototype):克隆对象的高效方案 📋
1. 为什么需要原型模式?
🎯 场景:创建对象成本高(如:查数据库初始化、复杂计算)
❌ 问题:每次 new 都要重复初始化,浪费资源
✅ 解决:克隆已有对象(原型),快速复制 + 局部修改
2. Cloneable 接口与 clone() 方法
java
// ✅ 标准实现:实现 Cloneable + 重写 clone()
public class Resume implements Cloneable {
private String name;
private String experience; // 工作经历(可变对象)
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅克隆:调用 Object.clone()
}
}
// 使用
Resume r1 = new Resume();
r1.setName("Alice");
Resume r2 = (Resume) r1.clone(); // 快速复制
3. ⚠️ 浅克隆 vs 深克隆(⭐ 面试高频)
java
// 🐛 浅克隆问题:只克隆对象本身,内部引用对象共享
Resume r1 = new Resume();
r1.setExperience(new Experience("2020-2024", "Java 开发"));
Resume r2 = (Resume) r1.clone();
r2.getExperience().setCompany("New Corp"); // 修改 r2 的经历
System.out.println(r1.getExperience().getCompany()); // ❌ r1 也被改了!
// ✅ 深克隆方案 1:手动克隆引用字段
@Override
protected Object clone() throws CloneNotSupportedException {
Resume cloned = (Resume) super.clone();
// 手动克隆可变字段
cloned.experience = (Experience) this.experience.clone();
return cloned;
}
// ✅ 深克隆方案 2:序列化(通用但性能略低)
public Resume deepClone() {
// 1. 序列化到字节数组
// 2. 反序列化为新对象
// 所有引用字段自动克隆,无需手动处理
}
4. 🔍 深浅克隆对比表
| 特性 | 浅克隆 | 深克隆 |
|---|---|---|
| 实现方式 | super.clone() |
手动克隆 / 序列化 |
| 引用字段 | 共享同一对象 | 克隆新对象 |
| 性能 | ⚡ 快(内存拷贝) | 🐢 略慢(递归/序列化) |
| 适用场景 | 对象无引用字段 / 引用不可变 | 对象含可变引用字段 |
| 代码复杂度 | ✅ 简单 | ⚠️ 需处理每个引用字段 |
💡 记忆口诀 :
"浅克隆只拷贝外壳,深克隆连内脏一起复制"
三、🎯 今日实战任务:模式综合应用
任务 1:用建造者模式创建"电脑配置"
java
/**
* 要求:
* 1. 定义 Computer 类,包含 8 个字段(cpu/ram/disk/gpu/主板/电源/机箱/散热)
* 2. 实现 Builder 内部类,支持链式调用
* 3. 设置默认值,build() 时校验关键字段(如 cpu 不能为空)
* 4. 测试:用不同参数构建 3 台电脑,打印配置
*
* 💡 挑战:
* - 添加"构建报告"功能:build() 时输出配置摘要
* - 思考:如果字段增加到 20 个,Builder 模式的优势是否更明显?
*/
任务 2:用原型模式实现"简历克隆"
java
/**
* 业务场景:求职者投递不同公司,简历内容相似但公司名/职位不同
*
* 要求:
* 1. 定义 Resume 类(姓名/经历列表),实现 Cloneable
* 2. 实现浅克隆,测试修改克隆对象的经历是否影响原对象
* 3. 实现深克隆(手动克隆经历列表),验证隔离性
* 4. 对比:new 新简历 vs 克隆简历的耗时(模拟经历初始化耗时)
*
* 💡 提示:
* - 经历列表用 ArrayList,注意集合的克隆
* - 深克隆可用序列化:ObjectOutputStream / ObjectInputStream
*/
任务 3:建造者 + 原型组合实战
java
/**
* 业务场景:订单系统 - 复制历史订单生成新订单
*
* 要求:
* 1. 用建造者模式创建 Order 对象(商品列表/收货地址/支付方式)
* 2. 为 Order 实现 clone() 方法(深克隆商品列表)
* 3. 场景:用户"再来一单",克隆旧订单 → 修改收货地址 → 提交
* 4. 验证:新旧订单的商品列表是否独立(修改一个不影响另一个)
*
* 💡 思考:
* - 为什么订单克隆要用深克隆?(商品可能后续修改价格/库存)
* - 如果商品对象本身很大,克隆是否划算?(空间换时间)
*/
任务 4:对比实验:Builder vs 传统构造器
java
/**
* 要求:
* 1. 用传统构造器 + 用 Builder 分别创建同一个复杂对象
* 2. 统计代码行数、可读性、维护成本
* 3. 模拟"新增一个可选字段",对比两种方案的修改范围
*
* 💡 预期结论:
* - Builder 代码略多,但调用方代码极简
* - 新增字段时,Builder 只需加一个 setter,调用方无需修改
* - 符合开闭原则:对扩展开放,对修改关闭
*/
📝 第 32 天 · 核心总结(极简背诵版)
-
建造者模式核心:
java// 静态内部类 + 链式 setter + build() 构建 new Builder().cpu("i7").ram("16G").build(); // ✅ 适用:参数多、有默认值、需校验的复杂对象 -
原型模式核心:
java// 实现 Cloneable + 重写 clone() Object clone = obj.clone(); // ⚠️ 默认浅克隆,引用字段需手动深克隆 -
深浅克隆区别:
浅克隆:只拷贝对象本身,引用字段共享 深克隆:递归拷贝所有引用字段,完全独立 选择:可变引用用深克隆,不可变/无引用用浅克隆 -
生产建议:
- ✅ 复杂对象构建优先 Builder 模式(或 Lombok @Builder)
- ✅ 模板复制场景用原型模式(如配置克隆、订单复制)
- ❌ 避免滥用 clone(),优先用构造器 + 工厂(更清晰)
明天预告 :🎭 设计模式(代理、装饰器) ------ 增强对象功能的两种套路!
- 静态代理 vs 动态代理(JDK/CGLIB)
- 装饰器模式:动态增强对象功能(如 IO 缓冲流)
- 代理与装饰器的区别(意图不同)
- 实战:用动态代理为 Service 添加日志/事务功能
准备好了吗?明天我们学习"对象增强"的两种经典方案! ✨🔧