java-11之前的内部类访问字段

yqhsw0fo  于 2023-01-04  发布在  Java
关注(0)|答案(3)|浏览(115)

有这样的课:

public class Sample1 {

    public class Inner {
        private int f;
    }

    void go() {
        Inner in = new Inner();
        int value = in.f;
    }

}

go方法(java-11之前)的字节码调用了已知的合成方法:

static int access$000(nestmates.Sample1$Inner);
    descriptor: (Lnestmates/Sample1$Inner;)I
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #1                  // Field f:I
         4: ireturn

发件人:

0: new  #2  // class nestmates/Sample1$Inner
3: dup
4: aload_0
5: invokespecial #3 // Method nestmates/Sample1$Inner."<init>":(Lnestmates/Sample1;)V
8: astore_1
9: aload_1
10: invokestatic #4 // Method nestmates/Sample1$Inner.access$000:(Lnestmates/Sample1$Inner;)I

我知道这件事已经有一段时间了,但是我从来没有问过自己 * 为什么 * 会这样?为什么javac必须首先这样做,为什么不直接使用getField和通过静态方法的间接访问。谢谢。

z2acfund

z2acfund1#

因为JVMS不允许访问不同类的私有成员,不管它是否是内部类。JDK 11添加了nestmates的概念来克服这个限制。

ujv3wf0j

ujv3wf0j2#

原因是外部类和内部类编译到不同的类文件,这意味着它们不能访问彼此的私有成员。
生成合成方法是为了有效地将访问从私有扩展到包私有,但它是通过不必要的间接实现的。
在Java 11中,他们为此引入了一个新概念,允许不同文件中的类在某些情况下(例如,这个)访问彼此的私有成员。
参见JEP:https://openjdk.org/jeps/181

eiee3dmh

eiee3dmh3#

这是一个简单的结论,基于许多因素:
1.在JVM中(class file /java.exe)级别,**内部类根本不存在。**Javac通过将Inner类命名为Sample1$Inner来“伪造”它(美元不是一个呈现,而是它实际JVM级别名称,$只是一个符号,与In一样有效),将Sample1类型的参数作为第一参数添加到Inner的所有构造函数,用new Sample1(this)替换new Sample1(),用new Sample1(instanceOfOuter)替换instanceOfOuter.new Sample1(),具有Sample1类型的final字段(这些构造函数通过使用第一个参数设置),将对外部方法的所有调用转换为在该字段上调用(假设在JVM级别没有“outter this”,因为没有外部类),等等。使用javap -c可以看到所有这些。

  1. private成员不能被除自身之外的任何类型访问,因此,假设内部类在编译结束后就不再是内部类(因为JVM首先没有内部类的概念),它需要一种包私有(或者受保护或公共,根据您的需要)的方式来访问它。
  2. JVM * 也 * 不知道synthetic是什么意思。它是一个标志,是的,JVM完全忽略了它。它不知道它意味着什么,它根本不影响代码的运行或解释。javac知道synthetic是什么意思。也就是说:标记您创建的任何内容,以便将精心管理的伪代码粘合在一起,使内部类看起来像是一个纯粹的Java语言(即编译器)特性,并且不存在于java运行时级别--并且在阅读类文件时,对它们视而不见。就像这些合成方法不存在一样。例如,如果javac被要求编译试图调用合成方法的代码,操作方式与编译尝试调用不存在的方法的代码相同。
    正如一个评论已经指出的,java do 的最新版本在JVM级别引入了内部类的概念,但不是通过在JVM规范中体现“内部类”的概念,而是通过“嵌套”的概念,它允许类文件列出其他可以“看到”私有元素并调用/与私有元素交互的类名。如果目标是现代JVM(“现代”在此定义为:“有可用的巢友功能”),将使用巢友和放弃所有的合成。

相关问题