Java 9推出的JPMS(Java Platform Module System)为Java项目带来了更细粒度的访问控制能力,传统仅靠public、protected等访问修饰符控制对象变量跨包访问的方式存在局限,JPMS通过模块声明文件可以进一步约束包级别的可访问范围。

JPMS核心访问控制基础
JPMS中每个模块都需要通过module-info.java文件声明自身信息,其中exports指令是控制跨包访问的核心配置,只有被exports的包,其内部public类型的成员才能被其他模块访问。
传统的访问修饰符作用范围仅在一个模块内部有效,跨模块访问时首先会校验模块是否导出了对应的包,再校验成员的访问修饰符权限。
module-info.java基础语法
模块声明文件的基本结构如下:
// 声明一个名为com.example.core的模块
module com.example.core {
// 导出com.example.core.model包,允许其他模块访问该包下的public成员
exports com.example.core.model;
// 不导出com.example.core.internal包,其他模块无法访问该包内容
}
对象变量跨包访问的权限判定流程
在JPMS环境下,对象变量要被其他包访问,需要满足两层校验:
- 第一层:变量所在的包是否被所在模块通过
exports导出,未导出的包无论变量是什么修饰符,其他模块都无法访问。 - 第二层:变量的访问修饰符是否为public,只有public的变量才能被跨包访问,protected和默认修饰符的变量即使包被导出也无法跨包访问。
不同场景的访问示例
我们先定义两个模块,一个是提供核心能力的com.example.core模块,一个是使用能力的com.example.app模块。
首先看com.example.core模块的结构:
// 模块声明文件,仅导出model包
module com.example.core {
exports com.example.core.model;
}
// com.example.core.model包下的用户类
package com.example.core.model;
public class User {
// public修饰的变量,所在包被导出,可跨包访问
public String username;
// 默认修饰符的变量,即使包被导出也无法跨包访问
String nickName;
// protected修饰的变量,无法跨包访问
protected int age;
// private修饰的变量,仅在类内部可访问
private String password;
}
// com.example.core.internal包下的配置类,该包未被导出
package com.example.core.internal;
public class Config {
public String appName;
}
再看com.example.app模块的调用代码:
// 模块声明文件,声明依赖core模块
module com.example.app {
requires com.example.core;
}
package com.example.app;
import com.example.core.model.User;
// 导入Config会编译报错,因为Config所在的包未被导出
// import com.example.core.internal.Config;
public class Main {
public static void main(String[] args) {
User user = new User();
// 可以访问,username是public且所在包被导出
user.username = "test";
// 编译报错,nickName是默认修饰符,无法跨包访问
// user.nickName = "nick";
// 编译报错,age是protected修饰符,无法跨包访问
// user.age = 18;
// Config类无法被导入,以下代码编译报错
// Config config = new Config();
// config.appName = "app";
}
}
进阶:限制特定模块的访问权限
如果只想让某个包被特定的模块访问,而不是对所有模块开放,可以使用exports...to指令指定导出的目标模块。
module com.example.core {
// 仅导出com.example.core.model包给com.example.app模块访问
exports com.example.core.model to com.example.app;
// 其他模块即使requires了com.example.core,也无法访问model包内容
}
此时如果有一个com.example.other模块依赖com.example.core,尝试导入com.example.core.model.User类会直接编译报错。
与传统访问控制方式的对比
| 控制维度 | 传统访问修饰符 | JPMS模块化控制 |
|---|---|---|
| 控制范围 | 类、变量、方法级别的单个成员 | 包级别的整包权限,可配合访问修饰符使用 |
| 跨模块生效性 | 仅在单个模块内部有效,跨模块无约束 | 跨模块访问的第一层校验,优先级高于访问修饰符 |
| 配置灵活性 | 修饰符绑定在代码上,修改需要改源码 | 通过module-info.java配置,无需修改业务代码 |
注意事项
module-info.java文件需要放在模块源码的根目录下,编译后会被打包到模块的根路径。- 未导出的包中的public类,即使其他模块通过反射也无法访问,JPMS会在运行时校验模块访问权限。
- 如果变量是static的public变量,跨包访问规则和普通public变量一致,同样需要先满足包导出的条件。
JPMS的访问控制是在类加载阶段就完成的,相比运行时的权限校验性能更高,也更适合大型项目的依赖管控。
JPMSJava_9模块化跨包访问权限module_infoexports修改时间:2026-06-29 16:39:49