解析

所有方法调用中的目标方法在Class文件里面都是常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用。这种解析的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的,即“编译期可知,运行期不可变”,这类目标的方法的调用称为解析(Resolution)

分派

解析调用一定是个静态的过程,在编译期就完全确定,在类加载的解析阶段就将涉及的符号引用全部转变为可以确定的直接引用,不会延迟到运行期       再去完成。而分派(Dispatch)调用则可能是静态的也可能是动态的。于是分派方式就有静态分派和动态分派。

java是一门面向对象的编程语言,具备面向对象的三个基本特征:继承、封装、多态。

  接下来我将演示下分派调用过程来揭示多态性的一些最基本的体现。如重载(Overload)和重写(Override)在jvm中是如何实现的。

1、静态分派

  静态分派发生在编译阶段,不是由jvm执行的,典型的应用是重载(Overload)。

  静态分派的最直接的解释是在重载的时候是通过参数的静态类型而不是实际类型作为判断依据的。因此在编译阶段,Javac编译器会根据参数的静态类型决定使用哪个重载版本。

方法静态分派代码演示:
/**
* 方法静态分派
*
* @author sun
*
*/
public class StaticDispatch {
static abstract class Human { } static class Man extends Human { } static class Woman extends Human { } public void sayHello(Human guy) {
System.out.println("hello,guy");
} public void sayHello(Man guy) {
System.out.println("hello,sun");
} public void sayHello(Woman guy) {
System.out.println("hello,ting");
} public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
StaticDispatch staticDispatch = new StaticDispatch();
staticDispatch.sayHello(man);
staticDispatch.sayHello(woman);
}
}
运行结果:

hello,guy
hello,guy

对于有经验的程序员来说,出现上面的结果并不感到惊讶。接下来我们要分析为什么会出现这样的结果,但在分析之前,我们要明确两个概念。

即变量的静态类型和变量的实际类型。如下:

Human man = new Man();

 Human 称为变量的静态类型;Man称为变量的实际类型。二者的区别在于静态类型在编译期是可知的,而实际类型是在运行期才确定,编译时并不知道一个对象的实际类型是什么。

// 实际类型变化
Human man = new Man();
man = new Woman();
// 静态类型变化
sr.sayHello((Man) man);
sr.sayHello((Woman) man);

  

在方法接收者已经确定是对象sr的前提下,使用哪个重载版本就完全取决于传入参数的数量和数据类型。代码中刻意使用了两个静态类型相同而实际类型不同的变量,编译器在重载时是通过参数的静态类型而不是实际类型作为判定依据的,man和woman的静态类型都是Human。静态类型在编译期可知,因此在编译阶段,编译期根据man和woman的静态类型为Human的事实,来选择public void sayHello(Human guy)作为调用方法,这就是方法重载的具体体现

所有依赖静态类型来定位方法执行版本的分派动作称为静态分派

2、动态分派

动态分派和多态性的体现-重写(Override)有着本质的联系。

/**
* 方法动态分派
*
* @author sun
*
*/
public class DynemicDispatch { static abstract class Human {
protected abstract void sayHello();
} static class Man extends Human { @Override
protected void sayHello() {
System.out.println("man say hello");
} } static class Woman extends Human { @Override
protected void sayHello() {
System.out.println("woman say hello");
} } public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
man.sayHello();
woman.sayHello();
man = new Woman();
man.sayHello();
}
}  

运行结果:

man say hello
woman say hello
woman say hello

 显然这里不可能根据静态类型来决定调用那个方法。导致这个现象很明显的原因是因为这两个变量的实际类型不一样,jvm根据实际类型来分派方法执行版本。我们使用javap命令来查看这段代码的字节码。以下是该段代码main方法的字节码:

 public static void main(java.lang.String[]);
Code:
0: new #16 // class Demo/DynemicDispatch$Man
3: dup
4: invokespecial #18 // Method Demo/DynemicDispatch$Man."<init>":()V
7: astore_1
8: new #19 // class Demo/DynemicDispatch$Woman
11: dup
12: invokespecial #21 // Method Demo/DynemicDispatch$Woman."<init>":()V
15: astore_2
16: aload_1
17: invokevirtual #22 // Method Demo/DynemicDispatch$Human.sayHello:()V
20: aload_2
21: invokevirtual #22 // Method Demo/DynemicDispatch$Human.sayHello:()V
24: new #19 // class Demo/DynemicDispatch$Woman
27: dup
28: invokespecial #21 // Method Demo/DynemicDispatch$Woman."<init>":()V
31: astore_1
32: aload_1
33: invokevirtual #22 // Method Demo/DynemicDispatch$Human.sayHello:()V
36: return

  0~15行的字节码作用是建立man和woman的内存空间、调用Man和Woman类型的实例构造器的。

16、20两句把刚刚创建的两个对象的引用压到栈顶,这两个对象是将要执行的sayHello()方法的所有者,称为接收者(Receiver)!

17、21两句是方法调用指令,这两句指令最终的目标方法并不相同。

这需要说说jvm的invokevirtual指令了,这个指令的解析过程有助于我们更深刻理解重写的本质。该指令的具体解析过程如下:

  1. 找到操作数栈栈顶的第一个元素所指向的对象的实际类型,记为C

  2. 如果在类型C中找到与常量中描述符和简单名称都相符的方法,则进行访问权限的校验,如果通过则返回这个方法的直接引用,查找结束;如果不通过,则返回非法访问异常

  3. 如果在类型C中没有找到,则按照继承关系从下到上依次对C的各个父类进行第2步的搜索和验证过程

  4. 如果始终没有找到合适的方法,则抛出抽象方法错误的异常

