重载和重写

重载:同一个类中定义名字相同的方法,但是参数类型或者参数个数必须不同。

重载的方法在编译过程中就可完成识别。具体到每一个方法的调用,Java 编译器会根据所传入参数的生命类型来选取重载方法。选取的过程分以下三个阶段:
1:在不考虑对基本类型自动装拆箱,以及可变长参数的情况下选取重载方法。
2:如果第 1 个阶段未找到,那么在允许自动装拆箱,但是不允许可变长参数的情况下选出重载方法。
3:如果第 2 个阶段未找到,那么在允许自动装拆箱以及允许可变长参数的情况下选出重载方法。

如果 Java 编译器在同一个阶段中找到了多个适配的方法,那么它会在其中选择一个最为贴切的,而决定贴切程度的一个关键就是形式参数类型的继承关系。

重写:子类定义了跟父类方法同名,参数类型一致的方法,就叫做子类重写了父类方法。

Java 是一门面向对象的变成语言,它的一个重要的特性就是多态。而方法的重写,正是多态最重要的一种体现方式。

JVM 的静态绑定和动态绑定

Java 虚拟机识别方法的关键在于类型,方法名以及方法的描述符。方法的描述符是由方法的参数类型以及返回类型所构成的。在同一个类中,如果出现多个名字相同,方法描述符也相同的方法,那么 Java 虚拟机会在类的验证阶段报错。

由此观之,Java 虚拟机与 Java 语言不通,它并不限制名字与参数类型相同,但返回类型不同的方法出现在同一个类中。对于调用这些方法的字节码来说,由于字节码附带的方法描述符包含了返回类型,因此 Java 虚拟机能够准确地识别目标方法。所以,Java 虚拟机判定重写的依据是:方法名,参数类型以及返回类型都相同。

由于重载方法的区分在编译阶段已经完成,我们可以认为 Java 虚拟机不存在重载这一概念。因此,重载也被称为静态绑定,或者叫编译时多态。而重写则被称为动态绑定。

Java 虚拟机中的静态绑定指的是解析时便能够直接识别目标方法的情况,动态绑定指的是需要在运行过程中根据调用者的动态类型来识别目标方法的情况。

Java 字节码中与调用相关的指令有五种。
1:invokestatic,用于调用静态方法
2:invokespecial,用于调用私有实例方法,构造器,以及实用 super 关键字调用父类的实例方法或构造器,以及所实现接口的默认方法。
3:invokevirtual,用于调用非私有实例方法。
4:invokeinterface,用于调用接口方法。
5:invokedynamic,用于调用动态方法。

调用指令的符号引用

编译过程中,我们并不知道目标方法的具体内存地址。因此,Java 编译器会暂时用符号引用来表示该目标的方法。这一符号引用包括目标方法所在的类或接口的名字,以及目标方法的方法名和方法描述符。

符号引用存储在 class 文件的常量池之中。根据目标方法是否为接口方法,这些引用分为:接口符号引用和非接口符号引用。

在执行使用了符号引用的字节码钱,Java 虚拟机需要解析这些符号引用,并替换为实际引用。

对于非接口符号引用,假定该符号引用所指向的类为 C,则 Java 虚拟机会按照如下步骤进行查找:
1:在 C 中查找符号名字及描述符的方法。
2:如果没找到,在 C 的父类总继续搜索,直至 Object 类。
3:如果没找到,在 C 所直接实现或间接实现的接口中搜索,搜索的目标方法必须是非私有非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。

对于接口符号引用,假定该符号引用所指向的接口为 I,则 Java 虚拟机会按照如下步骤进行查找:
1:在 I 中查找符合名字及描述符的方法。
2:如果没有找到,在 Object 类中的共有实例方法中搜索。
3:如果没有找到,则在 I 的超接口中搜索,搜索的目标方法必须是非私有非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。

通过上述步骤,符号引用会被解析成实际引用。对于可以静态绑定的方法调用而言,实际引用是一个指向方法的指针。对于需要动态绑定的方法调用而言,实际引用则是一个方法表的索引。

问答

Q:什么是直接实现?什么是间接实现?

C implements Interface1,Interface1 extends Interface2,C直接实现Interface1,间接实现Interface2

Q:public final 或 public static final 的方法,是不是在 虚拟机中解析为静态绑定的

静态方法都是静态绑定。调用的目标方法是public final 的话,HotSpot虚拟机也会静态绑定。但这属于优化,其它虚拟机不一定这么做。

Q:请问子类可以调用父类的静态方法是什么意思

class Foo { static void m(){} }
class Bar extends Foo{}
Bar.m();

Q:重载方法选取阶段中:那么在允许自动装拆箱,但是不允许可变长参数的情况下选出重载方法。举例演示

public void method(String str){
...
}
public void method(Object obj){
...
}

method(null);一直调用的是string参数的方法

总结

本文创作灵感来源于 极客时间 郑雨迪老师的《深入拆解 Java 虚拟机》课程,通过课后反思以及借鉴各位学友的发言总结,现整理出自己的知识架构,以便日后温故知新,查漏补缺。

关注本人公众号,第一时间获取最新文章发布,每日更新一篇技术文章。

