exports 控制包级可见性而非变量访问:仅允许外部模块使用被 exports 包中的 public 类、方法和字段;非 public 变量即使在已 exports 包中也无法被直接访问,反射还需 opens 授权。

exports 关键字不控制“变量”的跨模块访问,它控制的是整个包对其他模块的可见性——仅限于该包中被声明为 public 的类、接口、方法和字段。模块内非 public 的变量(比如 package-private、private 或 protected 字段),哪怕所在包被 exports,外部模块依然无法直接读写。
exports 的作用边界很明确
它只在模块边界起效,解决的是“能不能看到并正常使用”这个问题,不是“能不能拿到某个具体变量”。Java 模块系统(JPMS)本身不介入类内部成员的访问控制逻辑,那是传统访问修饰符(public/protected/包私有/private)的事。
- exports com.example.api; → 其他模块能 import 这个包、new 其 public 类、调用其 public 方法
- 但若 User 类有个
String id;(无修饰符,即包私有),外部模块连编译都过不了,更别说访问这个变量 - 即使通过反射尝试获取该字段,也会抛
IllegalAccessException,因为 exports 不开放反射权限
想让外部模块访问某个变量?先看它是不是 public
只有变量本身是 public 且定义在被 exports 的包里,才可能被外部模块访问:
- ✅ 正确示例:
public class Config { public static final String API_URL = "https://api.example.com"; },配合exports com.example.config;,其他模块就能写Config.API_URL - ❌ 错误理解:把一个
String token;(非 public)放在已 exports 的包里,指望外部能直接读取——这行不通 - ⚠️ 注意:静态常量(
public static final)因编译期内联特性,行为略有不同,但普通 public 变量仍需走常规访问流程
需要反射访问非 public 变量?opens 才是答案
如果框架(如 Spring、Jackson)需要读写 private 或包私有字段,exports 完全无效,必须用 opens:
-
opens com.example.model;→ 所有模块可通过反射访问该包下所有类的任意成员 -
opens com.example.config to spring.core;→ 更安全,只授权指定模块反射权限 - opens 不影响编译,也不让外部模块“直接引用”非 public 类或变量,它只为反射开运行时绿灯
常见误操作与替代思路
不要试图靠 exports “导出单个变量”,Java 模块系统不支持这种粒度。合理做法是:
- 把需要暴露的变量封装进 public 类或 public 静态工具类中,并确保该类所在包被 exports
- 避免暴露内部状态变量,优先提供 public getter/setter 方法(方法本身也需是 public)
- 若变量属于配置类,考虑用
java.util.ServiceLoader+provides/uses实现解耦访问 - Node.js 中的
module.exports是另一套机制,和 Java 的 exports 无关,不可混淆










