exports仅允许其他模块访问包内public成员,不开放反射权限;opens才允许反射访问private及package-private成员,且不影响编译期可见性。

Java 9 的模块化系统(JPMS)不控制“对象变量”的跨包访问权限,它控制的是跨模块对包、类、成员的访问权限。包内访问(比如同一模块内不同包之间)仍由传统的 Java 访问修饰符(public、protected、包私有、private)决定;而模块系统只在模块边界上起作用——即当代码来自另一个模块时,能否看到某个包、能否加载其中的类、能否通过反射读写字段。
exports 控制“能不能用”——仅限 public 成员
当你在 module-info.java 中写 exports com.example.api;,表示:其他模块可以编译并运行时使用这个包里的 public 类和 public 成员。
- 其他模块能 new 这个包里的 public 类,能调用它的 public 方法,能读写它的 public 字段
- 但哪怕该包里有个
public class User { String name; }(name是包私有字段),其他模块也无法访问user.name—— 编译报错,运行也失败 - 即使用反射调用
field.setAccessible(true),也会抛IllegalAccessException,因为exports不开放反射权限
opens 控制“能不能反射”——包括 private 和 package-private 成员
如果你需要让其他模块(比如 Spring、Hibernate、JUnit)通过反射操作你的类的非 public 成员,必须显式声明 opens:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
-
opens com.example.model;→ 允许所有模块反射访问该包内所有类的所有成员(含private字段/方法) -
opens com.example.config to spring.core;→ 只允许spring.core模块反射访问,更安全 - 注意:
opens不影响编译期可见性,也不让其他模块“直接引用”包私有类;它只在运行期为反射开绿灯
package-private 类根本跨不了模块
一个类如果没有 public 修饰符(即包私有),哪怕你 exports 了所在包,其他模块也完全看不到这个类:
- 编译时报
cannot find symbol - 运行时报
NoClassDefFoundError或IllegalAccessError - 这不是反射问题,是类加载器连类名都不认识——模块系统不会“穿透”包级封装
- 想复用又不想暴露 public API?可改用
provides ... with+ service interface,把实现类保持包私有
常见误判与调试提示
遇到“明明 exports 了却访问不到”,先检查这几点:
- 目标类或字段是不是
public?不是的话,exports无效,必须配opens - 是否用了
--illegal-access=deny(JDK 16+ 默认)?没opens就直接失败,不给警告 - IDE 可能不报错(因编译器只校验
exports),但 JVM 启动时才真正拦截,建议用jlink或真实模块路径测试 - 第三方库(如 commons-lang3)没
module-info.class?它会变成“自动模块”,exports规则不生效,需用--add-opens临时绕过(仅开发)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