04 JVM是如何执行方法调用的(上)的更多相关文章

  1. 04 JVM是如何执行方法调用的(下)

    虚方法调用 Java 里所有非私有实例方法调用都会被编译成 invokevirtual 指令,而接口方法调用会被编译成 invokeinterface 指令.这两种指令,均属于 Java 虚拟机中的虚 ...

  2. JVM(十二):方法调用

    JVM(十二):方法调用 在 JVM(七):JVM内存结构 中,我们说到了方法执行在何种内存结构上执行:Java 方法活动在虚拟机栈中的栈帧上,栈帧的具体结构在内存结构中已经详细讲解过了,下面就让我们 ...

  3. 多态:JVM是如何进行方法调用的

    在我们平时的工作学习中写java代码时,如果我们在同一个类中定义了两个方法名和参数类型都相同的方法时,编译器会直接报错给我们.还有在代码运行的时候,如果子类定义了一个与父类完全相同的方法的时候,父类的 ...

  4. 深入解析多态和方法调用在JVM中的实现

    深入解析多态和方法调用在JVM中的实现 1. 什么是多态 多态(polymorphism)是面向对象编程的三大特性之一,它建立在继承的基础之上.在<Java核心技术卷>中这样定义: 一个对 ...

  5. 第31篇-方法调用指令之invokevirtual

    invokevirtual字节码指令的模板定义如下: def(Bytecodes::_invokevirtual , ubcp|disp|clvm|____, vtos, vtos, invokevi ...

  6. 图解JVM执行引擎之方法调用

    一.方法调用 方法调用不同于方法执行,方法调用阶段的唯一任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程.Class文件的编译过程中不包括传统编译器中的连接步骤,一 ...

  7. jvm 字节码执行 (一)方法调用

    “虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上,而虚拟机的执行引擎是 由自己实现的,因此可以自行制定指令集 ...

  8. JVM方法调用过程

    JVM方法调用过程 重载和重写 同一个类中,如果出现多个名称相同,并且参数类型相同的方法,将无法通过编译.因此,想要在同一个类中定义名字相同的方法,那么它们的参数类型必须不同.这种方法上的联系就是重载 ...

  9. Spring杂谈 | 从桥接方法到JVM方法调用

    前言 之所以写这么一篇文章是因为在Spring中,经常会出现下面这种代码 // 判断是否是桥接方法,如果是的话就返回这个方法 BridgeMethodResolver.findBridgedMetho ...

随机推荐

  1. Internationalization(i18n) support in SAP CRM,UI5 and Hybris

    i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数)是"国际化"的简称.对程序来说,在不修改内部代码的情况下,能根据不同语言及 ...

  2. UVA 11404 Plalidromic Subsquence (回文子序列,LCS)

    最长回文子序列可以用求解原串s和反转串rv的LCS来得到,因为要求回文串分奇偶,dp[i][j]保存长度, 要求字典序最小,dp[i][j]应该表示回文子序列的端点,所以边界为单个字符,即i+j=le ...

  3. 一、Web 如何工作的

    平常我们在浏览器中输入一个网址,随即看到一个页面,这个过程是怎样实现的呢?下面用一幅图来说明: 整个流程如下: 1.域名解析  浏览器会解析域名对应的IP地址 PS:DNS服务器的知识 2.建立TCP ...

  4. fdisk - Linux分区表操作工具软件

    总览 fdisk [-u]设备名 fdisk -l [-u] [设备名 ...] fdisk -s分区 ... fdisk -v 描述 硬盘可以被分成一个或多个逻辑磁盘,称为 分区. 这些分区信息都存 ...

  5. AngularJs学习笔记-表单处理

    表单处理 (1)Angular表单API 1.模板式表单,需引入FormsModule 2.响应式表单,需引入ReactiveFormsModule   (2)模板式表单 在Angular中使用for ...

  6. 问题002:我们要使用的Java是哪个版本的?什么是JVM、JRE、JDK、IDE、API?

    三个版本:1.java SE 标准版 2.java EE企业版 3.Java ME 小型版本 JVM (java virtual machine) java虚拟机 JRE(java runtime e ...

  7. C++ 无限定名称查找

    无限定名称查找 (关键字:懒惰,挑捡,using指令的特殊性) 无限定名称查找实际上就是指没有限定(名称空间和名称空间运算符)名存在的一个名字的出现,其中对于using指令,其内部包含的所有的声明是被 ...

  8. LGTB 学分块

    总时间限制:  10000ms 单个测试点时间限制:  1000ms 内存限制:  65536kB 描述 LGTB 最近在学分块,但是他太菜了,分的块数量太多他就混乱了,所以只能分成 3 块 今天他得 ...

  9. 对于新能源Can数据、电池BMS等字节和比特位的解析

    1.对于1个字节(8个bit)以上的数据需要先进行倒序(因为高位在前 低位在后). CanID CanData 排序后的 字节数据 十进制 分辨率(0.005) 偏移量(40) 0x18FEC117 ...

  10. centos下 将(jgp、png)图片转换成webp格式

    由于项目要求需要将jpg.png类型的图片  转换成webp格式,最开始使用了php gd类库里 imagewebp 方法实现,结果发现转换成的webp格式文件会偶尔出现空白内容的情况.像创建了一个透 ...