一、问题
Java方法调用过程中,Jvm是如何知道调用的是哪个类的方法?Jvm又是如何处理?
 
二、概念
a、当子类和父类(接口和实现类)存在同一个方法时,子类重写父类(接口)方法时,程序在运行时调用的方法时,是调用父类(接口)的方法呢?还是调用子类的方法呢?我们将确定这种调用何种方法的操作称之为绑定。
 绑定又分为静态绑定和动态绑定。
 
静态绑定
静态绑定是在程序执行前就已经被绑定了(也就是在程序编译过程中就已经知道这个方法是哪个类中的方法)。
public class StaticBindDemo {

public static void s1() {
System.out.println("static s1");
} private void p1() {
System.out.println("private p1");
} public final void f1() {
System.out.println("final f1");
}
}

  

 
调用方:
public class StaticCall {
public static void main(String[] args) {
StaticBindDemo sbd = new StaticBindDemo();
StaticBindDemo.s1();
sbd.f1();
}
}

  

 
反编译后的文件:
 
 
上面的源代码反编译后,我们可以看到
调用的是静态方法
8: invokestatic #4 // Method com/jstar/jvm/sync/bind/StaticBindDemo.s1:()V
 
1、#4指的是常量沲中的第4个常量表索引项,记录的是方法s1的符号引用,jvm会根据这个符号引用找到方法f1
所在的类的全限定名: com/jstar/jvm/sync/bind/StaticBindDemo
2、紧接着JVM会加载、边接和初始化类StaticBindDemo类
3、然后在StaticBindDem类所在的方法区中找到s1()方法的直接地址,并将这个直接地址记录到StaticCall类的常量池索引为4的常量表中。这个过程叫常量池解析 ,以后再次调用StaticBindDemo.s1时,将直接找到s1方法的字节码;
4、 完成了StaticCall类常量池索引项4的常量表的解析之后,JVM就可以调用s1()方法,并开始解释执行f1()方法中的指令了。
 
通过上面的过程,我们发现经过常量池解析之后,JVM就能够确定要调用的s1()方法具体在内存的什么位置上了。实际上,这个信息在编译阶段就已经在StaticCall类的常量池中记录了下来。这种在编译阶段就能够确定调用哪个方法的方式,我们叫做静态绑定机制
 
注:Java中只有private、static和final修饰的方法以及构造方法是静态绑定。
a、private方法的特点是不能被继承,也就是不存在调用其子类的对象,只能调用对象自身,因此private方法和定义该方法的类绑定在一起。
 
b、static方法又称类方法,类方法属于类文件。它不依赖对象而存在,在调用的时候就已经知道是哪个类的,所以是类方法是属于静态绑定。
 
c、final方法:final方法可以被继承,但是不能被重写,所以也就是说final方法是属于静态绑定的,因为调用的方法是一样的。
 总结:如果一个方法不可被继承或者继承后不可被覆盖,那么这个方法就采用的静态绑定。
 
 
动态绑定
编译器在每次调用方法时都要进行搜索,时间开销相当大。因此虚拟机会预先为每个类创建一个方发表(method table),其中列出了所有方法的签名和实际调用的方法。  
 
public class Father {
public void f1() {
System.out.println("father-f1");
} public void f1(int i) {
System.out.println("father-f1 params-int :" + i);
}
} public class Son extends Father{
public void f1() {
System.out.println("son f1");
} public void f1(char c) {
System.out.println("son-f1 params-c:" + c);
}
}
public class Demo {
public static void main(String[] args) {
Father f = new Son();
f.f1();
//f.f1('c');
}
}

  

 
通过反编译Demo我们来看看jvm是怎么执行的

其中invokevirtual指令的详细调用过程是这样的:
(1) invokevirtual指令中的#4指的是Demo类的常量池中第4个常量表的索引项。这个常量表(Methodref ) 记录的是方法f1信息的符号引用(包括f1所在的类名,方法名和返回类型)。JVM会首先根据这个符号引用找到调用方法f1的类的全限定名: Father。这是因为调用方法f1的类的对象father声明为Father类型。
(2) 在Father类型的方法表中查找方法f1,如果找到,则将方法f1在方法表中的索引项记录到Demo类的常量池中第4个常量表中(常量池解析 )。这里有一点要注意:如果Father类型方法表中没有方法f1,那么即使Son类型中方法表有,编译的时候也通过不了。因为调用方法f1的类的对象father的声明为Father类型。
(3) 在调用invokevirtual指令前有一个aload_1指令,它会将开始创建在堆中的Son对象的引用压入操作数栈。然后invokevirtual指令会根据这个Son对象的引用首先找到堆中的Son对象,然后进一步找到Son对象所属类型的方法表.
(4) 这是通过第(2)步中解析完成的#4常量表中的方法表的索引项,可以定位到Son类型方法表中的方法f1(),然后通过直接地址找到该方法字节码所在的内存空间。
很明显,根据对象(father)的声明类型(Father)还不能够确定调用方法f1的位置,必须根据father在堆中实际创建的对象类型Son来确定f1方法所在的位置。这种在程序运行过程中,通过动态创建的对象的方法表来定位方法的方式,我们叫做 动态绑定机制 。
 
