Java虚拟机 - 多态性实现机制
【深入Java虚拟机】之五:多态性实现机制——静态分派与动态分派
方法解析
Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址。这个特性给Java带来了更强大的动态扩展能力,使得可以在类运行期间才能确定某些目标方法的直接引用,称为动态连接,也有一部分方法的符号引用在类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析。这在前面的“Java内存区域与内存溢出”一文中有提到。
静态解析成立的前提是:方法在程序真正执行前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在编译器进行编译时就必须确定下来,这类方法的调用称为解析。
在Java语言中,符合“编译器可知,运行期不可变”这个要求的方法主要有静态方法和私有方法两大类,前者与类型直接关联,后者在外部不可被访问,这两种方法都不可能通过继承或别的方式重写出其他的版本,因此它们都适合在类加载阶段进行解析。
Java虚拟机里共提供了四条方法调用字节指令,分别是:
- invokestatic:调用静态方法。
- invokespecial:调用实例构造器<init>方法、私有方法和父类方法。
- invokevirtual:调用所有的虚方法。
- invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
静态分派
所有依赖静态类型来定位方法执行版本的分派动作,都称为静态分派,静态分派的最典型应用就是多态性中的方法重载。静态分派发生在编译阶段,因此确定静态分配的动作实际上不是由虚拟机来执行的。下面通过一段方法重载的示例程序来更清晰地说明这种分派机制:
- class Human{
- }
- class Man extends Human{
- }
- class Woman extends Human{
- }
- public class StaticPai{
- public void say(Human hum){
- System.out.println("I am human");
- }
- public void say(Man hum){
- System.out.println("I am man");
- }
- public void say(Woman hum){
- System.out.println("I am woman");
- }
- public static void main(String[] args){
- Human man = new Man();
- Human woman = new Woman();
- StaticPai sp = new StaticPai();
- sp.say(man);
- sp.say(woman);
- }
- }
上面代码的执行结果如下:
I am human
I am human
以上结果的得出应该不难分析。在分析为什么会选择参数类型为Human的重载方法去执行之前,先看如下代码:
动态分派
动态分派与多态性的另一个重要体现——方法覆写有着很紧密的关系。向上转型后调用子类覆写的方法便是一个很好地说明动态分派的例子。这种情况很常见,因此这里不再用示例程序进行分析。很显然,在判断执行父类中的方法还是子类中覆盖的方法时,如果用静态类型来判断,那么无论怎么进行向上转型,都只会调用父类中的方法,但实际情况是,根据对父类实例化的子类的不同,调用的是不同子类中覆写的方法,很明显,这里是要根据变量的实际类型来分派方法的执行版本的。而实际类型的确定需要在程序运行时才能确定下来,这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
单分派和多分派
前面给出:方法的接受者(亦即方法的调用者)与方法的参数统称为方法的宗量。但分派是根据一个宗量对目标方法进行选择,多分派是根据多于一个宗量对目标方法进行选择。
- class Eat{
- }
- class Drink{
- }
- class Father{
- public void doSomething(Eat arg){
- System.out.println("爸爸在吃饭");
- }
- public void doSomething(Drink arg){
- System.out.println("爸爸在喝水");
- }
- }
- class Child extends Father{
- public void doSomething(Eat arg){
- System.out.println("儿子在吃饭");
- }
- public void doSomething(Drink arg){
- System.out.println("儿子在喝水");
- }
- }
- public class SingleDoublePai{
- public static void main(String[] args){
- Father father = new Father();
- Father child = new Child();
- father.doSomething(new Eat());
- child.doSomething(new Drink());
- }
- }
儿子在喝水
我们首先来看编译阶段编译器的选择过程,即静态分派过程。这时候选择目标方法的依据有两点:一是方法的接受者(即调用者)的静态类型是Father还是Child,二是方法参数类型是Eat还是Drink。因为是根据两个宗量进行选择,所以Java语言的静态分派属于多分派类型。
再来看运行阶段虚拟机的选择,即动态分派过程。由于编译期已经了确定了目标方法的参数类型(编译期根据参数的静态类型进行静态分派),因此唯一可以影响到虚拟机选择的因素只有此方法的接受者的实际类型是Father还是Child。因为只有一个宗量作为选择依据,所以Java语言的动态分派属于单分派类型。
Java虚拟机 - 多态性实现机制的更多相关文章
- JVM学习001通过实例总结Java虚拟机的运行机制
JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html 文章转载自:http://www.c ...
- java 虚拟机的类加载机制
Java 虚拟机的类加载机制 关于类加载机制: 虚拟机把描述类的数据从Class 文件加载到内存,并对数据进行效验.转换解析和初始化,最终 形成可以被虚拟机直接使用的Java 类型,就是虚拟机的类 ...
- JVM学习(1)——通过实例总结Java虚拟机的运行机制
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: JVM的历史 JVM的运行流程简介 JVM的组成(基于 Java 7) JVM调优参数:-Xmx和-Xms ...
- Java虚拟机内存管理机制
自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区 ...
- 深入理解java虚拟机【类加载机制】
Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 在加载阶段,java虚拟机需要完成以下 ...
- 深入解析java虚拟机-jvm运行机制
转自oschina 一:JVM基础概念 JVM(Java虚拟机)一种用于计算设备的规范,可用不同的方式(软件或硬件)加以实现.编译虚拟机的指令集与编译微处理器的指令集非常类似.Java虚拟机包括一套字 ...
- JVM学习(1)——通过实例总结Java虚拟机的运行机制(转)
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: JVM的历史 JVM的运行流程简介 JVM的组成(基于 Java 7) JVM调优参数:-Xmx和-Xms ...
- JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html
JVM系类的文章全部转载自:http://www.cnblogs.com/kubixuesheng/p/5199200.html 特别在此声明.那位博主写的真的很好 ,感谢!! 俗话说,自己写的代码, ...
- Java虚拟机:类加载机制详解
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 大家知道,我们的Java程序被编译器编译成class文件,在class文件中描述的各种信息,最终都需要加载到虚拟机内存才能运行和使用,那么 ...
随机推荐
- iis支持asp.net4.0的注册命令使用方法
32位的Windows: 1. 运行->cmd 2. cd C:\Windows\Microsoft.NET\Framework\v4.0.30319 3. aspnet_regiis.exe ...
- 全球第一开源ERP Odoo操作手册 数据库简介
1.3 数据库简介 每一个独立核算的企业都有一套相互关联的账簿体系, 把这一套完整的账簿体系建立在计算机系统中就称为一个数据库. 一般一个企业只用一个数据库. 如果企业有几个下属的独立核算的实体,也可 ...
- AJPFX:什么是止盈?什么是止损?
在您进行外汇交易后,会碰到Take Profit(止盈)和 Stop Loss(止损)这两个词,均是用作控制风险的工具. 止盈(Take Profit):当单子达到预期的获利价格时锁定盈利.当订单在盈 ...
- Spring Boot切换为APR模式
Spring Boot内置了tomcat容器,直接运行Application就可以启动web服务器. 在tomcat中提供了三种方式:BIO.NIO.APR. BIO tomcat7以下的版本都是BI ...
- Go语言学习笔记(1)——Hello World!
第一个go程序——HelloWorld.go 源码 : package main import ("fmt") // import "fmt" func mai ...
- POJ 2782
#include <iostream> #include <algorithm> #define MAXN 100005 using namespace std; int _m ...
- 解决 ORA-27102: out of memory
记一次故障处理总结: 操作系统:windows server 2008 R2数据库版本:11.2.0.2 故障描述:外部应用连接数据库,提示连接不正常: 排错过程:1.首先 检查数据库监听服务状态是否 ...
- 了解fortran语言
最近看了一些文献,发现用了Fortran语言编程,并且还是近几年的,了解了之后才知道,其实Fortran已经慢慢没有人再用了,之所有还有一批人在用,极大可能是历史遗留问题吧.而这,也得从Fortran ...
- IIS 站点部署多级域名
当站点的规模达到一定程度,往往会对业务进行拆分,部署到一台服务器的不同站点,,而一个域名(顶级域名)只能绑定一个站点(核心站点),这个时候就通过给顶级域名创建子域名的方式(理论上一个顶级域名可以绑定5 ...
- SQL Server 跨域访问
# SQL Server 跨服务器访问数据 参考链接: [sp_addlinkedserver](https://msdn.microsoft.com/zh-cn/library/ms190479.a ...