@ToString/@EqualsAndHashCode
这两个注解也比较好理解,就是生成toString,equals和hashcode方法,同时后者还会生成一个canEqual方法,用于判断某个对象是否是当前类的实例。,生成方法时只会使用类中的非静态成员变量,这些都比较好理解。毕竟静态的东西并不属于对象本身
@ToString public class Demo { private final int finalVal = 10; private transient String name = "aa"; private int age; } public static void main(String[] args) throws Exception { Demo demo = new Demo(); System.out.println(demo); //Demo(finalVal=10, age=0) }
我们发现静态字段它是不输出的。
有些关键的属性,可以控制toString的输出,我们可以了解一下:
@ToString( includeFieldNames = true, //是否使用字段名 exclude = {"name"}, //排除某些字段 of = {"age"}, //只使用某些字段 callSuper = true //是否让父类字段也参与 默认false )
备注:大多数情况下,使用默认的即可,毕竟大多数情况都是POJO
@Generated:暂时貌似没什么用
@Getter/@Setter
这一对注解从名字上就很好理解,用在成员变量上面或者类上面,相当于为成员变量生成对应的get和set方法,同时还可以为生成的方法指定访问修饰符,当然,默认为public
这两个注解直接用在类上,可以为此类里的所有非静态成员变量生成对应的get和set方法。如果是final变量,那就只会有get方法
@Getter @Setter public class Demo { private final int finalVal = 10; private String name; private int age; }
编译后:
public class Demo { private final int finalVal = 10; private String name; private int age; public Demo() { } public int getFinalVal() { Objects.requireNonNull(this); return 10; } public String getName() { return this.name; } public int getAge() { return this.age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } }
@NonNull
这个注解可以用在成员方法或者构造方法的参数前面,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常。
//成员方法参数加上@NonNull注解 public String getName(@NonNull Person p){ return p.getName(); }
编译后:
public String getName(@NonNull Person p){ if(p==null){ throw new NullPointerException("person"); } return p.getName(); }
@Singular 默认值 暂时也没太大用处
@SneakyThrows
这个注解用在方法上,可以将方法中的代码用try-catch语句包裹起来,捕获异常并在catch中用Lombok.sneakyThrow(e)把异常抛出,可以使用@SneakyThrows(Exception.class)的形式指定抛出哪种异常
@SneakyThrows(UnsupportedEncodingException.class) public String utf8ToString(byte[] bytes) { return new String(bytes, "UTF-8"); }
编译后:
@SneakyThrows(UnsupportedEncodingException.class) public String utf8ToString(byte[] bytes) { try{ return new String(bytes, "UTF-8"); }catch(UnsupportedEncodingException uee){ throw Lombok.sneakyThrow(uee); } }
这里有必要贴出来Lombok.sneakyThrow的代码:
public static RuntimeException sneakyThrow(Throwable t) { if (t == null) { throw new NullPointerException("t"); } else { return (RuntimeException)sneakyThrow0(t); } } private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T { throw t; }
其实就是转化为了RuntimeException,其实我想说,这个注解也没大用。毕竟我们碰到异常,是希望自己处理的
@Synchronized
这个注解用在类方法或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,而@Synchronized得锁对象分别是私有静态final对象LOCK和私有final对象lock,当然,也可以自己指定锁对象
@Synchronized public static void hello() { System.out.println("world"); } @Synchronized public int answerToLife() { return 42; } @Synchronized("readLock") public void foo() { System.out.println("bar"); }
编译后:
public static void hello() { Object var0 = $LOCK; synchronized($LOCK) { System.out.println("world"); } } public int answerToLife() { Object var1 = this.$lock; synchronized(this.$lock) { return 42; } } public void foo() { Object var1 = this.readLock; synchronized(this.readLock) { System.out.println("bar"); } }
我只能说,这个注解也挺鸡肋的。
@Val 很强的类型推断 var注解,在Java10之后就不能使用了
class Parent { //private static final val set = new HashSet<String>(); //编译不通过 public static void main(String[] args) { val set = new HashSet<String>(); set.add("aa"); System.out.println(set); //[aa] } }
编译后:
class Parent { Parent() { } public static void main(String[] args) { HashSet<String> set = new HashSet(); set.add("aa"); System.out.println(set); } }
这个和Java10里的Var很像,强大的类型推断。并且不能使用在全局变量上,只能使用在局部变量的定义中。
@Log、CommonsLog、Slf4j、XSlf4j、Log4j、Log4j2等日志注解
这个注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名。不同的日志注解总结如下(上面是注解,下面是实际作用):
@CommonsLog private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class); @JBossLog private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class); @Log private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName()); @Log4j private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class); @Log4j2 private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class); @Slf4j private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class); @XSlf4j private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
这个注解还是非常有用的,特别是Slf4j这个,在平时开发中挺有用的
@Slf4j class Parent { }
编译后:
class Parent { private static final Logger log = LoggerFactory.getLogger(Parent.class); Parent() { } }
也可topic的名称:
@Slf4j @CommonsLog(topic = "commonLog") class Parent { }
lombok中有experimental的包:
实验性因为:
1.我们可能想将这些特性和更完全的性质支持概念融为一体(普通话:这些性能还在研究)
2.新特性-需要社区反馈
@Accessors 一个为getter和setter设计的更流畅的注解
这个注解要搭配@Getter与@Setter使用,用来修改默认的setter与getter方法的形式。所以单独使用是没有意义的
@Accessors(fluent = true) @Getter @Setter public class Demo extends Parent { private final int finalVal = 10; private String name; private int age; }
编译后
public class Demo extends Parent { private final int finalVal = 10; private String name; private int age; public Demo() { } public int finalVal() { Objects.requireNonNull(this); return 10; } public String name() { return this.name; } public int age() { return this.age; } public Demo name(String name) { this.name = name; return this; } public Demo age(int age) { this.age = age; return this; } }
它的三个参数解释:
1.chain 链式的形式 这个特别好用,方法连缀越来越方便了
2.fluent 流式的形式(若无显示指定chain的值,也会把chain设置为true)
3.prefix 生成指定前缀的属性的getter与setter方法,并且生成的getter与setter方法时会去除前缀
@Accessors(prefix = "xxx") @Getter @Setter public class Demo extends Parent { private final int finalVal = 10; private String xxxName; private int age; }
编译后:
public class Demo extends Parent { private final int finalVal = 10; private String xxxName; private int age; public Demo() { } public String getName() { return this.xxxName; } public void setName(String xxxName) { this.xxxName = xxxName; } }
我们发现prefix可以在生成get/set的时候,去掉xxx等prefix前缀,达到很好的一致性。但是,但是需要注意,因为此处age没有匹配上xxx前缀,所有根本就不给生成,所以使用的时候一定要注意。
属性名没有一个以其中的一个前缀开头,则属性会被lombok完全忽略掉,并且会产生一个警告。