一、Lombok背景介绍
官方介绍如下:
Project Lombok makes java a spicier language by adding 'handlers' that know how to build and compile simple, boilerplate-free, not-quite-java code.
大致意思是Lombok通过增加一些“处理程序”,可以让java变得简洁、快速。
二、Lombok安装
Lombok的使用非常简单:
2.1、引入相应的maven包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
2.2、添加IDE工具对Lombok的支持
IDEA中引入Lombok支持如下:
点击File-- Settings设置界面,安装Lombok插件:
点击File-- Settings设置界面,开启 AnnocationProcessors: 开启该项是为了让Lombok注解在编译阶段起到作用。
Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率。例如开发中经常需要写的javabean,都需要花时间去添加相应的getter/setter,也许还要去写构造器、equals等方法,而且需要维护,当属性多时会出现大量的getter/setter方法,这些显得很冗长也没有太多技术含量,一旦修改属性,就容易出现忘记修改对应方法的失误。
Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。出现的神奇就是在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁些。
Lombok的使用跟引用jar包一样,可以在官网(https://projectlombok.org/download)下载jar包,也可以使用maven添加依赖:
三、Lombok使用详解
Lombok 提供注解方式来提高代码的简洁性,常用注解概览:
- val:用在局部变量前面,相当于将变量声明为final
- @Data:注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法,相当于同时加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
- @Value: 用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
- @Setter、@Getter:注解在类和属性上;为属性提供 setting、getting 方法
- @Getter(lazy=true) : 可以替代经典的Double Check Lock样板代码
- @ToString:生成toString方法,默认情况下,会输出类名、所有属性,属性按照顺序输出,以逗号分割。
- @EqualsAndHashCode:实现equals()方法和hashCode()方法
- @Builder:构建 建造者模式
- @NonNull:该注解快速判断是否为空,如果为空,则抛出java.lang.NullPointerException
- @Synchronized:该注解自动添加到同步机制,有趣的是,生成的代码并不是直接锁方法,而是锁代码块, 作用范围是方法上
- @Log : 根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类
- @NoArgsConstructor:注解在类上; 会生成一个返回类对象的静态工厂方法。
- @RequiredArgsConstructor:注解在类上;为类提供一个部分参的构造方法(使用类中所有带有@NonNull注解的或者带有final修饰的成员变量生成对应的构造方法)
- @AllArgsConstructor:注解在类上;为类提供一个全参的构造方法
- @Cleanup:用于确保已分配的资源被释放,如IO的连接关闭
- @SneakyThrows:抛异常
- @Accessors: 用于配置getter和setter方法的生成结果
3.1、val
用在局部变量前面,相当于将变量声明为final
public static void main(String[] args) {
/**
* str1 和 str2的两种定义方法都可以
*/
val str1="abc";
final String str2="abc";
}
3.2、@Data
注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法,相当于同时加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
@Data
public class Book {
private String isbn;
private String book_name;
private String price;
}
效果同@Value
3.3、@Value
@Value
@AllArgsConstructor
public class Book {
private String isbn;
}
相当于
public class Book {
private final String isbn;
public String getIsbn() {
return isbn;
}
}
调用、
Book book = new Book("1");
3.4、@Getter@Setter
注解在类和属性上;为属性提供 setting、getting 方法
public class Book {
@Setter@Getter
private String isbn;
}
3.5、@Getter(lazy=true)
如果 Bean 的一个字段的初始化是代价比较高的操作,比如加载大量的数据;同时这个字段并不是必定使用的。那么使用懒加载机制,可以保证节省资源。
懒加载机制,是对象初始化时,该字段并不会真正的初始化,而是第一次访问该字段时才进行初始化字段的操作。
public class GetterLazyExample {
@Getter(lazy = true)
private final double[] cached = expensive();
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
// 相当于如下所示:
import java.util.concurrent.atomic.AtomicReference;
public class GetterLazyExample {
private final AtomicReference<java.lang.Object> cached = new AtomicReference<>();
public double[] getCached() {
java.lang.Object value = this.cached.get();
if (value == null) {
synchronized (this.cached) {
value = this.cached.get();
if (value == null) {
final double[] actualValue = expensive();
value = actualValue == null ? this.cached : actualValue;
this.cached.set(value);
}
}
}
return (double[]) (value == this.cached ? null : value);
}
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
3.6、@ToString
生成 toString 方法,默认情况下,会输出类名、所有属性,属性按照顺序输出,以逗号分割。但需要注意的是:@ToString
有多个属性可以进一步设置:
- callSuper 是否输出父类的toString方法,默认为false
- includeFieldNames 是否包含字段名称,默认为true
- exclude 排除生成tostring的字段
@ToString(callSuper = true,exclude ={"name"})
public class Person {
private String name;
private String address;
}
3.7、@EqualsAndHashCode
用在类上,自动生成 equals 方法和 hashCode 方法
参数exclude排除一些属性 ; 参数of指定仅使用哪些属性 ; 默认仅使用该类中定义的属性且不调用父类的方法 (即 callSuper=false)。
//父类
public class Person {
private String name;
private String sex;
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
}
@EqualsAndHashCode(exclude = {"className"},callSuper = false)
public class Student extends Person{
@Getter@Setter
private int age;
@Getter@Setter
private String className;
public Student(String name,String sex,int age,String className) {
super(name,sex);
this.age = age;
this.className = className;
}
}
Student s1 = new Student("hresh","man",22,"Lv3");
Student s2 = new Student("hresh","woman",22,"Lv5");
System.out.println(s1.equals(s2));//true
解析: 子类实现@EqualsAndHashCode(callSuper = false) ,不调用父类的属性,那么子类属性里面的相同的话,那 hashcode 的值就相同,再加上排除对 className 属性的比对,所以代码里面的2个对象的 equals 方法的返回值是 true 。
3.8、@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;
}
3.8、@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);
}
}
3.9、@NoArgsConstructor
注解在类上;为类提供一个无参的构造方法
@Data
@NoArgsConstructor(staticName = "init")
public class Student {
private long id = new Long(0);
private String name = " ";
private String className = " ";
}
等价于:
@Data
//@NoArgsConstructor(staticName = "init")
public class Student {
private long id = new Long(0);
private String name = " ";
private String className = " ";
public static Student init(){
return new Student();
}
}
3.10、@AllArgsConstructor
注解在类上,为类提供一个全参的构造方法
@Data
@AllArgsConstructor
public class Student {
private long id = new Long(0);
private String name = " ";
private String className = " ";
}
相当于:
@Data
public class Student {
private long id = new Long(0);
private String name = " ";
private String className = " ";
public Student(long id, String name, String className) {
this.id = id;
this.name = name;
this.className = className;
}
}
3.11、@RequiredArgsConstructor
注解在类上;为类提供一个部分参的构造方法(使用类中所有带有@NonNull注解的或者带有final修饰的成员变量生成对应的构造方法)
@Data
@RequiredArgsConstructor
public class Student {
@NonNull
private long id ;
@NonNull
private String name ;
private String className;
}
实际调用:
Student student = new Student(101,"hresh");
System.out.println(student);
3.12、@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();
}
}
3.13、@Log
@CommonsLog
Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
已slf4j 日志对象为例。
注解在类上;为类提供一个 属性名为log 的 slf4j 日志对象
@Slf4j
public class StudentTest {
public static void main(String[] args) {
log.info("Here is some INFO");
}
}
等价于:
public class StudentTest {
public static Logger log = LoggerFactory.getLogger(StudentTest.class);
public static void main(String[] args) {
log.info("Here is some INFO");
}
}
2.14、@Accessors
Accessor 的中文含义是存取器,@Accessors
用于配置 getter 和 setter 方法的生成结果,下面介绍三个属性
1、 fluent
fluent 的中文含义是流畅的,设置为true 则 生成的get/set方法则没有 set/get 前缀,默认为 false 。
@Data
@Accessors(fluent = true)
public class Student {
private long id = new Long(0);
private String name = " ";
private String className = " ";
}
实际调用
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
student.name("hresh");//相当于SetName()
student.className("Lv5");
System.out.println(student);
System.out.println(student.name());//相当于getName()
}
}
2、 chain
chain 的中文含义是链式的,为一个布尔值, 如果为 true 生成的 set 方法返回 this,为 false 生成的 set 方法是 void 类型。 默认为 false,除非当 fluent 为 true时,chain 默认则为 true 。
@Setter
@Getter
@Accessors(chain = true)
public class User {
private String id;
private String name;
private Integer age;
}
public static void main(String[] args) {
//使用@Accessors(chain = true)
User userChain = new User();
userChain.setId("1").setName("chain").setAge(1);
}
3、 prefix
prefix 为一系列 string 类型,可以指定前缀,生成 get/set 方法时会去掉指定的前缀 。
@Data
@Accessors(prefix = "n")
public class Student {
// private long id = new Long(0);
private String nAme = " ";
private String name = " ";
private String className = " ";
private int nId = new Integer(0);
}
实际调用:
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
student.setId(101);
System.out.println(student.getId());
student.setAme("hresh");
student.setName("hresh");
System.out.println(student);
}
}
需要注意的是,此时类中的属性命名存在这样的规则,指定前缀+大写字符,像上述代码中如果属性为 name 而非 nAme,则不成立。
2.15、@SneakyThrows
自动抛受检异常,而无需显式在方法上使用 throws 语句
@SneakyThrows
public void read(){
InputStream inputStream = new FileInputStream("");
}
//相当于
public void read() throws FileNotFoundException {
InputStream inputStream = new FileInputStream("");
}
2.16、@Builder
用在类、构造器、方法上,为你提供复杂的 builder APIs
@Builder
@Data
public class Student {
private String name ;
private int age;
}
实际调用:
public class StudentTest {
public static void main(String[] args) {
Student student = Student.builder().name("hresh").age(22).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;
}
}
}
2.17、@UtilityClass:工具类注解
@UtilityClass
public class Utility {
public String getName() {
return "name";
}
}
public static void main(String[] args) {
// Utility utility = new Utility(); 构造函数为私有的,
System.out.println(Utility.getName());
}