java访问修饰符本质是权限开关而非加密锁:private仅本类可见,protected扩展至同包及不同包子类,default限于同包,public全局可达;字段私有化配getter/setter旨在控制数据访问逻辑而非简单暴露内存布局。

Java 类的内部数据不是靠“保护”实现的,而是靠“拒绝访问”——访问修饰符本质是权限开关,不是加密锁。
public / protected / default / private 到底谁能看到谁
别记口诀,看编译器怎么报错最实在:private 成员在子类里直接写 super.xxx 会报 error: xxx has private access in YYY;protected 在不同包的子类里能用,但在不同包的非子类里写 new A().xxx 就报错;default(包级私有)连同包子类都进不去,跨包即失联;public 是唯一不设防的出口。
-
private:只对本类有效,哪怕嵌套类也要分静态/非静态——static内部类不能直接访问外部类的非静态private字段 -
protected:本类 + 同包任意类 + 不同包的子类——注意,“子类”指继承链上的直接或间接子类,不是所有子孙对象实例 -
default:本类 + 同包任意类——哪怕只是同包的一个工具类,也能肆意读写 -
public:任何地方都能调,包括反射绕过——它不提供安全,只提供可达性
为什么把字段设成 private 还要配 getter/setter
因为字段本身不是数据接口,而是存储载体;暴露字段 = 暴露内存布局和修改逻辑。比如一个 balance 字段,如果设为 public double balance,调用方就能写 acc.balance = -1000.0,而你完全拦不住。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 用
private double balance+public double getBalance(),至少能确保读取时返回的是当前一致值(比如加个synchronized或从缓存取) - 写操作必须走
public void deposit(double amount),里面可以校验amount > 0、触发事件、更新时间戳 - 不要无脑生成
setBalance(double b)——一旦提供了,就等于放弃对字段状态的所有控制权 - Lombok 的
@Data默认生成所有 getter/setter,容易误伤;更安全的是@Value(不可变)或手写精简的访问方法
package-private(default)常被当成“内部API”,其实很危险
很多框架或模块喜欢把工具类、上下文对象的字段设成 default,以为“只要不导出包就安全”。但只要对方和你在同一模块路径下(比如测试源码目录 src/test/java 和主代码在同一 classpath),就能直接访问——Maven 多模块项目里,test 依赖 main,测试类能自由读写 default 成员,这根本不是封装,是裸奔。
- IDE 不会警告,编译器也不拦,但违反了“最小暴露面”原则
- 模块化(
module-info.java)后,default 成员默认不可见,除非显式exports整个包——这时你就得直面“到底要不要对外暴露”的决策 - 想表达“仅限本模块使用”,优先用
private+ 静态工厂方法,而不是赌别人不会碰 default 字段
真正难的不是选哪个修饰符,而是识别哪些字段本就不该存在——比如把配置值硬编码成 public static final 常量,结果上线后发现要动态刷新;或者把缓存对象设为 protected,让子类直接修改导致状态不一致。修饰符管边界,设计管意图;边界划得再严,意图错了,照样崩。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










