JAVA重点基础、进阶知识及易错点总结(36)Lombok 实战 + 阶段总结

🚀 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 天 · 核心总结(极简背诵版)

  1. Lombok 核心注解

    java 复制代码
    @Data              // get/set/toString/equals/hashCode
    @Builder           // 建造者模式
    @NoArgsConstructor // 无参构造
    @AllArgsConstructor// 全参构造
    @Slf4j            // 日志对象
  2. Lombok 原理

    复制代码
    注解处理器(APT)→ 编译时生成代码 → 合并到 AST → 生成字节码
    运行时无 Lombok 依赖,无性能损耗
  3. 避坑指南

    复制代码
    继承:@EqualsAndHashCode(callSuper = true)
    JPA 实体:避免@Data,用@Getter/@Setter + 自定义 equals
    Builder 冲突:同时加 @NoArgsConstructor
  4. 最佳实践

    • ✅ 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 基础的系统性巩固!继续加油!💪✨

相关推荐
xyq20242 小时前
过滤器模式
开发语言
spencer_tseng2 小时前
AffineTransform cannot be resolved
java
freejackman2 小时前
Java从0到1---基础篇
java·开发语言·后端·idea
CQU_JIAKE2 小时前
4.4【Q】
java·前端·javascript
2301_771717212 小时前
Java自定义注解创建详解
java·开发语言
小陈工2 小时前
Python Web开发入门(十二):使用Flask-RESTful构建API——让后端开发更优雅
开发语言·前端·python·安全·oracle·flask·restful
艾莉丝努力练剑2 小时前
【Linux系统:信号】线程安全不等于可重入:深度拆解变量作用域与原子操作
java·linux·运维·服务器·开发语言·c++·学习
笑鸿的学习笔记2 小时前
Qt与CMake笔记之option、宏传递与Qt Creator项目设置
开发语言·笔记·qt