19.3 指令invokespecial
Invokespecial和invokeirtual的主要区别在于: invokespcial通常(只有一个例子) 根据引用的类型选择方法,而不是根据对象的类来选择,换句话说。它使用静态绑定而不是动态绑定。在下列使用invokespecial的三种情况中,动态绑定并不会产生所预期的效果。
19.3.1 指令invokespecial和<init>()方法
<init>() 方法(或者实例初始化方法)是编译器为构造方法和实例变量初始化方法放置代码的地方,类不会为源文件中的每个构造方法提供一个<init>()方法。如果没有在源文件中显示声明一个构造方法。编译器就会为这个类产生一个默认的无参的构造方法。这个默认构造方法通常以class文件中的一个<init>()方法结束。因此就像每个类都至少会有一个构造方法一样。每个类都至少会有一个<init>()方法。这些方法通常是用invokespecial调用。
只有创建一个新的势力的时候,才调用 <init>()方法。新创建的对象继承路径中,每个类都至少会调用一个<init>() 方法,而且继承路径中的任何一个类都有可能调用多个<init>()方法。
使用invokespecial来调用<init>()的原因在于:子类的<init>()方法需要拥有调用超类的<init>()方法的能力。这就揭示了为什么一个对象实例化时,有多个<init>()方法被调用的原因。虚拟机调用对象类中的<init>()方法。这个过程贯穿于对象的这个生命周期。
例如,考虑下列代码:
class Dog{
}
class CockerSpaniel extends Dog{
public static void main(String args[]){
CockerSpaniel bootsie = new CockerSpaniel();
}
}
当调用main()方法的时候,虚拟就就会为新的CockerSpanicl对象分配空间,容纳后调用CockerSpaniel的默认无参的<init>()方法来初始化那段空间。那个方法将首先调用Dog的<init>()方法,然后再一次调用Object的<init>()方法。类CockerSpanie的main()方法的字节码如下:
//Create a new CockerSpaniel Object , push ref
0 new #1 <Class CockerSpanial>
//Invoke <init>() method on object ref
//new CockerSpaniel();
3 invokespecial #3 <Method <init>() of class CockerSpaniel>
//Note compiler didn’t store resulting ref in a var
//representing bootsia, because it war neer used
6 return //return void from main
CockerSpaniel类的main()方法为新的CckerSpaniel对象分配内存,并使用new指令将分配的内存初始化为默认的初始化值(“#1” 之处了需要实例化的类的常量入口, 这里特指CockerSpaniel类)。New指令吧一个纸箱CockerSpaniel对象的引用压入栈。然后main()方法使用invokerspecial指令通过该对象引用调用类CockerSpaniel的<init>()方法(“#3”指出了常量池入口,其中包含了对CockerSpaniel的<init>()方法的引用)。Java虚拟机把一个新的栈帧压入了java栈,然后把对象引用赋给新的栈帧中的局部变量0.类CockerSpaniel 的
<init>()方法的字节码如下:
0 aload_0 //push object ref from local var 1 Invoke Dog’s <init>() on object ref
1 invokespecial $4 <Method <init>() of class Dog>
4 return //return void from CockerSpaniel’s <init>()
如前所述,这里的<init>()方法相当于编译器为类CockerSpaniel自动糊产生的默认无参构造方法。这个方法首先把从局部变量0中取出来的已被初始化的对象引用压入栈。然后通过这个引用调用Dog的<init>()方法(#4 之处了常量池入口,其中那个包含了指向了Dog的<init>()方法的引用)。Dog类的<init>()方法的字节码如下所示:
0 aload_0 //push obj ref from local var 0
//invoke object’s <init>() method on obj ref
1 invkespecial #3 <Method <init>() of class java.lang.Object>
0 return //return void from Dog’s <init>()
这里的<init>()方法相当于编译器Dog自动产生的默认无参构造方法。这个方法相当于编译器为类Dog自动产生 的默认无参构造方法。 这个方法首先从局部变量0中取出已被初始化的对性爱那个的引用压入栈。然后通过这个引用调用Object的<init>()方法(这里#3指出来常量池的入口,其中包含指向Object的<init>()方法的引用。这个常量池并不是指向类CockerSpaniel的方法的那个常量池,每个类都有自己的常量池)。当三个<init>()方法都返回后。出main()新建的cockerspaniel对象才完成了初始化工作。
由于每个类至少有一个<init>()方法。类的<init>()方法拥有相同的特征签名是很普遍的现象。(方法的特征签名是指他的名字,参数的数量和类型)。例如,CockerSpaniel类继承路径中的三个<init>()方法。它们的特征签名相同。CockerSpaniel,Dog和Object东圃有个一个名为<init>()的无参方法。
Invokevirtual指令会执行动态绑定和调用CockerSpanicl的<init>()方法,所以从CockerSpaniel的<init>()方法使用invokevirtual指令调用Dog的<init>()方法是不肯能的。然而通过使用invokespecial指令,CockerSpaniel的<init>()方法能够调用Dog类的<init>()方法。因为放在CockerSpaniel的class文件(常量池入口#4)中的引用的类型为Dog。
19.3.2 指令invokespecial和私有方法。
当处理自由实例方法的时候,必需玉虚子类使用于超类中实例方法通用的特征签名类声明实例方法(invokespecial只用来调用私有实例方法,不能调用私有类方法,私有类方法由invokestatic指令调用)例如在下列代码中,interestingMethod90是超累中的私有方法。子类对其具有包访问权限。
class Superclass{
private void interestingMethod(){
System.out.println("Superclass's intersting method.");
}
void exampleMethod(){
interestingMethod();
}
}
class Subclass extends Superclass{
void interstingMethod(){
System.out.println("Subclass's interesting method.");
}
public static void main(String args[]){
Subclass me = new Subclass();
me.exampleMethod();
}
}
如前所述。当调用像前面所定义的subclass中的main()方法的时候, 虚拟机会实例化一个新的Subclass对象, 然后调用exampleMethod()方法。类Subclass的main(0方法的字节码如下所示:
//cerate a new instance of class subclass, push ref
0 new #2 <Class Subclass>
3 dup // Duplicate ref , push duplicate
//Invoke <init>() method on new object Subclass me = new SubClass();
4 invokespacial #6 <Method <init>() of class Subcliass>
7 astore_1 //pop object ref into local var 1
8 aload_1 // push re from local var 1
//Invoke exampleMethod() on object ref;
// me.exampleMethod();
9 invokeirtual #8 <method void exampleMethod() of class Superclass>
12 return // return void from main()
Subclass从Superclass处继承了exampleMethod()方法,当方法main()调用subclass对象汇总的方法exampleMethod()时。它使用invokevirtual指令,正如类Superclass所定义的。Java虚拟机将会闯将一个新的栈帧并将其压入栈,然后开始执行examplemethod()的字节码。方法exampleMethod()的字节码如下所示:
0 aload_0 //push obj ref from local var 1
//invoke interestingMethod() on obj ref
// interestingMethod()l
1 invokespecial #7 <method void interestingMethod() of Superclass>
4 return // return void from exampleMethod()
需要注意的是。 方法exampleMethod()首相将赋给局部变量0的引用压入栈(隐含参数tims被传个所有的实例方法) 然后使用invokespecial指令通过这个引用调用interstingMethod)方法。尽管这里说的对象是Subclass类的实例,而且Subclass类中的interestingmethod()方法也是能够访问的,但是java虚拟机最终还是调用了Superclas类中的interestingMethod()方法。
程序争取输入为” Subclass's interesting method.” 如果使用指invokevirtual而不是指令invokespecial来调用interestingMethod(). 那么程序的输出结果会是哪个interestingMethod()。这里对象实际所述的类是subclass。 因此虚拟机会使用Subclass的interestingMethod。因此就会调用Superclass的interestingMethod()版本呢。
19.3.3 指令invokespecial和私有方法。
当使用super关键字来调用方法时(例如super.someMethod()),尽管当前类重载了该方法。但是使用者真正希望调用的还是超累的方法。再说一次,指invokevirual只能调用当前类的方法。无法使用超累的方法。例如:
class Cat{
void someMethod(){
}
}
class TabbyCat extends Cat{
void someMethod(){
super.someMethod();
}
}
类TabbyCat的someMethod()方法的字节码如下:
0 aload_0 // push obj ref from local var 0
// invoke cat’s someMethod() on obj ref
1 invokespecial #4 <method voiud someMethod() of class Cat>
4 return // return void from TabbyCat’s someMethod()
如果这里使用invokevirtual指令,那么将会调用TabbyCat的someMethod()方法。但是因为这里使用了invokespecial指令。并且常量池入口(这里是#4)指明了调用类Cat中声明的someMethod()方法。因此java虚拟机将会调用超累的someMethod().
Java 虚拟机是否是使用静态绑定来执行invokespecial指令(或者一种特殊的动态绑定)取决于所指向的类是否制定了ACC_SUPER标志。JDK1.0.2版本以前的各种版本中。Invokespecial指令的名称为invokenonvirtual。而且总会导致静态绑定的使用。结果却是invokenonvirtual所坚持的静态绑定无法办证所有情况下java语义的正确使用。在JDK1.0.2中。Invokenonvirtual指令更名为invokespecial。它的语义也改变了。此外,Java class文件中的access_flags项中还加入了一个新的标志:ACC_SUPER.
Class文件的ACC_SUPER标志项指明。Java虚拟机应该使用哪一种语义来执行class文件字节码所遇到的invokespecial指令。如果没有设置ACC_SUPER标志,虚拟机将会使用旧语义(invokenovirtual);若果设置了ACC_SUPER标志。 虚拟机会使用新语义,所有新版本的java编译器都会在省城的class文件中默认设置ACC_SUPER标志。
根据旧语义。当执行invokespecial指令时。虚拟机将会在二和情况下使用静态绑定。而新语义除了调用超类方法外,其他的情况一律使用静态绑定。
根据新语义。当java虚拟机解析一个invokespecial指令中指向超累方法的符号引用时。他会动态搜索当前的超累。找到离得最近的超类中的钙方法的实现,在大多数情况下,虚拟机很可能发现最近方法实现粗在于符号引用列的超类中。但是虚拟机也可能在另外一个不同的超类中发现最近的方法实现。
例如,如果创建了一颗包含三个类的继承数(Animal,Dog CockerSpaniel). 假如Dog是Animal的子类。CockerSpaniel是Dog的子类。在CockerSpaniel类中定义了一个方法。它使用invokespecial来调用一个名为walk()的方法, 但是dog类没有定义该方法。此时,CockerSpaniel类中指向walk()方法的符号引用将会把animal类作为它的类。当执行CockerSpaniel类中invokespecial指令时虚拟机会进行动态选择,并巧用Animal类的walk()方法。
如果后来的Dog类中加入了walk()方法。斌企鹅重新编译了Dog类。但是没有重新编译CockerSpaniel类。它的指向超类的walk()方法的符号引用仍然会将Animal作为自己的类。尽管爱Dog类中已经有了walk()方法的实现。不过,当执行CockerSpaniel类中的invokespecial指令时,虚拟机将会动态选择并且调用Dog类中的walk()方法实现。
分享到:
相关推荐
`invokevirtual`、`invokespecial`、`invokestatic`、`invokeinterface`是JVM用于调用方法的指令集,分别对应虚拟方法调用、特殊方法调用、静态方法调用和接口方法调用。 控制流指令如`ifeq`、`ifne`、`ifle`等用于...
"00-JVM指令手册.zip"包含的"00-JVM指令手册.pdf"很可能是对JVM字节码指令的详细解析,这对于深入理解Java程序的运行机制至关重要。下面将详细介绍JVM指令集及其相关知识点。 1. **JVM字节码**:Java源代码被编译成...
在Java虚拟机(JVM)中,`invokespecial`和`invokevirtual`指令是用于调用方法的关键指令。它们在字节码层面扮演着重要角色,分别处理对象的构造函数调用、私有方法调用以及实例方法的常规调用。理解这两个指令的...
`JVM指令手册`是深入理解JVM内部工作原理的重要参考资料,它详细列出了JVM所支持的所有操作指令,这对于Java开发者提升技能、进行性能优化以及排查问题具有极高的价值。以下是一些关键的JVM指令及相关的知识点: 1....
例如,JVM通过不同的指令(如Invokestatic、invokevirtual、invokeinterface和invokespecial)来调用静态方法、实例方法、接口方法以及private方法或构造器。 **总结** 在Java分布式应用中,理解JVM的运作机制能够...
下面,我们将深入探讨JVM指令集及其在Java程序执行中的作用。 1. **JVM指令分类** JVM指令分为五类:操作数栈管理指令、局部变量表操作指令、控制流指令、字节码操作指令和对象及数组操作指令。这些指令共同构成了...
- `invokespecial`:调用实例初始化方法、私有方法或者父类方法。 #### 程序控制指令 ##### 控制流程指令 - `ifeq`、`ifne`、`iflt`等:条件分支指令,根据栈顶的int比较结果进行跳转。 - `goto`、`jsr`、`ret`:...
《深入理解Java虚拟机_JVM高级特性与最佳实践 第2版》是一本专注于Java开发者深入探索JVM(Java Virtual Machine)的重要参考资料。本书全面讲解了Java虚拟机的内部工作机制,涵盖了JVM的高级特性和最佳实践,旨在...
下面我们将深入探讨JVM指令手册中的关键概念和知识点。 1. **字节码**:Java源代码编译后生成的是字节码文件(.class),这些文件包含了可被JVM理解的二进制指令。每个字节码指令由一个单字节的操作码(opcode)和...
6. **方法调用与返回指令**:`invokevirtual`用于调用对象的虚方法,`invokespecial`用于构造函数调用和私有方法,`return`表示方法的结束。 7. **异常处理指令**:如`athrow`抛出异常,`catch`配合`try-catch`块...
《深入Java虚拟机(原书第2版)》,原书名《Inside the Java Virtual Machine,Second Edition》,作者:【美】Bill Venners,翻译:曹晓钢、蒋靖,出版社:机械工业出版社,ISBN:7111128052,出版日期:2003 年 9 ...
本文将深入探讨JVM的指令集及其工作原理。 JVM虚拟机指令集是Java字节码的基石,它由一系列低级、简单的操作码组成,这些操作码负责执行基本的计算和控制任务。每条指令通常对应一个字节,并且有特定的含义和作用。...
通过分析这个C语言实现的JVM,开发者不仅可以深入理解JVM的工作原理,还能学习到如何用C语言实现高级抽象的概念,这对理解操作系统、编译器设计和底层编程等都有很大帮助。不过,要注意的是,实际的JVM实现非常复杂...
在执行方法时JVM提供了invokestatic、invokevirtual、invokeinterface和invokespecial四种指令来执行 (1)invokestatic:调用类的static方法 (2)invokevirtual:调用对象实例的方法 (3)invokeinterface:将属性...
JVM指令集是Java字节码的基础,理解这些指令的含义和作用,对于优化Java代码、调试程序以及深入理解JVM的工作原理至关重要。通过学习JVM指令手册,开发者可以更好地掌握Java程序的底层运行机制,从而编写出更高效、...
Java虚拟机(JVM)指令集是Java编程语言在运行时执行的基本操作单元,它构成了Java字节码的基础。...通过理解这些指令,开发者可以更好地优化代码性能,减少垃圾收集的负担,并深入理解JVM的内部工作原理。
Java虚拟机(JVM)是Java程序运行的核心组件,它...掌握JVM指令手册对于深入理解Java程序的执行机制、性能优化以及调试都非常有帮助。通过学习和分析这些指令,开发者能够更好地利用JVM特性,编写出高效、健壮的代码。
JVM7还加强了对动态语言的支持,通过JSR 292( invokespecial 字节码指令的扩展)使得其他非Java语言能在JVM上运行得更加顺畅,为JVM上的语言多元化奠定了基础。这一改变为Scala、Groovy等现代动态语言在Java平台上...
在分析和优化性能时,我们需要全面考虑CPU利用率、I/O延迟、系统吞吐量等多个因素,并可能需要借助各种工具(如JProfiler, VisualVM等)进行深入分析。只有这样,才能确定性能瓶颈所在,并针对性地改进代码。