java模块化强封装性通过模块边界与显式exports双重约束实现:未导出的public类对外不可见、不可导入、不可加载,编译期即报错,而非依赖private修饰符。

Java 中的强封装性在模块化系统里不是靠“加更多 private”实现的,而是通过模块边界 + 显式导出双重约束达成的——包内类可以是 public 的,但只要没被模块导出,外部就根本看不到它。
模块默认不对外暴露任何内容
一个模块创建后,所有包都默认私有。即使你写了 public class Utils 放在 com.example.internal 包里,只要没在 module-info.java 中 exports com.example.internal,其他模块连 import 都写不了,编译直接报错。
- 这和传统类路径完全不同:以前只要类是 public,反射或直接引用都能访问;现在 JVM 在模块路径下会强制拦截
- 不需要改类的访问修饰符,封装由模块定义驱动,更符合“设计意图”
exports 控制的是“可见范围”,不是“访问权限”
exports 不是让外部能调用 private 方法,而是决定“这个包是否出现在模块的 API 表面”。一旦导出,外部只能访问该包中 public 类的 public 成员;private、package-private、protected 全部照常受限,语言层规则不变。
- 例如导出
com.example.api,但其中ApiHelper类有个void logInternal()方法(包级访问),外部仍无法调用 - 导出本身不降低安全性,只是打开“入口门”,门后的一切访问控制依然由 Java 原有机制执行
未导出包里的 public 类,对外等于不存在
这点最体现“强”字:不是“不能用”,而是“找不到”。JVM 不会把未导出包中的 public 类加载进模块的可解析符号表。
- IDE 里 Ctrl+Click 跳转失败,提示 “Cannot resolve symbol”
- 编译时报
package com.example.internal is not visible,不是运行时异常 - 连
Class.forName("com.example.internal.Secret")都会抛ClassNotFoundException(除非走不安全的类加载器绕过)
配合 requires 和限定导出,实现细粒度隔离
你可以只对特定模块开放某个包,比如:
exports com.example.spi to com.example.plugin;
这意味着只有 com.example.plugin 模块能用这个 SPI,其他模块哪怕依赖了你的模块,也看不到它。
- 避免“为一个模块开全库权限”的粗放做法
- 内部服务接口、测试专用工具类、适配器实现等,都可以按需定向暴露
- 这种控制在类路径时代完全无法表达
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











