🚀 Java 巩固进阶 · 第 36 天
主题:Lombok 实战 + 阶段总结 ------ 解放双手,高效开发
📅 进度概览 :今天是 设计模式与注解阶段(31-36 天)的最后一天 ,也是 Java 基础巩固阶段的收官之日!学习 Lombok 提升开发效率,并全面复盘本阶段知识。
💡 核心价值:
- 效率提升:用注解自动生成 get/set/构造器,减少 80% 样板代码。
- 代码整洁:类只关注业务逻辑,不含 getter/setter 等噪音代码。
- 原理理解:了解注解处理器(APT)工作机制,深化对编译期的理解。
- 阶段复盘:梳理设计模式与注解知识体系,为 SpringBoot 学习做好准备。
一、Lombok 核心注解:一行顶百行 🛠️
1. 什么是 Lombok?
┌─────────────────────────────────────┐
│ 🔄 Lombok 本质 │
│ 是一个注解库,编译时自动生成代码 │
│ │
│ • 减少样板代码(get/set/构造器等) │
│ • 通过注解处理器(APT)实现 │
│ • 编译后字节码包含生成的代码 │
└─────────────────────────────────────┘
2. 核心注解速查表(⭐ 必背)
| 注解 | 作用 | 等价手写代码 | 使用频率 |
|---|---|---|---|
@Data |
生成 get/set/toString/equals/hashCode | 全部 | ⭐⭐⭐⭐⭐ |
@Getter/@Setter |
生成 get 或 set 方法 | get/set 方法 | ⭐⭐⭐⭐ |
@NoArgsConstructor |
生成无参构造器 | public Class() {} |
⭐⭐⭐⭐ |
@AllArgsConstructor |
生成全参构造器 | public Class(所有字段) {} |
⭐⭐⭐⭐ |
@RequiredArgsConstructor |
生成 required 构造器(final 字段) | public Class(final 字段) {} |
⭐⭐⭐ |
@Builder |
生成建造者模式代码 | Builder 内部类 | ⭐⭐⭐⭐ |
@ToString |
生成 toString 方法 | toString() 方法 |
⭐⭐⭐ |
@EqualsAndHashCode |
生成 equals/hashCode | equals/hashCode() 方法 |
⭐⭐⭐ |
@Slf4j |
生成 log 日志对象 | private static final Logger... |
⭐⭐⭐⭐⭐ |
3. 实战对比:手写 vs Lombok
java
// ❌ 传统手写(约 50 行代码)
public class User {
private Long id;
private String name;
private Integer age;
public User() {}
public User(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
@Override
public String toString() {
return "User{id=" + id + ", name=" + name + ", age=" + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
// ✅ Lombok 版(约 10 行代码)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
private Long id;
private String name;
private Integer age;
}
💡 效率对比:
- 代码量减少 80%+
- 可读性提升 50%+
- 维护成本降低 70%+
二、Lombok 原理:注解处理器(APT)🔍
1. 编译流程图解
源码 (.java + Lombok 注解)
↓
javac 编译
↓
注解处理器 (Lombok APT)
↓
生成代码 (get/set/构造器等)
↓
合并到抽象语法树 (AST)
↓
生成字节码 (.class)
↓
运行时:普通 Java 类(无 Lombok 依赖)
2. 关键特性
java
// ✅ 编译时生成:运行时没有 Lombok 依赖
// ✅ 字节码完整:IDE 和运行时都能正常调用 get/set
// ✅ 无性能损耗:生成的代码与手写等价
// ⚠️ 注意:
// 1. IDE 需安装 Lombok 插件(否则提示找不到方法)
// 2. 编译时需添加 Lombok 依赖
// 3. 反编译.class 文件能看到生成的方法
3. Maven/Gradle 配置
xml
<!-- Maven 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
gradle
// Gradle 依赖
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
三、⚠️ 注意事项与避坑指南
1. 常见坑点
java
// ❌ 坑 1:@Data + 继承 = equals/hashCode 问题
@Data
public class Parent { private String name; }
@Data
public class Child extends Parent { private Integer age; }
// 问题:子类 equals 不包含父类字段
// ✅ 解决:用 @EqualsAndHashCode(callSuper = true)
@Data
@EqualsAndHashCode(callSuper = true)
public class Child extends Parent { ... }
// ❌ 坑 2:@Builder + @NoArgsConstructor 冲突
@Builder
public class User { ... }
// 问题:@Builder 会生成全参构造,与无参构造冲突
// ✅ 解决:显式添加 @NoArgsConstructor
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User { ... }
// ❌ 坑 3:@Data + JPA 实体 = 性能问题
@Data
@Entity
public class UserEntity { ... }
// 问题:@Data 生成的 equals/hashCode 可能导致懒加载触发
// ✅ 解决:JPA 实体只生成 getId 的 equals/hashCode
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Data
@Entity
public class UserEntity {
@EqualsAndHashCode.Include
private Long id;
}
2. 最佳实践建议
| 场景 | 推荐注解 | 说明 |
|---|---|---|
| 普通 DTO/VO | @Data |
最常用,生成全部方法 |
| 实体类(JPA) | @Getter/@Setter + 自定义 @EqualsAndHashCode |
避免懒加载问题 |
| 不可变对象 | @Value |
类似 @Data,但字段默认 final |
| 构建复杂对象 | @Builder |
链式调用,代码优雅 |
| 日志对象 | @Slf4j |
自动生成 log 对象 |
四、🎯 今日实战任务:Lombok 重构练习
任务 1:用 Lombok 重构用户类
java
/**
* 要求:
* 1. 找到之前手写的 User 类(含 get/set/构造器/toString)
* 2. 用 @Data @NoArgsConstructor @AllArgsConstructor 替代
* 3. 添加 @Builder,测试链式创建对象
* 4. 添加 @Slf4j,在方法中使用 log.info()
*
* 💡 提示:
* 确保 IDE 已安装 Lombok 插件
* 编译后反编译.class 文件,查看生成的方法
*/
任务 2:Lombok 与继承
java
/**
* 业务场景:父类 + 子类的 equals/hashCode 问题
*
* 要求:
* 1. 创建 Parent 类(@Data,含 name 字段)
* 2. 创建 Child 类继承 Parent(@Data,含 age 字段)
* 3. 测试:两个 Child 对象,name 和 age 相同,equals 是否返回 true?
* 4. 用 @EqualsAndHashCode(callSuper = true) 修复问题
*
* 💡 思考:
* 为什么子类 equals 默认不包含父类字段?
*/
任务 3:Lombok 与 JPA 实体
java
/**
* 业务场景:Spring Data JPA 实体类
*
* 要求:
* 1. 创建 UserEntity 类,添加 @Entity @Id 注解
* 2. 使用 @Getter/@Setter 替代 @Data
* 3. 使用 @EqualsAndHashCode(onlyExplicitlyIncluded = true)
* 4. 只将 id 字段包含在 equals/hashCode 中
*
* 💡 提示:
* 这是 SpringBoot + JPA 的最佳实践
*/
任务 4:Lombok 原理探索
java
/**
* 要求:
* 1. 创建一个简单的 @Data 类
* 2. 编译项目,找到生成的.class 文件
* 3. 用反编译工具(如 IDEA 内置或 javap)查看字节码
* 4. 记录:生成了哪些方法?与手写的是否一致?
*
* 💡 命令:
* javap -c -p User.class
*/
📝 第 36 天 · 核心总结(极简背诵版)
-
Lombok 核心注解:
java@Data // get/set/toString/equals/hashCode @Builder // 建造者模式 @NoArgsConstructor // 无参构造 @AllArgsConstructor// 全参构造 @Slf4j // 日志对象 -
Lombok 原理:
注解处理器(APT)→ 编译时生成代码 → 合并到 AST → 生成字节码 运行时无 Lombok 依赖,无性能损耗 -
避坑指南:
继承:@EqualsAndHashCode(callSuper = true) JPA 实体:避免@Data,用@Getter/@Setter + 自定义 equals Builder 冲突:同时加 @NoArgsConstructor -
最佳实践:
- ✅ DTO/VO 用 @Data
- ✅ 实体类用 @Getter/@Setter + 自定义 @EqualsAndHashCode
- ✅ 日志用 @Slf4j
- ❌ 避免过度使用,关键逻辑仍需手写
🎉 恭喜!Java 基础巩固阶段(1-36 天)正式完结!
📊 完整知识地图复盘
📅 第 1 阶段:Java 基础巩固(1-36 天)
│
├─ 📚 第 1-6 天:语法与面向对象
│ ├─ 数据类型/流程控制/数组/字符串
│ ├─ 封装/继承/多态/抽象类/接口
│ └─ 异常处理体系
│
├─ 📦 第 7-12 天:集合框架
│ ├─ List (ArrayList/LinkedList)
│ ├─ Set (HashSet/TreeSet)
│ ├─ Map (HashMap/ConcurrentHashMap)
│ └─ 泛型与工具类
│
├─ 📁 第 13-18 天:IO 流与日期时间
│ ├─ 字节流/字符流/缓冲流/转换流
│ ├─ File 文件操作
│ └─ JDK8 日期 API(LocalDate/DateTimeFormatter)
│
├─ 🧵 第 19-24 天:多线程与反射
│ ├─ 线程创建/同步/通信/线程池
│ ├─ Callable/Future/CompletableFuture
│ ├─ 反射基础 (Class/Field/Method)
│ └─ 反射进阶 + 注解初探
│
├─ 🆕 第 25-30 天:JDK8+ 新特性
│ ├─ Lambda 表达式
│ ├─ Stream 流式编程
│ ├─ Optional 空值处理
│ ├─ 接口默认/静态方法
│ └─ JDK9-11 新特性
│
└─ 🏗️ 第 31-36 天:设计模式与注解
├─ 单例/工厂/建造者/原型
├─ 代理/装饰器
├─ 注解基础与自定义
├─ 注解 + 反射实战
└─ Lombok 高效开发
📝 高频面试题清单(自测 30 题)
| 分类 | 面试题 |
|---|---|
| 集合 | HashMap 底层结构?JDK7 与 JDK8 区别? |
| 集合 | ArrayList 扩容机制?LinkedList 适用场景? |
| 集合 | ConcurrentHashMap 如何保证线程安全? |
| 并发 | synchronized 与 ReentrantLock 区别? |
| 并发 | volatile 关键字作用?能保证原子性吗? |
| 并发 | 线程池 7 大参数?拒绝策略有哪些? |
| 并发 | ThreadLocal 原理?内存泄漏问题? |
| IO | BIO/NIO/AIO 区别? |
| IO | 缓冲流为什么比普通流快? |
| JDK8 | Lambda 表达式本质?函数式接口有哪些? |
| JDK8 | Stream 中间操作与终止操作区别? |
| JDK8 | Optional 使用禁忌? |
| 反射 | 反射获取 Class 的三种方式? |
| 反射 | 反射破坏单例如何防御? |
| 设计模式 | 单例模式几种实现?双重检查锁为什么需要 volatile? |
| 设计模式 | 工厂模式与抽象工厂区别? |
| 设计模式 | 代理模式与装饰器模式区别? |
| 设计模式 | JDK 动态代理与 CGLIB 区别? |
| 注解 | 元注解有哪些?@Retention 三种策略区别? |
| 注解 | 如何通过反射读取注解信息? |
| 其他 | String 为什么不可变? |
| 其他 | == 与 equals() 区别? |
| 其他 | 接口与抽象类区别? |
| 其他 | 重载与重写区别? |
| 其他 | 深克隆与浅克隆区别? |
| 其他 | 检查异常与非检查异常区别? |
| 其他 | final/finally/finalize 区别? |
| 其他 | 静态方法与实例方法区别? |
| 其他 | 序列化与反序列化?serialVersionUID 作用? |
| 其他 | Java 反射的性能开销及优化? |
恭喜你完成了 Java 基础的系统性巩固!继续加油!💪✨