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 的类访问规则看,OuterOuter$Inner 是两个 class,Outer$Inner 直接访问 Outerprivate 成员本应不被允许。于是编译器会在 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$InnerOuter$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 的 host
  • isNestmateOf(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 的正式访问控制规则。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