Java类文件上的ACC_SUPER访问标志的目的是什么?

Jiv*_*ngs 27 java jvm class opcode

invokespecialJVM指令用于调用(初始化方法<init>)创建新对象时.指令的描述建议(但不澄清)关于是否调用超类的构造函数或当前类的构造函数的决定取决于文件中ACC_SUPER设置的标志的状态class.

来自Sun JVM规范:

接下来,选择已解析的方法进行调用,除非满足以下所有条件:

  • 为当前类设置ACC_SUPER标志(参见表4.1"类访问和属性修饰符").

- 来源(invokespecial操作码定义)

ACC_SUPER标志的设置指示Java虚拟机要表达的其invokespecial指令的两个备选语义中的哪一个; 存在ACC_SUPER标志,以便向后兼容Sun的旧编译器为Java编程语言编译的代码.Java虚拟机的所有新实现都应该实现本规范中记录的invokespecial的语义.Java虚拟机指令集的所有新编译器都应设置ACC_SUPER标志.Sun的旧编译器生成了Class_Sile标志,并且未设置ACC_SUPER.Sun的旧Java虚拟机实现会在设置时忽略该标志.

- 来源(ClassFile格式)

该定义指出该标志是为了与旧编译器向后兼容.然而它继续与之相矛盾Sun's older Java virtual machine implementations ignore the flag if it is set.

该标志是否仍与invokespecial操作码一起使用?从我所知,它似乎没有任何目的,我找不到建议它曾经做过的资源.

谢谢.

x4u*_*x4u 36

引入ACC_SUPER来纠正超级方法调用的问题.ACC_SUPER标志将类标记为针对操作码183指令的改变的语义而编译的.它的目的与类文件版本号类似,因为它允许JVM检测是否为该指令的较旧或较新语义编译了类.Java 1.0.2没有设置和忽略ACC_SUPER,而Java 1.1及更高版本总是设置ACC_SUPER.

在Java 1.1之前,现在调用的带有操作码183的字节代码指令invokespecial被调用,invokenonvirtual并且具有部分不同的规范.只要在没有虚方法查找的情况下调用实例方法,就会使用它.这是私有方法,实例初始化器(构造函数)和实现方法调用的情况super.但后一种情况导致了类库不断发展的问题.

字节代码(CONSTANT_Methodref_info)中的方法引用不仅定义了方法的名称,参数和返回类型,还定义了它所属的类.操作码183获得这样的方法引用参数,并且意味着直接从指定的类调用引用的方法而无需进一步查找.在调用的情况下,super编译器有责任解决实现此方法的最近的超类,并在字节代码中生成对它的引用.

从Java 1.1开始,它被改为基本上忽略了引用的类CONSTANT_Methodref_info,而是使用JVM中给定的方法名和签名来查找最接近的超级方法.这通常在类加载时或在执行指令或第一次编译JIT之前完成.

以下是为什么需要进行此更改的示例.在Java 1.0.2中,AWT类Container和Component以这种方式定义:

class Component
{
    public void paint( Graphics g ) {}
}

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}
Run Code Online (Sandbox Code Playgroud)

在Java 1.1中,类Conatiner被更改为拥有它自己的实现paint:

class Container extends Component
{
    public void paint( Graphics g ) {/*...*/}
}
Run Code Online (Sandbox Code Playgroud)

现在当你有一个Container的直接或间接子类进行调用super.paint(g)并为1.0.2编译它时,它生成了一条invokenonvirtual指令,Component.paint因为这是第一个拥有此方法的父类.但是如果你在JVM上使用了这个编译过的类,Container.paint它仍然可以调用Component.paint它,这不是你所期望的.

另一方面,当您编译1.1的类并在1.0.2 JVM上执行它时,它会抛出一个AbstractMethodError,或者更可能是那个时代的虚拟机崩溃.为了避免崩溃,您必须((Component)super).paint(g)使用1.1编译器编写并编译它,以在任一VM中获得所需的行为.这将设置ACC_SUPER但仍然生成要调用的指令Component.paint.1.0.2 VM会忽略ACC_SUPER并直接调用Component.paint哪个很好,而1.1 VM会发现ACC_SUPER设置,因此执行查找本身Container.paint即使字节码方法引用也会调用它Component.paint.

您可以在ikvm.net博客上的这篇旧帖子中找到更多相关信息.

  • 从Java 7u13安全更新开始,JVM不再支持缺少ACC_SUPER标志.这是因为这里描述的问题:http://weblog.ikvm.net/PermaLink.aspx?guid = 23cced47-ccdb-460d-acc9-ce16154ab6a5 (13认同)