Java?Lombok中的@Data、@Getter、@Setter使用技巧全面指南
深入理解Lombok:@Data、@Getter、@Setter全面指南
作為Java開發(fā)者,你是否厭倦了寫大量重復(fù)的getter/setter方法?是否在為維護(hù)冗長(zhǎng)的toString、equals、hashCode方法而煩惱?本文將帶你深入了解Lombok注解的使用技巧,讓你的代碼更加簡(jiǎn)潔優(yōu)雅。
?? 前言:告別樣板代碼的時(shí)代
在傳統(tǒng)的Java開發(fā)中,一個(gè)簡(jiǎn)單的User類可能需要寫50+行代碼:
public class User {
private String name;
private Integer age;
private String email;
// 需要手動(dòng)寫的方法:
// - 3個(gè)getter方法
// - 3個(gè)setter方法
// - toString方法
// - equals方法
// - hashCode方法
// - 構(gòu)造函數(shù)
// 總共50+行重復(fù)代碼!??
}
而使用Lombok,同樣的功能只需要:
@Data
public class User {
private String name;
private Integer age;
private String email;
}
// 7行代碼搞定!??
這就是Lombok的魅力所在。讓我們深入探索這個(gè)"懶人神器"的奧秘。
?? Lombok簡(jiǎn)介
什么是Lombok?
Lombok是一個(gè)Java庫(kù),通過注解的方式在編譯時(shí)自動(dòng)生成常用的樣板代碼,如getter、setter、toString等方法。它的核心理念是:讓開發(fā)者專注于業(yè)務(wù)邏輯,而不是重復(fù)的樣板代碼。
工作原理

