学习目标 :掌握 Lombok 的高级注解,学会使用
@Builder
构建复杂对象、用@SneakyThrows
简化异常处理、用@Cleanup
自动管理资源,并理解它们的适用场景与注意事项。
1. 为什么需要进阶功能?
基础注解解决了 POJO 的样板代码问题,但在实际开发中,我们还会遇到:
- 复杂对象创建:参数过多的构造器难以使用(" telescoping constructor" 问题)
- 检查异常(Checked Exception):强制 try-catch 导致代码臃肿
- 资源管理:忘记关闭流、连接等资源导致内存泄漏
Lombok 的进阶注解正是为了解决这些痛点而设计。
2. @Builder:优雅构建复杂对象
2.1 什么是 Builder 模式?
Builder 模式是一种创建型设计模式,用于分步构建复杂对象,特别适合:
- 参数很多的对象(>4 个)
- 参数有可选字段
- 需要保证对象创建的不可变性
2.2 基本用法
java
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class User {
private Long id;
private String username;
private String email;
private Integer age;
private String address;
}
✅ 使用方式:
java
User user = User.builder()
.id(1L)
.username("john_doe")
.email("john@example.com")
.age(28)
.address("New York")
.build();
System.out.println(user);
// 输出:User(id=1, username=john_doe, email=john@example.com, age=28, address=New York)
✅ 生成效果:
- 自动生成
UserBuilder
内部静态类 - 为每个字段生成链式 setter 方法(如
username(String)
) - 生成
build()
方法返回最终对象
2.3 高级用法
2.3.1 与 @Data 组合使用
java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private Long id;
private String name;
private Double price;
private String description;
}
💡 注意 :
@Builder
默认不生成无参构造器,如果需要(如 Jackson 反序列化),需额外添加@NoArgsConstructor
。
2.3.2 自定义 Builder 方法名
java
@Builder(builderMethodName = "customBuilder", buildMethodName = "create")
public class Order {
private String orderId;
private Double amount;
}
// 使用自定义方法名
Order order = Order.customBuilder()
.orderId("ORD-001")
.amount(99.99)
.create();
2.3.3 处理集合字段(@Singular)
java
import lombok.Singular;
@Builder
public class ShoppingCart {
@Singular("item") // 单数形式
private List<String> items;
@Singular
private Set<String> tags;
}
// 使用方式
ShoppingCart cart = ShoppingCart.builder()
.item("Laptop") // 添加单个元素
.item("Mouse") // 继续添加
.items(Arrays.asList("Keyboard", "Monitor")) // 批量添加
.tag("electronics")
.tag("sale")
.build();
// items = ["Laptop", "Mouse", "Keyboard", "Monitor"]
// tags = {"electronics", "sale"}
✅ @Singular 优势:
- 提供单元素添加方法(如
item()
)- 自动处理 null 和重复元素
- 集合类型支持:List、Set、Map
2.3.4 Builder 与继承(@SuperBuilder)
普通 @Builder
不支持继承 ,如果需要继承,请使用 @SuperBuilder
:
java
import lombok.experimental.SuperBuilder;
@SuperBuilder
public class Person {
private String name;
private Integer age;
}
@SuperBuilder
public class Employee extends Person {
private String department;
private Double salary;
}
// 使用
Employee emp = Employee.builder()
.name("Alice")
.age(30)
.department("Engineering")
.salary(85000.0)
.build();
⚠️ 注意 :
@SuperBuilder
在lombok.experimental
包中,是实验性功能但已稳定。
2.4 使用场景
- ✅ API 请求/响应对象:参数多且部分可选
- ✅ 测试数据构造:快速创建测试对象
- ✅ 不可变对象创建 :配合
@Value
使用 - ❌ 简单对象:字段少于 3 个时,直接用构造器更简洁
3. @SneakyThrows:优雅处理检查异常
3.1 什么是检查异常问题?
Java 要求必须处理检查异常(Checked Exception),导致代码臃肿:
java
// 传统写法:大量 try-catch
public String readFile(String path) {
try {
return Files.readString(Paths.get(path));
} catch (IOException e) {
throw new RuntimeException(e); // 通常只是包装
}
}
3.2 @SneakyThrows 解决方案
java
import lombok.SneakyThrows;
import java.nio.file.Files;
import java.nio.file.Paths;
public class FileService {
@SneakyThrows
public String readFile(String path) {
return Files.readString(Paths.get(path)); // IOException 被自动包装
}
@SneakyThrows({IOException.class, InterruptedException.class})
public void doSomething() {
Thread.sleep(1000); // InterruptedException
Files.readAllBytes(Paths.get("test.txt")); // IOException
}
}
✅ 工作原理:
- 编译期将检查异常包装为 RuntimeException
- 不改变字节码行为,只是省略了显式的 try-catch
- 方法签名不会声明 throws,调用方无需处理
🔍 反编译效果:
javapublic String readFile(String path) { try { return Files.readString(Paths.get(path)); } catch (IOException e) { throw lombok.Lombok.sneakyThrow(e); // 实际调用 } }
3.3 使用场景
- ✅ 工具类方法:如文件读写、反射调用
- ✅ 测试代码:快速编写测试,避免异常处理干扰
- ✅ 回调函数:如 Stream 中的操作
- ❌ 业务核心逻辑:重要异常仍应显式处理
3.4 注意事项
- 不要滥用:仅用于"确定不会抛出"或"抛出即致命"的场景
- 日志记录:如果异常可能发生,建议在调用处记录日志
- 团队规范:确保团队理解其行为,避免误用
4. @Cleanup:自动资源管理
4.1 传统资源管理问题
忘记关闭资源会导致内存泄漏:
java
// 危险写法:可能忘记关闭
FileInputStream fis = new FileInputStream("file.txt");
byte[] data = fis.readAllBytes();
// 忘记 fis.close()!
Java 7 引入了 try-with-resources,但 Lombok 提供了更简洁的方式。
4.2 @Cleanup 基本用法
java
import lombok.Cleanup;
import java.io.FileInputStream;
import java.io.IOException;
public class ResourceExample {
public byte[] readFile(String path) throws IOException {
@Cleanup
FileInputStream fis = new FileInputStream(path);
return fis.readAllBytes();
}
}
✅ 等价于:
java
public byte[] readFile(String path) throws IOException {
FileInputStream fis = new FileInputStream(path);
try {
return fis.readAllBytes();
} finally {
if (fis != null) {
fis.close();
}
}
}
✅ 工作原理:
- 在变量作用域结束时自动调用 close() 方法
- 支持任何有
close()
方法的对象(InputStream、OutputStream、Connection 等)
4.3 自定义清理方法
如果资源的清理方法不是 close()
,可以指定:
java
@Cleanup("destroy")
MyResource resource = new MyResource();
// 会调用 resource.destroy()
4.4 使用场景
- ✅ 文件操作:InputStream、OutputStream、Reader、Writer
- ✅ 数据库连接:Connection、Statement、ResultSet
- ✅ 网络连接:Socket、URLConnection
- ✅ 自定义资源:任何需要显式释放的资源
4.5 注意事项
- 作用域限制:只在当前代码块结束时清理
- 异常处理 :如果
close()
抛异常,会被抑制(suppressed),可通过 try-with-resources 更好控制 - 性能考虑:对于高频操作,传统 try-with-resources 可能更清晰
5. 其他实用进阶注解
5.1 @With:创建不可变副本
java
import lombok.With;
@With
public class Point {
private final int x;
private final int y;
}
// 使用
Point p1 = new Point(1, 2);
Point p2 = p1.withX(10); // 创建新对象:Point(10, 2)
✅ 适用场景:函数式编程、不可变对象的状态变更
5.2 @Synchronized:安全的同步方法
替代 synchronized
关键字,避免锁定 this
:
java
import lombok.Synchronized;
public class Counter {
private int count = 0;
private final Object lock = new Object();
@Synchronized("lock")
public void increment() {
count++;
}
}
✅ 优势:避免外部代码锁定你的对象,提高安全性
6. 实战案例:完整的订单处理服务
java
import lombok.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
// 订单实体
@Data
@Builder
@ToString(exclude = "items")
public class Order {
private String orderId;
private String customerName;
private Double totalAmount;
@Singular
private List<OrderItem> items;
}
// 订单项
@Data
@With
public class OrderItem {
private String productId;
private String productName;
private Integer quantity;
private Double price;
}
// 订单服务
@Slf4j
public class OrderService {
// 使用 Builder 创建复杂订单
public Order createSampleOrder() {
return Order.builder()
.orderId("ORD-2023-001")
.customerName("John Doe")
.totalAmount(299.99)
.item(OrderItem.builder()
.productId("P001")
.productName("Laptop")
.quantity(1)
.price(299.99)
.build())
.build();
}
// 使用 @SneakyThrows 简化文件操作
@SneakyThrows
public void saveOrderToFile(Order order, String filePath) {
@Cleanup
FileWriter writer = new FileWriter(filePath);
writer.write(order.toString());
log.info("Order saved to {}", filePath);
}
// 使用 @With 修改订单项
public OrderItem updateItemQuantity(OrderItem item, int newQty) {
return item.withQuantity(newQty);
}
}
7. 小结
✅ 你已掌握:
- @Builder:构建复杂对象,支持集合处理(@Singular)和继承(@SuperBuilder)
- @SneakyThrows:优雅绕过检查异常,减少样板代码
- @Cleanup:自动资源管理,防止内存泄漏
- @With 和 @Synchronized:其他实用进阶功能
➡️ 下一步 :进入 04-Lombok最佳实践指南,学习如何在团队项目中安全、高效地使用 Lombok!
💡 进阶使用原则:
- Builder 模式:参数 ≥ 4 个或有可选参数时使用
- @SneakyThrows:仅用于确定安全的场景,避免隐藏重要异常
- @Cleanup:优先用于资源明确的局部变量,全局资源仍用 try-with-resources