在Java实体类中,@Data
、@NoArgsConstructor
、@AllArgsConstructor
和 @ToString
是 Lombok 提供的常用注解
注意事项
- Lombok 需要安装插件:IDE 中需要安装 Lombok 插件才能正确识别这些注解。
- 编译时生成代码:这些注解是在编译时通过注解处理器生成代码,而不是运行时。
- 不可见但存在:生成的代码在源代码中不可见,但编译后的字节码中包含。
一、介绍
1. @Data
- 作用:这是一个组合注解,相当于同时添加了以下注解:
@Getter
:为所有字段生成 getter 方法@Setter
:为所有非 final 字段生成 setter 方法@ToString
:生成 toString() 方法@EqualsAndHashCode
:生成 equals() 和 hashCode() 方法@RequiredArgsConstructor
:为所有 final 字段或标记为@NonNull
的字段生成构造方法
2. @NoArgsConstructor
- 作用:生成一个无参的构造方法。
- 适用场景:某些框架(如 Hibernate、MyBatis)需要实体类有一个无参构造方法。
3. @AllArgsConstructor
- 作用:生成一个包含所有字段的构造方法(按字段声明顺序)。
- 注意:如果类中有
final
字段且未初始化,使用此注解可能会导致编译错误。
4. @ToString
- 作用:生成 toString() 方法,默认格式为:
类名(字段名=字段值, 字段名=字段值...)
。 - 常用参数:
exclude
:排除某些字段of
:只包含某些字段callSuper
:是否包含父类的 toString() 结果
import lombok.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private Long id;
private String name;
private Integer age;
}
对比一下上面的,下面为等效的传统Java代码:
public class User {
private Long id;
private String name;
private Integer age;
// @NoArgsConstructor
public User() {}
// @AllArgsConstructor
public User(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
// @ToString
@Override
public String toString() {
return "User(id=" + id + ", name=" + name + ", age=" + age + ")";
}
// @Data 生成的 getter/setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
// @Data 生成的 equals 和 hashCode
@Override
public boolean equals(Object o) { /* 省略实现 */ }
@Override
public int hashCode() { /* 省略实现 */ }
}
是不是清楚了一些?
二、为什么要用这些
在Java实体类中设置无参构造方法、有参构造方法和toString()
方法,主要是为了满足编程规范、框架兼容性和开发便利性的需求。
1. 为什么要设置无参构造方法(@NoArgsConstructor
)?
(1) 框架反射依赖
- Hibernate、MyBatis、Spring Data JPA 等ORM框架在从数据库查询数据并映射到对象时,通常需要先调用无参构造方法创建对象,再通过反射设置字段值。
- 如果没有无参构造方法,这些框架可能会报错。
(2) 序列化/反序列化需求
- JSON/XML 反序列化(如
Jackson
、Gson
)通常需要无参构造方法创建对象,再填充数据。 - 例如:
// 反序列化时,Jackson 会先调用无参构造方法,再通过 setter 填充数据 User user = objectMapper.readValue(json, User.class);
(3) Java Bean 规范
- 标准的 Java Bean 要求提供无参构造方法,以便工具和框架能动态创建实例。
2. 为什么要设置全参构造方法(@AllArgsConstructor
)?
(1) 方便对象初始化
- 在手动创建对象时,可以直接传入所有参数,避免逐个调用
setter
:// 使用全参构造方法 User user = new User(1L, "张三", 25); // 对比:没有全参构造方法时,需要逐个 set User user = new User(); user.setId(1L); user.setName("张三"); user.setAge(25);
(2) 不可变对象(Immutable Objects)
- 如果字段是
final
的(不可变),就必须在构造方法中初始化,而不能用setter
:@AllArgsConstructor public class User { private final Long id; // final 字段必须通过构造方法初始化 private final String name; }
(3) 配合 @Builder
使用
- 如果使用
@Builder
(建造者模式),通常需要全参构造方法支持:@Builder @AllArgsConstructor public class User { private Long id; private String name; }
3. 为什么要重写 toString()
方法(@ToString
)?
(1) 调试和日志输出
- 默认的
Object.toString()
返回的是类名@哈希码
(如User@1a2b3c
),对调试毫无帮助。 - 重写后,可以直观地看到对象内容:
User user = new User(1L, "张三", 25); System.out.println(user); // 输出:User(id=1, name=张三, age=25)
(2) 日志记录
- 在日志中打印对象时,
toString()
能提供更友好的信息:log.info("用户信息:{}", user); // 输出:用户信息:User(id=1, name=张三, age=25)
(3) 避免手动拼接字符串
- 如果没有
@ToString
,每次打印对象都要手动拼接字段:// 没有 @ToString 时,需要手动拼接 System.out.println("User(id=" + user.getId() + ", name=" + user.getName() + ")");
做一个小总结
注解 | 作用 | 适用场景 |
---|---|---|
@NoArgsConstructor | 生成无参构造方法 | ORM框架(Hibernate/MyBatis)、反序列化(JSON/XML) |
@AllArgsConstructor | 生成全参构造方法 | 快速初始化对象、不可变对象、Builder模式 |
@ToString | 生成 toString() | 调试、日志打印、避免手动拼接字符串 |
@Data | 组合注解(包含@Getter 、@Setter 、@ToString 、@EqualsAndHashCode 等) | 简化POJO类代码 |
最佳实践
- 大多数情况下,可以直接用
@Data
,它已经包含了@ToString
和@RequiredArgsConstructor
(部分场景替代@AllArgsConstructor
)。 - 如果涉及ORM框架,额外加上
@NoArgsConstructor
。 - 如果需要Builder模式,可以加
@AllArgsConstructor
和@Builder
。