java编译器通过生成access$开头的合成静态桥接方法,使成员内部类能合法访问外部类私有成员;这些方法由编译器自动生成、不可在源码调用,内部类通过隐式持有的外部类引用来调用它们。

Java 编译器在处理内部类(尤其是非静态内部类)访问外部类私有成员时,并不会直接破坏封装,而是通过生成桥接用的 access$ 开头的合成(synthetic)静态方法来间接实现。这些方法由编译器自动生成、不写在源码中,但存在于字节码里,是内部类“合法越界”的关键机制。
为什么需要 access$ 方法?
Java 语言规范要求私有成员只能被同一类中的代码访问。而内部类虽然逻辑上属于外部类的一部分,但在 JVM 层面它是一个独立的类(编译后为 Outer$Inner.class)。JVM 不认识“内部类”这个概念,只认类与类之间的严格访问控制。因此,当内部类要读写外部类的 private int x 或调用 private void helper() 时,直接访问会违反 JVM 的访问校验。
编译器的解决方案是:在外部类中自动插入包级可见(或更宽)的静态合成方法,把私有访问“代理”出去。例如:
private static int access$000(Outer o) { return o.x; }private static void access$100(Outer o) { o.helper(); }
这些方法名以 access$ 开头,后缀数字按生成顺序递增(有时也含字段/方法签名缩写),标记为 synthetic 和 bridge,对 Java 源码不可见,但内部类字节码可正常调用。
access$ 方法如何被内部类使用?
编译后的内部类字节码中,所有对外部类私有成员的访问,都会被重写为对对应 access$ 静态方法的调用。例如:
class Outer {
private int value = 42;
class Inner {
void print() { System.out.println(value); } // 源码写法
}
}
实际编译后,Inner.print() 的字节码等价于:
System.out.println(Outer.access$000(this.this$0));
其中 this.this$0 是编译器为内部类自动生成的隐式字段,指向其关联的外部类实例。
编译器何时生成 access$ 方法?
不是所有私有成员访问都会触发 access$ 方法生成。编译器会做优化判断:
- 如果外部类的私有字段/方法被内部类访问,且该成员未被其他方式(如 public getter)暴露,则生成对应
access$方法 - 若外部类已提供同功能的 package-private 或更宽松访问级别的方法(比如
int getValue()),编译器可能复用它,而不额外生成access$ - 静态内部类不能持有外部类实例引用,因此无法访问外部类的非静态私有成员;若它访问外部类的
private static成员,编译器仍可能生成access$,但仅限于静态上下文
查看 access$ 方法的实践方式
你可以用以下方式验证它的存在:
- 用
javap -c -p Outer.class查看反编译结果,搜索access$,能看到合成方法定义和字节码 - 用 IDE(如 IntelliJ)开启 “Show synthetic members” 选项,在外部类结构视图中看到灰色标注的
access$xxx方法 - 注意:这些方法不能在 Java 源码中直接调用,编译器会报错 —— 它们仅对 JVM 和编译器可见
这种机制体现了 Java 编译器在语言语义与 JVM 底层约束之间做的精巧适配:既维持了源码层面的封装承诺,又支撑了内部类的自然表达能力。










