从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"); 假 ...
随机推荐
- velocity 是如何实现内省 屏蔽反射的
velocity的标签中支持$abc 这样的语法,如果abc是一个对象,则写模板时就可以利用它来进行反射,调用一些危险的方法,如 $vm.getClass().newInstance() #set ( ...
- ElasticSearch 学习记录之集群分片内部原理
分片内部原理 分片是如何工作的 为什么ES搜索是近实时性的 为什么CRUD 操作也是实时性 ES 是怎么保证更新被持久化时断电也不丢失数据 为什么删除文档不会立即释放空间 refresh, flush ...
- 3.python元组与列表
Python的元组与列表类似,同样可通过索引访问,支持异构,任意嵌套.不同之处在于元组的元素不能修改.元组使用小括号,列表使用方括号. 创建元组 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开 ...
- Linux已经全然统治了这个世界:反对开源社区愚不可及
原文来自:http://readwrite.jp/archives/9977 不管一个企业多强大,它都不存在和开源社区抗衡的实力 十年前.Unix占有最快的计算机世界排名前10位的五席,以及超级计算机 ...
- Jena将owl文件持久化到数据库中
package cn.edu.shu.db; import java.io.File; import java.io.FileInputStream; import java.io.IOExcepti ...
- Shiro学习(一)总体介绍
1.1 简介 Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Securi ...
- Office Add-in 设计规范与最佳实践
作者:陈希章 发表于 2017年8月6日 引子 离上一篇Office Add-in的文章已经过去了一段时间,期间有去年Office 365 Asia Devday & Hackathon的二等 ...
- 在Docker中运行asp.net core 跨平台应用程序
概述 Docker已经热了有一两年了,而且我相信这不是一个昙花一现的技术,而是一个将深远影响我们日后开发和部署.运营应用系统的一种创新(很多人将其作为devops的一种非常重要的基石).学习docke ...
- 【Jquery】之DOM操作
Questions 本篇文章主要讲解Jquery中对DOM的操作,主要内容如下: 1 内容区 1.1 .addClass() (1).addClass(className) <!DOCT ...
- Asp.net MVC 生成zip并下载
前面有生成Excel或Word的示例,所以就不再重新写了. 这里只提供将指定文件以ZIP的方式下载. 创建一个 Zip工具类 public class ZIPCompressUtil { public ...