private是编译期限制而非运行时保护,TS/Java中仅影响类型检查或JVM反射可绕过;真私有需WeakMap等运行时手段,ES2022#field为原生私有语法。

private字段为什么不能直接用obj.field读写
因为private是编译期限制,不是运行时保护。TypeScript或Java里声明private name: string后,TS编译器会报错Property 'name' is private and only accessible within class 'User',但JS运行时根本不存在这个检查——只要绕过TS编译(比如直接写JS、改生成代码),照样能obj['name'] = 'xxx'。
所以private本质是给开发者和IDE看的契约,不是安全边界。真要防篡改得靠Object.defineProperty或WeakMap这类运行时手段。
- TS中
private仅影响类型检查,不改变生成JS代码结构 - JS原生无
private字段(ES2022前),#field语法才是真正的私有(需Babel/现代环境支持) - Java的
private在JVM层可被反射绕过,setAccessible(true)就能强行访问
Getter/Setter不是必须配对,但混用容易出bug
很多人以为写了get name()就必须写set name(),其实完全不用。只读属性就只写get;只写(极少)就只写set;但一旦两者都存在,就要注意逻辑一致性。
常见坑是set里做了校验或转换,但get没同步处理,导致读写值不等价。比如set price(v) { this._price = Math.round(v * 100); },但get price() { return this._price; }——这时候obj.price = 19.99再读出来是1999,不是预期的19.99。
- 只读场景:用
get计算派生值(如get fullName() { return this.firstName + ' ' + this.lastName; }) - 校验场景:在
set里抛错或归一化(如set email(v) { if (!v.includes('@')) throw new Error('Invalid'); this._email = v.toLowerCase(); }) - 避免在
get/set里做异步操作或重计算,会影响性能且破坏直觉
TS中private字段+get/set的典型写法
最稳妥的组合是:用private修饰存储字段(如private _name: string),再用public get name()和public set name()暴露访问接口。这样既满足封装,又让外部能用点号操作,IDE也能正确推导类型。
注意别漏掉public修饰符——TS类成员默认是public,但get/set必须显式声明访问修饰符,否则编译报错Accessors must have the same accessibility。
class User {
private _age: number = 0;
<p>public get age(): number {
return this._age;
}</p><p>public set age(value: number) {
if (value < 0 || value > 150) throw new Error('Invalid age');
this._age = value;
}
}-
private _age确保字段不被外部直接访问 -
get age()和set age()必须同为public或同为protected - 不要用
private get age()——外部根本调用不到,失去意义
Java里private字段配getter/setter的取舍
Java没有语言级的属性语法,所以private String name;必须靠getName()/setName(String)暴露。但不是所有字段都值得包一层——简单POJO里加一堆空壳方法反而增加噪音。
真正需要getter/setter的场景就三个:字段要校验、要监听变更、或未来可能加逻辑。其余情况用Lombok的@Data或Record类型更干净。
- 字段带业务约束(如
status只能是"active"/"inactive")→ 必须setter校验 - 字段变更要触发事件(如
onNameChanged)→ 在setter里发通知 - 字段是计算值(如
getFullName())→ 用getter,不存冗余字段 - 纯数据载体(DTO、Entity)且确定永不加逻辑 → 直接
public final或Record更轻量
复杂点在于,一旦对外暴露了getter/setter,后续想改成计算属性或加校验,就变成API-breaking change。所以一开始就要想清楚字段的“可变性”和“语义稳定性”。









