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

2022-09-01 07:41:07

JVM 指令用于在创建新对象时调用初始化方法 ()。指令的描述表明(但没有澄清)关于是调用超类的构造函数还是当前类的构造函数的决定取决于文件中设置的标志的状态。invokespecial<init>ACC_SUPERclass

来自 Sun JVM 规范:

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

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

-- (操作码定义)invokespecial

ACC_SUPER标志的设置指示 Java 虚拟机要表达的其调用专用指令的两种替代语义中的哪一种;ACC_SUPER标志的存在是为了向后兼容 Sun 的旧编译器为 Java 编程语言编译的代码。Java 虚拟机的所有新实现都应实现本规范中特别记录的 invokes 的语义。所有对 Java 虚拟机指令集的新编译器都应设置 ACC_SUPER 标志。Sun 的旧编译器生成了未设置ACC_SUPER的 ClassFile 标志。Sun 较旧的 Java 虚拟机实现会忽略该标志(如果已设置)。

-- ( 格式)ClassFile

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

该标志是否仍与操作码一起使用?据我所知,它似乎没有任何目的,我找不到一个资源来暗示它曾经这样做过。invokespecial

谢谢。


答案 1

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

在Java 1.1之前,现在调用的opcode为183的字节码指令被调用并且具有部分不同的规范。每当必须在没有虚拟方法查找的情况下调用实例方法时,就会使用它。私有方法、实例初始值设定项(构造函数)和 在 上实现方法调用就是这种情况。但后一种情况导致了类库的演变问题。invokespecialinvokenonvirtualsuper

字节码 () 中的方法引用不仅定义方法的名称、参数和返回类型,还定义方法所属的类。Opcode 183 获取这样的方法引用参数,旨在直接从指定的类调用引用的方法,而无需进一步查找。在对它进行调用的情况下,编译器负责解析实现此方法的最接近的超类,并在字节代码中生成对它的引用。CONSTANT_Methodref_infosuper

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

下面是一个示例,说明为什么需要进行此更改。在Java 1.0.2中,AWT类是这样定义的:ContainerComponent

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

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}

在 Java 1.1 中,该类被更改为具有自己的实现:Containerpaint

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

现在,当您有一个直接或间接的子类调用并编译它以用于1.0.2时,它会生成一个指令,因为这是第一个具有此方法的父级。但是,如果您在JVM上使用这个编译的类,并且该类也具有它,它仍然会调用它,这不是您所期望的。Containersuper.paint(g)invokenonvirtualComponent.paintContainer.paintComponent.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.paintComponent.paintContainer.paintComponent.paint

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


答案 2

自 Java 8 以来,该标志没有做任何事情。ACC_SUPER

每个 JEP 草稿:定义更好的 JVM 类文件验证

该标志自 Java 8 以来一直不起作用,将不再指定。ACC_SUPER

虽然我还没有找到一个简洁易链接的权威来源,但似乎在Java 7安全更新中,Java 8之前已经删除了该功能。