Java 9+中模块路径优先于类路径,类路径内容被视为未命名模块,自动读取所有命名模块且导出全部包,但同名类以模块路径为准;跨路径资源访问须用上下文类加载器。

Java 9+ 支持模块路径(--module-path)与类路径(-cp 或 -classpath)共存,但必须清楚两者角色不同、加载优先级明确、资源访问需主动适配——不是简单并列,而是有主次、有边界、有协作逻辑。
模块路径为主干,类路径为兼容层
当 JVM 同时看到 --module-path 和 -cp,它会:
- 先解析模块路径上的所有 JAR,构建模块图;只有模块图完整,模块系统才真正启用
- 将类路径上所有内容统一视为一个“未命名模块”(Unnamed Module),它自动读取所有命名模块,也导出全部包,但不参与
requires声明 - 同名类(如
com.example.User)在模块路径中存在时,类路径中的同名类会被完全忽略,不会触发冲突或警告
跨路径资源访问要用上下文类加载器
模块内代码默认无法通过 MyClass.class.getResource("/config.properties") 加载类路径下的资源,因为模块不自动“打开”对未命名模块的访问。
- 正确做法是:用
Thread.currentThread().getContextClassLoader().getResource("config.properties") - 该加载器即系统类加载器(AppClassLoader),它能稳定定位类路径资源
- 若需反射访问类路径中的类(如旧版插件),在
module-info.java中加opens mypackage to java.base;,而非尝试requires unnamed.module(语法非法)
避免自动模块干扰封装性
把传统 JAR 放进模块路径(而非类路径)会让 JVM 把它当作“自动模块”(Automatic Module),虽可被 requires,但会导出所有包、无封装、无版本语义——这削弱模块化收益。
立即学习“Java免费学习笔记(深入)”;
- 原则:只把真正模块化的 JAR(含
module-info.class)放进--module-path - 遗留 JAR、配置文件、脚本类、动态插件等,一律留在
-cp,由未命名模块承载 - 如需模块代码调用类路径中的服务,改用
ServiceLoader.load(Service.class, ClassLoader.getSystemClassLoader())
启动命令写法要规范
顺序和写法影响行为,推荐显式分隔、避免歧义:
- ✅ 正确:
java --module-path mods --class-path "lib/*:config/" -m com.example.app/com.example.Main - ❌ 错误:
java -cp "lib/*" --module-path mods ...(部分 JVM 版本可能忽略--module-path) - 注意:
--class-path是标准写法,-cp是别名,二者不可混用在同一命令中


















