从JVM字节码执行看重载和重写
Java 重写(Override)与重载(Overload)
重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
- (1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
- (2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
- (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
——————————————————————————————————————————————————————————————————————
分派
JVM字节码的执行涉及到方法的调用,其中分派调用过程将会解释Java多态特性,
1. 静态分派
首先是一段重载代码:
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, gentleman!");
}
public void sayHello(Woman guy){
System.out.println("Hello, lady!");
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
StaticDispatch sr = new StaticDispatch();
sr.sayHello(man);
sr.sayHello(woman);
}
}
与重载有关,代码的执行结果为:

但为什么会选择执行参数类型为Human的重载呢?
Human man = new Man();
在上面那段代码中,Human称为变量的静态类型(static type),Man称为变量的实际类型(actual type), 静态类型和实际类型在程序中都会发生变化,区别是静态类型的变化仅仅发生在使用期,变量本身的静态类型不会发生改变,并且静态类型是编译期可
知的。而实际类型变化的结果只有运行期才可以确定,编译期并不知道变量的实际类型是什么。
例如
//实际类型变化举例
Human man = new Man();
man = new Woman(); //静态类型变化举例
sr.sayHello((Man)man);
sr.sayHello((Woman)woman);
在之前的代码中,方法的接收者已经确定为sr的情况下,使用哪一个重载的版本取决于传入参数的数据类型。但编译器在重载的时候是以参数的静态类型而不是实际类型作为判断依据的。
因此结果就是执行sayHello(Human)作为调用的目标。
2. 动态分派
首先是一段重写代码:
public class Dynamicdispatch {
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{
@Override
protected void sayHello() {
// TODO Auto-generated method stub
System.out.println("man say hello!");
}
}
static class Woman extends Human{
@Override
protected void sayHello() {
// TODO Auto-generated method stub
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();
}
}
代码执行的结果:

现在的问题是虚拟机如何知道要调用什么方法?
可以javap -verbose来反编译输出的信息。

16和20句把创建好的两个对象的引用压到栈顶,这两个对象是将要执行sayHello()方法的所有者,为接收者; 17和21是方法调用指令,这两个指令都指向了#22号,就是Human.sayHello(),但是最终执行的目标方法却是不相同的。
这与invokevirtual指令的运行时解析有关:
- 找到操作数栈顶的第一个元素所指向的对象的实际类型;
- 如果在该类型中找到与常量中的描述符和简单名称都相同的方法,进行权限访问,通过则返回这个方法的引用;
- 否则对该类型的父类进行第二步的搜索和验证;
- 如果始终找不到就跑出异常。
由于invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以两次指令都将符号引用解析到了不同的直接引用上去了。这就是重写的本质。
3. 单分派与多分派
静态多分派,动态单分派(到目前为止)
方法的接收者和方法的参数统称为方法的宗量。
静态分派过程中,选择目标方法的依据:静态类型的选择和方法参数的选择
动态分派过程中,选择目标方法的依据:静态类型,因为在动态执行时,编译期已经决定目标方法的签名(参数),所以不会关心传过来的参数。
从JVM字节码执行看重载和重写的更多相关文章
- JVM总结(五):JVM字节码执行引擎
JVM字节码执行引擎 运行时栈帧结构 局部变量表 操作数栈 动态连接 方法返回地址 附加信息 方法调用 解析 分派 –“重载”和“重写”的实现 静态分派 动态分派 单分派和多分派 JVM动态分派的实现 ...
- 一夜搞懂 | JVM 字节码执行引擎
前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习字节码执行引擎? 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一 ...
- JVM 字节码执行实例分析
前言 最近在看<Java 虚拟机规范>和<深入理解JVM虚拟机>,对于字节码的执行有了进一步的了解.字节码就像是汇编语言,是 JVM 的指令集.下面我们先对 JVM 执行引擎做 ...
- 从jvm字节码指令看i=i++和i=++i的区别
1. 场景的产生 先来看下下面代码展示的两个场景 @Testvoid testIPP() { int i = 0; for (int j = 0; j < 10; j++) { i = i++; ...
- 深入理解JVM—字节码执行引擎
原文地址:http://yhjhappy234.blog.163.com/blog/static/3163283220122204355694/ 前面我们不止一次的提到,Java是一种跨平台的语言,为 ...
- JVM字节码执行引擎和动态绑定原理
1.执行引擎 所有Java虚拟机的执行引擎都是一致的: 输入的是字节码文件,处理过程就是解析过程,最后输出执行结果. 在整个过程不同的数据在不同的结构中进行处理. 2.栈帧 jvm进行方法调用和方法执 ...
- jvm 字节码执行 (一)方法调用
“虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上,而虚拟机的执行引擎是 由自己实现的,因此可以自行制定指令集 ...
- JVM字节码执行引擎
一.概述 在不同的虚拟机实现里面,执行引擎在执行Java代码的时候可能会有解释执行(通过解释器执行)和编译器执行(通过即时编译器产生本地代码执行)两种选择,所有的Java虚拟机的执行引擎都是一致的:输 ...
- jvm 字节码执行 (二)动态类型支持与基于栈的字节码解释执行
动态类型语言 动态类型语言的关键特征是它的类型检查的主体过程是在运行期而不是编译期. 举例子解释“类型检查”,例如代码: obj.println("hello world"); 假 ...
随机推荐
- JavaScript学习笔记(一)数组排序
数组(Array)在JavaScript中是非常常用的类型,关于数组的排序,与C#等语法中的排序,乍看相似,其实差别比较大. Array的排序方法有两个,分别是reverse()和sort(). re ...
- 二叉树的递归遍历 The Falling Leaves UVa 699
题意:对于每一棵树,每一个结点都有它的水平位置,左子结点在根节点的水平位置-1,右子节点在根节点的位置+1,从左至右输出每个水平位置的节点之和 解题思路:由于上题所示的遍历方式如同二叉树的前序遍历,与 ...
- 深入了解MyBatis返回值
深入了解MyBatis返回值 想了解返回值,我们须要了解resultType,resultMap以及接口方法中定义的返回值. 我们先看resultType和resultMap resultType和r ...
- JavaScript实现八大内部排序算法
注:基数排序中:r是关键字的基数,d是长度,n是关键字的个数 1.插入排序 基本思想:在序号i之前的元素(0到i-1)已经排好序,本趟需要找到i对应的元素x (此时即arr[i]) 的正确位置k,在寻 ...
- Iframe简单介绍(一)
Iframe可以用在以下几个场景 1.典型系统结构,左侧是功能树,右侧就是一些常见table或者表单之类的.为了每一个功能,单独分离出来,采用iframe. 2.AJAX上传文件 3.加载别的网站内容 ...
- 自学Python3.2-函数分类
函数的分类 内置函数,自定义函数,匿名函数 一.内置函数(python3.x) 内置参数详解官方文档: https://docs.python.org/3/library/functions.html ...
- springMVC学习总结(一)快速入门
springMVC学习总结(一)快速入门 一.初步认识 springMVC执行流程 主要组件 DispatcherServlet(中央控制器) 配置在web.xml中的前端控制器,客户端请求的入口,调 ...
- redis的事务(简单介绍)
1.简单描述 redis对事务的支持目前还是比较简单.redis只能保证一个client发起的事务中的命令是可以连续的执行,而中间不会插入其他client的命令.由于redis是但现场来处理所有cli ...
- Python 项目实践二(生成数据)第一篇
上面那个小游戏教程写不下去了,以后再写吧,今天学点新东西,了解的越多,发现python越强大啊! 数据可视化指的是通过可视化表示来探索数据,它与数据挖掘紧密相关,而数据挖掘指的是使用代码来探索数据集的 ...
- Android百分比布局成功导入及简单使用
最近学习第一行代码第二版这本书,里面有介绍百分比布局的使用,经过一番摸索,终于是成功导入了百分比布局 就是这样,appcompat是25.3.1,那么百分比布局percent也是25.3.1 这样便是 ...