@Getter、@Setter

可以使用 @Getter 或 @Setter 注释任何类或字段,Lombok 会自动生成默认的 getter/setter 方法。如果该字段被称为 foo,则 getter 的方法名为 getFoo;如果该字段的类型为 boolean,则为 isFoo。当在类上使用该注释时,该注释会注释该类中的所有非静态字段。

  1. @Target({ElementType.FIELD, ElementType.TYPE})
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface Getter {
  4. // 生成的getter方法的访问级别
  5. lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
  6. // 废弃
  7. AnyAnnotation[] onMethod() default {};
  8. // 是否启用延迟初始化
  9. boolean lazy() default false;
  10. }

默认生成的 getter/setter 方法是公共的,除非你明确指定一个 AccessLevel

  1. public enum AccessLevel {
  2. PUBLIC, MODULE, PROTECTED, PACKAGE, PRIVATE,
  3. /** Represents not generating anything or the complete lack of a method. */
  4. NONE;
  5. }

可以使用特殊的 AccessLevel.NONE 访问级别来手动禁用任何字段的 getter/setter 自动生成。这使得我们可以覆盖类上的 @Getter,@Setter 或 @Data 注释的行为。

@Getter 注解还支持一个 lazy 属性,该属性默认为 false。当设置为 true 时,会启用延迟初始化,即当首次调用相应 getter 方法时才进行初始化,内部通过同步代码块进行首次初始化。

构造函数

1. @NoArgsConstructor

使用 @NoArgsConstructor 注解可以为指定类生成默认构造函数,@NoArgsConstructor 注解定义如下:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface NoArgsConstructor {
  4. /**
  5. * 如果设置了该属性,生成的构造函数将会是私有的,并且生成一个staticName指定的静态构造方法(内部调用构造函数)
  6. */
  7. String staticName() default "";
  8. /**
  9. * 设置生成的构造函数的访问级别,默认是public
  10. */
  11. AccessLevel access() default lombok.AccessLevel.PUBLIC;
  12. /**
  13. * 若设置为true,则初始化所有final的字段为0/null/false
  14. * 如果为false,当有final字段存在时编译器会报错
  15. */
  16. boolean force() default false;
  17. }

代码示例:

  1. @NoArgsConstructor(staticName = "getInstance")
  2. public class NoArgsConstructorDemo {
  3. private long id;
  4. private String name;
  5. private int age;
  6. }

以上代码经过 Lombok 编译后,会生成如下代码:

  1. public class NoArgsConstructorDemo {
  2. private long id;
  3. private String name;
  4. private int age;
  5. private NoArgsConstructorDemo() {
  6. }
  7. public static NoArgsConstructorDemo getInstance() {
  8. return new NoArgsConstructorDemo();
  9. }
  10. }

2. @AllArgsConstructor

使用 @AllArgsConstructor 注解可以为指定类生成包含所有成员的构造函数。

3. @RequiredArgsConstructor

使用 @RequiredArgsConstructor 注解可以为指定类初始化必需的成员变量,如 final 成员变量,针对必需初始化的成员变量生成对应的构造函数。

@EqualsAndHashCode

使用 @EqualsAndHashCode 注解可以为指定类生成 equals 和 hashCode 方法,默认使用的是所有非 static、非 transient 修饰的字段,且不考虑父类属性。@EqualsAndHashCode 注解的定义如下:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface EqualsAndHashCode {
  4. /**
  5. * 如果为true,则子类生成的equals和hashCode方法会调用父类的相应方法
  6. */
  7. boolean callSuper() default false;
  8. /**
  9. * 如果字段的getter方法可用,在生成的equals和hashCode方法中会调用它们,否则直接访问字段。
  10. * 如果设置为true,则始终会使用直接字段访问而不会调用字段的getter方法。
  11. */
  12. boolean doNotUseGetters() default false;
  13. /**
  14. * 如果为true,则生成的equals和hashCode方法仅包含显式标记为@EqualsAndHashCode.Include的字段和方法。
  15. * 如果为false,则默认包含所有非static、非transient字段。
  16. */
  17. boolean onlyExplicitlyIncluded() default false;
  18. /**
  19. * 如果想要在生成的equals和hashCode方法中排除某字段,则通过该注解修饰对应字段
  20. */
  21. @Target(ElementType.FIELD)
  22. @Retention(RetentionPolicy.SOURCE)
  23. public @interface Exclude {}
  24. /**
  25. * 如果想要在生成的equals和hashCode方法中使用某字段,则通过该注解修饰对应字段
  26. * 需要将onlyExplicitlyIncluded设为true,来精确控制使用的字段
  27. */
  28. @Target({ElementType.FIELD, ElementType.METHOD})
  29. @Retention(RetentionPolicy.SOURCE)
  30. public @interface Include {
  31. String replaces() default "";
  32. }
  33. }

