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类容器和组件被定义是这样的:
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或更可能导致该时代的VM崩溃。为避免崩溃,您必须编写((Component)super).paint(g)
并使用1.1编译器进行编译,以便在任一VM中获得所需的行为。这将设置ACC_SUPER,但仍会生成调用Component.paint
的指令。 1.0.2虚拟机会忽略ACC_SUPER并直接调用Component.paint
,这很好,而1.1 VM会发现ACC_SUPER集,因此执行查找本身,即使字节码方法引用是Component.paint
,也会引起Container.paint
。
你可以在this old post on the ikvm.net weblog找到更多关于它的信息。
来源
2012-01-21 04:07:33
x4u
这不是一个答案,但要理解为什么使用该标志,我认为您还需要了解该标志在JVM版本1规范下的含义 - 请参阅http://java.sun.com/docs/books /jvms/first_edition/html/Instructions2.doc7.html#invokespecial –
并排,我看不出任何区别。 – Jivings