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

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

来自Sun JVM规范:

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

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

– Source ( invokespecial opcode definition)

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.相矛盾, Sun's older Java virtual machine implementations ignore the flag if it is set.

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

谢谢。

引入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 } 

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

 class Container extends Component { public void paint( Graphics g ) {/*...*/} } 

现在当你有一个Container的直接或间接子类,它在super.paint(g)上进行调用并为1.0.2编译它时,它为Component.paint生成了一个invokenonvirtual指令,因为这是第一个拥有这个方法的父类。 但是如果你在一个也有Container.paint的JVM上使用这个编译类,它仍然会调用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博客上的这篇旧post中找到更多相关信息。