正文
前言
在Java中,封裝是一個非常好的機制,最常見的封裝莫過於get,set方法了,無論是Intellij idea 還是Eclipse,都提供了快速生成get,set方法的快捷鍵,使用起來很是方便,其實,我們還有更方便的辦法,那就是-Lombok:非常強大的POJO注解器。
Lombok是什么?
lombok 提供了簡單的注解的形式來幫助我們簡化消除一些必須有但顯得很臃腫的 java 代碼。特別是相對於 POJO。
如何安裝Lombok?
使用 lombok 是需要安裝的,如果不安裝,IDE 則無法解析 lombok 注解。先在官網下載最新版本的 JAR 包。
- 雙擊下載下來的 JAR 包安裝 lombok;
我選擇這種方式安裝的時候提示沒有發現任何 IDE,所以我沒安裝成功,我是手動安裝的。 - eclipse / myeclipse 手動安裝 lombok(Mac 下eclipse安裝Lombok插件)
- 將 lombok.jar 復制到 myeclipse.ini / eclipse.ini 所在的文件夾目錄下
- 打開 eclipse.ini / myeclipse.ini,在最后面插入以下兩行並保存:
-Xbootclasspath/a:lombok.jar -javaagent:lombok.jar
- 重啟 eclipse / myeclipse
Lombok使用詳解
-
添加POM依賴:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.0</version> </dependency>
-
Lombok提供注解方式來提高代碼的簡潔性,常用注解概覽:
- @Data:注解在類上;提供類所有屬性的 getting 和 setting 方法,此外還提供了equals、canEqual、hashCode、toString 方法,相當於同時加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
- @Setter、@Getter:注解在類和屬性上;為屬性提供 setting、getting 方法
- @ToString:生成toString方法,默認情況下,會輸出類名、所有屬性,屬性按照順序輸出,以逗號分割。
- @EqualsAndHashCode:實現equals()方法和hashCode()方法
- @Builder:構建 建造者模式
- @NonNull:該注解快速判斷是否為空,如果為空,則拋出java.lang.NullPointerException
- @Synchronized:該注解自動添加到同步機制,有趣的是,生成的代碼並不是直接鎖方法,而是鎖代碼塊, 作用范圍是方法上
- @Log4j :注解在類上;為類提供一個 屬性名為log 的 log4j 日志對象
- @NoArgsConstructor:注解在類上;為類提供一個無參的構造方法
- @RequiredArgsConstructor:注解在類上;為類提供一個部分參的構造方法(使用類中所有帶有@NonNull注解的或者帶有final修飾的成員變量生成對應的構造方法)
- @AllArgsConstructor:注解在類上;為類提供一個全參的構造方法
- @Cleanup:用於確保已分配的資源被釋放,如IO的連接關閉
- @SneakyThrows:拋異常
- @Accessors(chain = true):使用鏈式結構
@Data
注解在類上;提供類所有屬性的 getting 和 setting 方法,此外還提供了equals、canEqual、hashCode、toString 方法,相當於同時加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
1 @Data 2 public class Person { 3 private String name; 4 private String address; 5 private String city; 6 private String state; 7 private String zip; 8 private Date brithday; 9 }
效果如下:
@Getter@Setter
注解在類和屬性上;為屬性提供 setting、getting 方法
public class Person { @Getter@Setter private String name; }
等價源碼:
1 public String getName() { 2 return name; 3 } 4 5 public void setName(String name) { 6 this.name = name; 7 }
@ToString
生成toString方法,默認情況下,會輸出類名、所有屬性,屬性按照順序輸出,以逗號分割。但需要注意的是:@ToString有多個屬性可以進一步設置:
-
-
callSuper 是否輸出父類的toString方法,默認為false
-
includeFieldNames 是否包含字段名稱,默認為true
-
exclude 排除生成tostring的字段
使用方法:
1 @ToString(callSuper = true,exclude ={"name"}) 2 public class Person { 3 private String name; 4 private String address; 5 }
等價源碼:
1 public String toString() { 2 return "Person{" + 3 "address='" + address + '\'' + 4 '}'; 5 }
-
@NonNull
該注解快速判斷是否為空,如果為空,則拋出java.lang.NullPointerException
使用方法
1 public class Person { 2 3 private String name; 4 5 @Setter@Getter@NonNull 6 private List<Person> member; 7 }
等價源碼:
1 @NonNull 2 private List<Person> members; 3 4 public Family(@NonNull final List<Person> members) { 5 if (members == null) throw new java.lang.NullPointerException("members"); 6 this.members = members; 7 } 8 9 @NonNull 10 public List<Person> getMembers() { 11 return members; 12 } 13 14 public void setMembers(@NonNull final List<Person> members) { 15 if (members == null) throw new java.lang.NullPointerException("members"); 16 this.members = members; 17 }
@Synchronized
該注解自動添加到同步機制,有趣的是,生成的代碼並不是直接鎖方法,而是鎖代碼塊, 作用范圍是方法上。
使用方法:
1 private DateFormat format = new SimpleDateFormat("MM-dd-YYYY"); 2 3 @Synchronized 4 public String synchronizedFormat(Date date) { 5 return format.format(date); 6 }
等價源碼:
1 private final java.lang.Object $lock = new java.lang.Object[0]; 2 private DateFormat format = new SimpleDateFormat("MM-dd-YYYY"); 3 4 public String synchronizedFormat(Date date) { 5 synchronized ($lock) { 6 return format.format(date); 7 } 8 }
@Cleanup
注釋可用於確保已分配的資源被釋放,如IO的連接關閉。
使用方法:
1 public void testCleanUp() { 2 try { 3 @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream(); 4 baos.write(new byte[] {'Y','e','s'}); 5 System.out.println(baos.toString()); 6 } catch (IOException e) { 7 e.printStackTrace(); 8 } 9 }
等價源碼:
1 public void testCleanUp() { 2 try { 3 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 4 try { 5 baos.write(new byte[]{'Y', 'e', 's'}); 6 System.out.println(baos.toString()); 7 } finally { 8 baos.close(); 9 } 10 } catch (IOException e) { 11 e.printStackTrace(); 12 } 13 }
@Accessors(chain = true)
使用鏈式結構
- 使用方法:
@Accessors(chain=true) public class Student { private String name; private int age; public String getName() { return name; } public Student setName(String name) { this.name = name; return this; } public int getAge() { return age; } public Student setAge(int age) { return this; } }
- 等價源碼:
1 @Accessors(chain = true) 2 @Data 3 @NoArgsConstructor(staticName = "of") 4 public class Student { 5 private String name; 6 private int age; 7 }
- 調用
1 Student student = Student.of().setName("wsk").setAge(12);
@Builder
使用builder,構建 建造者模式
- 例一:
- 使用@Builder
1 @Builder 2 public class Student { 3 private String name; 4 private int age; 5 }
- 調用示例:
1 Student student = Student.builder().name("zs").age(24).build();
- 等價源碼:
1 public class Student { 2 private String name; 3 private int age; 4 5 public String getName() { 6 return name; 7 } 8 9 public void setName(String name) { 10 this.name = name; 11 } 12 13 public int getAge() { 14 return age; 15 } 16 17 public void setAge(int age) { 18 this.age = age; 19 } 20 21 public static Builder builder(){ 22 return new Builder(); 23 } 24 public static class Builder{ 25 private String name; 26 private int age; 27 public Builder name(String name){ 28 this.name = name; 29 return this; 30 } 31 32 public Builder age(int age){ 33 this.age = age; 34 return this; 35 } 36 37 public Student build(){ 38 Student student = new Student(); 39 student.setAge(age); 40 student.setName(name); 41 return student; 42 } 43 } 44 }
- 使用@Builder
- 例二:利用builder模式設計的Java類
- 如果能將創建JavaBean和設置內容揉在一起,在傳入builder中的參數不合乎業務或者非法,那么就不能創建student對象,這時候可以通過捕獲IllegalArgumentException,從而得知失敗的原因;
- 引入Builder設計模式以后,代碼保持JavaBean好的可讀性,但同時增強了安全性,將Student類的創建和設置內容揉在了一起,並增加了安全性檢查,提高了系統的健壯性,同時防止了編碼中的一些疏忽。
- Java示例:
1 public class Student { 2 private String id; 3 private String name; 4 private String sex; 5 private int age; 6 private String department; 7 8 public static class Builder { 9 /* 10 * 只能指定一次。 11 */ 12 private final String id; 13 private final String department; 14 15 private String name = ""; 16 private String sex = "男"; 17 private int age = 20; 18 19 /* 20 * 非空屬性,必須在構造器中指定。 21 */ 22 public Builder(String id, String department) { 23 this.id = id; 24 this.department = department; 25 } 26 27 /* 28 * name,sex,age可選擇屬性,提供特殊的setter方法。 29 */ 30 public Builder name(String name) { 31 this.name = name; 32 return this; 33 } 34 35 public Builder sex(String sex) { 36 this.sex = sex; 37 return this; 38 } 39 40 public Builder age(int age) { 41 this.age = age; 42 return this; 43 } 44 45 /* 46 * Student對象創建器,想得到一個Student對象必須使用build 方法, 47 * 在方法中增加對Builder參數的驗證,並以異常的形式告訴給開發人員。 48 */ 49 public Student build() { 50 /* 檢查Builder對象中的數據是否合法。 51 * 針對這個例子,就是檢查主鍵沖突,外鍵制約等 52 * 如果不滿足我們可以拋出一個IllegalArgumentException 53 */ 54 return new Student(this); 55 56 } 57 58 } 59 60 private Student(Builder builder) { 61 this.id = builder.id; 62 this.name = builder.name; 63 this.sex = builder.sex; 64 this.age = builder.age; 65 this.department = builder.department; 66 } 67 68 /* 69 * 只提供getter方法 70 */ 71 public String getId() { 72 return id; 73 } 74 75 public String getName() { 76 return name; 77 } 78 79 public String getSex() { 80 return sex; 81 } 82 83 public int getAge() { 84 return age; 85 } 86 87 public String getDepartment() { 88 return department; 89 } 90 91 }
- 創建對象一:
1 student = new Student.Builder("03041013", "計算機").name("李華").build();
- 創建對象二:
1 Student.Builder builder = new Student.Builder("03041013", "計算機"); 2 builder.name("李華"); 3 Student student = builder.build();
小結:
- 很明顯,使用 lombok 要簡潔許多,特別是在類的屬性較多的情況下;
- 同時也避免了修改字段名字時候忘記修改方法名所犯的低級錯誤;