jvm之方法调用(学习笔记)

java中方法主要分为“虚方法”和非虚方法:

  非虚方法:在类加载的时候就可以把符号引用解析为该方法的直接引用。比如:静态方法、私有方法、实例构造器、父类方法和被final修饰的方法。

  虚方法:需要在运行时才能将符号引用转换成直接引用。如:分派

方法调用并不等同于方法中的代码被执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还未设计方法内部的具体运行过程。一切方法调用在Class文件里面存储的都只是符号引用,而不是在实际运行时内存布局中的入口地址(直接引用)。

静态方法和私有方法的调用被称为解析。因为该方法在程序代码写好,编译器进行编译的那一刻就已经确定下来了。

而所有方法调用的目标方法在class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用(此处指的就是静态方法和私有方法)转换为直接引用。

分派:可能是静态的也可能是动态的,按照分派依据的宗量(方法的接收者与方法的参数统称为方法的宗量)数可以分为单分派和多分派(单分派是根据一个宗量对目标方法进行选择,多分派则是根据两个及以上的宗量对目标方法进行选择)。

静态分派:依赖静态类型来决定方法执行版本的分派

  虚拟机(或者准确的说是编译器)在重载(方法重载是静态分派的典型表现)时是通过参数的静态类型来作为判断依据的。

 这里的Human就是变量的“静态类型”或者说是“外观类型”

而Man或Woman则是变量的“实际类型”或者说是“运行时类型”

动态分派:在运行期根据实际类型确定方法执行版本的分派过程,最典型的表现就是重写。

 动态分派的实现

基于性能考虑,虚拟机会为类型在方法中创建一个虚方法表,使用虚方法表索引来替代元数据查找。虚方法表中存放着各个方法的实际入口地址。如果某个方法在子类中没有被重写,那子类的虚方法表中的地址入口和父类相同方法的地址入口是一致的,都指向父类的实现入口。如果子类中重写了这个方法,子类虚方法表中的地址会被替换成指向子类实现版本的入口地址。

虚方法表一般在类加载的连接阶段进行初始化,准备了类的变量初始值后,虚拟机会把该类的虚方法表也一同初始化完毕。

 学习该篇的地址:https://mp.weixin.qq.com/s/vL6-bDqwFSIrvfVl1Hv2sg

原文地址:https://www.cnblogs.com/zhlblogs/p/12426767.html