真正封装需private字段+校验型setter+不可变副本getter:setter校验输入(如age∈(0,150)),getter返回防御性拷贝(如unmodifiablelist),禁用全量set方法,因string等不可变类可public final而list等可变容器必须控制访问路径。

Java里怎么用private+getter/setter才算真正封装
封装不是加个private就完事,关键在“控制访问路径”。很多代码把字段设成private,但setter无脑透传、getter直接返回可变对象引用,等于没封。
- 字段必须
private,连protected都不行——子类访问会绕过校验逻辑 -
setter里要校验:比如setAge(int age)得检查age > 0 && age ,不能只做赋值 -
getter返回不可变副本:如果字段是List<string></string>,别直接return原集合,改用new ArrayList(this.items)或Collections.unmodifiableList(this.items) - 避免提供“全量修改”方法:比如不要写
setUser(User user),而应拆成setName()、setEmail()等带校验的单点方法
为什么String、LocalDateTime这类字段可以直接public final但List不行
因为String和LocalDateTime是不可变类(immutable),即使你把它设为public final,外部也无法修改其内部状态;但List、Map、数组这些可变容器,一旦暴露引用,调用方就能add/remove/clear,彻底破坏封装边界。
- 安全做法:
private final List<string> tags = new ArrayList();</string>,然后只提供addTag(String tag)和getTagsCopy() - 错误示范:
public List<string> getTags() { return tags; }</string>—— 这会让外部随意修改内部列表 - 注意
final只保证引用不变,不保证对象内容不变。对可变对象,final只是第一道弱防线
IDE自动生成getter/setter时最常漏掉的三件事
IntelliJ或Eclipse一键生成的getter/setter默认不处理校验、深拷贝、空值防御,直接用等于埋雷。
- 字符串字段的
setter没判null:应该写if (name == null) throw new IllegalArgumentException("name cannot be null"); - 集合字段的
getter没做防御性拷贝:生成后手动替换成return Collections.unmodifiableList(new ArrayList(this.items)); - 数值字段没范围校验:比如
score字段,生成的setScore(int score)应补上if (score 100) throw ...
当需要序列化(JSON/DB)时,封装和Jackson/Hibernate怎么共存
框架需要访问字段或调用setter,但又不能破坏封装逻辑——这时候别开后门,用标准机制解耦。
立即学习“Java免费学习笔记(深入)”;
- Jackson:用
@JsonCreator和@JsonProperty标注全参构造器,让反序列化走构造路径,避开无校验的setter - Hibernate:用
@Access(AccessType.PROPERTY)强制它通过getter/setter访问,确保校验逻辑生效 - 禁止用
@JsonIgnore屏蔽字段再另写“工具方法”:那等于在封装墙上凿洞,后续维护者根本看不出数据流从哪进哪出 - 如果必须用字段直访(如Lombok的
@Data),至少加上@Setter(AccessLevel.PRIVATE)并手写带校验的public方法
真正的封装难点不在语法,而在每次新增一个字段时,是否条件反射地问自己:“这个值可能被谁改?改的时候有没有业务规则要 enforce?”——漏掉一次,数据就可能从那个口子开始腐烂。