@EqualsAndHashCode.Exclude 代码示例:

  1. public class EqualsAndHashCodeExample {
  2. public static void main(String[] args) {
  3. Person person1 = new Person("a", "1");
  4. Person person2 = new Person("b", "1");
  5. System.out.println(person1.equals(person2));
  6. }
  7. @EqualsAndHashCode
  8. @AllArgsConstructor
  9. private static class Person {
  10. @EqualsAndHashCode.Exclude
  11. private final String name;
  12. private final String identity;
  13. }
  14. }
  15. // 输出为:true

@EqualsAndHashCode.Include 代码示例:

  1. public class EqualsAndHashCodeExample {
  2. public static void main(String[] args) {
  3. Person person1 = new Person("a", "1");
  4. Person person2 = new Person("b", "1");
  5. System.out.println(person1.equals(person2));
  6. }
  7. @EqualsAndHashCode(onlyExplicitlyIncluded = true)
  8. @AllArgsConstructor
  9. private static class Person {
  10. private final String name;
  11. @EqualsAndHashCode.Include
  12. private final String identity;
  13. }
  14. }
  15. // 输出为:true

callSuper 使用示例:

  1. public class EqualsAndHashCodeExample {
  2. public static void main(String[] args) {
  3. Employee employee1 = new Employee("a", "1", "A");
  4. Employee employee2 = new Employee("b", "1", "A");
  5. System.out.println(employee1.equals(employee2));
  6. }
  7. @EqualsAndHashCode
  8. @AllArgsConstructor
  9. private static class Person {
  10. private final String name;
  11. private final String identity;
  12. }
  13. @EqualsAndHashCode(callSuper = true)
  14. private static class Employee extends Person {
  15. private String company;
  16. public Employee(String name, String identity, String company) {
  17. super(name, identity);
  18. this.company = company;
  19. }
  20. }
  21. }
  22. // 输出为:false,因为考虑了父类的name属性

@ToString

使用 @ToString 注解可以为指定类生成 toString 方法,@ToString 注解的定义如下:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface ToString {
  4. /**
  5. * 打印输出时是否包含字段的名称
  6. */
  7. boolean includeFieldNames() default true;
  8. /**
  9. * 打印输出的结果中是否包含父类的toString方法的返回结果
  10. */
  11. boolean callSuper() default false;
  12. /**
  13. * 如果字段的getter方法可用,在生成的toString方法中会调用它们,否则直接访问字段。
  14. * 如果设置为true,则始终会使用直接字段访问而不会调用字段的getter方法。
  15. */
  16. boolean doNotUseGetters() default false;
  17. /**
  18. * 如果为true,则生成的toString方法仅包含显式标记为@ToString.Include的字段和方法。
  19. * 如果为false,则默认包含所有非static字段。
  20. */
  21. boolean onlyExplicitlyIncluded() default false;
  22. /**
  23. * 如果想要在生成的toString方法中排除某字段,则通过该注解修饰对应字段
  24. */
  25. @Target(ElementType.FIELD)
  26. @Retention(RetentionPolicy.SOURCE)
  27. public @interface Exclude {}
  28. /**
  29. * 如果想要在生成的toString方法中使用某字段,则通过该注解修饰对应字段
  30. * 需要将onlyExplicitlyIncluded设为true,来精确控制使用的字段
  31. * 如果修饰在一个方法上,则在输出中包含该方法的返回值。
  32. */
  33. @Target({ElementType.FIELD, ElementType.METHOD})
  34. @Retention(RetentionPolicy.SOURCE)
  35. public @interface Include {
  36. // 打印顺序的优先级,首先打印更高的等级,相同等级的成员按它们在源文件中出现的顺序打印
  37. int rank() default 0;
  38. String name() default "";
  39. }
  40. }

