Java访问修饰符未失效,而是与模块系统叠加生效:修饰符控制类内可见性,模块控制包是否导出;成员被外部访问需同时满足足够修饰符权限和包已导出且调用方已requires。

Java 的访问修饰符在模块化系统中不是失效了,而是和模块边界叠加生效——它们仍管“类内可见性”,模块系统额外管“包是否对外暴露”。两者配合,才能准确定义“谁能在哪看到什么”。
访问修饰符仍是基础门禁,模块是外层围墙
private、default、protected、public 这四类修饰符的语义没有变,但它们的作用范围被 module-info.java 中的导出声明进一步收紧:
- 一个 public 类 如果所在包没被
exports,外部模块即使知道类名也无法访问它(编译报错:package not visible) - 一个 default 成员 即使同包,若该包未导出,连同模块内的其他包也访问不到(模块内未导出的包默认不可见)
-
protected 成员 跨包继承时,子类能否访问父类 protected 字段,不仅要看继承关系,还要看父类所在包是否导出、子类所在模块是否
requires了父模块
模块导出 + 访问修饰符 = 可见性的双重校验
某成员要被外部模块访问,必须同时满足两个条件:
- 其声明所在的类、字段或方法具有足够宽的访问修饰符(如 public 或 protected)
- 该成员所属的包已被模块声明为
exports,且调用方模块已通过requires声明依赖
例如:exports com.example.service; 后,只有该包下 public 类中的 public 方法 才真正对外可用;包内 default 工具类、protected 辅助方法,哪怕在导出包里,外部模块依然无法使用。
立即学习“Java免费学习笔记(深入)”;
模块化让 default 修饰符更精准,也更易被忽略
在非模块项目中,default 成员对同包类“天然可见”;模块化后,这个“同包”必须落在同一个已导出或未导出但内部可访问的包内。常见陷阱:
- 把工具类设为
default,却忘了它所在的包没exports→ 模块内其他包也访问不了 - 测试代码放在不同模块(如 test-module),即使包名相同,也不算“同包” → default 成员不可见,需改用 public 或加
opens配合反射 - 想让某包仅对特定模块开放?用
exports com.example.api to com.example.client;—— 此时 default 成员仍不可见,但 public 成员只对 client 模块开放
static + 访问修饰符 + 模块导出,三层控制共享状态
静态成员的可见性由三者共同决定:
-
private static:模块内外都只能本类用,最安全 -
public static final:若所在类在导出包中,可被外部模块直接引用(如MyModuleConstants.VERSION) -
protected static:跨包子类能继承并访问,但前提是父类包已导出,且子类模块正确 requires - 不推荐
public static非 final:模块化也无法阻止外部篡改,破坏封装


















