前言
在Java中,封装是一个非常好的机制,最常见的封装莫过于get,set方法了,无论是Intellij idea 还是Eclipse,都提供了快速生成get,set方法的快捷键,使用起来很是方便,其实,我们还有更方便的办法,那就是-Lombok:非常强大的POJO注解器。
Lombok是什么?
lombok 提供了简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 java 代码。特别是相对于 POJO。
如何安装Lombok?
使用 lombok 是需要安装的,如果不安装,IDE 则无法解析 lombok 注解。先在官网下载最新版本的 JAR 包。
- 双击下载下来的 JAR 包安装 lombok;
我选择这种方式安装的时候提示没有发现任何 IDE,所以我没安装成功,我是手动安装的。 - eclipse / myeclipse 手动安装 lombok(Mac 下eclipse安装Lombok插件)
- 将 lombok.jar 复制到 myeclipse.ini / eclipse.ini 所在的文件夹目录下
- 打开 eclipse.ini / myeclipse.ini,在最后面插入以下两行并保存:
-Xbootclasspath/a:lombok.jar
-javaagent:lombok.jar - 重启 eclipse / myeclipse
Lombok使用详解
-
添加POM依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency> Lombok提供注解方式来提高代码的简洁性,常用注解概览:
- @Data:注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法,相当于同时加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
- @Setter、@Getter:注解在类和属性上;为属性提供 setting、getting 方法
- @ToString:生成toString方法,默认情况下,会输出类名、所有属性,属性按照顺序输出,以逗号分割。
- @EqualsAndHashCode:实现equals()方法和hashCode()方法
- @Builder:构建 建造者模式
- @NonNull:该注解快速判断是否为空,如果为空,则抛出java.lang.NullPointerException
- @Synchronized:该注解自动添加到同步机制,有趣的是,生成的代码并不是直接锁方法,而是锁代码块, 作用范围是方法上
- @Log4j :注解在类上;为类提供一个 属性名为log 的 log4j 日志对象
- @NoArgsConstructor:注解在类上;为类提供一个无参的构造方法
- @RequiredArgsConstructor:注解在类上;为类提供一个部分参的构造方法(使用类中所有带有@NonNull注解的或者带有final修饰的成员变量生成对应的构造方法)
- @AllArgsConstructor:注解在类上;为类提供一个全参的构造方法
- @Cleanup:用于确保已分配的资源被释放,如IO的连接关闭
- @SneakyThrows:抛异常
- @Accessors(chain = true):使用链式结构
@Data
注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法,相当于同时加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
@Data
public class Person {
private String name;
private String address;
private String city;
private String state;
private String zip;
private Date brithday;
}
效果如下:
@Getter@Setter
注解在类和属性上;为属性提供 setting、getting 方法
public class Person {
@Getter@Setter
private String name;
}
等价源码:
public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
@ToString
生成toString方法,默认情况下,会输出类名、所有属性,属性按照顺序输出,以逗号分割。但需要注意的是:@ToString有多个属性可以进一步设置:
callSuper 是否输出父类的toString方法,默认为false
includeFieldNames 是否包含字段名称,默认为true
-
exclude 排除生成tostring的字段
使用方法:
@ToString(callSuper = true,exclude ={"name"})
public class Person {
private String name;
private String address;
}等价源码:
public String toString() {
return "Person{" +
"address='" + address + '\'' +
'}';
}
@NonNull
该注解快速判断是否为空,如果为空,则抛出java.lang.NullPointerException
使用方法
public class Person { private String name; @Setter@Getter@NonNull
private List<Person> member;
}
等价源码:
@NonNull
private List<Person> members; public Family(@NonNull final List<Person> members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
} @NonNull
public List<Person> getMembers() {
return members;
} public void setMembers(@NonNull final List<Person> members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
}
@Synchronized
该注解自动添加到同步机制,有趣的是,生成的代码并不是直接锁方法,而是锁代码块, 作用范围是方法上。
使用方法:
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY"); @Synchronized
public String synchronizedFormat(Date date) {
return format.format(date);
}
等价源码:
private final java.lang.Object $lock = new java.lang.Object[0];
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY"); public String synchronizedFormat(Date date) {
synchronized ($lock) {
return format.format(date);
}
}
@Cleanup
注释可用于确保已分配的资源被释放,如IO的连接关闭。
使用方法:
public void testCleanUp() {
try {
@Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(new byte[] {'Y','e','s'});
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
等价源码:
public void testCleanUp() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
baos.write(new byte[]{'Y', 'e', 's'});
System.out.println(baos.toString());
} finally {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Accessors(chain = true)
使用链式结构
- 使用方法:
@Accessors(chain=true)
public class Student {
private String name;
private int age; public String getName() {
return name;
} public Student setName(String name) {
this.name = name;
return this;
} public int getAge() {
return age;
} public Student setAge(int age) {
return this;
}
} - 等价源码:
@Accessors(chain = true)
@Data
@NoArgsConstructor(staticName = "of")
public class Student {
private String name;
private int age;
} - 调用
Student student = Student.of().setName("wsk").setAge(12);
@Builder
使用builder,构建 建造者模式
- 例一:
- 使用@Builder
@Builder
public class Student {
private String name;
private int age;
} - 调用示例:
Student student = Student.builder().name("zs").age(24).build();
- 等价源码:
public class Student {
private String name;
private int age; 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;
} public static Builder builder(){
return new Builder();
}
public static class Builder{
private String name;
private int age;
public Builder name(String name){
this.name = name;
return this;
} public Builder age(int age){
this.age = age;
return this;
} public Student build(){
Student student = new Student();
student.setAge(age);
student.setName(name);
return student;
}
}
}
- 使用@Builder
- 例二:利用builder模式设计的Java类
- 如果能将创建JavaBean和设置内容揉在一起,在传入builder中的参数不合乎业务或者非法,那么就不能创建student对象,这时候可以通过捕获IllegalArgumentException,从而得知失败的原因;
- 引入Builder设计模式以后,代码保持JavaBean好的可读性,但同时增强了安全性,将Student类的创建和设置内容揉在了一起,并增加了安全性检查,提高了系统的健壮性,同时防止了编码中的一些疏忽。
- Java示例:
public class Student {
private String id;
private String name;
private String sex;
private int age;
private String department; public static class Builder {
/*
* 只能指定一次。
*/
private final String id;
private final String department; private String name = "";
private String sex = "男";
private int age = 20; /*
* 非空属性,必须在构造器中指定。
*/
public Builder(String id, String department) {
this.id = id;
this.department = department;
} /*
* name,sex,age可选择属性,提供特殊的setter方法。
*/
public Builder name(String name) {
this.name = name;
return this;
} public Builder sex(String sex) {
this.sex = sex;
return this;
} public Builder age(int age) {
this.age = age;
return this;
} /*
* Student对象创建器,想得到一个Student对象必须使用build 方法,
* 在方法中增加对Builder参数的验证,并以异常的形式告诉给开发人员。
*/
public Student build() {
/* 检查Builder对象中的数据是否合法。
* 针对这个例子,就是检查主键冲突,外键制约等
* 如果不满足我们可以抛出一个IllegalArgumentException
*/
return new Student(this); } } private Student(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.sex = builder.sex;
this.age = builder.age;
this.department = builder.department;
} /*
* 只提供getter方法
*/
public String getId() {
return id;
} public String getName() {
return name;
} public String getSex() {
return sex;
} public int getAge() {
return age;
} public String getDepartment() {
return department;
} } - 创建对象一:
student = new Student.Builder("03041013", "计算机").name("李华").build();
- 创建对象二:
Student.Builder builder = new Student.Builder("03041013", "计算机");
builder.name("李华");
Student student = builder.build();
小结:
- 很明显,使用 lombok 要简洁许多,特别是在类的属性较多的情况下;
- 同时也避免了修改字段名字时候忘记修改方法名所犯的低级错误;