以下是为你精心撰写的 《Java 建造者模式深度学习指南》,专为 Java 后端开发者打造,内容涵盖:定义、作用、真实业务场景、实现方式对比、Spring 集成、避坑指南、最佳实践、面试高频题,所有示例均含中文注释,可直接用于项目重构、DTO 构建、SQL 拼接、API 请求封装等复杂对象创建场景。
📘 Java 建造者模式深度学习指南
—— 从“构造函数爆炸”到“链式优雅构建”的架构升级
作者:Java 后端架构实战导师
适用对象:Java 后端开发者(Spring Boot / 微服务 / DTO / SQL / API 请求)
目标:彻底告别“10个参数的构造函数”和“setXXX() 泛滥”,掌握构建复杂对象的终极优雅方案
核心原则:“分步构建,链式调用,不可变对象,类型安全”
✅ 一、什么是建造者模式?
📌 定义:
建造者模式(Builder Pattern) 是一种创建型设计模式,它将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
🔍 核心思想:
- 将对象的创建步骤(如设置属性)封装在独立的建造者类中
- 使用链式调用(Fluent Interface)逐步设置属性
- 最终通过
build()方法返回一个不可变对象 - 客户端无需关心构造细节,只需按需设置所需字段
💡 一句话记忆:
“我要一个复杂的对象,但不想一次性传10个参数——我一步一步来,最后交给你组装。”
🆚 对比传统方式 vs 建造者模式:
| 方式 | 传统构造函数 | 建造者模式 |
|---|---|---|
| 参数数量 | 10+ 参数 → 构造函数爆炸 | 只有一个 build() |
| 可读性 | new User("张三", "13800138000", "北京", "男", 25, true, "工程师", "123@abc.com", "VIP", "2024-01-01") | new User.Builder().name("张三").phone("138...").city("北京").build() |
| 可选参数 | 必须传所有参数,或写多个重载构造函数 | 只设置需要的字段,其余默认 |
| 不可变性 | 难以实现(需 setXXX) | ✅ 天然支持(final 字段) |
| 扩展性 | 新增字段需改所有构造函数 | 只需在 Builder 中加一个方法 |
| 类型安全 | 易错(参数顺序错乱) | ✅ 编译期检查,IDE 自动提示 |
✅ 经典比喻:
你去麦当劳点“巨无霸套餐”:
- 传统方式:直接给收银员一长串清单:“我要汉堡、生菜、芝士、洋葱、酱料、薯条、可乐、纸巾、吸管……”
- 建造者方式:
- 选汉堡
- 加生菜
- 加芝士
- 加酱料
- 加薯条
- 加可乐
- 点“完成”
→ 每一步清晰,可选可跳,最终一次性组装
✅ 二、建造者模式有什么作用?(Why Use Builder Pattern?)
| 作用 | 说明 |
|---|---|
| ✅ 解决构造函数参数过多 | 避免出现 User(String a, String b, String c, ..., boolean z) 的“参数地狱” |
| ✅ 支持可选参数 | 不强制传所有字段,只设置需要的,其余使用默认值 |
| ✅ 提高代码可读性 | builder.name("张三").age(25).email("xxx@xx.com").build() 清晰易懂 |
| ✅ 支持不可变对象(Immutable) | build() 返回 final 字段对象,线程安全,无副作用 |
| ✅ 类型安全,防错 | IDE 自动提示可用方法,避免参数顺序错误 |
| ✅ 便于扩展 | 新增字段只需在 Builder 中添加一个方法,不影响已有代码 |
| ✅ 支持链式调用(Fluent API) | 符合现代 Java 开发风格(如 Stream、Lombok、JPA) |
| ✅ 适合复杂对象构建 | 如 SQL、JSON、HTTP 请求、配置对象、POJO |
💡 为什么 Java 标准库大量使用它?
StringBuilderStringJoinerjava.time.LocalDateTime.Builder(Java 17+)AlertDialog.Builder(Android)
→ 证明:建造者模式是构建复杂对象的黄金标准!
✅ 三、建造者模式的典型使用场景(Java 后端真实案例)
| 场景 | 说明 | 是否推荐使用建造者模式 |
|---|---|---|
| ✅ DTO / VO 对象 | 接口返回/接收的复杂数据对象(含 5+ 字段) | ✅ 强烈推荐 |
| ✅ SQL 构建器 | 拼接动态 WHERE 条件、ORDER BY、分页 | ✅ 推荐 |
| ✅ HTTP 请求构造 | RestTemplate / FeignClient 请求体(含 Header、Body、Param) | ✅ 推荐 |
| ✅ 配置对象 | 数据库连接配置、缓存策略、日志格式 | ✅ 推荐 |
| ✅ JSON / XML 构建 | 手动构造复杂 JSON 结构(避免用字符串拼接) | ✅ 推荐 |
| ✅ 消息体构建 | Kafka / RabbitMQ 消息内容(含元数据、业务体) | ✅ 推荐 |
| ✅ 测试数据构造 | 单元测试中构造复杂对象(避免重复代码) | ✅ 推荐 |
❌ 简单 POJO(如 User 只有 name、age) | 字段少于 3 个 | ❌ 用构造函数即可 |
❌ 工具类(如 StringUtils) | 无状态,静态方法 | ❌ 不推荐 |
❌ 实体类(如 OrderItem) | 通常由 ORM 自动创建 | ❌ 一般不需手动构建 |
✅ 判断标准:
“这个对象有 3 个以上字段,且其中部分是可选的、或构建过程复杂?”
→ 是 → 用建造者模式!
✅ 四、建造者模式的四种实现方式详解(含中文注释)
我们从手写经典版到Lombok 简化版,再到Java Record + Builder,逐步深入。
🔹 1. 手写经典建造者模式(推荐学习,面试必考)
/**
* 【1】不可变实体类:订单信息(所有字段 final,线程安全)
*/
public class Order {
private final String orderId; // 订单ID(必填)
private final String customerId; // 客户ID(必填)
private final String address; // 收货地址(必填)
private final double totalAmount; // 总金额(必填)
private final String paymentMethod; // 支付方式(可选)
private final String note; // 备注(可选)
private final String deliveryTime; // 配送时间(可选)
private final boolean isExpress; // 是否加急(可选)
// 私有构造函数:只能通过 Builder 创建
private Order(Builder builder) {
this.orderId = builder.orderId;
this.customerId = builder.customerId;
this.address = builder.address;
this.totalAmount = builder.totalAmount;
this.paymentMethod = builder.paymentMethod; // 可选
this.note = builder.note; // 可选
this.deliveryTime = builder.deliveryTime; // 可选
this.isExpress = builder.isExpress; // 可选
}
// getter 方法(无 setter,保证不可变)
public String getOrderId() { return orderId; }
public String getCustomerId() { return customerId; }
public String getAddress() { return address; }
public double getTotalAmount() { return totalAmount; }
public String getPaymentMethod() { return paymentMethod; }
public String getNote() { return note; }
public String getDeliveryTime() { return deliveryTime; }
public boolean isExpress() { return isExpress; }
// 【2】建造者内部类:所有字段的 setter 方法,返回 this 实现链式调用
public static class Builder {
private String orderId; // 必填
private String customerId; // 必填
private String address; // 必填
private double totalAmount; // 必填
private String paymentMethod; // 可选
private String note; // 可选
private String deliveryTime; // 可选
private boolean isExpress; // 可选
// 必填字段必须在构造时传入
public Builder(String orderId, String customerId, String address, double totalAmount) {
this.orderId = orderId;
this.customerId = customerId;
this.address = address;
this.totalAmount = totalAmount;
}
// 可选字段:链式调用,返回 this
public Builder paymentMethod(String paymentMethod) {
this.paymentMethod = paymentMethod;
return this; // 关键!支持链式调用
}
public Builder note(String note) {
this.note = note;
return this;
}
public Builder deliveryTime(String deliveryTime) {
this.deliveryTime = deliveryTime;
return this;
}
public Builder express(boolean isExpress) {
this.isExpress = isExpress;
return this;
}
// 【3】build() 方法:校验必填项,返回不可变对象
public Order build() {
// 校验必填字段
if (orderId == null || orderId.trim().isEmpty()) {
throw new IllegalArgumentException("订单ID不能为空");
}
if (customerId == null || customerId.trim().isEmpty()) {
throw new IllegalArgumentException("客户ID不能为空");
}
if (address == null || address.trim().isEmpty()) {
throw new IllegalArgumentException("收货地址不能为空");
}
if (totalAmount <= 0) {
throw new IllegalArgumentException("总金额必须大于0");
}
// 创建并返回不可变对象
return new Order(this);
}
}
// 重写 toString,便于调试
@Override
public String toString() {
return "Order{" +
"orderId='" + orderId + '\'' +
", customerId='" + customerId + '\'' +
", address='" + address + '\'' +
", totalAmount=" + totalAmount +
", paymentMethod='" + paymentMethod + '\'' +
", note='" + note + '\'' +
", deliveryTime='" + deliveryTime + '\'' +
", isExpress=" + isExpress +
'}';
}
}
/**
* 【4】客户端:使用建造者模式创建对象
*/
public class BuilderDemo {
public static void main(String[] args) {
// ✅ 链式调用,清晰、可读、可选字段自由组合
Order order = new Order.Builder("ORD20240501001", "CUST10086", "北京市朝阳区XX路1号", 299.9)
.paymentMethod("支付宝") // 可选
.note("请尽快发货,谢谢!") // 可选
.deliveryTime("2024-05-02 18:00") // 可选
.express(true) // 可选
.build(); // 最终构建
System.out.println(order);
// 输出:
// Order{orderId='ORD20240501001', customerId='CUST10086', address='北京市朝阳区XX路1号', totalAmount=299.9, paymentMethod='支付宝', note='请尽快发货,谢谢!', deliveryTime='2024-05-02 18:00', isExpress=true}
// ✅ 如果不设置可选字段,也完全没问题
Order simpleOrder = new Order.Builder("ORD002", "CUST999", "上海市浦东新区", 199.0)
.build();
System.out.println(simpleOrder);
// 输出:Order{orderId='ORD002', customerId='CUST999', address='上海市浦东新区', totalAmount=199.0, paymentMethod='null', note='null', deliveryTime='null', isExpress=false}
}
}
✅ 优点:
- 完全不可变:线程安全,无副作用
- 类型安全:编译期检查字段
- 链式调用:代码优雅,可读性强
- 必填校验:
build()中统一校验,防止脏数据 - 扩展性好:新增字段只需在 Builder 中加一个方法
⚠️ 缺点:
- 代码量多(需写 Builder 类)
- 需手动维护
toString()、equals()、hashCode()
🔹 2. 建造者模式 + Lombok(企业级推荐写法)
使用
@Builder注解,自动生成 Builder 类,大幅减少样板代码!
📦 引入依赖(Maven)
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
/**
* 使用 Lombok 自动生成 Builder
*/
@Getter
@ToString
@Builder // ✅ 自动生成 Builder 类
public class Product {
private final String productId; // 必填
private final String name; // 必填
private final double price; // 必填
private final String category; // 可选
private final String description; // 可选
private final boolean inStock; // 可选,默认 true
private final String supplier; // 可选
// 构造函数:Lombok 会自动生成全参构造(但不推荐直接使用)
// 建议只通过 Builder 创建
}
/**
* 客户端使用
*/
public class LombokBuilderDemo {
public static void main(String[] args) {
// ✅ 一行构建,Lombok 自动生成 Builder
Product product = Product.builder()
.productId("P001")
.name("iPhone 15")
.price(6999.0)
.category("手机")
.description("最新款苹果手机")
.inStock(true)
.supplier("苹果中国")
.build();
System.out.println(product);
// 输出:Product(productId=P001, name=iPhone 15, price=6999.0, category=手机, description=最新款苹果手机, inStock=true, supplier=苹果中国)
// ✅ 可选字段可跳过
Product simple = Product.builder()
.productId("P002")
.name("充电器")
.price(99.0)
.build();
System.out.println(simple);
// 输出:Product(productId=P002, name=充电器, price=99.0, category=null, description=null, inStock=true, supplier=null)
}
}
✅ 优点:
- 代码极简:一行
@Builder替代几十行 Builder 类 - 自动维护:Lombok 自动生成
build()、builder()、setter方法 - 支持默认值:
@Builder.Default注解可设默认值 - 与 Spring Boot 兼容:完美支持 JSON 序列化
⚠️ 注意事项:
- 必须开启 Lombok 插件(IDEA:Settings → Plugins → Lombok)
- 不支持校验逻辑:
build()中无法校验必填字段(需手动加@NonNull或@Builder配合校验) - 调试时看不到 Builder 源码(但 IDE 可跳转)
✅ 推荐写法(带默认值和校验):
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
@Builder
@Getter
@ToString
public class User {
@NonNull // Lombok 会自动生成非空校验
private final String username;
@NonNull
private final String email;
private final String phone; // 可选
@Builder.Default // 默认值
private final boolean isActive = true;
private final String role = "USER"; // 默认值
}
✅ 企业级建议:Spring Boot 项目优先使用 Lombok + @Builder
🔹 3. 建造者模式 + Java Record(Java 14+ 最新趋势)
Java 14+ 引入
record,天然支持不可变对象,搭配@Builder更优雅!
import lombok.Builder;
/**
* Java Record:自动提供构造函数、getter、toString、equals、hashCode
* 搭配 @Builder 自动生成建造者
*/
@Builder
public record OrderRecord(
String orderId, // 必填
String customerId, // 必填
String address, // 必填
double totalAmount, // 必填
String paymentMethod, // 可选
String note, // 可选
String deliveryTime, // 可选
boolean isExpress // 可选
) {
// ✅ Record 自动提供:
// - 全参构造函数
// - 所有字段的 getter
// - toString()
// - equals() 和 hashCode()
// 可添加校验逻辑(构造时校验)
public OrderRecord {
if (orderId == null || orderId.trim().isEmpty()) {
throw new IllegalArgumentException("订单ID不能为空");
}
if (customerId == null || customerId.trim().isEmpty()) {
throw new IllegalArgumentException("客户ID不能为空");
}
if (address == null || address.trim().isEmpty()) {
throw new IllegalArgumentException("收货地址不能为空");
}
if (totalAmount <= 0) {
throw new IllegalArgumentException("总金额必须大于0");
}
}
}
/**
* 使用示例
*/
public class RecordBuilderDemo {
public static void main(String[] args) {
OrderRecord order = OrderRecord.builder()
.orderId("ORD20240501")
.customerId("CUST001")
.address("北京市朝阳区")
.totalAmount(199.9)
.paymentMethod("微信支付")
.isExpress(true)
.build();
System.out.println(order);
// 输出:OrderRecord[orderId=ORD20240501, customerId=CUST001, address=北京市朝阳区, totalAmount=199.9, paymentMethod=微信支付, note=null, deliveryTime=null, isExpress=true]
}
}
✅ 优点:
- 最简洁:
record+@Builder一行搞定 - 不可变:天然线程安全
- 无 setter:杜绝状态污染
- 编译器优化:性能优于传统类
⚠️ 缺点:
- Java 14+ 才支持(需升级 JDK)
- 不支持继承(record 是 final)
- 不支持复杂逻辑(如字段间依赖校验)
💡 未来趋势:
Java 17+ 项目中,Record + @Builder 将成为构建不可变对象的首选!
🔹 4. 建造者模式 + SQL 构建器(真实业务场景)
用建造者模式构建动态 SQL,避免字符串拼接!
/**
* SQL 构建器:动态拼接 WHERE 条件、ORDER BY、分页
*/
public class SqlBuilder {
private StringBuilder select = new StringBuilder("SELECT * FROM ");
private StringBuilder from = new StringBuilder();
private StringBuilder where = new StringBuilder(" WHERE 1=1 "); // 防止空条件
private StringBuilder orderBy = new StringBuilder();
private int limit = -1;
private int offset = 0;
// 构造函数私有,只能通过静态方法创建
private SqlBuilder() {}
// 静态工厂方法:获取 Builder 实例
public static SqlBuilder create() {
return new SqlBuilder();
}
// 链式方法:构建 SQL
public SqlBuilder from(String tableName) {
this.from.append(tableName);
return this;
}
public SqlBuilder where(String condition) {
this.where.append(" AND ").append(condition);
return this;
}
public SqlBuilder and(String condition) {
return where(condition);
}
public SqlBuilder or(String condition) {
this.where.append(" OR ").append(condition);
return this;
}
public SqlBuilder orderBy(String field, String direction) {
this.orderBy.append(" ORDER BY ").append(field).append(" ").append(direction);
return this;
}
public SqlBuilder limit(int limit) {
this.limit = limit;
return this;
}
public SqlBuilder offset(int offset) {
this.offset = offset;
return this;
}
// 构建最终 SQL
public String build() {
StringBuilder sql = new StringBuilder();
sql.append(select).append(from);
if (where.length() > 0) {
sql.append(where);
}
if (orderBy.length() > 0) {
sql.append(orderBy);
}
if (limit > 0) {
sql.append(" LIMIT ").append(limit);
if (offset > 0) {
sql.append(" OFFSET ").append(offset);
}
}
return sql.toString();
}
}
/**
* 使用示例:动态查询用户订单
*/
public class SqlBuilderDemo {
public static void main(String[] args) {
String sql = SqlBuilder.create()
.from("orders")
.where("status = 'PAID'")
.and("amount > 100")
.or("user_id = 'U1001'")
.orderBy("created_at", "DESC")
.limit(10)
.offset(0)
.build();
System.out.println(sql);
// 输出:
// SELECT * FROM orders WHERE 1=1 AND status = 'PAID' AND amount > 100 OR user_id = 'U1001' ORDER BY created_at DESC LIMIT 10 OFFSET 0
// ✅ 灵活组合,无拼接混乱,无空指针,易测试
}
}
✅ 优点:
- 完全避免字符串拼接错误
- 支持链式调用,逻辑清晰
- 可复用、可测试、可缓存
- 适用于 MyBatis、JDBC、JPA 原生 SQL
✅ 五、建造者模式的避坑指南(Java 后端高频踩坑)
| 问题 | 原因 | 解决方案 |
|---|---|---|
❌ 用 new 直接构造复杂对象 | 参数多、易错、不可读 | ✅ 改用 Builder |
| ❌ Builder 中不校验必填字段 | 导致脏数据入库 | ✅ 在 build() 方法中校验,抛异常 |
❌ 忘记 return this | 链式调用失败 | ✅ 每个 setter 方法必须返回 this |
| ❌ 使用 Lombok 但未开启插件 | 编译报错 | ✅ IDEA 安装 Lombok 插件,开启 Annotation Processing |
| ❌ 在 Builder 中修改对象状态 | 破坏不可变性 | ✅ Builder 是临时对象,build() 后不再使用 |
| ❌ 用 Builder 构建 Entity 实体 | 与 JPA 冲突 | ✅ 用 Builder 构建 DTO / VO,Entity 仍用 JPA 注解 |
| ❌ 所有字段都用 Builder | 小对象过度设计 | ✅ 字段少于 3 个,用构造函数即可 |
✅ 六、建造者模式 vs 工厂方法 vs 策略模式 对比
| 模式 | 目的 | 关键词 | 举例 |
|---|---|---|---|
| 工厂方法 | 创建对象 | “谁来造?” | PaymentFactory.create("alipay") |
| 策略模式 | 执行行为 | “怎么干?” | discountStrategy.calculate(price) |
| 建造者模式 | 构建复杂对象 | “怎么组装?” | Order.Builder().name(...).build() |
✅ 组合使用:
你用工厂方法创建一个OrderService,
用策略模式选择它的折扣算法,
用建造者模式构建Order对象
→ 三位一体,架构完美!
✅ 七、学习建议与进阶路径
| 阶段 | 建议 |
|---|---|
| 📚 第一周 | 将你项目中所有“参数超过 4 个”的构造函数,重构为 Builder |
| 📚 第二周 | 用 Lombok 的 @Builder 替换手写 Builder,提升开发效率 |
| 📚 第三周 | 用 Builder 重构一个 SQL 拼接工具类,避免字符串拼接 |
| 📚 第四周 | 在 Spring Boot 项目中,用 record + @Builder 重构 DTO 层 |
| 📚 面试准备 | 准备回答: |
“你项目中哪里用了建造者模式?”
“Lombok 的 @Builder 是怎么实现的?”
“建造者模式和工厂方法区别?”
“为什么用 Builder 而不用 setXXX()?” |
✅ 八、建造者模式面试高频题(附答案)
Q1:建造者模式解决了什么问题?
A:解决了“构造函数参数过多”、“可选参数难处理”、“对象创建过程复杂”等问题,提供链式、可读、安全的构建方式。
Q2:Lombok 的 @Builder 是如何实现的?
A:Lombok 在编译期自动生成一个静态内部类
Builder,包含所有字段的 setter 方法和build()方法,返回原类实例。
Q3:建造者模式和 setXXX() 方法有什么区别?
A:
setXXX():对象可变,可能中途被修改,线程不安全- Builder:构建完成后返回不可变对象,线程安全,语义清晰
Q4:什么时候不该用建造者模式?
A:对象字段少于 3 个,或对象是简单 POJO,直接用构造函数更简洁。
Q5:Java Record 和 Builder 能一起用吗?
A:可以!
record+@Builder是 Java 17+ 的最佳实践,推荐用于 DTO 层。
✅ 九、总结:建造者模式选型决策树
graph TD
A[需要构建复杂对象吗?] --> B{字段数量 > 3 且有可选参数?}
B -->|是| C{是否使用 Java 17+?}
C -->|是| D[使用 record + @Builder]
C -->|否| E{是否使用 Lombok?}
E -->|是| F[使用 @Builder]
E -->|否| G[手写 Builder 类]
B -->|否| H[直接使用构造函数]
✅ 最终推荐:
- Java 17+ 项目 → ✅ record + @Builder(最现代)
- Spring Boot 项目 → ✅ Lombok @Builder(最高效)
- 无 Lombok、老项目 → ✅ 手写 Builder(最标准)
- 绝对不要:10+ 参数构造函数、
setXXX()滥用、字符串拼接 SQL
✅ 十、结语:真正的高手,不写 new,而写 .builder().xxx().build()
你不是在学“建造者模式”,你是在学“如何优雅地管理复杂性”。
✅ 当你看到
Order.builder().userId("U1001").amount(99.9).build()时,你已经拥有了架构师的思维。
✅ 当你用 Lombok 一键生成 50 个 DTO 的 Builder,团队开发效率提升 50% 时,你已经是团队的效率引擎。
不要为了“用模式”而用模式,
要为了“代码可读、可维护、可扩展”而选择它。
Java建造者模式深度解析
96

被折叠的 条评论
为什么被折叠?