Lombok在編譯階段通過APT(Annotation Processing Tool)技術(shù),分析注解并生成相應(yīng)的Java代碼,最終編譯成標(biāo)準(zhǔn)的字節(jié)碼文件。
?? 核心注解詳解
1. @Data:萬能選手
@Data是Lombok中最強(qiáng)大的注解,它是以下注解的組合:
@Getter- 所有字段的getter方法@Setter- 所有非final字段的setter方法@ToString- toString方法@EqualsAndHashCode- equals和hashCode方法@RequiredArgsConstructor- 必需參數(shù)構(gòu)造函數(shù)
基礎(chǔ)用法
@Data
public class Student {
private String name;
private Integer age;
private String major;
private List<String> hobbies;
}
編譯后自動(dòng)生成的方法:
// 自動(dòng)生成的getter方法
public String getName() { return this.name; }
public Integer getAge() { return this.age; }
public String getMajor() { return this.major; }
public List<String> getHobbies() { return this.hobbies; }
// 自動(dòng)生成的setter方法
public void setName(String name) { this.name = name; }
public void setAge(Integer age) { this.age = age; }
public void setMajor(String major) { this.major = major; }
public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
// 自動(dòng)生成的toString方法
public String toString() {
return "Student(name=" + this.name + ", age=" + this.age +
", major=" + this.major + ", hobbies=" + this.hobbies + ")";
}
// 自動(dòng)生成的equals和hashCode方法
public boolean equals(Object o) { /* 完整的equals實(shí)現(xiàn) */ }
public int hashCode() { /* 完整的hashCode實(shí)現(xiàn) */ }
// 自動(dòng)生成的構(gòu)造函數(shù)
public Student() {}高級(jí)配置
@Data
@ToString(exclude = {"password"}) // toString時(shí)排除敏感字段
@EqualsAndHashCode(exclude = {"id"}) // equals比較時(shí)排除id字段
public class User {
private Long id;
private String username;
private String password;
private String email;
}
2. @Getter:只讀屬性的守護(hù)者
@Getter注解專門用于生成getter方法,適合需要數(shù)據(jù)封裝和只讀訪問的場(chǎng)景。
類級(jí)別使用
@Getter
public class ConfigInfo {
private String appName = "MyApp";
private String version = "1.0.0";
private Date buildTime = new Date();
// 只生成getter方法,保護(hù)數(shù)據(jù)不被外部修改
}
// 使用示例
ConfigInfo config = new ConfigInfo();
String appName = config.getAppName(); // ? 可以讀取
config.setAppName("NewApp"); // ? 編譯錯(cuò)誤,沒有setter字段級(jí)別使用
public class BankAccount {
@Getter
private String accountNumber; // 只讀:賬號(hào)不能修改
@Getter @Setter
private BigDecimal balance; // 可讀寫:余額可以修改
private String password; // 完全私有:密碼不能直接訪問
// 自定義密碼驗(yàn)證方法
public boolean verifyPassword(String inputPassword) {
return this.password.equals(inputPassword);
}
}訪問級(jí)別控制
public class SecurityUser {
@Getter(AccessLevel.PUBLIC)
private String username; // public getter
@Getter(AccessLevel.PROTECTED)
private String internalId; // protected getter
@Getter(AccessLevel.PACKAGE)
private String sessionToken; // package-private getter
@Getter(AccessLevel.PRIVATE)
private String encryptionKey; // private getter(基本無用)
@Getter(AccessLevel.NONE)
private String secretData; // 不生成getter
}3. @Setter:數(shù)據(jù)修改的管家
@Setter注解用于生成setter方法,適合需要數(shù)據(jù)輸入和修改的場(chǎng)景。
基礎(chǔ)用法
@Setter
public class UserRegistrationForm {
private String username;
private String password;
private String email;
private String captcha;
// 只有setter方法,適合表單數(shù)據(jù)收集
}
// 使用示例
UserRegistrationForm form = new UserRegistrationForm();
form.setUsername("john_doe"); // ? 可以設(shè)置
form.setPassword("secret123"); // ? 可以設(shè)置
String name = form.getUsername(); // ? 編譯錯(cuò)誤,沒有g(shù)etter鏈?zhǔn)秸{(diào)用支持
@Setter
@Accessors(chain = true) // 啟用鏈?zhǔn)秸{(diào)用
public class FluentUser {
private String name;
private Integer age;
private String email;
}
// 鏈?zhǔn)秸{(diào)用示例
FluentUser user = new FluentUser()
.setName("Alice")
.setAge(25)
.setEmail("alice@example.com");參數(shù)驗(yàn)證
public class ValidatedUser {
@Setter
private String email;
@Setter
private Integer age;
// 自定義setter with validation
public void setEmail(String email) {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email format");
}
this.email = email;
}
public void setAge(Integer age) {
if (age == null || age < 0 || age > 150) {
throw new IllegalArgumentException("Invalid age");
}
this.age = age;
}
}??? 實(shí)戰(zhàn)應(yīng)用場(chǎng)景
場(chǎng)景1:數(shù)據(jù)傳輸對(duì)象(DTO)
// 前端請(qǐng)求參數(shù)
@Data
@ApiModel("用戶查詢請(qǐng)求")
public class UserQueryDTO {
@ApiModelProperty("用戶名(支持模糊查詢)")
private String username;
@ApiModelProperty("郵箱")
private String email;
@ApiModelProperty("年齡范圍-最小值")
private Integer minAge;
@ApiModelProperty("年齡范圍-最大值")
private Integer maxAge;
@ApiModelProperty("分頁(yè)信息")
private PageInfo pageInfo;
}
// 響應(yīng)數(shù)據(jù)
@Data
@ApiModel("用戶信息響應(yīng)")
public class UserResponseVO {
@ApiModelProperty("用戶ID")
private Long id;
@ApiModelProperty("用戶名")
private String username;
@ApiModelProperty("郵箱")
private String email;
@ApiModelProperty("年齡")
private Integer age;
@ApiModelProperty("注冊(cè)時(shí)間")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date registerTime;
}場(chǎng)景2:數(shù)據(jù)庫(kù)實(shí)體類(Entity)
@Data
@Entity
@Table(name = "users")
@EqualsAndHashCode(exclude = {"id", "createTime", "updateTime"})
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", unique = true, nullable = false)
private String username;
@Column(name = "email", unique = true)
private String email;
@Column(name = "password_hash")
private String passwordHash;
@Column(name = "status")
@Enumerated(EnumType.STRING)
private UserStatus status;
@CreationTimestamp
@Column(name = "create_time")
private LocalDateTime createTime;
@UpdateTimestamp
@Column(name = "update_time")
private LocalDateTime updateTime;
// 關(guān)聯(lián)關(guān)系
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
@ToString.Exclude // 避免循環(huán)引用
private List<OrderEntity> orders;
}場(chǎng)景3:配置類
@Data
@ConfigurationProperties(prefix = "app.security")
@Component
public class SecurityConfig {
private String jwtSecret = "defaultSecret";
private Long jwtExpiration = 86400L; // 24 hours
private Integer maxLoginAttempts = 5;
private Long lockoutDuration = 1800L; // 30 minutes
private Cors cors = new Cors();
@Data
public static class Cors {
private List<String> allowedOrigins = Arrays.asList("*");
private List<String> allowedMethods = Arrays.asList("GET", "POST", "PUT", "DELETE");
private List<String> allowedHeaders = Arrays.asList("*");
private Boolean allowCredentials = true;
}
}場(chǎng)景4:構(gòu)建器模式增強(qiáng)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ComplexUser {
private String firstName;
private String lastName;
private String email;
private Integer age;
private Address address;
private List<String> roles;
private Map<String, Object> metadata;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Address {
private String street;
private String city;
private String state;
private String zipCode;
private String country;
}
}
// 使用示例
ComplexUser user = ComplexUser.builder()
.firstName("John")
.lastName("Doe")
.email("john.doe@example.com")
.age(30)
.address(ComplexUser.Address.builder()
.street("123 Main St")
.city("New York")
.state("NY")
.zipCode("10001")
.country("USA")
.build())
.roles(Arrays.asList("USER", "ADMIN"))
.metadata(Map.of("department", "IT", "level", "senior"))
.build();? 性能與最佳實(shí)踐
性能考慮
1. 編譯時(shí) vs 運(yùn)行時(shí)
// Lombok:編譯時(shí)生成代碼,運(yùn)行時(shí)無性能損耗
@Data
public class LombokUser {
private String name;
private Integer age;
}
// 反射:運(yùn)行時(shí)動(dòng)態(tài)調(diào)用,有性能損耗
public class ReflectionUser {
private String name;
private Integer age;
public void setProperty(String propertyName, Object value) {
Field field = this.getClass().getDeclaredField(propertyName);
field.setAccessible(true);
field.set(this, value); // 運(yùn)行時(shí)反射調(diào)用
}
}2. 內(nèi)存使用優(yōu)化
@Data
@ToString(exclude = {"largeDataSet"}) // 排除大數(shù)據(jù)集,避免toString時(shí)內(nèi)存問題
@EqualsAndHashCode(exclude = {"id", "createTime"}) // 排除不參與比較的字段
public class OptimizedEntity {
private Long id;
private String name;
private LocalDateTime createTime;
@ToString.Exclude
private List<byte[]> largeDataSet; // 大數(shù)據(jù)不參與toString
}最佳實(shí)踐
1. 團(tuán)隊(duì)規(guī)范建議
// ? 推薦:明確的注解使用規(guī)范
@Data
@ApiModel("用戶信息")
public class UserDTO {
@ApiModelProperty(value = "用戶ID", example = "1001")
private Long id;
@ApiModelProperty(value = "用戶名", required = true)
@NotBlank(message = "用戶名不能為空")
private String username;
}
// ? 不推薦:缺乏文檔和驗(yàn)證
@Data
public class UserDTO {
private Long id;
private String username;
}2. 繼承關(guān)系處理
// 父類
@Getter
@Setter
@ToString
public class BaseEntity {
private Long id;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
// 子類
@Data
@EqualsAndHashCode(callSuper = true) // 重要:包含父類字段
@ToString(callSuper = true) // 重要:包含父類字段
public class UserEntity extends BaseEntity {
private String username;
private String email;
}3. 循環(huán)引用處理
@Data
public class Department {
private String name;
@ToString.Exclude
@EqualsAndHashCode.Exclude
private List<Employee> employees; // 避免循環(huán)引用
}
@Data
public class Employee {
private String name;
@ToString.Exclude
@EqualsAndHashCode.Exclude
private Department department; // 避免循環(huán)引用
}?? 高級(jí)特性與技巧
1. 自定義命名策略
@Data
@Accessors(prefix = "m") // 字段前綴為m
public class PrefixedUser {
private String mName; // getter: getName(), setter: setName()
private Integer mAge; // getter: getAge(), setter: setAge()
}
@Data
@Accessors(fluent = true) // 流式接口
public class FluentUser {
private String name; // getter: name(), setter: name(String)
private Integer age; // getter: age(), setter: age(Integer)
}
// 使用示例
FluentUser user = new FluentUser().name("Alice").age(25);
String name = user.name();2. 條件生成
public class ConditionalUser {
@Getter
@Setter(onParam_ = {@Valid}) // setter參數(shù)添加@Valid注解
private @NotNull String username;
@Getter(onMethod_ = {@JsonIgnore}) // getter方法添加@JsonIgnore注解
private String password;
@Getter(lazy = true) // 懶加載getter
private final String expensiveCalculation = calculateExpensive();
private String calculateExpensive() {
// 模擬耗時(shí)計(jì)算
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "Expensive Result";
}
}3. 與其他框架集成
// 與Jackson集成
@Data
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JsonUser {
private String firstName; // JSON: first_name
private String lastName; // JSON: last_name
private Integer userAge; // JSON: user_age
@JsonProperty("email_address")
private String email; // JSON: email_address
@JsonIgnore
private String password; // 不序列化
}
// 與JPA集成
@Data
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class JpaUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String username;
@CreatedDate
private LocalDateTime createTime;
@LastModifiedDate
private LocalDateTime updateTime;
}?? 常見陷阱與解決方案
1. equals和hashCode陷阱
// ? 問題代碼:包含可變字段導(dǎo)致HashMap行為異常
@Data
public class ProblematicUser {
private String name;
private List<String> hobbies; // 可變集合參與equals/hashCode
}
// 測(cè)試問題
Map<ProblematicUser, String> map = new HashMap<>();
ProblematicUser user = new ProblematicUser();
user.setName("Alice");
user.setHobbies(new ArrayList<>(Arrays.asList("reading")));
map.put(user, "value");
user.getHobbies().add("swimming"); // 修改了hashCode
String value = map.get(user); // null!找不到了
// ? 解決方案:排除可變字段或使用不可變集合
@Data
@EqualsAndHashCode(exclude = {"hobbies"})
public class SafeUser {
private String name;
private List<String> hobbies;
}2. 循環(huán)引用陷阱
// ? 問題代碼:toString時(shí)出現(xiàn)StackOverflowError
@Data
public class Parent {
private String name;
private List<Child> children;
}
@Data
public class Child {
private String name;
private Parent parent; // 循環(huán)引用
}
// ? 解決方案:使用@ToString.Exclude
@Data
public class SafeParent {
private String name;
@ToString.Exclude
private List<SafeChild> children;
}
@Data
public class SafeChild {
private String name;
@ToString.Exclude
private SafeParent parent;
}3. 性能陷阱
// ? 問題代碼:大集合參與toString
@Data
public class BigDataUser {
private String name;
private List<byte[]> bigDataList; // 大數(shù)據(jù)集合
}
BigDataUser user = new BigDataUser();
user.setBigDataList(generateMassiveData());
System.out.println(user); // 可能導(dǎo)致內(nèi)存問題
// ? 解決方案:排除大數(shù)據(jù)字段
@Data
@ToString(exclude = {"bigDataList"})
public class OptimizedUser {
private String name;
private List<byte[]> bigDataList;
// 自定義toString顯示摘要信息
public String getDataSummary() {
return bigDataList != null ?
"DataList[size=" + bigDataList.size() + "]" : "DataList[empty]";
}
}?? 遷移指南
從傳統(tǒng)代碼遷移到Lombok
步驟1:添加依賴和插件
<!-- Maven -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>// Gradle
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
}步驟2:漸進(jìn)式遷移
// 原始代碼
public class User {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
// ... 其他樣板代碼
}
// 遷移步驟1:添加@Getter @Setter,保留原有方法
@Getter
@Setter
public class User {
private String name;
// 保留原有方法,確保兼容性
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
// 遷移步驟2:測(cè)試通過后刪除原有方法
@Getter
@Setter
public class User {
private String name;
}
// 遷移步驟3:根據(jù)需要升級(jí)為@Data
@Data
public class User {
private String name;
}步驟3:團(tuán)隊(duì)培訓(xùn)清單
## Lombok使用規(guī)范 ### ? 推薦做法 - 優(yōu)先使用@Data處理簡(jiǎn)單數(shù)據(jù)類 - 使用@Getter處理只讀數(shù)據(jù) - 復(fù)雜繼承關(guān)系謹(jǐn)慎使用@EqualsAndHashCode - 循環(huán)引用場(chǎng)景使用@ToString.Exclude ### ? 避免做法 - 不要在所有類上盲目使用@Data - 不要忽略equals/hashCode的語(yǔ)義 - 不要在大數(shù)據(jù)字段上使用默認(rèn)toString - 不要在API接口類上使用@Data
?? 對(duì)比總結(jié)
| 特性 | 傳統(tǒng)寫法 | @Getter | @Setter | @Data |
|---|---|---|---|---|
| 代碼量 | 50+行 | 減少60% | 減少40% | 減少90% |
| 可讀性 | 差 | 良好 | 良好 | 優(yōu)秀 |
| 維護(hù)性 | 差 | 良好 | 良好 | 優(yōu)秀 |
| 性能 | 標(biāo)準(zhǔn) | 標(biāo)準(zhǔn) | 標(biāo)準(zhǔn) | 標(biāo)準(zhǔn) |
| 靈活性 | 最高 | 高 | 高 | 中等 |
| 學(xué)習(xí)成本 | 無 | 低 | 低 | 低 |
| 團(tuán)隊(duì)接受度 | 100% | 高 | 高 | 高 |
?? 總結(jié)
Lombok通過編譯時(shí)代碼生成技術(shù),極大地簡(jiǎn)化了Java開發(fā)中的樣板代碼編寫。合理使用@Data、@Getter、@Setter等注解,可以讓代碼更加簡(jiǎn)潔、易讀、易維護(hù)。
選擇建議
- ?? @Data:適用于80%的數(shù)據(jù)類場(chǎng)景,功能全面,使用簡(jiǎn)單
- ?? @Getter:適用于只讀數(shù)據(jù),如配置類、查詢結(jié)果等
- ?? @Setter:適用于只寫數(shù)據(jù),如表單參數(shù)、更新對(duì)象等
- ?? 組合使用:適用于需要精確控制的復(fù)雜場(chǎng)景
最后的建議
- 從小處開始:先在新的數(shù)據(jù)類上嘗試使用
- 保持一致性:團(tuán)隊(duì)內(nèi)部統(tǒng)一使用規(guī)范
- 關(guān)注細(xì)節(jié):注意equals/hashCode語(yǔ)義和循環(huán)引用問題
- 持續(xù)學(xué)習(xí):關(guān)注Lombok的新特性和最佳實(shí)踐
記?。?strong>工具是為了讓開發(fā)更高效,而不是炫技。選擇最適合你項(xiàng)目和團(tuán)隊(duì)的方案,才是最好的方案。
希望這篇文章能幫助你更好地理解和使用Lombok。如果你有任何問題或建議,歡迎在評(píng)論區(qū)交流討論!
?? 參考資源
到此這篇關(guān)于Java Lombok中的@Data、@Getter、@Setter使用技巧全面指南的文章就介紹到這了,更多相關(guān)深入理解Lombok:@Data、@Getter、@Setter全面指南內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot中的@ComponentScan注解使用解析
這篇文章主要介紹了Springboot中的@ComponentScan注解使用解析,@ComponentScan用于類或接口上主要是指定掃描路徑,spring會(huì)把指定路徑下帶有指定注解的類注冊(cè)到IOC容器中,需要的朋友可以參考下2024-01-01
WMTS中TileMatrix與ScaleDenominator淺析
這篇文章主要為大家介紹了WMTS中TileMatrix與ScaleDenominator淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Java CountDownLatch應(yīng)用場(chǎng)景代碼實(shí)例
這篇文章主要介紹了Java CountDownLatch應(yīng)用場(chǎng)景代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
SpringBoot中使用Jsoup爬取網(wǎng)站數(shù)據(jù)的方法
這篇文章主要介紹了SpringBoot中使用Jsoup爬取網(wǎng)站數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06

