Skip to content

S05-10 高级特性-Lombok

[TOC]

Lombok

Lombok 是 Java 开发中非常流行的一个工具库。它的核心作用是通过注解(Annotations) 来自动生成 Java 中的“样板代码(Boilerplate Code)”,例如 gettersetter、构造函数、toString() 等,从而极大地简化代码,让开发者专注于核心业务逻辑。

Lombok 的工作原理

Lombok 的工作原理:

很多人担心 Lombok 会影响程序运行时的性能,但实际上它完全没有运行时开销

Lombok 的工作发生在编译阶段(基于 JSR 269: Pluggable Annotation Processing API)。当 Java 编译器(如 javac)将 .java 源码编译为 .class 字节码时,Lombok 会介入并修改抽象语法树(AST),将需要生成的代码直接注入到最终的字节码文件中。因此,在 JVM 运行时,带有 Lombok 注解的类和手写了所有方法的类是没有任何区别的。

基础示例

Lombok 能帮你立刻省去那些繁琐的面向对象“样板代码”(比如构造方法、封装用的 Getter/Setter)。

我们就用最经典的 封装(Encapsulation)对象创建(构造方法) 来做一个入门案例。

案例背景:定义一个“学生”JavaBean类:

在学习面向对象时,老师一定会教你:属性要私有化(private),并对外提供公共的访问方法(public get/set

第一步:编写 Student 类(使用 Lombok)

java
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

// Lombok 会在编译时自动帮我们写好很多代码
@Data               // 自动生成所有 private 属性的 Getter/Setter 方法,以及 toString() 方法
@NoArgsConstructor  // 自动生成一个“无参数”的构造方法
@AllArgsConstructor // 自动生成一个包含“所有参数”的构造方法
public class Student {

    // 对象的属性(私有化,符合面向对象的封装特性)
    private String name;
    private int age;
    private String studentId;

    // 注意:这里里面什么都不用写!不需要手敲 get/set 和构造方法。
}

通过这个案例,你可以把刚学的 OOP(面向对象编程)知识和 Lombok 对应起来:

  1. 封装:你只需关注定义 private String name;,Lombok 的 @Data 替你完成了开放 public String getName() 的体力活。

  2. 构造方法重载:你不需要手写多个构造方法,@NoArgsConstructor@AllArgsConstructor 让你既能 new Student(),也能 new Student("小红", 19, "S1002")

  3. 注意事项:要在你的电脑上真正跑通这段代码,除了引入 Lombok 的代码库,你的开发工具(比如 IntelliJ IDEA 或 Eclipse)必须安装 Lombok 插件,否则代码里的 setName()getName() 会标红报错,因为编辑器默认“看”不到 Lombok 生成的方法。

    image-20260318150548784

    image-20260318150916701

第二步:编写 Main 测试类

在这个测试类里,我们要去使用刚才 Lombok 帮我们偷偷生成的那些方法。

java
public class Main {
    public static void main(String[] args) {

        // --- 演示 1:使用无参构造方法 & Setter 赋值 ---
        // 这里的无参构造方法是 @NoArgsConstructor 帮我们生成的
        Student student1 = new Student();

        // 这里的 set 方法是 @Data 帮我们生成的
        student1.setName("小明");
        student1.setAge(18);
        student1.setStudentId("S1001");

        // 这里的 get 方法也是 @Data 帮我们生成的
        System.out.println("学生1的姓名是:" + student1.getName());

        // --- 演示 2:使用全参构造方法快速创建对象 ---
        // 这里的全参构造方法是 @AllArgsConstructor 帮我们生成的
        Student student2 = new Student("小红", 19, "S1002");

        // --- 演示 3:打印对象信息 ---
        // 通常直接打印对象,输出的会是内存地址(如 Student@1b6d3586)
        // 但因为使用了 @Data,它帮我们重写了 toString() 方法,所以打印出来的是清晰的数据内容
        System.out.println("学生1详细信息:" + student1);
        System.out.println("学生2详细信息:" + student2);
    }
}

第三步:运行结果:

如果你运行上面的 Main 类,控制台会输出:

text
学生1的姓名是:小明
学生1详细信息:Student(name=小明, age=18, studentId=S1001)
学生2详细信息:Student(name=小红, age=19, studentId=S1002)

核心注解详解

基础属性注入

基础属性注入:

  • @Getter / @Setter
    • 作用:自动为类中的所有非静态字段生成 getset 方法。如果标注在类上,则作用于所有字段;标注在特定字段上,则只对该字段生效。
    • 附加配置:可以通过 AccessLevel 控制访问权限,例如 @Getter(AccessLevel.PROTECTED)
  • @ToString
    • 作用:自动生成 toString() 方法。
    • 注意:可以通过 exclude 参数排除某些字段,或者用 of 指定只包含哪些字段(避免循环引用时引起栈溢出)。
  • @EqualsAndHashCode
    • 作用:自动生成 equals()hashCode() 方法。默认使用所有非静态、非瞬态(transient)字段。

构造函数生成

构造函数生成:

  • @NoArgsConstructor:生成一个无参构造函数。
  • @AllArgsConstructor:生成一个包含类中所有字段的全参构造函数。
  • @RequiredArgsConstructor:生成一个包含所有带有 final 修饰符和 @NonNull 注解的字段的构造函数。

组合与高级注解

组合与高级注解:

  • @Data (最常用)

    • 作用:这是一个复合注解,等同于同时使用了 @Getter@Setter@RequiredArgsConstructor@ToString@EqualsAndHashCode
    • 场景:通常用于普通的 POJO(Plain Old Java Object)、DTO、VO 类。
  • @Value

    • 作用:相当于不可变(Immutable)版本的 @Data。它会将所有字段默认变为 private final,并且只生成 Getter,不生成 Setter。
  • @Builder

    • 作用:自动为类生成建造者模式(Builder Pattern) 的代码,使得对象的链式创建变得非常优雅。

    • 示例

      java
      User user = User.builder().name("张三").age(25).build();

日志注解

日志注解:

  • @Slf4j / @Log
    • 作用:自动在类中注入一个静态的日志对象。@Slf4j 会自动生成 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(YourClass.class);
    • 场景:极大地简化了打印日志的准备工作,直接在代码中调用 log.info("...") 即可。

传统代码与 Lombok 代码对比

传统代码与 Lombok 代码对比:

未使用 Lombok 的传统 Java 类:

java
public class User {
    private String name;
    private int age;

    public User() {}

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
    // 还需要手写 equals 和 hashCode 方法...
}

使用 Lombok 后:

java
import lombok.Data;

@Data
public class User {
    private String name;
    private int age;
}

Lombok 的优缺点分析

优点:

  1. 代码极度简洁:去除了大量冗余代码,类结构一目了然,只保留核心属性。

  2. 提高开发效率:修改字段时(如修改字段名、增删字段),不需要手动去修改对应的 getter、setter 和构造函数。

  3. 零运行时开销:由于是编译时生成,不影响程序运行性能。

缺点与争议:

  1. 环境依赖:不仅需要在项目中引入依赖,团队中所有开发者的 IDE(如 IDEA, Eclipse)都必须安装 Lombok 插件,否则代码会大面积报错(找不到 get/set 方法)。

  2. 降低可读性与调试难度:因为方法是隐式的,有时你很难一眼看出一个类的 hashCode 是基于哪些字段生成的。

  3. 滥用带来的坑

    • 循环引用:在 JPA/Hibernate 等 ORM 框架中,如果双向关联的实体类都使用了 @Data(包含了 @ToString@EqualsAndHashCode),在打印日志或比对时会引发 StackOverflowError
    • 继承问题:子类使用 @Data@EqualsAndHashCode 时,默认不会调用父类的 equals 方法(除非显式配置 callSuper = true),这可能导致对象比较出现意料之外的结果。

最佳实践建议

最佳实践建议:

  • POJO 类推荐:在普通的 DTO、VO、Param 类中大胆使用 @Data@Builder
  • ORM 实体类慎用:在 JPA Entity 中,尽量避免使用 @Data,推荐只使用 @Getter@Setter@ToString(exclude="关联字段"),以防止懒加载失效和循环引用问题。
  • 使用 @Slf4j:取代手动声明 Logger 的繁琐过程。