封装的核心是控制访问而非单纯隐藏字段,如private int age需配合setAge校验年龄范围(0≤age≤150),否则private形同虚设。
封装不是“把属性设成private就完事了”
很多初学者以为只要把字段加个 private,再补上 get/set 方法,就算完成了封装。其实不然——封装的核心是“控制访问”,而不是“隐藏字段”。比如你写了一个 private int age;,但又在 setAge(int age) 里不做校验,外部仍能传入 -5 或 200,那这个 private 就只是个摆设。
真正有效的封装要配合业务逻辑做约束:
-
setAge应该检查范围(如0 ),非法值直接抛IllegalArgumentException - 如果
age一旦设定就不该被修改,那就只提供getAge(),不写setAge() - 如果计算依赖多个字段(如
isAdult()基于age和当前年份),就把逻辑放在类内部,而不是让调用方自己拼条件
什么时候不该暴露setter:从可变性说起
Java 中的封装常被误用为“一律提供 setter”。但现实里大量对象本应不可变(immutable)——比如 Person 的身份证号、订单的创建时间、配置项的键名。一旦允许随意修改,就可能引发状态不一致、线程安全问题或缓存失效。
判断是否该加 setter 的关键不是“将来会不会改”,而是“这个值是否属于该对象的固有、稳定特征”:
立即学习“Java免费学习笔记(深入)”;
- 构造时确定且永不变更的字段 → 只在构造器中赋值,无
setter,字段声明为final - 需要更新但必须受控的字段(如用户邮箱)→ 提供带校验和审计日志的专用方法(如
changeEmail(String newEmail)),而非裸setEmail() - 纯粹的计算结果(如
getFullName())→ 不存为字段,每次调用实时拼接,避免数据滞后
getter/setter不是万能胶水,过度使用反而破坏封装
当你发现一个类里塞了十几个 getXXX()/setXXX(),而且调用方总在反复读-改-写多个字段(比如先 getA(),再 getB(),算出新值后 setC()),说明职责已经外泄。这违反了“信息专家原则”:行为应该靠近它所操作的数据。