JPMS通过模块边界和唯一包导出原则杜绝同名类冲突:禁止多模块导出同一包,启动时校验失败即报ResolutionException,强制设计合规。

module-info.java 本身不处理“包级变量冲突”,因为 Java 中没有“包级变量”这一概念。所谓“包级变量冲突”,实际是开发者对 同名类冲突 或 同包不同模块导出导致的类加载歧义 的误称。真正需要解决的是:多个模块各自打包了相同全限定名(如 com.example.utils.Helper)的类,且这些类被同时置于模块路径(--module-path)中——此时 JPMS 会直接拒绝启动,报 java.lang.module.ResolutionException。
明确模块边界,杜绝同名类共存
JPMS 在启动阶段强制执行“唯一模块原则”:同一模块名不可重复出现;更关键的是,它禁止两个不同模块导出(exports)同一个包名。一旦 module-info.java 中声明 exports com.example.utils,其他模块就无法再导出该包——编译或运行时即报错,而非静默覆盖。
- 若你发现两个模块都试图导出
com.example.utils,说明设计违规,必须合并、重命名或重构包结构 - 不要依赖“先加载哪个 jar”来决定用哪份类;模块系统主动拦截这种模糊性,这是保护机制,不是限制
- 检查冲突类是否意外被多个模块封装(未
exports却放在 classpath 上),应统一收口到一个模块并显式导出
用 requires static + transitive 精准控制传递依赖可见性
当模块 A 和模块 B 都依赖第三方库 X(如 org.slf4j:slf4j-api),但版本不同,容易引发方法签名冲突(如 NoSuchMethodError)。module-info.java 可配合构建工具做前置约束:
- 在核心模块中用
requires static org.slf4j.api声明编译期依赖,避免运行时强绑定 - 对稳定基础模块(如日志门面)使用
requires transitive org.slf4j.api,让下游模块自动继承该依赖,减少版本碎片 - 禁止在 module-info.java 中写
requires java.base等隐式模块——它们由平台自动解析,硬写反而干扰验证
隔离冲突包:用 unnamed module 或封装策略兜底
遗留代码或无法模块化的第三方 jar(如含 MANIFEST.MF 但无 module-info.class)会进入 unnamed module。此时若它与某模块导出相同包,JPMS 不阻止,但类加载器可能优先加载 unnamed 中的类,造成“看似导出却失效”。
立即学习“Java免费学习笔记(深入)”;
- 将这类 jar 单独放在 classpath(非 module-path),并通过
--add-opens或--add-exports显式开放必要包(仅限调试/迁移过渡期) - 在模块内用
opens com.example.internal to other.module替代无差别exports,仅对可信模块开放反射访问,不暴露给整个模块图 - 对敏感包(如
com.example.config)不导出,改用服务接口(uses/provides)解耦,从根源规避包名碰撞
验证与诊断:用 JVM 参数快速定位冲突源头
迁移中遇到类加载异常,别急着改代码,先用 JVM 内置能力确认模块行为:
- 启动时加
--show-module-resolution:打印每个类由哪个模块提供,一眼识别同名类归属 - 加
--illegal-access=deny强制暴露非法反射调用,提前发现因封装导致的IllegalAccessError - 结合
jdeps --list-deps --module-path mods/ myapp.jar查看模块间真实依赖链,比 pom.xml 更真实反映运行时图谱



















