引入ACC_SUPER是为了纠正超级方法调用的问题。ACC_SUPER标志将类标记为针对操作码 183 指令的已更改语义进行编译。它的目的类似于类文件版本号,因为它允许JVM检测类是针对该指令的较旧还是较新的语义编译的。Java 1.0.2 没有设置并忽略了ACC_SUPER而 Java 1.1 及更高版本总是设置ACC_SUPER。
在Java 1.1之前,现在调用的opcode为183的字节码指令被调用并且具有部分不同的规范。每当必须在没有虚拟方法查找的情况下调用实例方法时,就会使用它。私有方法、实例初始值设定项(构造函数)和 在 上实现方法调用就是这种情况。但后一种情况导致了类库的演变问题。invokespecial
invokenonvirtual
super
字节码 () 中的方法引用不仅定义方法的名称、参数和返回类型,还定义方法所属的类。Opcode 183 获取这样的方法引用参数,旨在直接从指定的类调用引用的方法,而无需进一步查找。在对它进行调用的情况下,编译器负责解析实现此方法的最接近的超类,并在字节代码中生成对它的引用。CONSTANT_Methodref_info
super
从Java 1.1开始,它被更改为基本上忽略中引用的类,而是在JVM中查找具有给定方法名称和签名的最接近的超级方法。这通常是现在在类被加载时或在指令执行或第一次JIT编译之前完成的。CONSTANT_Methodref_info
下面是一个示例,说明为什么需要进行此更改。在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 中,该类被更改为具有自己的实现:Container
paint
class Container extends Component
{
public void paint( Graphics g ) {/*...*/}
}
现在,当您有一个直接或间接的子类调用并编译它以用于1.0.2时,它会生成一个指令,因为这是第一个具有此方法的父级。但是,如果您在JVM上使用这个编译的类,并且该类也具有它,它仍然会调用它,这不是您所期望的。Container
super.paint(g)
invokenonvirtual
Component.paint
Container.paint
Component.paint
另一方面,当您编译1.1的类并在1.0.2 JVM上执行它时,它会抛出一个抽象方法错误,或者更有可能是那个时代的VM崩溃。为了避免崩溃,您必须使用1.1编译器编写并编译它,以便在任一VM中获得所需的行为。这将设置ACC_SUPER但仍会生成调用 的指令。1.0.2 VM 将忽略ACC_SUPER并直接调用,这很好,而 1.1 VM 会找到ACC_SUPER集,从而执行查找本身,即使字节代码方法引用是 。((Component)super).paint(g)
Component.paint
Component.paint
Container.paint
Component.paint
您可以在 ikvm.net 博客上的这篇旧帖子中找到有关此内容的更多信息。