Lombok是一个经过注解的形式或简单关键字简化和消除Java应用程序中一些必须可是重复或显得臃肿的样板代码的实用工具,使用Lombok会在编译阶段根据相应的注解生成对应的字节码,使编译前的源码看起来更加简洁,但功能不变。java
缺点是增长了依赖和学习Lombok的成本,还有必定程度上对代码的可读性形成影响。apache
安装:Lombok须要经过插件的形式与IDE集成,若是使用IntelliJ IDEA可直接到插件仓库搜索Lombok进行安装,若是使用Eclipse,首先须要下载lombok.jar(https://www.projectlombok.org/download),而后在CMD下执行命令:java -jar lombok.jar
,等待扫描出本机安装的eclipse后进行安装确认,最后重启eclipse便可。 还有一种安装方式在直接拷贝lombok.jar到eclipse.ini的同级目录下,而后编辑eclipse.ini,在最后一行加上-javaagent:lombok.jar
,最后重启eclipse。缓存
使用:要在项目中使用Lombok,首先要在项目中引入lombok的依赖,从新编译源代码。框架
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency>
注解/关键字 | 可以使用位置 | 说明 |
---|---|---|
val | 局部变量 | 简化局部变量声明的类型 |
@NonNull | 字段、方法、入参、本地变量 | 生成检查NullPointException代码 |
@Cleanup | 可关闭资源的本地变量对象,且销毁方法没有参数 | 简化资源清理回收的代码,消除try-catch-finally代码块 |
@Getter/@Setter | 字段、枚举常量、接口、类、枚举、注解 | 简化getter、setter代码 |
@ToString | 接口、类、枚举、注解 | 自动生成toString方法 |
@EqualsAndHashCode | 接口、类、枚举、注解 | 自动生成equals方法和hashCode方法 |
@NoArgsConstructor | 接口、类、枚举、注解 | 生成无参构造函数 |
@RequiredArgsConstructor | 接口、类、枚举、注解 | 生成全部标识为@NonNull的成员属性的构造函数 |
@AllArgsConstructor | 接口、类、枚举、注解 | 生成包含全部成员属性的构造函数 |
@Data | 接口、类、枚举、注解 | 是@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor的组合效果 |
@Value | 接口、类、枚举、注解 | 相似于@Data,区别在于字段会转换为final类型,且没有setter方法 |
@NonFinal | 字段、方法、方法参数、本地变量、注解、接口、类、枚举 | 用来取消因使用@FieldDefaults和@Value而加上的final修饰符 |
@SneakyThrows | 方法、构造函数 | 粗粒度的try-catch |
@Synchronized | 方法 | 做用等同于synchronized关键字,可自定义锁对象 |
@Log | 接口、类、枚举、注解 | 简化定义日志记录器对象的代码,根据日志框架的不一样选择不一样的Log注解 |
val用来简化局部变量声明的类型,与Java10中的var关键字相似,都是从初始化表达式中推断出变量的声明类型,起到本地类型推断的做用。须要注意的是val修饰的变量都会变成final类型,其引用不可更改。eclipse
val example = new ArrayList<String>(); example.add("hello"); example.add("lombok"); val element = example.get(0);
等价于:ide
final ArrayList<String> example = new ArrayList<String>(); example.add("hello"); example.add("lombok"); final String element = example.get(0);
@NonNull注解经常使用于加在方法和构造函数的入参上,它会帮助咱们生成检查NullPointerException的代码.函数
public NonNullExample(@NonNull Person person) { this.name = person.getName(); }
等价于:工具
public NonNullExample(@NonNull Person person) { if(person == null) { throw new NullPointException("person"); } this.name = person.getName(); }
@Cleanup注解用来简化资源清理回收的代码,确保指定的资源在退出当前代码执行范围前进行自动清理,消除常见的try-catch-finally代码样板,做用等同于try-with-resource,不过须要注意@Cleanup只能指定没有参数的资源销毁方法,若是销毁方法有入参则不能使用@Cleanup注解。学习
public static void tradition() { InputStream in = null; OutputStream out = null; try { in = new FileInputStream("test.txt"); out = new FileOutputStream("output.txt"); byte[] buffer = new byte[1024]; int begin = 0; while (true) { int len = in.read(buffer); if (len == -1) break; out.write(buffer, begin, len); begin += len; } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void tryWithResource() { try (InputStream in = new FileInputStream("test.txt"); OutputStream out = new FileOutputStream("output.txt")) { byte[] buffer = new byte[1024]; int begin = 0; while (true) { int len = in.read(buffer); if (len == -1) break; out.write(buffer, begin, len); begin += len; } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void cleanUp() { try { @Cleanup InputStream in = new FileInputStream("test.txt"); @Cleanup OutputStream out = new FileOutputStream("output.txt"); byte[] buffer = new byte[1024]; int begin = 0; while (true) { int len = in.read(buffer); if (len == -1) break; out.write(buffer, begin, len); begin += len; } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
@Getter和@Setter分别用来简化getter和setter样板代码,默认生成的getter、setter方法修饰符为public,若是须要指定方法的访问范围,能够设置AccessLevel属性,如:ui
@Getter @Setter(AccessLevel.PROTECTED) private String password;
另外,@Getter注解还有一个lazy=true的属性,设置了该属性会使咱们调用getter方法时才真正去计算获取到的值,而且将第一次计算后的结果缓存下来,以后的调用直接返回该缓存值。
@Getter(lazy = true) private final double[] cached = expensive(); private double[] expensive() { long begin = System.currentTimeMillis(); double[] result = new double[5]; for (int i = 0; i < result.length; i++) { result[i] = Math.asin(i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println((System.currentTimeMillis() - begin) / 1000); return result; } public static void main(String[] args) { GetterLazyExample example = new GetterLazyExample(); System.out.println(example.getCached()); System.out.println(example.getCached()); }
等价于:
private final AtomicReference<Object> cached = new AtomicReference<>(); public double[] getCached() { 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() { long begin = System.currentTimeMillis(); double[] result = new double[5]; for (int i = 0; i < result.length; i++) { result[i] = Math.asin(i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println((System.currentTimeMillis() - begin) / 1000); return result; } public static void main(String[] args) { GetterLazyExample_Src example = new GetterLazyExample_Src(); System.out.println(example.getCached()); System.out.println(example.getCached()); }
@ToString 用来自动生成toString方法,默认的toString方法会打印出类名和字段属性和值,若是须要排除指定字段能够用exclude='字段名'的方式进行排除;若是要嵌套调用父类的toString方法,则加上callSuper=true,includeFieldNames=true等属性。
// @ToString // 默认打印类名、每一个字段名=值,用逗号分隔 // @ToString(exclude="password") //exclude属性指定排除哪些字段 @ToString(callSuper = true,includeFieldNames=true) public class ToStringExample extends Parent { @Getter @Setter private String name; @Getter @Setter private String password; @Getter @Setter private int age; public static void main(String[] args) { System.out.println(new ToStringExample()); } } @ToString class Parent { @Getter @Setter private String address; @Getter @Setter private String city; }
@EqualsAndHashCode用来从字段中自动生成equals和hashCode方法,默认状况下使用的是全部非静态字段,也可使用exclude属性排除指定的字段。
@EqualsAndHashCode(exclude= {"name"}) public class EqualsAndHashCodeExample { @Getter @Setter private String name; @Getter @Setter private int age; @Getter @Setter private double weight; public static void main(String[] args) { EqualsAndHashCodeExample example1 = new EqualsAndHashCodeExample(); example1.setName("小明"); example1.setAge(10); EqualsAndHashCodeExample example2 = new EqualsAndHashCodeExample(); example2.setName("小红"); example2.setAge(10); System.out.println(example1.hashCode()); System.out.println(example2.hashCode()); System.out.println(example1.equals(example2)); } }
@NoArgsConstructor用来生成无参构造函数。
@NoArgsConstructor public class NoArgsConstructorExample { @Getter @Setter private String name; }
等价于:
public class NoArgsConstructorExample { private String name; public NoArgsConstructorExample() { //public无参构造器 } //省略getter、setter方法 ...... }
@RequiredArgsConstructor用来生成包含全部修饰为@NonNull的成员属性的构造函数。
@RequiredArgsConstructor public class RequiredArgsConstructorExample { @Getter @Setter @NonNull private String name; @Getter @Setter private String password; @Getter @Setter @NonNull private Character sex; }
等价于:
public class RequiredArgsConstructorExample { private String name; private String password; private Character sex; private RequiredArgsConstructorExample(String name, Character sex) { if(name == null) { throw new NullPointerException("name"); } if(sex == null) { throw new NullPointerException("sex"); } this.name = name; this.sex = sex; } //省略getter、setter方法 ...... }
@AllArgsConstructor用来生成一个包含全部变量的公有构造函数。
@AllArgsConstructor public class AllArgsContructorExample { @Getter @Setter private String name; @Getter @Setter private Integer age; @Getter @Setter private String address; }
等价于:
public class AllArgsContructorExample { private String name; private Integer age; private String address; public AllArgsContructorExample(String name, Integer age, String address) { this.name = name, this.age = age; this.address = address; } //省略getter、setter方法 ...... }
@Data是一个简单粗暴的组合注解,使用@Data注解至关于同时使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor这几个注解
@Data public class DataExample { private String name; private int age; private String password; }
@Value跟@Data相似,区别在于若是变量不加@NonFinal修饰,@Value会将字段变成final类型,同时也没有setter方法。
修饰字段,用来取消因使用@FieldDefaults和@Value而加上的final修饰符。
@Value public class NonFinalExample { private String id; //final private String name; //final @NonFinal private String password; //非final }
@Builder简化了普通的建造者模式API,能够用在类、构造器、方法上,若是字段属于集合类型,加上@Singular,会生成两个向集合中添加单一元素和全部元素的方法,以及一个清除集合的方法。
@Builder public class Example { private int foo; private final String bar; }
等价于
public class Example<T> { private T foo; private final String bar; private Example(T foo, String bar) { this.foo = foo; this.bar = bar; } public static <T> ExampleBuilder<T> builder() { return new ExampleBuilder<T>(); } public static class ExampleBuilder<T> { private T foo; private String bar; private ExampleBuilder() {} public ExampleBuilder foo(T foo) { this.foo = foo; return this; } public ExampleBuilder bar(String bar) { this.bar = bar; return this; } @java.lang.Override public String toString() { return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")"; } public Example build() { return new Example(foo, bar); } } }
@SneakyThrows注解用在方法和构造函数上,它会将方法中的全部代码用try-catch语句包裹起来,当捕获到异常后经过Lombok.sneakyThrow(e)将原始异常抛出,不过须要注意的是调用该方法的Client端并不知道会抛出哪一种异常,即便这是一个CheckException。
public class SneakyThrowsExample { @SneakyThrows(UnsupportedEncodingException.class) public static String utf8ToString(byte[] bytes) { return new String(bytes, "UTF-8"); } public static void main(String[] args) { String str = SneakyThrowsExample.utf8ToString("hello lomboks".getBytes()); System.out.println(str); } }
@Synchronized注解用在方法上,做用等同于synchronized关键字,区别在于锁对象不一样,对于synchronized关键字,修饰类方法时锁对象是class对象,修饰成员方法时锁对象是this对象,而使用@synchronized注解时锁对象分别是私有静态变量LOCK和私有final对象lock,也能够本身指定锁对象。
public class SynchronizedExample { private final Object readLock = new Object(); @Synchronized("readLock") @SneakyThrows public void read() { System.out.println(Thread.currentThread().getName() + " read"); Thread.sleep(3000); } public static void main(String[] args) { SynchronizedExample example = new SynchronizedExample(); new Thread(()->example.read()).start(); new Thread(()->example.read()).start(); } }
Log注解能够省去从日志工厂生成日志记录器对象的代码,可使用topic指定生成log对象时的类名,根据项目中使用的日志框架不一样,有不一样的注解能够选择。
@CommonsLog(topic="LogExample") //等价于 org.apache.commons.logging.LogFactory.getLog(LogExample.class); @Log(topic="LogExample") //等价于 java.util.loggin.Logger.getLogger(LogExample.class); @Log4j(topic="LogExample") //等价于 org.apache.log4j.Logger.getLogger(LogExample.class); @Log4j2(topic="LogExample") //等价于 org.apache.loggin.log4j.LogManager.getLoggerr(LogExample.class); @Slf4j(topic="LogExample") //等价于 org.slf4j.LoggerFactory.getLogger(LogExample.class); @XSLf4j(topic="LogExample") //等价于 org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class); @JBossLog(topic="LogExample") //等价于 org.jboss.logging.Logger.getLogger(LogExample.class);
除了以上经常使用的基本功能外,Lombok还有部分实验性质的特性没有正式推荐使用,有些可能违背了对Java的常规认知或者只支持部分开发环境,因此不推荐使用。
@Accessors是一个为getter和setter设计的更为流畅的API,当设置了chain=true属性,会自动生成符合链式调用的setter方法,返回this引用代替默认的void方法,若是设置flunt=true,则不会有get/set前缀,一样支持链式调用。
@Accessors(chain=true) @Data public class AccessorsExample { private String id; private String name; private String address; public static void main(String[] args) { AccessorsExample example = new AccessorsExample(); example.setId("123").setName("小明").setAddress("深圳"); } }
@Delegate可以将一个对象的某些方法代理给另外一个对象。
public class DelegateExample { private interface DelegateMethod { //该接口的方法必须是collection拥有的方法 boolean add(String item); int size(); } @Delegate(types=DelegateMethod.class) //声明代理的接口,即将collection的某些方法代理给当前对象 private final Collection<String> collection = new ArrayList<>(); public static void main(String[] args) { DelegateExample example = new DelegateExample(); example.add("element"); //实际上添加到了collection中 System.out.println(example.size()); } }
@ExtensionMethod能够Wie已经存在的类增长方法,可是这种方式在代码风格上冲击较大、在运行时没有好的办法让其余类引用,部分开发环境如netbeans下不可用等等,所以不建议使用。
@Data @ExtensionMethod({DateUtils.class}) //扩展的方法来自DateUtils类 public class ExtensionMehtodExample { public void test() { Date date = new Date(); String str = date.formate(); System.out.println(str); } public static void main(String[] args) { new ExtensionMehtodExample().test(); } } class DateUtils { public static final String formate(Date date) { //必须是static方法 DateFormat formator = new SimpleDateFormat("HH:mm:ss"); return formator.format(date); } }
设置默认的字段修饰符,即若是字段不加修饰符则采用FieldDefaults注解设置的修饰符.
@FieldDefaults(level = AccessLevel.PRIVATE) @Data public class FieldDefaultsExample { String id; String name; protected String address; }
修饰在包含局部类的方法上,用来指示将局部类中的全部方法暴露给外围方法,就好像它们是其辅助方法同样。
public class HelperExample { public void doSomething(String name) { @Helper // 修饰局部类,将局部类中的全部方法暴露给外围的方法 class Helpers { void sayHello(String name) { System.out.println("hello " + name); } void sayGoodBye(String name) { System.out.println("Goodbye " + name); } } sayHello(name); // 直接使用局部类中的方法,等同于new Helpers().sayHello(name); sayGoodBye(name); } public static void main(String[] args) { new HelperExample().doSomething("小明"); } }
修饰字段,将当前字段设置为访问权限设置为package-private,通常用来取消因使用@Value和@FieldDefaults而加上的修饰符。
@FieldDefaults(level = AccessLevel.PRIVATE) @Data public class PackagePrivateExample { String id; // public String name; // public @PackagePrivate String address; // package-private }
该注解用来解决某些状况下使用Lombok注解生成的构造器或方法与开发者本身写构造器或方法由于冲突而被跳过的状况,将@Tolerate修饰在构造器/方法上,会被lombok视为该构造器/方法不存在,典型的如当@Data和@Builder同时使用时Lombok生成构造器只有一个包含全部成员属性的构造函数,若是再自定义一个无参构造函数将会冲突,此时可使用@Tolerate解决。
@Data @Builder public class TolerateExample { private String name; private String age; @Tolerate public TolerateExample() { } }
建立工具类的注释,当在类上加上该注解,该类会被修饰为final类型,若是该类声明了构造函数编译器将会提示错误,不然会自动生成一个私有的构造函数,内部抛出一个UnsupportedOperationException异常。而且全部的方法、内部类和属性都会被修饰为static。
@UtilityClass public class UtilityClassExample { private DateFormat df = new SimpleDateFormat("YYYY-MM-DD"); public String formateToDay(Date date) { return df.format(date); } }
等价于:
public class UtilityClassExample { private static DateFormat df = new SimpleDateFormat("YYYY-MM-DD"); public static String formateToDay(Date date) { return df.format(date); } private UtilityClassExample() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } }
使用@Wither修饰字段X,会自动生成一个withX方法,该方法会生成该对象的一个克隆,该注解也可使用在类上,此时至关于全部非静态字段都加上了@Wither注解。
public class WitherExample { @Wither private final int foo; private final String name; public WitherExample(int foo, String name) { this.foo = foo; this.name = name; } }
等价于:
public class WitherExample { private final int foo; private final String name; public WitherExample(int foo, String name) { this.foo = foo; this.name = name; } public WitherExample withFoo(int foo) { //传入的foo相同返回当前对象,不然返回除foo字段不一样的一个克隆对象 return this.foo == foo ? this ? new WitherExample(foo, this.name); } }
与val关键字相似,一样起到本地类型推断的做用,区别在于var修饰的变量不会转变为final类型,而val修饰的变量都会变成final类型。