java 9+模块化需显式声明:module-info.java必须置于源码根目录,requires/transitive/static区分依赖传递性,exports/opens控制访问权限,运行时须用--module-path而非-cp。

Java 9+ 的模块化系统(Project Jigsaw)不是“开箱即用”的自动功能,它依赖显式声明和严格路径规则。核心是 module-info.java 文件——没有它,哪怕用了 Java 9+,项目仍是传统类路径模式。
module-info.java 必须放在正确位置
它必须位于源码根目录下,与第一个包目录平级。Maven 项目中就是 src/main/java/module-info.java。放错位置(比如放进 src/main/java/com/example/ 或和 pom.xml 同级)会导致编译器报错:error: module not found 或直接忽略模块声明。IntelliJ 等 IDE 升级老项目时容易自动生成到错误路径,需手动拖到正确位置。
requires 声明依赖要分清场景
模块之间通信靠 requires,但写法有关键区别:
-
requires java.sql;:当前模块能访问java.sql包,但不会把它的依赖“透传”给上游模块 -
requires transitive com.example.data;:不仅本模块可用 data 模块的 public 类,引用本模块的其他模块也能间接使用 data 所依赖的模块(如java.sql) -
requires static java.annotation;:仅编译期需要(如处理@NonNull),运行时不加载;误写成requires可能导致启动失败,写成requires static却又在运行时用到该模块类,则抛NoClassDefFoundError
exports 和 opens 控制不同层级的可见性
二者不能互换,作用完全不同:
-
exports com.example.config;:允许其他模块调用你这个包里的 public 类(如new Config()) -
opens com.example.config;:允许反射访问该包内所有成员(字段、方法、构造器),用于 JSON 反序列化、JPA 字段注入等 - 只
exports不opens→ Jackson 解析私有字段失败;只opens不exports→ 其他模块连类名都看不到,反射也无从谈起
启动必须用模块路径,不能混用 -cp
一旦项目含 module-info.java,运行时就得用 --module-path(缩写 -p),禁用 -cp:
- 正确命令:
java --module-path mods -m my.module/com.example.Main - 若仍用
-cp,JAR 会被当作匿名模块,命名模块默认无法读取它,即使写了requires也会提示 package 不可见 - 验证依赖关系可用:
jdeps --module-path mods my.module或java --list-modules
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











