java模块化中,变量可见性需类声明与module-info.java协同:public变量仅在exports包且requires依赖后才跨模块可用;反射访问私有变量必须opens包,否则抛inaccessibleobjectexception。

Java 类变量声明和程序配置的模块化不是两个孤立概念,而是分层协作的关系:类变量控制单个类内部的数据可见性与生命周期,模块化(通过 module-info.java)则控制整个包乃至类在跨模块场景下的**外部可访问边界**。真正落地时,二者必须协同——变量设为 public 不代表能被其他模块使用,模块没导出包、没声明依赖,照样编译失败。
类变量声明:作用域决定“能不能用”,但仅限本模块内
Java 中的类变量(静态变量)和实例变量,其访问修饰符(private、protected、public、默认包级)只影响同一模块内其他类能否直接引用。例如:
-
public static final String VERSION = "1.2";—— 其他类可直接调用,但前提是它所在的包已被当前模块exports; -
static int counter;(包私有)—— 即使模块导出了该包,其他模块仍无法访问这个变量; -
private String configPath;—— 只能被本类访问;若需被 Gson 等框架反射读取,模块必须opens所在包,且最好指定to框架模块名(如opens mypackage to com.google.gson;)。
module-info.java:决定“谁能在什么条件下用你的变量/类”
模块描述文件不处理变量本身的定义,但它决定了变量所属的类、所在包是否具备跨模块可用性。关键指令对应关系如下:
-
requires xxx;→ 告诉 JVM:“我要用 xxx 模块里的 public 类”,否则连new Gson()都会报错 “cannot find symbol”; -
exports com.example.config;→ 允许其他模块 import 并使用该包下的 public 类(含其 public static 变量),但不开放反射; -
opens com.example.model;或opens com.example.model to com.fasterxml.jackson.databind;→ 允许指定模块在运行时通过反射访问该包内类的私有字段(比如 Jackson 序列化private String name;); -
requires transitive xxx;→ 当前模块导出的 API 本身用到了 xxx 的类型(如返回值是xxx.SomeType),则引入本模块的其他模块也自动获得对 xxx 的访问权,避免下游重复写requires。
实战常见陷阱与修复路径
典型报错往往暴露的是模块配置缺失,而非变量写法错误:
-
package com.example.util is not visible→ 缺少exports com.example.util;,哪怕所有类都是public; -
error: module not found→module-info.java放错了位置(必须在src/main/java/根目录,不能在子包里); -
InaccessibleObjectException(反射失败)→ 不是变量没设public,而是模块未opens包,或未opens ... to具体框架模块; - 第三方库类“找不到”→ 检查它是标准模块(如
com.google.gson)、自动模块(如slf4j.simple),还是需要手动加Automatic-Module-Name到 MANIFEST.MF。
配置建议:从最小闭环开始迭代
不要一上来就写满 exports/opens/uses/provides。推荐步骤:
- 先确保
module-info.java在正确路径,JDK 版本 ≥ 11,Maven 中启用<release>11</release>; - 写一个最简模块,只
requires java.base,验证能编译运行; - 添加一个工具类并
exports其包,让另一模块requires后调用其public static方法或常量; - 引入 Gson/Jackson 后,遇到反射报错,再针对性加
opens; - 最后按需加入
uses/provides支持 ServiceLoader 场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











