java模块系统(jpms)通过编译期限制实现封装:exports显式导出api包,未导出包即使public也无法被外部访问;opens精准控制反射权限;requires强制声明依赖并建立编译期准入契约;包内默认访问与模块边界协同构建双重防护。

Java 中封装通过模块化访问控制限制包间可见性,核心是让“看不见”成为默认状态,而不是靠运行时检查去拦。模块系统(JPMS)从编译期和类加载期就切断非法访问路径,比在代码里写一堆 if 判断更彻底、更轻量。
exports 只导出真正需要的包
模块默认不对外暴露任何包。你必须显式声明哪些包能被其他模块看到:
- 在 module-info.java 中写 exports com.example.api;,外部模块才能引用该包下的 public 类
- 把实现类、配置类、工具类放在 com.example.internal 或 com.example.config 这类未导出包里,即使类是 public,外部也编译不过
- 避免使用 exports to 泛化开放,除非明确知道调用方模块名;否则别人加个 requires 就能直接读你的内部结构
opens 精准放开反射入口,不等于全盘开放
有些框架(如 Jackson、Hibernate)依赖反射读写私有字段,但 exports 对反射无效。这时用 opens 控制:
- opens com.example.model; 允许任意模块反射访问——慎用,等同于松开安全带
- opens com.example.config to spring.core; 只放行指定模块,其他模块反射调用会触发 IllegalAccessError,根本进不了你的 setter
- 没写 opens 的包,哪怕类是 public、字段是 private,反射 setAccessible(true) 也会失败
requires 是权限契约,不是功能依赖
requires 不只是“我要用它”,更是“我被允许用它”:
- A 模块没声明 requires B,就算 B 在 classpath 上,A 也看不到 B 导出的任何类型,编译直接报错
- 第三方库想调用你的能力,必须先出现在你的 requires 列表中——这本身就是一道编译期准入门槛
- 避免滥用 requires transitive,否则下游模块会被动获得不该有的可读权限,破坏隔离边界
包可见性 + 模块边界 = 双重防护
模块内协作仍可高效进行,靠的是包级访问(default 修饰符)与模块结构协同:
- 把高内聚的类(如 TokenStore、AuthHelper、DbConfigBuilder)全放在 com.example.auth.core 包下,字段和方法用默认修饰符,同包内自由调用
- 对外只提供 AuthClient(public 接口)和 AuthToken(public DTO),其余全部不可见
- 测试类放在相同路径(src/test/java/com/example/auth/core),可直接访问包级成员,无需破环封装换 protected
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