动态绑定过程: 
 <1>虚拟机提取对象的实际类型的方法表。 
 <2>虚拟机搜索方法签名,此时虚拟机已经知道应该调用哪种方法。(PS:方法的签名包括了:1.方法名 2.参数的数量和类型~~~~返回类型不是签名的一部分。) 
 <3>虚拟机调用方法
 
PS:由于动态绑定需要在运行时确定执行哪个版本的方法实现或者变量,比起静态绑定起来要耗时。所以在不影响整体设计,我们可以考虑将方法或者变量使用private,static或者final进行修饰。这边优化的内容就涉及到了内联的知识(我们在Java方法内联中专门介绍)。

Java动态绑定和静态绑定-多态的更多相关文章

  1. java动态绑定与静态绑定【转】

    程序绑定的概念: 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来.对java来说,绑定分为静态绑定和动态绑定:或者叫做前期绑定和后期绑定.静态绑定: 在程序执行前方法已经被绑定(也就是说在 ...

  2. Java动态绑定与静态绑定

    Java动态绑定来自于继承体现,子类继承父类,子类重新覆盖了父类的方法,就是动态绑定,以下举例: (动态绑定是在运行期间) 动物类: /** * 创建一个动物类 * @author Liudeli * ...

  3. C++ 多态Polymorphism 介绍+动态绑定、静态绑定

    什么是多态? 多态(polymorphism)一词最初来源于希腊语polumorphos,含义是一种物质的多种形态. 在专业术语中,多态是一种运行时绑定机制(run-time binding) ,通过 ...

  4. c++的动态绑定和静态绑定及多态的实现原理(摘)

    C++多态的实现原理 为了支持c++的多态性,才用了动态绑定和静态绑定.理解它们的区别有助于更好的理解多态性,以及在编程的过程中避免犯错误. 需要理解四个名词:对象的静态类型:对象在声明时采用的类型. ...

  5. Java动态绑定与多态

    在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特性.多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来.在一开始接触多态这个词的时候,我们或许会因为这个词本身而感到困惑, ...

  6. [转帖]java基础学习总结——多态(动态绑定)

    https://www.cnblogs.com/xdp-gacl/p/3644035.html 多态的概念 java基础学习总结——多态(动态绑定) 一.面向对象最核心的机制——动态绑定,也叫多态

  7. java基础学习总结——多态(动态绑定)

    一.面向对象最核心的机制——动态绑定,也叫多态

  8. Java动态绑定的内部实现机制

     JAVA虚拟机调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法.相反,当虚拟机调用一个实例方法时,它会基于对象实际的类型(只能在运行时得知)来选择所调用的方法,这就是动 ...

  9. Java中继承与多态

    Java类的继承继承的语法结构:    [修饰符列表] class 子类名 extends 父类名{        类体;    }子类就是当前这个类,父类就是我们要复用的那个类java中只支持单继承 ...

随机推荐

  1. 【LeetCode】820. 单词的压缩编码 Short Encoding of Words(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址:https://leetcode-cn.com/problems/short- ...

  2. Codeforce C. Pearls in a Row

    C. Pearls in a Row time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  3. C++模拟python风格的print函数--打印vector,map,list等结构

    // 最基本实现 template<typename T> static void print(T t) { std::cout << t; } // 处理 std::pair ...

  4. Spring Boot 整合 Fisco Bcos(区块链)

    简介 FISCO BCOS是由国内企业主导研发.对外开源.安全可控的企业级金融联盟链底层平台,由金链盟开源工作组协作打造,并于2017年正式对外开源. 目前,成熟的区块链的平台不少,之所以选择FISC ...

  5. docker学习:docker---kafka安装

    下载zookeeper镜像与kafka镜像: docker pull wurstmeister/zookeeper docker pull wurstmeister/kafka 本地启动zookeep ...

  6. 使用.NET 6开发TodoList应用(19)——处理OPTION和HEAD请求

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 在HTTP请求中,我们还剩下两类不常使用的请求没有讲到,本文就来实现以下关于OPTIONS和HEAD请求.OPTIONS请求用 ...

  7. APP自动化测试之手机滑屏

    相信大家在安装一个APP之后,进入之前会有几个页面组成的滑屏欢迎页面,要对这个APP进行自动化测试之前,就需要实现自动滑屏,怎么实现呢?请继续往下看 滑屏分 左滑和右滑,上滑.下滑 实现的原理(左滑) ...

  8. 解决ubuntu 18.04(桌面版)搜狗输入法不能正常使用的问题

    ubuntu下搜狗输入法的配置文件在~/.config目录下,一般有三个目录SogouPY.SogouPY.users.sogou-qimpanel 执行命令 $ cd ~/.config $ rm ...

  9. 基于CentOS6.5-Hadoop2.7.3-hive-2.1.1安装sqoop1.4.7

    注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6627736198431375879/ 系统版本,Hadoop已安装完成.链接<CentOS6.5下安装Had ...

  10. Java 简单操作hdfs API

    注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6632047118376780295/ 启动Hadoop出现问题:datanode的clusterID 和 name ...