Java 9模块系统(JPMS)在访问修饰符之上新增模块可见性约束,形成“先过exports关、再过修饰符关”的双重权限控制:exports决定包是否可见,private/default/protected/public语义不变但受模块边界限制。

Java 的访问修饰符本质是封装的落地工具,而 Java 9 引入的模块系统(JPMS)在原有包级访问控制之上,叠加了一层更粗粒度的“模块可见性”约束——它不替代 private/default/protected/public,而是与之协同工作,形成两层权限过滤:先过模块导出关,再过访问修饰符关。
四个基础修饰符的核心行为没变
Java 9 并未修改 public、protected、default(包私有)、private 的语义和作用范围。它们依然按类、包、继承关系决定可见性:
- private:仅本类可访问,模块化下仍完全不可穿透
-
default(无修饰符):仍限于同一包内;但若该包未被模块声明为
exports,即使同包,跨模块也无法访问(因为根本看不到这个包) -
protected:同包 + 跨包子类仍成立;但子类必须位于被
exports且opens(如需反射)或至少exports对应包的模块中 - public:仍是最高开放级别;但前提是它所在的类、所在包、所在模块都必须被显式导出
模块化新增的关键约束:exports 和 opens
模块描述文件 module-info.java 中的声明,成了访问的“第一道闸门”:
-
exports com.example.service;:允许其他模块访问该包下的 public 类及其 public/protected 成员;不 exports,哪怕全是 public,外部模块也编译报错“package not visible” -
exports com.example.service to com.acme.app;:定向导出,仅指定模块可见 -
opens com.example.config;:专为反射设计——允许其他模块对该包进行运行时反射访问(如 Spring 注入 private 字段),但不影响编译期访问修饰符检查 -
opens com.example.config to com.acme.app;:定向开放反射权限
实际影响最明显的两个场景
日常开发中,以下情况会直接暴露模块与修饰符的双重作用:
-
想用别人模块里的 public 工具类,却编译失败:不是它没写 public,而是对方模块没
exports对应包。你看到的类名甚至不会出现在 import 提示里 - 自己模块里 default 方法被同包其他类调用正常,但被另一个模块的子类重写失败:default 成员本身就不支持跨包访问,模块化只是让这个限制更早暴露(编译期就拦住,而不是运行时报 NoSuchMethodError)
-
Spring 或 JPA 反射设置 private 字段失败:模块未
opens相关包,JVM 在模块边界拦截了反射请求,抛出InaccessibleObjectException
开发建议:权限要分两步想
判断一个成员能否被访问,现在需要连续回答两个问题:
- 它的模块是否
exports所在包?(模块可见性) - 它的修饰符是否允许当前调用方访问?(类/包/继承关系)
只要任一环节否决,访问即被拒绝。实践中,优先用 private 封装内部细节,对需要对外提供的能力,明确 exports 包并配以 public 接口——模块化让“最小权限原则”从编码习惯升级为编译强制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











