private修饰符锁住的是字段、方法或内部类的直接外部访问,仅限本类内部(含构造函数、getter/setter)访问;它不阻止间接操作,关键取决于getter/setter是否含校验逻辑。

private修饰符在类中到底锁住了什么
它只阻止外部代码直接访问字段,不阻止类内部(包括构造函数、方法、getter/setter)读写。很多初学者误以为加了private就“彻底隐藏”,其实只要暴露了public的getXXX()或setXXX(),外部依然能间接操作——关键看getter/setter里有没有逻辑控制。
Getter和Setter不是必须成对出现
常见误区是“有private字段就得写get和set”。实际取决于使用场景:
- 只读字段:只写
getAge(),不提供setAge(),比如计算出的isAdult - 只写字段(少见但合法):只写
setName(),getName()抛UnsupportedOperationException或返回null(如日志上下文ID) - 带校验的setter:比如
setAge(int age)里判断age 150并抛IllegalArgumentException - 延迟初始化的getter:字段为
null时才创建对象,避免构造开销
Java中IDE自动生成的Getter/Setter有陷阱
IntelliJ或Eclipse一键生成的getXXX()/setXXX()默认不做空值检查、类型转换或边界校验。例如:
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
这会让setName(null)静默通过。如果业务要求name非空,必须手动改写setter:
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("name cannot be null or blank");
}
this.name = name.trim();
}
同理,getter若返回可变对象(如ArrayList),应返回副本而非原引用,否则外部可绕过封装修改内部状态。
Kotlin里private set和val的区别很关键
很多人混淆val name: String和var name: String private set:
-
val name: String→ 编译后是private final字段 +public getName(),完全不可变(连类内都不能改) -
var name: String private set→ 字段仍是private,但类内可调用name = "new",外部只能读不能写
后者适合需要内部更新但对外只读的场景,比如计数器:var retryCount: Int = 0 private set,每次失败时类内自增,外部只观察当前值。
真正容易被忽略的是:getter/setter里的逻辑可能成为性能瓶颈(比如每次get都查数据库),或者引发隐式副作用(比如setter里触发事件通知)。封装不是加个private和两个方法就完事,得想清楚“谁该改”“改之前要确认什么”“改之后要同步什么”。

















