bigywater.tech
Java · 336

Nestmates:嵌套类如何访问彼此的私有成员

内部类能直接访问外部类的私有成员,JVM 以前是靠编译器生成 access$000 桥接方法来实现的。Java 11 的嵌套伙伴(Nestmates)让 JVM 直接允许。这篇用 javap 对比两种编译结果,并用反射与故意破坏的类文件验证行为。

0 个
--release 21 编译的 Outer 里 access$ 合成方法的个数(--release 8 是 2 个)
2440 vs 2696 字节
同一份源码 --release 21 与 --release 8 的类文件总大小
IllegalAccessError
嵌套关系不一致时抛出的错误(旧桥接方式是 NoSuchMethodError)

编译结果对比

实测 · 本机执行输出
  [--release 21] 类文件 ['Outer$Inner.class', 'Outer$Nested.class', 'Outer.class'];总大小 2440 字节;major version 65
     Outer 里的 access$ 合成方法: 无
     Outer 有 NestMembers 属性: True ;Outer$Nested 有 NestHost 属性: True
  [--release 8] 类文件 ['Outer$1.class', 'Outer$Inner.class', 'Outer$Nested.class', 'Outer.class'];总大小 2696 字节;major version 52
     Outer 里的 access$ 合成方法: ['static int access$000(Outer);', 'static java.lang.String access$100();']
     Outer 有 NestMembers 属性: False ;Outer$Nested 有 NestHost 属性: False

运行与嵌套 API

实测 · 本机执行输出
  peek=7,call=hidden,both=12,viaInner=5
  getNestHost: Outer→Outer,Nested→Outer,Inner→Outer
  getNestMembers: [Outer, Outer$Inner, Outer$Nested]
  isNestmateOf: Nested~Inner=true,Nested~String=false;String.getNestHost()=String
  反射读私有字段 Outer.secret:从 Probe(不是嵌套成员)不调用 setAccessible → IllegalAccessException: class Probe cannot access a member of class Outer with modifiers "private"
    但经由 Outer$Nested.peek(它是 Outer 的嵌套成员)读到:7

嵌套关系不一致

实测 · 本机执行输出
  旧的 Outer$Nested(声明 NestHost: Outer)+ 新的 Outer(没有 NestMembers):
   Caused by: java.lang.IllegalAccessError: class Outer$Nested tried to access private field Outer.secret (Outer$Nested and Outer are in unnamed module of loader 'app'), (Type Outer$Nested (loader: 'app') is not a nest member of type Outer (loader: 'app'): curren
  同样的组合用 --release 8 编出来的旧 Outer$Nested(用 access$ 方法):
   Caused by: java.lang.NoSuchMethodError: 'int Outer.access$000(Outer)'

分步演示

Nestmates
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,javac --release 21 与 --release 8
    • 没有验证:嵌套类很多时的类加载差别、lambda 体与私有接口方法在 Java 11 前后的访问方式、模块边界与嵌套伙伴的关系、字节码工具(ASM)生成的类的 NestHost 属性写法、JDK 27 的行为
    京ICP备2026067502号-1京公网安备11010502063487号