opens 声明用于显式授权反射访问包内成员,仅 exports 不足;需在 module-info.java 中用 opens com.example.model; 或 opens com.example.model to com.example.app; 语法,不可通配;未命名模块须用 --add-opens 指定 all-unnamed;exports 与 opens 可共存,且缺一不可。

用 `opens` 声明允许反射访问的包
模块内需显式声明哪些包允许被其他模块通过反射访问,仅靠 exports 不够——它只控制 public 类的编译期可见性,不放开字段、方法或构造器的运行时反射权限。
在 module-info.java 中使用 opens 关键字,格式为:opens com.example.model;(对所有读取该模块的命名模块开放)
或更精确地:opens com.example.model to com.example.app;(仅开放给指定模块)
- 必须写全包名,不能通配(JPMS 不支持
opens com.example.*) - 如果目标模块是未命名模块(如传统 classpath 启动的代码),不能用
to ALL-UNNAMED——ALL-UNNAMED是运行时概念,不能出现在module-info.java中 -
opens和exports可共存:先exports让类可见,再opens让其私有成员可反射
启动时用 --add-opens 补充临时授权
当无法修改源模块(比如用的是第三方库 JAR),或测试阶段快速验证,可在 JVM 启动时加参数放开反射权限。
语法:--add-opens 源模块/包=目标模块
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 例如,让自己的模块
com.example.app反射访问java.util.ArrayList的私有字段:--add-opens java.base/java.util=com.example.app - 若项目尚未模块化(无
module-info.java),目标模块写ALL-UNNAMED:--add-opens java.base/java.lang=ALL-UNNAMED - 多个包要开放?必须重复写多条,不能合并或通配:
--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED
注意边界和常见陷阱
反射权限受双重控制:模块声明 + 运行时参数。漏掉任一环节都会失败。
- 只
exports不opens→ 外部能 new 类,但调用getDeclaredField("xxx").setAccessible(true)仍抛InaccessibleObjectException(JDK 16+ 默认行为) - 只
opens不exports→ 外部连类名都看不到,反射无从谈起 - 用
--add-opens是绕过手段,上线前应优先补全module-info.java中的opens声明,否则模块封装形同虚设 - 自动模块(classpath 加载的 JAR)默认拥有完全反射权限,但会破坏强隔离;建议为其补全
Automatic-Module-Name并逐步迁移为命名模块
验证是否生效
运行时可通过以下方式确认反射权限已正确开放:
- 启动时加
--illegal-access=debug,观察日志是否还有WARNING: Illegal reflective access - 用
jdeps --module-path mods/ --list-deps your-module.jar查看模块依赖图,确认opens声明已被解析 - 在代码中尝试
Class.forName("xxx").getDeclaredField("yyy").trySetAccessible(),检查返回值是否为true
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










