public字段比private+getter/setter更危险,因其直接暴露导致无法校验(如age=-5),而setter可加约束;封装核心是控制访问而非凑数,应按需提供getter/setter,避免@data滥用破坏边界。

为什么 public 字段比 private + getter/setter 更危险
因为直接暴露字段会让调用方绕过所有校验逻辑和内部约束。比如 age 字段设为 public int age,外部代码就能写 user.age = -5 或 user.age = 200,而你完全无法拦截。
用 private 配合 getAge()/setAge(int age),才能在 setAge 里加判断:
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在 0~150 之间");
}
this.age = age;
}
- 哪怕现在没校验需求,也建议默认用
private+ 方法——后期加逻辑不破坏 API -
final字段如果真不需要修改,就别写setter;写了却空实现或直接抛异常,反而误导调用方 - IDE 自动生成的 getter/setter(如 IntelliJ 的
Alt+Insert)默认不带校验,别直接交出去
getter/setter 不是“必须成对出现”的铁律
封装的核心是控制访问,不是凑数。只读字段就只写 getXxx(),禁止修改就压根不写 setXxx()。
常见误用:
立即学习“Java免费学习笔记(深入)”;
- 给
id字段配setId(),但数据库主键本就不该被外部改 - 给计算属性(如
getFullName())配setFullName(),结果内部拆分逻辑混乱 - 为布尔字段生成
isXxx()和setXxx()没问题,但别把isRunning()改成getRunning()—— 这违反 JavaBeans 规范,部分框架(如 Jackson、Spring Data)会识别失败
lombok @Data 会悄悄破坏封装边界
@Data 看似省事,但它无差别生成所有字段的 getter/setter,包括你不希望暴露的内部状态字段(如 retryCount、cacheKey)。
更隐蔽的问题:
- 生成的
toString()可能打印敏感信息(如密码哈希、token) -
equals()/hashCode()默认包含所有字段,若某个字段是临时缓存值,会导致比较失真 - 如果某字段需要特殊序列化(如
@JsonIgnore),@Data生成的 getter 仍会被 Jackson 调用,得额外加注解压制
建议:用 @Getter + @Setter 显式标注需要暴露的字段,而不是一揽子 @Data。
访问权限不是越严越好,而是按协作层级设防
private 是底线,但 protected 和包级(default)权限常被忽略其实际用途。
- 子类需要扩展行为?用
protected暴露核心字段或方法,比public更安全(仅限继承树内可见) - 同包内工具类需复用逻辑?包级权限比
public更精确——不对外暴露,又避免重复实现 - 别为了“看起来封装好”把一切塞进
private,结果导致测试时只能靠反射或加@TestOnly方法,反而增加维护成本
真正难的是判断“谁该信任”,而不是机械套用权限关键词。










