java封装通过private字段、带校验的setter和防御性编程阻断非法修改:private使字段仅类内可访问,外部直接赋值编译失败;setter强制校验(如年龄非负);反射需显式启用且不推荐。

Java 封装通过访问控制、受控接口和防御性编程三重机制,切实阻断外部对类内部状态的非法修改。
用 private 彻底切断直接访问
把字段声明为 private 是最基础也是最关键的一步。它让字段只能在本类内部被读写,编译器会直接拒绝任何跨类的点号访问或反射绕过(除非显式启用 unsafe 操作)。
- 外部代码写
user.age = -5会编译失败 - 子类、同包类、甚至反射默认也无法修改 private 字段(需 setAccessible(true),且不推荐)
- 字段一旦私有化,就等于从外部视角“消失”,只剩类自己能碰
用带校验的 setter 控制写入逻辑
setter 不是摆设,而是数据进入对象前的最后一道闸门。所有修改必须经过它,才能插入业务规则。
- 年龄不能为负数:
if (age - 订单状态只能按流程变更:
if (this.status == PENDING && newState == PROCESSING) { this.status = newState; } - 邮箱格式需验证:
if (!email.matches("^[\w.-]+@\w+\.\w+$")) throw ...
用不可变副本防止可变对象逃逸
如果字段是 List、Map、数组等可变类型,getter 直接返回原始引用,外部就能绕过 setter 修改内部状态——这等于把锁打开又把钥匙交出去。
- 错例:
public List<string> getTags() { return tags; }</string>→ 外部调用list.add("hack")破坏封装 - 正解:
public List<string> getTags() { return Collections.unmodifiableList(tags); }</string>或return new ArrayList(tags); - 对 Date、Calendar 等也适用:getter 中返回
new Date(original.getTime())
用 final 锁定不可变字段
对创建后绝不该变的数据(如 ID、创建时间、配置常量),用 final 修饰并仅在构造时赋值,从语言层面杜绝后续篡改可能。
-
private final String userId;+ 构造方法中this.userId = UUID.randomUUID().toString(); - 配合 private,final 字段既无法被外部修改,也无法在类内被二次赋值
- 天然线程安全,无需额外同步
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











