创建新对象时,invokespecial
JVM指令用于调用初始化方法(<init>
)。该指令的描述表明(但未阐明)是决定调用超类的构造函数还是当前类的构造函数取决于文件中ACC_SUPER
标志设置的状态class
。
根据Sun JVM规范:
接下来,除非满足以下所有条件,否则将选择已解析的方法进行调用:
- 为当前类设置ACC_SUPER标志(请参见表4.1,“类访问和属性修饰符”)。
-
源(invokespecial
操作码定义)
ACC_SUPER标志的设置指示Java虚拟机要表达其调用特殊指令的两种替代语义中的哪一种。ACC_SUPER标志的存在是为了向后兼容Sun较早的编译器为Java编程语言编译的代码。Java虚拟机的所有新实现都应实现本规范中记录的invokeinspecial的语义。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
操作码一起使用?据我所知,它似乎没有任何目的,我也找不到资源可以证明它曾经做到过。
谢谢。
引入了ACC_SUPER来纠正超级方法的调用问题。ACC_SUPER标志将一个类标记为针对操作码183指令的更改的语义进行编译。它的用途类似于类html" target="_blank">文件版本号,因为它允许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中使用给定的方法名称和签名查找最接近的super方法。现在通常在加载类时执行此操作,或者恰好在执行指令或第一次编译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或更可能使该时代的VM崩溃。为避免崩溃,您必须((Component)super).paint(g)
使用1.1编译器进行编写和编译,以在任一VM中获得所需的行为。这将设置ACC_SUPER,但仍生成要调用的指令Component.paint
。1.0.2虚拟机将忽略ACC_SUPER并直接进行调用Component.paint
,而1.1虚拟机将找到ACC_SUPER集,并进行查找本身(Container.paint
即使字节码方法引用为)也将使其调用Component.paint
。
您可以在ikvm.net
Weblog上的旧文章中找到有关此内容的更多信息。
问题内容: 我知道这不仅适用于Java,而且适用于许多语言,但这是我最熟悉的语言。 我了解修饰符的作用以及如何使用它们。我只想知道,为什么我们需要它们?为什么每个对象都无法访问,无论是否需要访问? 问题答案: 当您必须维护更大的项目时,原因变得更加明显。当方法或变量是公共的时,对它进行更改时必须小心,因为您永远不知道代码库的哪些部分依赖于它的确切行为。 但是,当变量或方法是私有的时,您会知道该变量
我想使用weblogic控制台访问服务器的诊断日志文件。 最初,日志位于这里:/u01/projects/domains/prd_soa_domain/servers/prd_soa_ms1/logs 我应该把它放在哪里,这样我就可以通过我的浏览器访问/下载它们了。 http:ip地址:端口/控制台/日志/
问题内容: 这是该Java文件的内容: 问题答案: 这些是保存匿名内部类的文件。 在你的示例中,包含一个顶级类(称为)和一个匿名内部类,它们将存储在中。 请注意,包含匿名内部类的文件的确切名称尚未标准化,并且可能会有所不同。但是实际上,除了这里描述的方案之外,我还没有看到其他方案。 的值特定主体也是匿名内部类: 枚举常量的可选类主体隐式定义了一个匿名类声明(第15.9.5节),该声明扩展了直接封闭
问题内容: 做什么,并且html文件是什么样的? 我的最终目标是将文本文件中的任意逗号分隔值加载到html文件中(例如)。我只能推断出一个视图是html文件,而回调将该html文件返回。 这是文档:http : //expressjs.com/api.html#res.render。 现在,给出一些示例代码,我发现背景下,有一些关于使用(嵌入的JavaScript)与和。 但是,如果我要补充的话,
问题内容: 我注意到当在try {}中使用以下变量时,例如,从最后我不能在它们上使用方法: 但是,如果将声明放置在Try {}之前的main()中,则程序编译时没有错误,那么有人可以指出解决方案/答案/解决方法吗? 问题答案: 在进入块之前,需要声明变量,以使它们在方法的其余部分范围内:
问题内容: 在ubuntu服务器上使用ruby进行Redis设置,但是无法弄清楚如何访问其日志文件。教程说应该在这里: 但甚至找不到/ var /文件夹 问题答案: 通过以下方式发现它: 因此,如果设置是更标准的,则应为: 这将输出文件的最后100行。 日志文件所在的位置是您可以通过以下方式访问的配置: 使用上述内容可能并不总是显示日志文件。在那种情况下使用