module-path 与 classpath 是 Java 9+ 并行的两套加载机制:前者加载命名/自动模块,强制依赖声明与包可见性控制;后者将所有类归入 unnamed module,无访问约束。

module-path 不是 classpath 的替代品,而是另一套加载规则
Java 9+ 启动时,--module-path 和 -cp(即 --class-path)不是“选一个用”的关系,而是两套并行、互不兼容的类加载逻辑。JVM 看到 --module-path 就启用模块系统(JPMS),所有在该路径下的 JAR 若含 module-info.class,就被当作命名模块处理;而 -cp 下的所有内容——不管有没有模块信息——全被塞进一个叫 unnamed module 的“大杂烩”里,没有依赖声明、没有包可见性控制。
常见错误现象:
- 把 Spring Boot 3.x(含
module-info.class)放进-cp:模块声明完全失效,requires和exports形同虚设 - 把 Guava 32.1.3-jre.jar(无
module-info.class)硬塞进--module-path:它变成自动模块(guava.32.1.3.jre),但所有包自动导出,可能意外暴露内部类 - 同一 JAR 既出现在
--module-path又出现在-cp:触发error: the unnamed module reads package xxx from both A and B
命令行里 -p 和 -cp 的顺序和优先级怎么影响运行
JVM 加载类时严格按路径优先级:模块路径 > 类路径。这意味着即使 -cp 里有新版依赖,只要 --module-path 里存在同名模块(哪怕版本更旧、API 更少),JVM 就一定加载后者。
实操建议:
-
--module-path只影响模块解析,对传统类查找无效;-cp只影响unnamed module的类加载,对模块内requires的依赖完全没用 - 不要靠顺序“覆盖”:写成
java -cp lib/ -p mods/ Main并不能让lib/里的类优先于mods/中的模块 - 验证是否生效:运行
java --list-modules | grep your-module-name,若没输出,说明它根本没被识别为模块
IDE(如 IntelliJ/Eclipse)里 Classpath 和 Modulepath 选错会怎样
IDE 的构建路径配置中,“Classpath” 和 “Modulepath” 是两个独立标签页,不是开关切换。选错位置 = 告诉 JVM 用错加载规则。
使用场景与判断依据:
- 项目没有
module-info.java→ 只能走 Classpath;即使 JDK 是 21,模块系统也不会激活 - 项目有
module-info.java,但依赖的是老版 Log4j 2.17(无模块信息)→ 必须放 Modulepath,它会转为自动模块;放 Classpath 会导致NoClassDefFoundError,因为模块内代码看不到unnamed module的类 - 依赖库同时提供模块化和非模块化版本(如 Jetty 12 vs 11)→ 查看 JAR 内是否含
module-info.class:jar -f jetty-server-12.0.0.jar | grep module-info
运行时报 “Module not found” 或 “Unable to derive module descriptor” 怎么快速定位
这两个报错本质都是模块身份识别失败,不是路径写错了,而是 JVM 在 --module-path 下找到了 JAR,却无法确认它是哪个模块。
排查步骤:
-
Module not found: xxx:检查requires xxx;是否拼写正确;再确认xxx对应的 JAR 确实放在--module-path下,且名字能被推导(如slf4j-api-2.0.13.jar→ 模块名slf4j.api);若名字含数字点号太多,可显式用--add-modules slf4j.api -
Unable to derive module descriptor:JAR 里既没有module-info.class,又没通过--describe-module或--add-modules显式声明模块名 - 平台模块漏加:用了
java.logging却没加--add-modules java.logging,尤其在 jlink 构建的最小镜像中极易发生
最容易被忽略的一点:模块路径中的 JAR 名字决定了自动模块名,而模块名一旦生成,就会影响 requires 的写法——比如 guava-32.1.3-jre.jar 推导出的模块名是 guava.32.1.3.jre,不是 guava,写 requires guava; 就会失败。











