invokespecial
JVM指令用于在创建新对象时调用初始化方法(<init>
)。指令的描述建议(但不澄清)关于是否调用超类的构造函数或当前类的构造函数的决定取决于{{1}中设置的ACC_SUPER
标志的状态。文件。
来自Sun JVM规范:
接下来,选择已解析的方法进行调用,除非满足以下所有条件:
- 为当前类设置ACC_SUPER标志(参见表4.1“类访问和属性修饰符”)。
- Source(class
操作码定义)
ACC_SUPER标志的设置指示Java虚拟机要表达的其invokespecial指令的两个备选语义中的哪一个;存在ACC_SUPER标志,以便向后兼容Sun的旧编译器为Java编程语言编译的代码。 Java虚拟机的所有新实现都应该实现本规范中记录的invokespecial的语义。 Java虚拟机指令集的所有新编译器都应设置ACC_SUPER标志。 Sun的旧编译器生成了Class_Sile标志,并且未设置ACC_SUPER。 Sun的旧Java虚拟机实现会在设置时忽略该标志。
- Source(invokespecial
格式)
该定义指出该标志是为了与旧编译器向后兼容。然而,它继续与ClassFile
该标志是否仍与Sun's older Java virtual machine implementations ignore the flag if it is set.
操作码一起使用?从我所知道的,它似乎没有任何目的,我找不到建议它曾经做过的资源。
感谢。
答案 0 :(得分: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
}
在Java 1.1中,类Conatiner已更改为拥有自己的paint
实现:
class Container extends Component
{
public void paint( Graphics g ) {/*...*/}
}
现在当你有一个Container的直接或间接子类在super.paint(g)
上进行调用并为1.0.2编译它时,它为invokenonvirtual
生成了Component.paint
指令,因为这是有这种方法的第一个父母。但是如果你在一个也有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
。
您可以在this old post on the ikvm.net weblog中找到更多相关信息。