当前位置: 首页 > news >正文

java虚拟机学习笔记——方法的调用与返回(第19章 )

19.1 方法调用

Java提供了两种基本的方法:实例方法和类(静态)方法。这两种方法的区别在于:

1)实例方法在被调用之前,需要一个实例,而类方法不需要。

2)实例方法使用动态绑定,而类方法使用静态绑定

当Java虚拟机调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当虚拟机调用一个实例方法时,它会基于对象实例的类(只能在运行时得知)来选择所调用的方法。

Java虚拟机使用两种不同的指令分别调用这两种方法。对于实例方法,使用invokevirtual指令,对于类方法使用invokestatic指令。

所有的调用指令都指向一个最初包含符号引用的常量池入口。当java虚拟机遇到一个调用指令时,如果还没有解析符号引用,那么虚拟机把解析符号引用作为执行指令调用执行过程中的一部分。

当虚拟机处理实例方法时,虚拟机从所调用方法栈帧内的操作数栈中弹出objectref和args(objectref是隐式传给所有实例方法的this指针)。虚拟机把objectref作为局部变量0放到新的栈帧中(为被调用的方法建立的栈帧),把所有的args作为局部变量1,2,…等处理,虚拟机把新的栈帧作为当前栈帧,然后将程序计数器指向新方法的第一条指令。

当虚拟机处理类方法时,虚拟机只从调用方法栈帧中的操作数栈中弹出参数,并将它们放到新的栈帧作为当前栈帧,然后将程序计数器指向新方法的第一条指令。

补:虚拟机为每个装载的非抽象类都生成一个方法表,把它作为类信息的一部分保存在方法区。方法表是关于指向方法区的指针的数组,它的元素是所有它的实例方法的直接引用,包括从超类继承过程的实例方法。注意方法表中只有非私有的实例方法才会出现,用invokestatic指令调用的类方法不在这里出现,因为它们是静态的,不需要在方法表中间接指向,私有的方法和实例的初始化方法不需要在这里出现,因为它们是被invokespecial指令调用的,所以也是静态绑定的。在虚拟机解析符号引用的方法时,

19.3 指令invokespecial
Invokespecial和invokeirtual的主要区别在于: invokespcial通常(只有一个例子) 根据引用的类型选择方法,而不是根据对象的类来选择。换句话说,它使用静态绑定而不是动态绑定。在下列使用invokespecial的三种情况中,动态绑定并不会产生所预期的效果。
19.3.1 <init>()方法
<init>() 方法(或者实例初始化方法)是编译器为构造方法和实例变量初始化方法放置代码的地方。类会为源文件中的每个构造方法提供一个<init> ()方法。如果没有在源文件中显示声明一个构造方法,编译器就会为这个类产生一个默认的无参的构造方法。这个默认构造方法通常以class文件中的一个<init>()方法结束。因此,就像每个类都至少会有一个构造方法一样,每个类都至少会有一个<init>()方法,这些方法通常是用invokespecial调用。
使 用invokespecial来调用<init>()的原因在于:子类的<init>()方法需要拥有调用超类的<init>()方法的能力。这就揭示了为什么一个对象实例化时,有多个<init>()方法被调用的原因。虚拟机调用对象类中声明过的<init>()方法,这个<init>()方法首先调用同一个类中的其他<init>()方法或者超类中的<init>()方法,这个过程贯穿于对象的这个生命周期。
例如,考虑下列代码:

class Dog{
}
class CockerSpaniel extends Dog{
     public static void main(Stringargs[]){
          CockerSpaniel bootsie = newCockerSpaniel();
     }
}


当调用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 onobject ref
     //new CockerSpaniel();
3 invokespecial #3 <Method <init>() of class CockerSpaniel>
    //Note compiler didn’t storeresulting ref in a var
    //representing bootsia, because itwar 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 localvar 1 Invoke Dog’s <init>() on object ref
1 invokespecial $4 <Method <init>() of class Dog>
4 return    //return void fromCockerSpaniel’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>()的无参方法。
19.3.2私有方法
当处理自由实例方法的时候,必需允许子类使用与超类中实例方法通用的特征签名来声明实例方法(invokespecial只用来调用私有实例方法,不能调用私有类方法,私有类方法由invokestatic指令调用)例如在下列代码中,interestingMethod()是超类中的私有方法。子类对其具有包访问权限。
class Superclass{
    private void interestingMethod(){
    System.out.println("Superclass's interstingmethod.");
    }
    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 voidexampleMethod() 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()
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()方法。
java编译器在编译java源代码的时候碰到上面例子中的情况就把会编译成invokespecial指令,当java虚拟机解析一个invokespecial指令中指向超类方法的符号引用时。他会动态搜索当前的超类。找到离得最近的超类中的该方法的实现,在大多数情况下,虚拟机很可能发现最近方法实现存在于符号引用列出的超类中。但是虚拟机也可能在另外一个不同的超类中发现最近的方法实现。
19.3.3 super关键字

当使用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()。


相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Linux下eclipse提示快捷键失效解决办法
  • 从jvm运行机制来分析String对象
  • 轮播图-JavaScript
  • JVM的常量池
  • hdu 5997 rausen loves cakes(线段数合并+启发式修改)
  • 算法导论学习笔记——散列表
  • 百度网盘生成二维码api
  • 算法导论学习笔记——二叉查找树
  • win10安装blueCFD
  • 算法导论学习笔记——红黑树
  • 理解Linux文件系统
  • 沙盒SandBox
  • Python数据类型间互转(字典、字符串、列表、元组)
  • 输入n个整数,输出其中最小的k个
  • cocos2dx 3.x 精灵重叠时点击最上层的精灵
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • 3.7、@ResponseBody 和 @RestController
  • FineReport中如何实现自动滚屏效果
  • iOS编译提示和导航提示
  • JavaScript中的对象个人分享
  • Python实现BT种子转化为磁力链接【实战】
  • python学习笔记-类对象的信息
  • Redux 中间件分析
  • springboot_database项目介绍
  • swift基础之_对象 实例方法 对象方法。
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • webpack入门学习手记(二)
  • 不上全站https的网站你们就等着被恶心死吧
  • 浏览器缓存机制分析
  • 全栈开发——Linux
  • 物联网链路协议
  • 新书推荐|Windows黑客编程技术详解
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 译自由幺半群
  • 因为阿里,他们成了“杭漂”
  • ‌JavaScript 数据类型转换
  • #define用法
  • #HarmonyOS:基础语法
  • #在 README.md 中生成项目目录结构
  • (2)Java 简介
  • (2)STM32单片机上位机
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (附源码)ssm高校实验室 毕业设计 800008
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (简单) HDU 2612 Find a way,BFS。
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (强烈推荐)移动端音视频从零到上手(上)
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (十)c52学习之旅-定时器实验
  • (一)、python程序--模拟电脑鼠走迷宫
  • (转)C#调用WebService 基础