使用示例如下:

  1. public class ToStringDemo {
  2. public static void main(String[] args) {
  3. Employee employee = new Employee("a", "1", "A");
  4. System.out.println(employee.toString());
  5. }
  6. @ToString(onlyExplicitlyIncluded = true)
  7. @AllArgsConstructor
  8. private static class Person {
  9. @ToString.Include(rank = 1)
  10. private final String name;
  11. @ToString.Include(rank = 2)
  12. private final String identity;
  13. private final String other;
  14. }
  15. @ToString(includeFieldNames = false, callSuper = true)
  16. private static class Employee extends Person {
  17. private final String company;
  18. @ToString.Exclude
  19. private final String exclude;
  20. public Employee(String name, String identity, String company) {
  21. super(name, identity, "other");
  22. this.company = company;
  23. this.exclude = "exclude";
  24. }
  25. }
  26. }
  27. // 输出如下:ToStringDemo.Employee(super=ToStringDemo.Person(identity=1, name=a), A)

@Data

@Data 是一个复合注解,常用于普通的 POJO 类。它的功能与同时使用以下注解的效果是一样的:

  1. @Getter
  2. @Setter
  3. @RequiredArgsConstructor
  4. @ToString
  5. @EqualsAndHashCode

@Value

@Value 注解和 @Data 注解一样也是个复合注解,但 @Value 注解常用于不可变类,它的功能与同时使用以下注解的效果是一样的:

  1. @Getter
  2. @FieldDefaults
  3. @AllArgsConstructor
  4. @ToString
  5. @EqualsAndHashCode

可以看到 @Value 注解不会生成 setter 方法,并且构造器封装了全部属性。其中,@FieldDefaults 注解的使用方式为:@FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE),即它会把所有实例属性的访问级别修改为 private final 修饰。

@Builder

使用 @Builder 注解可以为指定类实现建造者模式,建造者模式就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。该注解可以放在类、构造函数或方法上。@Builder 注解的定义如下:

  1. @Target({TYPE, METHOD, CONSTRUCTOR})
  2. @Retention(SOURCE)
  3. public @interface Builder {
  4. /**
  5. * 用@Default注释的字段必须有一个初始化表达式;如果在构建期间未设置该字段,则将该表达式将作为字段默认值使用。
  6. */
  7. @Target(FIELD)
  8. @Retention(SOURCE)
  9. public @interface Default {}
  10. // 指定实体类中创建Builder的方法名称,默认为builder,一般不需要修改
  11. String builderMethodName() default "builder";
  12. // 指定Builder中用来构建实体类的方法名称,默认为build,一般不需要修改
  13. String buildMethodName() default "build";
  14. // 指定创建的建造者类的名称,默认为:实体类名+Builder后缀
  15. String builderClassName() default "";
  16. /**
  17. * 使用toBuilder可以实现以一个实例为基础继续创建一个对象,也就是可以重用原来对象的值
  18. * 但只是值复用,返回的是一个新的对象
  19. */
  20. boolean toBuilder() default false;
  21. @Target({FIELD, PARAMETER})
  22. @Retention(SOURCE)
  23. public @interface ObtainVia {
  24. String field() default "";
  25. String method() default "";
  26. boolean isStatic() default false;
  27. }
  28. }

@Builder 注解使用示例如下:

  1. public class BuilderDemo {
  2. @Builder(toBuilder = true)
  3. @ToString
  4. private static class Person {
  5. private final String name;
  6. private final String identity;
  7. @Builder.Default
  8. private final long birthday = System.currentTimeMillis();
  9. }
  10. public static void main(String[] args) {
  11. Person person = Person.builder().name("xl").identity("123").build();
  12. System.out.println(person);
  13. person = person.toBuilder().identity("456").birthday(111).build();
  14. System.out.println(person);
  15. }
  16. }
  17. // 输出如下:
  18. BuilderDemo.Person(name=xl, identity=123, birthday=1635586221889)
  19. BuilderDemo.Person(name=xl, identity=456, birthday=111)

