封装的核心是限制直接访问并提供可控入口,需四步落实:一、字段全用private显式声明;二、getter安全读取且返回副本;三、setter严格校验并防御性拷贝;四、按需提供而非机械成对。

封装的核心不是加个private就完事,而是通过限制直接访问 + 提供可控入口,让对象状态既安全又可用。关键在“怎么锁、怎么放、放多少”。
第一步:属性必须用private显式声明
所有需要保护的字段都得加上private关键字,不能省略,也不能用默认(包访问)或protected/public替代。
- 命名统一用小驼峰,比如
userName、accountBalance,不加m前缀或_下划线 -
private String email;✅ 正确String email;❌ 默认包可见,同包类可随意改public int age;❌ 彻底破坏封装,后续加校验要改所有调用点 - 常量(如
MAX_RETRY)不属于封装讨论范围,该用public static final
第二步:getter要安全读,不带副作用
getter只负责返回值,不修改状态,也不触发耗时操作。
- 方法名以
get开头(布尔类型可用is),首字母大写字段名,如private boolean isActive;→public boolean isActive() - 返回基本类型或不可变对象(
String、LocalDate)可直接返回 - 若字段是可变引用(如
List<String>),应返回副本:return new ArrayList(tags);,避免外部绕过封装修改内部数据 - 别在getter里加日志、数据库查询或计算逻辑——调用方无法预料性能开销
第三步:setter要校验再赋值,拒绝非法输入
setter是数据进入对象的唯一闸口,必须守住边界。
- 方法名以
set开头,参数类型与字段一致,如private int age;→public void setAge(int age) - 必做基础校验:空值检查、范围限制、格式验证。例如:
if (name == null || name.trim().isEmpty()) throw new IllegalArgumentException("姓名不能为空");this.name = name.trim(); - 接收可变对象时做防御性拷贝:
this.items = new ArrayList(items); - 避免在setter里调用复杂逻辑(如发通知、改其他字段),保持职责单一;副作用应由更高层协调
第四步:按需提供,不必机械成对
不是每个private字段都必须配齐getter和setter。
- 只读字段(如ID、创建时间):只写getter,不提供setter。若为
final,构造器中初始化即可 - 只写字段(少见):setter做校验并存值,getter可抛
UnsupportedOperationException或返回null/默认值 - 计算属性(如
isAdult):字段本身可能不存在,getter里根据age动态判断,不设对应setter - IDE自动生成的getter/setter只是起点,必须人工审查并补上校验和防御逻辑

















