【Java SE】Nest-Based Access Control(嵌套访问控制/嵌套伴随/巢穴机制)
Nest-Based Access Control
Java 语言一直允许同一顶层类中的嵌套类型(inner class、static nested class、局部类、匿名类)彼此访问对方的 private 成员。但在 Java 11 之前,这更多是“语言层的承诺”,JVM 并没有把“同一顶层类及其嵌套类集合”当作一个统一的访问控制单元,因此 javac 需要用一些技巧把它实现出来;而 Java 11 的 Nestmates / Nest-Based Access Control 则把这件事变成了 JVM 层面的正式规则。 (baeldung.com)
Java 11 之前:为什么需要“桥接方法(bridge / accessor)”?
先看一个典型例子:内部类调用外部类的 private 方法。
public class Outer {
private void outerPrivate() {}
class Inner {
public void innerPublic() {
outerPrivate();
}
}
}
从源码看完全合法;但从 JVM 的类访问规则看,Outer 与 Outer$Inner 是两个 class,Outer$Inner 直接访问 Outer 的 private 成员本应不被允许。于是编译器会在 Outer 里合成一个包可见的 synthetic accessor(常见形态是 access$000),Inner 通过它间接调用 outerPrivate()
但带来一堆副作用:
- 字节码更臃肿(多了很多
access$方法) - 反编译/调试更“脏”
- 某些安全审计/插桩/覆盖率统计会被 synthetic 方法干扰
- 访问链更长,性能虽通常影响不大,但确实不必要
另一个坑:反射在 Java 11 之前会“更严格”
更容易让人困惑的是:源码能调 private,但反射却可能不行。
例如从 Inner 里反射调用 Outer.outerPrivate():
public void innerPublicReflection(Outer ob) throws Exception {
var method = ob.getClass().getDeclaredMethod("outerPrivate");
method.invoke(ob);
}
在 Java 11 之前,这通常会抛出 IllegalAccessException(典型信息类似“Inner 不能访问 Outer 的 private 成员”)。这在直觉上很反常:反射调用理应与源码语义一致。
Java 11:Nest-Based Access Control(巢穴访问控制)怎么解决的?
Java 11 把“nest(巢穴)/ nestmates(嵌套伴随)”引入 JVM:
- 一个巢穴有一个 nest host(巢穴宿主):通常是顶层类,例如
Outer - 巢穴里包含若干 nest members(巢穴成员):例如
Outer$Inner、Outer$1(匿名类)等 - 同一巢穴内的类互为 nestmates(嵌套伴随) :JVM 允许它们彼此访问
private成员,从根上消除了“需要绕行”的问题
为此,.class 文件新增两个属性:
NestHost:写在成员类里,指向宿主(host)NestMembers:写在宿主类里,列出所有成员(members)
于是对 JVM 来说:Outer$Inner 访问 Outer.secret 不再是“陌生类访问 private”,而是“同一巢穴内访问”,直接通过校验。
直接收益:很多场景下不再需要生成 access$... 这种桥接方法;同时,核心反射对 nestmates 的访问也变得更符合源码语义,上面的 innerPublicReflection() 在 Java 11 里就能正常执行。
Java 11 提供的 Nestmate 反射 API
java.lang.Class 增加了 3 个常用方法:
getNestHost():返回当前类所属 nest 的 hostisNestmateOf(Class<?>):判断是否为同一 nest 的成员getNestMembers():返回该 nest 的所有成员类数组
一个简单示例:
System.out.println(Outer.class.getNestHost()); // Outer
System.out.println(Outer.Inner.class.getNestHost()); // Outer
System.out.println(Outer.Inner.class.isNestmateOf(Outer.class)); // true
如何观察 Nestmates
想验证“Java 11 前后”的差异,可以反编译 class 文件观察:
- Java 11 之前:更容易看到
Outer里出现access$000一类的 synthetic 方法,以及Inner通过它间接访问private - Java 11 之后:更关键的是 class 文件里会出现
NestHost/NestMembers,JVM 原生认可 nestmates 的private访问,从而减少/消除桥接方法需求
用 JDK 11+ 编译:
javac Outer.java
javap -v Outer.class
javap -v Outer$Inner.class
会在输出中看到类似信息:
- 在
Outer.class中:NestMembers: Outer$Inner ... - 在
Outer$Inner.class中:NestHost: Outer
这就是巢穴关系的证据。
Nestmates 的核心价值可以用一句话概括:
把“同一个顶层类的嵌套类之间可访问 private”从编译器技巧,升级为 JVM 的正式访问控制规则。
更多推荐




所有评论(0)