此外,@Builder 注解还可以配合 @Singular 注解使用,用来对集合类型的属性添加单个元素。当用 @Singular 注释一个集合字段(使用 @Builder 注释类)时,Lombok 会为该字段提供三个构建方法:

  • 一个用来向集合添加单个元素
  • 一个用来将另一个集合的所有元素添加到集合中
  • 清空集合元素

@Builder 配合 @Singular 注解使用示例:

  1. public class BuilderDemo2 {
  2. @Builder
  3. @ToString
  4. private static class Person {
  5. @Singular("hobby")
  6. private List<String> hobby;
  7. }
  8. public static void main(String[] args) {
  9. List<String> hobby = new ArrayList<>();
  10. hobby.add("football");
  11. Person person = Person.builder()
  12. .hobby(hobby)
  13. .hobby("basketball")
  14. // .clearHobby()
  15. .build();
  16. System.out.println(person);
  17. }
  18. }
  19. // 输入如下:
  20. BuilderDemo2.Person(hobby=[football, basketball])

日志

1. @Log

@Log 注解采用 JDK 提供的日志实现,为指定类生成一个 log 静态属性:

  1. private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

2. @Log4j、@Log4j2

@Log4j、Log4j2 注解采用 Log4j、Log4j2 提供的日志实现,为指定类生成一个 log 静态属性:

  1. private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
  2. private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

3. @CommonsLog

@CommonsLog 注解采用 Common Logging 提供的日志实现(日志接口),为指定类生成一个 log 静态属性:

  1. private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

4. @Slf4j

@Slf4j 注解采用 Slf4j 提供的日志实现(也是日志接口),为指定类生成一个 log 静态属性:

  1. private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

@Cleanup

@Cleanup 注解用于自动管理资源,用来饰局部变量,在当前变量范围内即将执行完毕退出之前会自动进行资源的清理,为变量自动生成 try-finally 这样的代码来关闭资源。

@SneakyThrows

@SneakyThrows 注解用于自动抛出受检异常,而无需在方法中使用 throw 语句显式抛出。注解定义如下:

  1. @Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface SneakyThrows {
  4. // 设置你希望向上抛的异常类
  5. Class<? extends Throwable>[] value() default java.lang.Throwable.class;
  6. }

@Synchronized

@Synchronized 是同步方法修饰符的更安全的变体。与 synchronized 一样,该注解只能应用在静态和实例方法上面。它的操作类似于 synchronized 关键字,但是它锁定在不同的对象上。synchronized 关键字应用在实例方法时锁定的是 this 对象,而应用在静态方法上锁定的是类对象。对于 @Synchronized 注解声明的方法来说,它锁定的是内部生成的 $lock(实例方法)或 $Lock(静态方法)对象。@Synchronized 注解的定义如下:

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface Synchronized {
  4. /**
  5. * 指定要锁定的对象的名称
  6. */
  7. String value() default "";
  8. }

@Synchronized 注解使用示例:

  1. public class SynchronizedDemo {
  2. private final Object readLock = new Object();
  3. @Synchronized
  4. public static void hello() {
  5. System.out.println("world");
  6. }
  7. @Synchronized
  8. public int answerToLife() {
  9. return 42;
  10. }
  11. @Synchronized("readLock")
  12. public void foo() {
  13. System.out.println("bar");
  14. }
  15. }

以上代码经过 Lombok 编译后,会生成如下代码:

  1. public class SynchronizedDemo {
  2. private static final Object $LOCK = new Object[0];
  3. private final Object $lock = new Object[0];
  4. private final Object readLock = new Object();
  5. public SynchronizedDemo() {
  6. }
  7. public static void hello() {
  8. synchronized($LOCK) {
  9. System.out.println("world");
  10. }
  11. }
  12. public int answerToLife() {
  13. synchronized(this.$lock) {
  14. return 42;
  15. }
  16. }
  17. public void foo() {
  18. synchronized(this.readLock) {
  19. System.out.println("bar");
  20. }
  21. }
  22. }