@SneakyThrows
让方法抛出指定的异常,该异常将会直接终止掉方法的运行,我们不能捕获,异常时直接结束,除非我们手动在方法上抛出异常。
Java代码(可以看到我们在main方法中调用print2的时候并没有提示我们捕获异常):
public class SneakyThrowsExample {
@SneakyThrows({NullPointerException.class})
public void print(Object s) throws Exception {
System.out.println(s.toString());
}
@SneakyThrows({NullPointerException.class})
public void print2(Object s) {
System.out.println(s.toString());
}
public static void main(String[] args) {
try {
new SneakyThrowsExample().print(null);
} catch (Exception e) {
System.out.println("error");
e.printStackTrace();
}
new SneakyThrowsExample().print2(null);
}
}
编译后(方法体是全部被try/catch块包裹了,并且抛出了异常,但是我们并没有去捕获):
public class SneakyThrowsExample {
public SneakyThrowsExample() {
}
public void print(Object s) throws Exception {
try {
System.out.println(s.toString());
} catch (NullPointerException var3) {
throw var3;
}
}
public void print2(Object s) {
try {
System.out.println(s.toString());
} catch (NullPointerException var3) {
throw var3;
}
}
public static void main(String[] args) {
try {
(new SneakyThrowsExample()).print((Object)null);
} catch (Exception var2) {
System.out.println("error");
var2.printStackTrace();
}
(new SneakyThrowsExample()).print2((Object)null);
}
}
@Synchronized
官方文档解释:
@synchronized是同步方法修改器的一个更安全的变体。与同步一样,注释只能在静态和实例方法上使用。它的操作类似于synchronized关键字,但是它锁定不同的对象。这个关键字锁定了this,但是注释锁定了一个名为$lock的字段,这个字段是私有的。
如果该字段不存在,它将为您创建。如果您注释一个静态方法,那么注释将锁定一个名为$LOCK的静态字段。
如果您愿意,您可以自己创建这些锁。$lock和$LOCK字段当然不会生成,如果您已经自己创建了它们。您还可以选择锁定另一个字段,将其指定为@synchronized注释的参数。在这个使用变体中,字段不会自动创建,您必须自己显式地创建它们,否则将会发出一个错误。
锁定this或您自己的类对象可能会有一些不幸的副作用,因为不在您的控制之下的其他代码也可以锁定这些对象,这可能导致竞争条件和其他讨厌的与线程相关的错误。
总结:
非静态方法,锁定字段$lock;
静态方法,锁定字段$LOCK;
自定义字段xxx时,需要显示申明xxx字段。
Java代码:
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public static void staticMethod() {
System.out.println("world");
}
@Synchronized
public int unStaticMethod() {
return 42;
}
@Synchronized("readLock")
public void customMethod() {
System.out.println("bar");
}
}
编译后:
public class SynchronizedExample {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public SynchronizedExample() {
}
public static void staticMethod() {
Object var0 = $LOCK;
synchronized($LOCK) {
System.out.println("world");
}
}
public int unStaticMethod() {
Object var1 = this.$lock;
synchronized(this.$lock) {
return 42;
}
}
public void customMethod() {
Object var1 = this.readLock;
synchronized(this.readLock) {
System.out.println("bar");
}
}
}
配置文件:
lombok.synchronized.flagUsage = [warning | error] (default: not set)
官方文档解释:
如果$lock和/或$LOCK是自动生成的,那么字段将用一个空的Object[]初始化,而不仅仅是一个new Object(),因为大多数代码片段都显示了这个模式。Lombok是这样做的,因为一个新的对象不是可序列化的,但是空的数组是可以的。因此,使用@synchronized不会阻止您的对象被序列化。
在类中至少有一个@synchronized方法意味着将有一个锁字段,但是如果您稍后删除所有这些方法,那么将不再有一个锁字段。这意味着您需要预先确定serialVersionUID的变化。如果您打算通过java的序列化机制长期存储它们,我们建议您总是向您的类添加一个serialVersionUID。如果您这样做,从您的方法中删除所有的@synchronized注释将不会破坏串行化。
如果您想知道为什么当您为锁对象选择自己定义的名字时,一个字段不会自动生成:因为如果这样做,在字段名中输入错误将导致很难找到bug!
@Getter(lazy=true)
官方文档解释:
您可以让lombok生成一个getter,它将在第一次调用这个getter的时候计算一个值一次,然后从那时开始缓存它。如果计算值需要大量CPU,或者值占用大量内存,那么这一点很有用。要使用这个特性,可以创建一个私有的final变量,用要运行的表达式来初始化它,并使用@getter(惰性=true)对您的字段进行注释。该字段将隐藏在您的代码的其余部分,当getter被首次调用时,表达式将会被调用不超过一次。没有任何神奇的标记值(例如,即使您的昂贵计算的结果是null,结果也会被缓存),并且您的昂贵计算不必是线程安全的,因为lombok会处理锁定。
解释:在调用该缓存起来的方法时,该方法只会被调用一次(可以看编译后的代码,会执行判断,如果缓存不为null就会直接返回)。
Java代码(由于集合使用Iterator遍历的时候不能对集合进行修改,所以expensive方法中没有使用foreach循环,使用foreach循环在编译后的代码就是用的Iterator进行遍历的):
public class GetterLazyExample {
@Getter(lazy = true)
private final double cached = expensive();
@Setter
@Getter
private List<Double> cacheValue;
private double expensive() {
double result = 0;
for (int i = 0; i < cacheValue.size(); i++) {
result += cacheValue.get(i);
}
return result;
}
public static void main(String[] args) {
GetterLazyExample example = new GetterLazyExample();
List<Double> ds = new ArrayList<>();
ds.add(1d);
ds.add(2d);
ds.add(3d);
example.setCacheValue(ds);
System.out.println(example.getCached());
example.getCacheValue().add(4d);
System.out.println(example.getCached());
}
}
编译后的代码(可以看到类在实例化之后,第一次调用getCatched方法的时候执行了一次计算,此后cached的值不为null之后,就不会在执行计算代码了,即官方文档解释的计算只会执行不超过一次):
public class GetterLazyExample {
private final AtomicReference<Object> cached = new AtomicReference();
private List<Double> cacheValue;
public GetterLazyExample() {
}
private double expensive() {
double result = 0.0D;
for(int i = 0; i < this.cacheValue.size(); ++i) {
result += (Double)this.cacheValue.get(i);
}
return result;
}
public static void main(String[] args) {
GetterLazyExample example = new GetterLazyExample();
List<Double> ds = new ArrayList();
ds.add(1.0D);
ds.add(2.0D);
ds.add(3.0D);
example.setCacheValue(ds);
System.out.println(example.getCached());
example.getCacheValue().add(4.0D);
example.setCacheValue(ds);
System.out.println(example.getCached());
System.out.println(example.getCacheValue().get(3));
}
public double getCached() {
Object value = this.cached.get();
if (value == null) {
AtomicReference var2 = this.cached;
synchronized(this.cached) {
value = this.cached.get();
if (value == null) {
double actualValue = this.expensive();
value = actualValue;
this.cached.set(value);
}
}
}
return (Double)value;
}
public void setCacheValue(List<Double> cacheValue) {
this.cacheValue = cacheValue;
}
public List<Double> getCacheValue() {
return this.cacheValue;
}
}
@Log (and friends)
使用该注解,会在类中生成名称为log的final的引用,相当于我们自己定义了一个引用。
使用的时候必须要用相应的jar包。
官方文档解释:
您可以让lombok生成一个getter,它将在第一次调用这个getter的时候计算一个值一次,然后从那时开始缓存它。如果计算值需要大量CPU,或者值占用大量内存,那么这一点很有用。要使用这个特性,可以创建一个私有的final变量,用要运行的表达式来初始化它,并使用@getter(惰性=true)对您的字段进行注释。该字段将隐藏在您的代码的其余部分,当getter被首次调用时,表达式将会被调用不超过一次。没有任何神奇的标记值(例如,即使您的昂贵计算的结果是null,结果也会被缓存),并且您的昂贵计算不必是线程安全的,因为lombok会处理锁定。
您将@Log的变体放在您的类上(任何一个应用于您所使用的日志系统);然后,您有一个静态的final log字段,初始化到您的类名,然后您可以使用它来编写日志语句。
有几种选择:(看字很不清楚,下面有截图)
@CommonsLog
Creates private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
Creates private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@Log
Creates private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
Creates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
Creates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
Creates private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
在默认情况下,日志记录器的topic将是带有@log注释的类的类名。这可以通过指定主题参数来定制。例如:@XSlf4j(topic=“customLog”)。
Java代码:
@Log
public class LogExample {
public static void main(String... args) {
log.info("Something's wrong here");
}
}
@Slf4j
class LogExampleOther {
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
@CommonsLog(topic = "CounterLog")
class LogExampleCategory {
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
编译后:
public class LogExample {
private static final Logger log = Logger.getLogger(LogExample.class.getName());
public LogExample() {
}
public static void main(String... args) {
log.info("Something's wrong here");
}
}
class LogExampleOther {
private static final Logger log = LoggerFactory.getLogger(LogExampleOther.class);
LogExampleOther() {
}
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
class LogExampleCategory {
private static final Log log = LogFactory.getLog("CounterLog");
LogExampleCategory() {
}
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
配置文件:
lombok.log.fieldName = an identifier (default: log)
配置生成的日志记录器引用的名称。
lombok.log.fieldIsStatic = [true | false] (default: true)
日志应用是否是静态的。
lombok.log.flagUsage = [warning | error] (default: not set)
lombok.log.apacheCommons.flagUsage = [warning | error] (default: not set)
lombok.log.javaUtilLogging.flagUsage = [warning | error] (default: not set)
lombok.log.jbosslog.flagUsage = [warning | error] (default: not set)
lombok.log.log4j.flagUsage = [warning | error] (default: not set)
lombok.log.log4j2.flagUsage = [warning | error] (default: not set)
lombok.log.slf4j.flagUsage = [warning | error] (default: not set)
lombok.log.xslf4j.flagUsage = [warning | error] (default: not set)
官方文档解释:
如果一个名为log的字段已经存在,就会发出一个警告,并且不会生成任何代码。
lombok不同的日志注释的未来特性是找到对logger字段的调用,如果所选的日志框架支持它,并且日志级别可以通过日志调用来确定,那么可以使用if语句来保护它。这样,如果日志语句最终被忽略,那么就完全避免了对日志字符串的潜在开销。这就意味着你不应该添加任何副作用的代码在你日志记录中。
Experimental(实验性功能)
官方文档解释:
可以使用Lombok javadoc,但是我们建议这些特性。
在您的正常的lombok安装中可以使用实验特性,但是不像lombok的核心特性那样健壮。特别是,实验特点:
不像核心特性那样测试。
不要像核心功能一样快速地修复bug。
如果我们找到一种不同的、更好的方法来解决同样的问题,可能会有一些api会发生变化。
如果这个特性太难支持或者没有足够的样板文件,可能会完全消失。
那些接收到积极的社区反馈的特性,以及看起来能够产生干净、灵活的代码的特性最终将被作为核心特性接受,并从实验包中移出。
这部分中很多注解在之前就已经用过了,暂不写笔记了,有空再写吧~~
(三)over~~