Lombok入门使用教程及其优缺点详解
一、Lombok是什么?
1. 官方网站地址:点击直达
2. 官网对Lombok的描述如下:
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
简单理解后说下意思:
Lombok项目其实就是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。以后你只需要一些简单的注解,就可以再也不用在类中反复去写get、equals等方法。
3、优点说明:
Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率。例如开发中经常需要写的javabean,都需要花时间去添加相应的getter/setter,也许还要去写构造器、equals等方法,而且需要维护,当属性多时会出现大量的getter/setter方法,这些显得很冗长也没有太多技术含量,一旦修改属性,就容易出现忘记修改对应方法的失误。
Lombok能通过注解的方式,在编译时自动为属性生成构造函数、getter/setter、equals、hashcode、toString等方法。奇妙之处在于源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁明了。
二、Lombok安装与使用
1、安装Lombok插件
Lombok插件支持Eclipse、IntelliJ IDEA、MyEclispe等IDE,在这里我们使用IntelliJ IDEA来演示安装:
1)打开File > Settings > Plugins >Marketplace,搜索Lombok,如图,点击install,弹窗Accept,然后安装好后Restart IDEA。
2)设置Enable annotation processing
打开File > Settings > Build, Execution, Deployment > Compiler > Annotation Processors,勾选Enable annotation processing,然后Apply和OK。
2、新建Java项目,引入jar包
1、常用jar包引入方法有两种,如下:
方法1:普通项目可以直接官网下载jar包,和使用普通jar包一样,导入项目即可。jar下载地址:点击去下载
方法2:Maven项目可以在pom.xml中配置依赖坐标即可,依赖坐标如下:
org.projectlombok lombok 1.18.12 provided 注意:
1、provided表示该包只在编译和测试的时候用,项目真正打成包时不会将Lombok包打进去。
2、Lombok还支持其他构建方法,比如Ant、Gradle、Kobalt,有需要的可以参考官网的Install菜单下的Build Tools,其他使用方法也可以参考Install菜单
2、使用说明可以参考官方资料,也可以看我这里的常用注解代码演示(重点关注:@NonNull,@Getter, @Setter,@AllArgsConstructor,@NoArgsConstructor, @ToString和@Data)
1、官方注解:
2、官方API文档:点击直达
1)@NonNull该注解用在属性或构造器上,Lombok会生成一个非空的声明,可用于校验参数,能帮助避免空指针。
示例代码:
//成员方法参数加上@NonNull注解,构造方法也一样,在此不做演示publicStringgetName(Personp){ returnp.getName(); } 实际效果相当于:publicStringgetName(Personp){ if(p==null){ thrownewNullPointerException("person"); } returnp.getName(); }
2)@Getter和@Setter:在JavaBean或类JavaBean中使用,使用此注解相当于为成员变量生成对应的get和set方法,方法默认修饰符为public,同时还可以使用AccessLevel为生成的方法指定访问修饰符。这两个注解还可以直接用在类上,可以为此类里的所有非静态成员变量生成对应的get和set方法。
示例代码:
publicclassStudent{ privateStringname; AccessLevel.PROTECTED) (privateintage; AccessLevel.PUBLIC) (privateStringlanguage; }
实际效果相当于:
publicclassStudent{ privateStringname; privateintage; privateStringlanguage; publicvoidsetName(Stringname){ this.name=name; } publicStringgetName(){ returnname; } protectedvoidsetAge(intage){ this.age=age; } publicStringgetLanguage(){ returnlanguage; } }
3)@Cleanup这个注解用在变量前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的close()方法,如果该资源有其它关闭方法,可使用@Cleanup(“methodName”)来指定要调用的方法。
示例代码:
publicstaticvoidmain(String[] args) throwsIOException { InputStreamin=newFileInputStream(args[0]); OutputStreamout=newFileOutputStream(args[1]); byte[] b=newbyte[1024]; while (true) { intr=in.read(b); if (r==-1) break; out.write(b, 0, r); } }
实际效果相当于:
public static void main(String[] args) throws IOException {
@Cleanup
InputStream in = new FileInputStream(args[0]);
@Cleanup
OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[1024];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
publicstaticvoidmain(String[] args) throwsIOException { InputStreamin=newFileInputStream(args[0]); OutputStreamout=newFileOutputStream(args[1]); byte[] b=newbyte[1024]; while (true) { intr=in.read(b); if (r==-1) break; out.write(b, 0, r); } }
4)@ToString在JavaBean或类JavaBean中使用,使用此注解会自动重写对应的toStirng方法,默认情况下,会输出类名、所有属性(会按照属性定义顺序),用逗号来分割,通过callSuper参数来指定是否引用父类,includeFieldNames参数设为true,就能明确的输出toString()属性。
@ToString(exclude=”column”)
意义:排除column列所对应的元素,即在生成toString方法时不包含column参数;
@ToString(exclude={“column1″,”column2″})
意义:排除多个column列所对应的元素,其中间用英文状态下的逗号进行分割,即在生成toString方法时不包含多个column参数;
@ToString(of=”column”)
意义:只生成包含column列所对应的元素的参数的toString方法,即在生成toString方法时只包含column参数;;
@ToString(of={“column1″,”column2”})
意义:只生成包含多个column列所对应的元素的参数的toString方法,其中间用英文状态下的逗号进行分割,即在生成toString方法时只包含多个column参数;
示例代码:
exclude="id") (publicclassToStringExample { privatestaticfinalintSTATIC_VAR=10; privateStringname; privateShapeshape=newSquare(5, 10); privateString[] tags; privateintid; publicStringgetName() { returnthis.getName(); } callSuper=true, includeFieldNames=true) (publicstaticclassSquareextendsShape { privatefinalintwidth, height; publicSquare(intwidth, intheight) { this.width=width; this.height=height; } } }
实际效果相当于:
publicclassToStringExample { privatestaticfinalintSTATIC_VAR=10; privateStringname; privateShapeshape=newSquare(5, 10); privateString[] tags; privateintid; publicStringgetName() { returnthis.getName(); } publicstaticclassSquareextendsShape { privatefinalintwidth, height; publicSquare(intwidth, intheight) { this.width=width; this.height=height; } publicStringtoString() { return"Square(super="+super.toString() +", width="+this.width+", height="+this.height+")"; } } publicStringtoString() { return"ToStringExample("+this.getName() +", "+this.shape+", "+Arrays.deepToString(this.tags) +")"; } }
5)@EqualsAndHashCode默认情况下,会使用所有非静态(non-static)和非瞬态(non-transient)属性来生成equals和hasCode,也能通过exclude注解来排除一些属性。
示例代码:
exclude={"id", "shape"}) (publicclassEqualsAndHashCodeExample { privatetransientinttransientVar=10; privateStringname; privatedoublescore; privateShapeshape=newSquare(5, 10); privateString[] tags; privateintid; publicStringgetName() { returnthis.name; } callSuper=true) (publicstaticclassSquareextendsShape { privatefinalintwidth, height; publicSquare(intwidth, intheight) { this.width=width; this.height=height; } } }
6)@NoArgsConstructor、@RequiredArgsConstructor和@AllArgsConstructor
这三个注解都是用在类上的,第一个和第三个都很好理解,就是为该类产生无参的构造方法和包含所有参数的构造方法,第二个注解则使用类中所有带有@NonNull注解的或者带有final修饰的成员变量生成对应的构造方法,当然,成员变量都是非静态的,另外,如果类中含有final修饰的成员变量,是无法使用@NoArgsConstructor注解的。
三个注解都可以指定生成的构造方法的访问权限,同时,第二个注解还可以用@RequiredArgsConstructor(staticName=”methodName”)的形式生成一个指定名称的静态方法,返回一个调用相应的构造方法产生的对象。
示例代码:
/
staticName="myShape") (access=AccessLevel.PROTECTED) (publicclassShape { privateintx; privatedoubley; privateStringname; } 实际效果相当于:publicclassShape { privateintx; privatedoubley; privateStringname; publicShape(){ } protectedShape(intx,doubley,Stringname){ this.x=x; this.y=y; this.name=name; } publicShape(doubley,Stringname){ this.y=y; this.name=name; } publicstaticShapemyShape(doubley,Stringname){ returnnewShape(y,name); } }
7)@Data注解在类上,会为类的所有属性自动生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。@Value注解和@Data类似,区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法。
官方实例如下:
publicclassDataExample { privatefinalStringname; AccessLevel.PACKAGE) privateintage; (privatedoublescore; privateString[] tags; includeFieldNames=true) (staticConstructor="of") (publicstaticclassExercise<T> { privatefinalStringname; privatefinalTvalue; } } 实际效果相当于:publicclassDataExample { privatefinalStringname; privateintage; privatedoublescore; privateString[] tags; publicDataExample(Stringname) { this.name=name; } publicStringgetName() { returnthis.name; } voidsetAge(intage) { this.age=age; } publicintgetAge() { returnthis.age; } publicvoidsetScore(doublescore) { this.score=score; } publicdoublegetScore() { returnthis.score; } publicString[] getTags() { returnthis.tags; } publicvoidsetTags(String[] tags) { this.tags=tags; } publicStringtoString() { return"DataExample("+this.getName() +", "+this.getAge() +", "+this.getScore() +", "+Arrays.deepToString(this.getTags()) +")"; } protectedbooleancanEqual(Objectother) { returnotherinstanceofDataExample; } publicbooleanequals(Objecto) { if (o==this) returntrue; if (!(oinstanceofDataExample)) returnfalse; DataExampleother= (DataExample) o; if (!other.canEqual((Object)this)) returnfalse; if (this.getName() ==null?other.getName() !=null : !this.getName().equals(other.getName())) returnfalse; if (this.getAge() !=other.getAge()) returnfalse; if (Double.compare(this.getScore(), other.getScore()) !=0) returnfalse; if (!Arrays.deepEquals(this.getTags(), other.getTags())) returnfalse; returntrue; } publicinthashCode() { finalintPRIME=59; intresult=1; finallongtemp1=Double.doubleToLongBits(this.getScore()); result= (result*PRIME) + (this.getName() ==null?43 : this.getName().hashCode()); result= (result*PRIME) +this.getAge(); result= (result*PRIME) + (int)(temp1^ (temp1>>>32)); result= (result*PRIME) +Arrays.deepHashCode(this.getTags()); returnresult; } publicstaticclassExercise<T> { privatefinalStringname; privatefinalTvalue; privateExercise(Stringname, Tvalue) { this.name=name; this.value=value; } publicstatic<T>Exercise<T>of(Stringname, Tvalue) { returnnewExercise<T>(name, value); } publicStringgetName() { returnthis.name; } publicTgetValue() { returnthis.value; } publicStringtoString() { return"Exercise(name="+this.getName() +", value="+this.getValue() +")"; } protectedbooleancanEqual(Objectother) { returnotherinstanceofExercise; } publicbooleanequals(Objecto) { if (o==this) returntrue; if (!(oinstanceofExercise)) returnfalse; Exercise<?>other= (Exercise<?>) o; if (!other.canEqual((Object)this)) returnfalse; if (this.getName() ==null?other.getValue() !=null : !this.getName().equals(other.getName())) returnfalse; if (this.getValue() ==null?other.getValue() !=null : !this.getValue().equals(other.getValue())) returnfalse; returntrue; } publicinthashCode() { finalintPRIME=59; intresult=1; result= (result*PRIME) + (this.getName() ==null?43 : this.getName().hashCode()); result= (result*PRIME) + (this.getValue() ==null?43 : this.getValue().hashCode()); returnresult; } } }
8)@SneakyThrows这个注解用在方法上,可以将方法中的代码用try-catch语句包裹起来,捕获异常并在catch中用Lombok.sneakyThrow(e)把异常抛出,可以使用@SneakyThrows(Exception.class)的形式指定抛出哪种异常,很简单的注解,直接看个例子:
publicclassSneakyThrowsimplementsRunnable { UnsupportedEncodingException.class) (publicStringutf8ToString(byte[] bytes) { returnnewString(bytes, "UTF-8"); } publicvoidrun() { thrownewThrowable(); } }
实际效果相当于:
publicclassSneakyThrowsimplementsRunnable { publicStringutf8ToString(byte[] bytes) { try{ returnnewString(bytes, "UTF-8"); }catch(UnsupportedEncodingExceptionuee){ throwLombok.sneakyThrow(uee); } } publicvoidrun() { try{ thrownewThrowable(); }catch(Throwablet){ throwLombok.sneakyThrow(t); } } }
9)@Synchronized这个注解用在类方法或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,而@Synchronized得锁对象分别是私有静态final对象LOCK和私有final对象lock,当然,也可以自己指定锁对象,例子也很简单:
publicclassSynchronized { privatefinalObjectreadLock=newObject(); publicstaticvoidhello() { System.out.println("world"); } publicintanswerToLife() { return42; } "readLock") (publicvoidfoo() { System.out.println("bar"); } }
实际效果相当于:
publicclassSynchronized { privatestaticfinalObject$LOCK=newObject[0]; privatefinalObject$lock=newObject[0]; privatefinalObjectreadLock=newObject(); publicstaticvoidhello() { synchronized($LOCK) { System.out.println("world"); } } publicintanswerToLife() { synchronized($lock) { return42; } } publicvoidfoo() { synchronized(readLock) { System.out.println("bar"); } } }
10)@Log这个注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名。不同的日志注解总结如下(上面是注解,下面是实际作用):
privatestaticfinalorg.apache.commons.logging.Loglog=org.apache.commons.logging.LogFactory.getLog(LogExample.class); privatestaticfinalorg.jboss.logging.Loggerlog=org.jboss.logging.Logger.getLogger(LogExample.class); privatestaticfinaljava.util.logging.Loggerlog=java.util.logging.Logger.getLogger(LogExample.class.getName()); privatestaticfinalorg.apache.log4j.Loggerlog=org.apache.log4j.Logger.getLogger(LogExample.class); privatestaticfinalorg.apache.logging.log4j.Loggerlog=org.apache.logging.log4j.LogManager.getLogger(LogExample.class); privatestaticfinalorg.slf4j.Loggerlog=org.slf4j.LoggerFactory.getLogger(LogExample.class); privatestaticfinalorg.slf4j.ext.XLoggerlog=org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);