JPMS彻底改变了Java类加载逻辑和封装模型:模块路径成为依赖解析权威来源,public类需配合exports才对外可见;未命名模块可访问具名模块导出包但不可反向访问,自动模块牺牲封装性换取兼容性,运行时校验提前暴露依赖问题。

JPMS 彻底改变了 Java 的类加载逻辑和封装模型:它让类路径(classpath)退居辅助地位,模块路径(modulepath)成为依赖解析的权威来源;同时把“public”从访问通行证降级为“需配合导出才有效”的受限标识。
模块路径取代类路径成为依赖主干
Java 9 起,JVM 启动时优先按模块路径解析依赖。若一个 JAR 包含 module-info.class,它就被当作具名模块加载,其依赖必须通过 requires 显式声明;而放在类路径下的传统 JAR,则被归入“未命名模块”,所有包默认可被反射或跨包访问——但无法参与模块化依赖图验证。
- 未命名模块能访问具名模块中
exports的包,但不能访问未导出包(哪怕类是 public) - 具名模块无法访问未命名模块中的类,除非用
--add-reads手动打通 - 同名 JAR 出现在类路径和模块路径时,模块路径版本优先生效,可能引发意外覆盖
强封装让 public 不再等于“对外可见”
在 JPMS 下,“public 类”只是语法可见性,真正能否被其他模块使用,取决于它所在的包是否被模块 exports。未导出包里的 public 类,对其他模块而言等同于不存在——编译报错,运行时报 IllegalAccessError。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
exports com.example.api;:仅该包下 public 类型对所有模块开放 -
exports com.example.spi to com.example.plugin;:只允许指定模块访问 -
opens com.example.internal.reflection;:允许反射访问(如序列化框架),但不开放编译期引用
自动模块带来兼容性妥协
老项目直接把传统 JAR 放进模块路径,JVM 会将其转为“自动模块”:模块名由 JAR 文件名推导(如 guava-32.1.3-jre.jar → guava),并自动导出全部包、隐式依赖 java.base。这虽能跑通,但失去封装控制和依赖显式性。
立即学习“Java免费学习笔记(深入)”;
- 自动模块无法声明
requires transitive或uses,服务发现机制失效 - 多个同名自动模块(如不同版本 guava)加载顺序不确定,易引发 NoClassDefFoundError
- 推荐做法:用
jdk.jdeps分析依赖,逐步为关键第三方库打上模块描述
运行时隔离更严格,错误更早暴露
类路径时代常见“侥幸运行”——比如某工具类没被直接调用,就躲过类加载失败;而 JPMS 在模块解析阶段就校验依赖完整性。缺失 requires 或未导出包,编译或启动即失败,杜绝了隐式耦合蔓延。
- 模块图在 JVM 初始化时构建,循环依赖、缺失依赖直接中断启动
- 反射访问受阻时不再静默失败,而是抛出明确异常,推动开发者修复封装边界
- 结合
jlink可裁剪出最小运行时,彻底排除无关类——这是纯类路径无法实现的

















