module-info.java必须位于src/main/java/根目录下,不可置于任何包路径内,否则编译报错“module info file not found”。

模块声明文件 module-info.java 必须放在源码根目录
Java 模块系统要求每个模块必须有且仅有一个 module-info.java,它不能放在 src/main/java/com/example/ 这类包路径下,而必须和 java 包同级——即位于 src/main/java/module-info.java。否则编译器直接报错:error: module info file not found。
常见错误现象:把 module-info.java 放进某个子包里(比如 src/main/java/com/example/module-info.java),结果 javac 完全无视它,还提示找不到模块描述。
- 模块声明文件不是普通 Java 类,不参与包结构,JVM 和编译器只认根目录下的那个
- IDE(如 IntelliJ)有时会自动创建在错误位置,需手动剪切到正确路径
- Maven 构建时,如果用了
maven-compiler-plugin但未显式启用模块支持(<release>9</release>或<source>9</source>),即使文件位置对,也会静默跳过模块解析
requires 和 requires static 的区别直接影响运行时行为
写 requires java.sql; 表示该模块**必须**在编译和运行时都可用;而 requires static java.xml.bind; 表示只在编译期需要,运行时缺失不会导致 NoClassDefFoundError——但前提是代码中没在运行时主动反射加载或条件调用相关类。
典型踩坑场景:升级到 JDK 11 后移除了 java.xml.bind,如果你用的是 requires static,且没通过 Class.forName("javax.xml.bind.JAXBContext") 这类方式触发加载,应用能正常启动;但如果写了 requires java.xml.bind;,哪怕一行代码都没用到 JAXB,JVM 启动就会失败,报错:Module java.xml.bind not found。
立即学习“Java免费学习笔记(深入)”;
-
requires static不传递依赖,下游模块无法访问被 static 依赖的类型 - 反射调用(如
Class.forName)、ServiceLoader 查找、或instanceof检查某类时,仍可能触发类加载,此时static也救不了你 - JDK 9+ 中很多旧 API(如
javax.annotation)已转为可选模块,必须用requires static+ 显式添加第三方 jar(如javax.annotation-api)才能安全使用
模块路径(--module-path)和类路径(-cp)不能混用
一旦用了 --module-path,JVM 就进入“模块模式”,此时 -cp 会被完全忽略(除非加 --class-path 并配合 --add-modules 等补救参数)。最直接的表现是:明明 jar 在 classpath 里,却报 ClassNotFoundException。
常见错误操作:Maven 打包后执行 java -cp target/app.jar -m com.example.app/com.example.Main,结果失败——因为 -m 要求主模块必须在模块路径上,而 -cp 加载的 jar 默认视为“未命名模块”(unnamed module),无法被 -m 识别。
- 正确做法是:
java --module-path target/app.jar -m com.example.app/com.example.Main - 如果依赖了传统 jar(如
guava.jar),它们也得放进--module-path,并确保你的模块声明里写了requires guava;(前提是该 jar 已加了Automatic-Module-NameMANIFEST 条目) - 没有模块声明的 jar 会变成 unnamed module,只能被其他 unnamed module 访问,不能被具名模块
requires
JDK 9+ 的默认模块图让 rt.jar 彻底消失
以前靠 -Xbootclasspath 黑科技替换核心类库的方式,在模块化之后彻底失效。因为 java.base 等平台模块由 JVM 内置加载,不再对应磁盘上的 rt.jar 或 classes.jsa 文件。试图用 --patch-module java.base=... 强行注入,只在开发调试阶段勉强可行,生产环境极易因签名、封装或启动顺序问题崩溃。
真实影响:所有依赖修改 String、ArrayList 或 ClassLoader 内部行为的工具(如某些字节码增强 agent、老版本 JMockit、或自定义启动类加载器),在 JDK 9+ 上要么抛 java.lang.SecurityException: Prohibited package name,要么在模块边界处静默失败。
-
--add-opens可临时打开模块封装(如--add-opens java.base/java.lang=ALL-UNNAMED),但这是权宜之计,不是长期方案 - 模块系统默认禁止跨模块反射访问非 exported 包,连
setAccessible(true)都会被拦截 - 如果你的项目还在用
sun.misc.Unsafe或jdk.internal.*,现在就得正视替代方案——这些包从 JDK 9 起就不是稳定 API,模块化只是让问题暴露得更早、更硬
module-info.java,而是没意识到:一旦启用模块,连 System.out.println 背后都牵扯到 java.base 的 exports、java.logging 的 optional usage、以及启动时模块解析器对 provides/uses 的校验。