从这个过程可以发现,在第一步的时候就在运行期确定接收者的实际类型,所以当调用invokevirtual指令就会把运行时常量池中符号引用解析为不同的直接引用,这就是方法重写的本质。我们把这种在运行期根据实际类型确定方法执行版本的分派称为动态分派

jvm004 解析与分派的更多相关文章

  1. JAVA方法调用中的解析与分派

    JAVA方法调用中的解析与分派 本文算是<深入理解JVM>的读书笔记,参考书中的相关代码示例,从字节码指令角度看看解析与分派的区别. 方法调用,其实就是要回答一个问题:JVM在执行一个方法 ...

  2. JVM 方法调用之静态分派

    分派(Dispatch)可能是静态也可能是动态的,根据分派依据的宗量数可分为单分派和多分派.这两种分派方式的两两组合就构成了静态单分派,静态多分派,动态单分派,动态多分派这4种组合.本章讲静态分派. ...

  3. 深入理解Java虚拟机--中

    深入理解Java虚拟机--中 第6章 类文件结构 6.2 无关性的基石 无关性的基石:有许多可以运行在各种不同平台上的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码(ByteCode),从而 ...

  4. 《深入理解Java虚拟机》-----第8章 虚拟机字节码执行引擎——Java高级开发必须懂的

    概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念 ,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而 ...

  5. jvm 字节码执行 (一)方法调用

    “虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上,而虚拟机的执行引擎是 由自己实现的,因此可以自行制定指令集 ...

  6. 深入理解Java虚拟机读书笔记5----虚拟机字节码执行引擎

    五 虚拟机字节码执行引擎   1 运行时栈帧结构     ---栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素.     ---栈帧中存储了方法的局部变 ...

  7. Java面试题 BAT 大公司面试题整理总结!

    本文只列出了问题,答案还是需要需要自己的总结,很多时候自己总结出来的语言在面试时比硬背的效果好很多. 这些题目是网友去百度.小米.乐视.美团.58.猎豹.360.新浪.搜狐等一线互联网公司面试被问到的 ...

  8. BATJ等大厂最全经典面试题分享

    金九银十,又到了面试求职高峰期,最近有很多网友都在求大厂面试题.正好我之前电脑里面有这方面的整理,于是就发上来分享给大家. 这些题目是网友去百度.蚂蚁金服.小米.乐视.美团.58.猎豹.360.新浪. ...

  9. tomcat架构分析(connector BIO 实现)

    出处:http://gearever.iteye.com 在tomcat架构分析(概览)中已经介绍过,connector组件是service容器中的一部分.它主要是接收,解析http请求,然后调用本s ...

随机推荐

  1. JQuery--Ajax 异步操作 动态添加节点 (新人试水,求支持)

    异步操作动态添加节点,导致在代码中给添加的节点全局绑定事件或者获取元素无效,上代码: $(function () { var IP = '...'; // 页面中的默认编号起始值 和 公用IP前缀 s ...

  2. 『珍藏】eclipse快捷键

    提示所有快捷键的快捷键是 ctrl+shift+L 菜单是在: window-->preferences-->general-->keys 提供能容帮助是 alt+/ Ctrl+1 ...

  3. oracle日期时间函数 总结

    表中存在伪列:sysdate,systimestamp 伪列存在但是不显示 select sysdate from dual; select systimestamp from dual; 日期计算公 ...

  4. PHP验证码的制作教程

    自己过去自学了PHP绘画验证码的教程,现在就把这一部分笔记跟大家分享,希望可以帮到大家. 顺带,我会在后面把我整理的一整套CSS3,PHP,MYSQL的开发的笔记打包放到百度云,有需要可以直接去百度云 ...

  5. sql 注入命令大全

    1.判断有无注入点 ; and 1=1 and 1=2 2.猜表一般的表的名称无非是admin adminuser user pass password 等.. and 0<>(selec ...

  6. Android 的 SDK Manager 无法启动 闪退解决方法

    [故障描述] 做 Android 开发就要下载 Android SDK,其中的 SDK Manager.exe 无法启动,一闪而过. 尝试重装 JDK.重新从官网下载 Android SDK.添加环境 ...

  7. Java IO学习笔记二

    Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...

  8. 你的MySQL服务器开启SSL了吗?

    最近,准备升级一组MySQL到5.7版本,在安装完MySQL5.7后,在其data目录下发现多了很多.pem类型的文件,然后通过查阅相关资料,才知这些文件是MySQL5.7使用SSL加密连接的.本篇主 ...

  9. [USACO09MAR]地震损失2Earthquake Damage 2

    地震破坏 时间限制: 1 Sec  内存限制: 128 MB 题目描述 威斯康星发生了一场地震!约翰的牧场遭到了打击,有一些牛棚变成了废墟,如果一间牛棚遭到 了破坏,那么所有和它相连的道路都不能使用了 ...

  10. vmware安装FreeBSD8.3全攻略【教程】

    原始日期:2013-08-02 23:15 前言:花了两天时间倒腾这个freebsd,安装并不简单,如果单单参照百度上的教程是会遇到各种问题的,所以我打算自己写一篇教程,为了更加直观,部分采用了互联网 ...