Java 9起模块可见性由JPMS控制,需在module-info.java中exports/opens包并requires依赖,而访问修饰符仍约束类成员级可见性,二者正交协同。

Java 本身没有“模块可见范围”这一层访问控制机制,访问修饰符(public、protected、default、private)只作用于类、成员变量、方法和构造器,不直接控制整个模块的可见性。模块级的可见性控制是从 Java 9 引入的 模块系统(JPMS, Java Platform Module System) 实现的,它与传统访问修饰符是正交且互补的两套机制。
模块声明决定包对外暴露范围
在模块系统中,一个模块是否能被其他模块“看到”以及“用到”,取决于它的 module-info.java 文件中的声明:
-
模块必须显式声明为
open或导出(exports)包,其他模块才能访问其中的 public 类型;否则即使类是 public 的,也无法被模块外访问。 -
exports pkg.name;:允许其他模块使用该包中 public 的类、接口等(但不包括反射访问 private 成员)。 -
opens pkg.name;:允许其他模块对该包进行反射访问(如 setAccessible),常用于序列化、测试框架等场景。 -
requires other.module;:声明依赖,只有声明了依赖,才能访问对方导出的包。
访问修饰符仍起基础作用,但受模块边界约束
模块导出后,其内部类的可访问性仍需满足传统访问修饰符规则:
- 即使包已
exports,如果某个类是package-private(默认修饰符),其他模块依然无法访问它。 - 如果一个 public 类中有个
protected方法,其他模块的子类可以继承并访问该方法——但前提是该子类所在包也属于被exports的模块,且父类确实被导出。 -
private和protected的语义不变,只是作用域被限制在模块+包+继承关系三层嵌套内。
常见误区与实践建议
开发者容易混淆“类可见”和“模块可见”。例如:
立即学习“Java免费学习笔记(深入)”;
- 写了一个 public 类,放在未导出的包里 → 其他模块编译失败:“package is not visible”。
- 导出了包,但类用 default 修饰 → 编译通过,但运行时其他模块找不到该类(JVM 拒绝链接)。
- 想让测试模块访问主模块的 package-private 代码 → 应使用
opens(配合反射)或qualified exports(如exports pkg to test.module)。
小结:两层控制协同工作
模块可见性由 module-info.java 控制(谁可以读、谁可以反射),而类/成员可见性由访问修饰符控制(谁可以在代码中引用、调用)。二者共同构成完整的封装体系:模块是“城门”,修饰符是“房间门”。不打开城门,再大的房间门也没用;打开了城门,还得看房间里每扇门是否开着。


















