一、问题
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】870. Advantage Shuffle 解题报告(Python)

    [LeetCode]870. Advantage Shuffle 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn ...

  2. '2'>'10'==true? JS是如何进行隐式类型转换的?

    前言 '2'>'10'返回的true,可能很多人都不是很能理解吧? 在js中,当运算符在运算时,如果两边数据不统一,CPU就无法计算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转 ...

  3. <学习opencv> opencv 概述及初探

    目录 Opencv3 当前模块 OpenCV 贡献库(opencv_contrib) OpenCV 头文件 旧式C风格头文件 新式C++风格头文件 例程 DEMO1 - 显示图片 DEMO2 - 视频 ...

  4. FP增长算法

    Apriori原理:如果某个项集是频繁的,那么它的所有子集都是频繁的. Apriori算法: 1 输入支持度阈值t和数据集 2 生成含有K个元素的项集的候选集(K初始为1) 3 对候选集每个项集,判断 ...

  5. Zookeeper集群安装(开启kerberos)

    安装规划 zookeeper集群模式,安装到如下三台机器 10.43.159.237 zdh-237 10.43.159.238 zdh-238 10.43.159.239 zdh-239 Kerbe ...

  6. Selenium_使用switch_to.frame处理网页框架切换(13)

    与在新窗口打开一个网页后需要切换窗口才能定位元素一样,在iframe标签中的元素也不能直接定位,需要切换到对应的iframe框架中才能进行元素定位. 完成网页框架切换操作需要用selenium中的两个 ...

  7. spring boot 使用 mybatis 开启事务回滚 的总结

    1.前言 以前没有使用mybatis,可以关闭自动提交,然后做sql操作,对操作进行catch捕获异常, 如果没有异常则commit 提交 ,有异常则 rollback 回滚,新增的数据则删除 ,修改 ...

  8. Linux shc 命令手册

    shc Generic shell script compiler. https://www.linux-man.cn/command/shc/ #Compile a shell script: sh ...

  9. PyCharm - 关联mysql失败 - Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezone' property manually.

    时区错误,MySQL默认的时区是UTC时区,比北京时间晚8个小时. 所以要修改mysql的时长 在mysql的命令模式下,输入: set global time_zone='+8:00'; 再次连接成 ...

  10. 实验 4 :Open vSwitch 实验 —— Mininet 中使用 OVS 命令

    实验 4 :Open vSwitch 实验 -- Mininet 中使用 OVS 命令 一.实验目的 Mininet 安装之后,会连带安装 Open vSwitch,可以直接通过 Python 脚本调 ...