类属性相关
@Getter
可以添加到类或字段上,可以自动生成字段相应的get方法
@Getter //添加到类上时,将为类中所有字段添加Getter方法
public class Account {
int id;
@Getter //当添加到字段上时,仅对此字段生效
String name;
int age;
}
编译后的生成代码
public class Account {
int id;
String name;
int age;
public Account() {
}
@Generated
public int getId() {
return this.id;
}
@Generated
public int getAge() {
return this.age;
}
@Generated
public String getName() {
return this.name;
}
}
方法的命名规则为get加上首字母大写的字段名,如果字段类型为boolean
,那么get则替换为is
Getter
属性
value
value
值为为AccessLevel
的枚举类型,可以控制生成方法访问权限
- PUBLIC - 对应public关键字
- PACKAGE - 相当于不添加任何访问权限关键字
- PRIVATE - 对应private关键字
- PROTECTED - 对应protected关键字
- MODULE - 仅限模块内使用,与PACKAGE类似,相当于不添加任何访问权限关键字
- NONE - 表示不生成对应的方法,这很适合对类中不需要生成的字段进行排除
onMethod
可以在方法上添加额外的注解,注意添加的时候指定属性名要用onMethod_
@Getter
public class Account {
private int id;
@Getter(onMethod_ = { @Deprecated })
private String name;
private int age;
}
这样的话getName()
就会被打上@Deprecated
的注解
lazy
属性
用于控制懒加载,要求字段必须具有private
和final
属性
懒加载就是在一开始的时候此字段没有值,当我们需要的时候再将值添加到此处。
它的具体表现是最开始我们通过其他方法去获取具有lazy
属性的字段时,该字段为null,只有在调用@Getter
生成的方法后才会被赋值
public class Test {
public static void main(String[] args) {
Account account = new Account();
account.test();
System.out.println(account.getId());
account.test();
}
}
Setter
注解与Getter
类似,但是比Getter
多一个属性onParam
,可以给形参添加额外的注解
如果类中已经有了同名不同参的函数,@Getter
可能会误判导致不会生成相应的get函数,可以使用@Tolerate
注解使Lombok忽略它的存在,继续生成。
构造方法相关
@AllArgsConstructor
它用于为类中所有字段生成一个构造方法,只能添加到类上
onConstructor
是给生成的构造方法添加额外注解,access
是控制生成构造方法的权限
staticName
用于生成静态构造方法
@AllArgsConstructor(staticName = "as")
@Getter
@Setter
public class Account {
private int id;
private String name;
private int age;
// @Tolerate
public void setName(int i){
System.out.println("hello world");
}
}
这样会自动帮我们生成一个as
的静态构造方法,使用Account.as(参数,...)
即可创建一个对象,同时也会生成一个带全部参数的private方法,不可以修改
@NoArgsConstructor
生成一个无参构造,要求类中无final属性的字段,如果有的话需要添加属性force=true
,它会为final
字段一个默认值
@RequiredArgsConstructor
用于生成那些需要初始化的参数的构造方法,也就是说类中哪些字段为final,它就只针对这些字段生成对应的构造方法
打印对象
@ToString
可以自动重写toString()方法
参数如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
//是否在打印的内容中带上对应字段的名字
boolean includeFieldNames() default true;
//用于排除不需要打印的字段(这种用法很快会被移除,不建议使用)
String[] exclude() default {};
//和上面相反,设置哪些字段需要打印,默认打印所有(这种用法很快会被移除,不建议使用)
String[] of() default {};
//不仅为当前类中所有字段生成,同时还调用父类toString进行拼接
boolean callSuper() default false;
//默认情况下生成的toString会尽可能使用get方法获取字段值,我们也可以手段关闭这个功能
boolean doNotUseGetters() default false;
//开启后将只为字段或get方法上添加了@ToString.Includ注解的内容生成toString方法,白名单模式
boolean onlyExplicitlyIncluded() default false;
/**
* 用于排除toString中的字段
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Exclude {}
/**
* 用于手动包含toString中的字段
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Include {
//配置字段打印顺序的优先级
int rank() default 0;
//配置一个自定义的字段名称进行打印
String name() default "";
}
}
其中需要注意的是@Include
这个子注解,它不仅可以对字段生效,还可以对方法生效,会输出方法名=返回值
比较相关
Lombok可以为我们自动生成类属性的比较方法以及对应的HashCode计算。我们只需要为类添加@EqualsAndHashCode
注解,即可开启
@EqualsAndHashCode
注解的参数大部分与我们上节讲解的@ToString
类似,比如exclude
、of
、callSuper
(默认关闭,开启后调用equals比较前先调用父类的equals进行一次比较)、doNotUseGetters
以及生成的方法中需要额外携带的注解onParam
属性等。
它同样可以使用onlyExplicitlyIncluded
属性来开启白名单模式,我们可以使用以下注解来自由控制哪些属性会作为比较的目标,哪些需要排除:
-
@EqualsAndHashCode.Exclude
- 用于排除不需要参与比较的字段。 -
@EqualsAndHashCode.Include
- 开启白名单模式后,用于标明哪些字段需要参与比较。
@Data
和@Value
很多情况下我们可能需要在用作数据传递的实体类上将它们一并用上,我们可以直接使用@Data
注解,它等价于我们在类上添加这些注解:@Getter
@Setter
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
如果我们希望某个类只作为结果,里面的数据不可进行修改,我们也可以使用@Data
的只读版本@Value
,它等价于添加注解:@Getter
@FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE)
@AllArgsConstructor
@ToString
@EqualsAndHashCode
建造者模式
使用@Builder
注解可以将一个类转换为建造者模式,使用Lombok生成的builder来创建对象
public static void main(String[] args) {
Account account = Account.builder()
.id(1)
.name("小明")
.age(18)
.build();
System.out.println(account);
}
类中生成的Builder内部类
public static class AccountBuilder {
private int id;
private String name;
private int age;
AccountBuilder() {
}
public AccountBuilder id(int id) {
this.id = id;
return this;
}
public AccountBuilder name(String name) {
this.name = name;
return this;
}
public AccountBuilder age(int age) {
this.age = age;
return this;
}
public Account build() {
return new Account(this.id, this.name, this.age);
}
public String toString() {
return "Account.AccountBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
}
Builder注解相关参数:
public @interface Builder {
//用于设置默认值
@Target(FIELD)
@Retention(SOURCE)
public @interface Default {}
//用于获取builder对象的静态方法,默认情况下名称为builder()
String builderMethodName() default "builder";
//Builder对象最终构建的方法名称,默认情况下名称为build()
String buildMethodName() default "build";
//内部生成的Builder类名称,默认为 类名称+Builder
String builderClassName() default "";
//生成一个用于将对象转回Builder的方法,默认情况下不启用
boolean toBuilder() default false;
//生成的Builder极其相关方法的访问权限级别
AccessLevel access() default lombok.AccessLevel.PUBLIC;
//Builder中各个属性设置器的前缀,默认没有
String setterPrefix() default "";
//用于配合toBuilder方法使用,指明如何获取指定字段的值
@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface ObtainVia {
//通过其他字段获取
String field() default "";
//通过方法获取
String method() default "";
//如果上面通过方法获取,这里可以指明是否为静态方法
boolean isStatic() default false;
}
}
@Builder.Default
用于设定默认值
@Builder
@ToString
public class Account {
int id;
@Builder.Default //默认值必须进行初始化
String name = "小米";
int age;
}
如果只为name字段初始化而不添加@Builder.Default
注解,那么设置的初始值并不会作为Builder采用的默认值,也就是说,这样的初始化是没有意义的。
toBuilder
默认为false,开启后可以为类生成一个toBuilder
方法,该方法可以根据已生成的对象生成对应的Builder对象,并通过Builder中的方法可以执行对成员变量重新赋值等操作
public static void main(String[] args) {
Account account = Account.builder()
.id(1)
.name("小黑")
.age(18)
.build();
Account.AccountBuilder builder = account.toBuilder();
}
@Builder.ObtainVia
配合toBuilder
使用,可以控制Lombok将实体类转换成Builder时,指定的字段值如何获取,如果不使用他,那么会直接获取当前实习类的对应属性值
@Builder(toBuilder = true)
@ToString
public class Account {
int id;
String name;
@Builder.ObtainVia(field = "id") //当从实体类转换回Builder时,age的值从id字段获取
int age;
}
field:表示字段赋值时从某个字段上取值,对应的字段必须存在
method:表示字段赋值时从某个方法上取值,对应的方法必须存在
isStatic:表示 method 是否静态
资源释放和异常处理
@Cleanup
在需要释放资源的变量前添加即可
public static void main(String[] args) throws IOException {
//添加即可自动释放资源
@Cleanup FileInputStream in = new FileInputStream("test.py");
byte[] bytes = in.readAllBytes();
System.out.println(new String(bytes));
}
生成的代码为:
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("test.py");
try {
byte[] bytes = in.readAllBytes(); //自动将后续操作添加到try语句块中
System.out.println(new String(bytes));
} finally { //在finally中对资源进行释放
if (Collections.singletonList(in).get(0) != null) {
in.close();
}
}
}
自定义需要关闭资源的方法
static class Test {
public void end() {
System.out.println("关闭资源");
}
}
public static void main(String[] args) throws IOException {
@Cleanup("end") Test test = new Test();
}
@SneakyThrows
在需要手动编写异常抛出的代码前添加
@SneakyThrows
public static void main(String[] args) {
@Cleanup FileInputStream in = new FileInputStream("test.py");
byte[] bytes = in.readAllBytes();
System.out.println(new String(bytes));
}
编译后生成
public static void main(String[] args) {
try { //自动使用trycatch包裹内部,出现问题直接原样抛出
...
} catch (Throwable var7) {
Throwable $ex = var7;
throw $ex;
}
}
非空判断
Java除了基本类型之外其他类型都有可能存在null
的情况了,在传递函数时需要校验确保参数不是null
再传入,可以在参数前添加@NonNull
public static void test(@NonNull String text){
System.out.println(text);
}
生成的代码为:
public static void test(@NonNull String text) {
if (text == null) {
throw new NullPointerException("text is marked non-null but is null");
} else {
System.out.println(text);
}
}
除了方法的形式参数外,@NonNull
也可以添加到局部变量上 ,但是只会有一个警告效果。
锁处理
@Synchronized
可以自动生成同步代码块
private final Object lock1 = new Object();
@Synchronized("lock1") //直接指定作为锁的变量名称
public void test() { }
自动生成对应的同步代码块
public void test() {
synchronized(this.lock1) {
;
}
}
如果我们不填写锁名称,那么它会按照我们的方法性质添加一把默认的锁:
- 成员方法:统一使用一个名称为
$lock
的锁作为对象锁。 - 静态方法:统一使用一个名称为
$LOCK
的锁作为类锁。
@Locked
它采用ReentrantLock作为锁,可以进一步细分为读写锁,我们可以单独为某个方法添加读锁或是写锁。