我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java和JVM的重载和重写方法是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java和JVM的重载和重写方法是什么

本文小编为大家详细介绍“Java和JVM的重载和重写方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java和JVM的重载和重写方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

1.案例

void invoke(Object obj, Object... args) { ... }void invoke(String s, Object obj, Object... args) { ... }invoke(null, 1);    // 调用第二个invoke方法invoke(null, 1, 2); // 调用第二个invoke方法invoke(null, new Object[]{1}); // 只有手动绕开可变长参数的语法糖,                               // 才能调用第一个invoke方法

某API定义了两个同名重载方法:

  • 第一个接收一个Object,以及声明为Object…的变长参数

  • 第二个则接收一个String、一个Object,以及声明为Object…的变长参数

想调用第一个方法,传参(null, 1),即声明为Object的形式参数所对应的实际参数为null,而变长参数则对应1。
之所以不提倡可变长参数方法重载,是因为Java编译器可能无法决定应该调用哪个目标方法。
这种情况下,编译器会报错,并且提示这方法调用有二义性。然而,Java编译器直接将我的方法调用识别为调用第二个方法,这究竟是为什么呢?

Java虚拟机是怎么识别目标方法的?

2.重载与重写

同一类中出现多个:

  • 名字相同

  • 参数类型相同

这限制可通过字节码工具绕开,编译完成后,可再向class文件中添加方法名和参数类型相同,而返回类型不同的方法。当这种包括多个方法名相同、参数类型相同,而返回类型不同的方法的类,出现在Java编译器的用户类路径上时,它是怎么确定需要调用哪个方法的呢?
当前版本的Java编译器会直接选取第一个方法名以及参数类型匹配的方法。并且,它会根据所选取方法的返回类型来决定可不可以通过编译,以及需不需要进行值转换等。
重载的方法在编译过程中即可完成识别。具体到每一个方法调用,Java编译器会根据所传入参数的声明类型(注意与实际类型区分)来选取重载方法。选取的过程共分为三个阶段:

  • 在不考虑对基本类型自动装拆箱及可变长参数情况下选取重载方法

  • 如在第1个阶段没找到适配方法,那在允许自动装拆箱,但不允许可变长参数情况下选取重载方法

  • 如在第2个阶段中没找到适配方法,那在允许自动装拆箱及可变长参数情况下选取重载方法

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

传入null时,它既可匹配第一个方法中声明为Object的形式参数,也可匹配第二个方法中声明为String的形式参数。由于String是Object的子类,因此Java编译器会认为第二个方法更贴切。
除同一个类中的方法,重载也可作用于这个类所继承而来的方法。如子类定义了与父类中非私有方法同名的方法,且这两个方法的参数类型不同,那在子类中,这两个方法同样构成重载。

若子类定义与父类中非private方法的同名方法,且这两方法参数类型相同,那这俩方法间啥关系:

  • 若这俩都是static方法,那子类中的方法隐藏了父类中的方法

  • 若都不是 static 的,则子类的方法重写了父类中的方法

Java的方法重写是多态的体现:允许子类在继承父类部分功能同时,拥有自己独特行为。
重写调用会根据调用者的动态类型选取实际的目标方法。

3.JVM的静态绑定和动态绑定

Java虚拟机识别方法的关键在于类名、方法名及方法描述符(method descriptor)。
方法描述符由方法的参数类型及返回类型构成。
同一类中,如同时出现多个名字相同且描述符相同的方法,那Java虚拟机会在类的验证阶段报错。
Java虚拟机与Java语言不同,它不限制名字与参数类型相同,但返回类型不同的方法出现在同一类,对调用这些方法的字节码,由于字节码所附带的方法描述符包含了返回类型,因此Java虚拟机能够准确识别目标方法。

JVM方法重写判定同样基于方法描述符。
如子类定义了与父类中非私有、非静态方法同名的方法,则仅当这俩方法的参数类型及返回类型一致,JVM才会判定为重写。

对Java中重写而Java虚拟机中非重写的情况,编译器会通过生成桥接方法[2]实现Java的重写语义。

由于对重载方法的区分在编译阶段已完成,可认为JVM不存在重载概念。因此,某些文章将

  • 重载称为静态绑定(static binding)或编译时多态(compile-time polymorphism)

  • 重写称为动态绑定(dynamic binding)

这说法在JVM语境下并非完全正确,因为某类中的重载方法可能被它的子类重写,因此JVM 会将所有对非私有实例方法的调用编译为需要动态绑定的类型。

JVM的:

  • 静态绑定指在解析时便能够直接识别目标方法

  • 动态绑定指要在运行过程中,根据调用者的动态类型来识别目标方法

Java字节码中与调用相关的指令有:

  • invokestatic:调用静态方法

  • invokespecial:调用私有实例方法、构造器及使用super关键字调用父类的实例方法或构造器,和所实现接口的默认方法

  • invokevirtual:用于调用非私有实例方法

  • invokeinterface:用于调用接口方法

  • invokedynamic:用于调用动态方法较为复杂

编译生成这四种调用指令的情况。

interface 客户 {  boolean isVIP();}class 商户 {  public double 折后价格(double 原价, 客户 某客户) {    return 原价 * 0.8d;  }}class 奸商 extends 商户 {  @Override  public double 折后价格(double 原价, 客户 某客户) {    if (某客户.isVIP()) {                         // invokeinterface            return 原价 * 价格歧视();                    // invokestatic    } else {      return super.折后价格(原价, 某客户);          // invokespecial    }  }  public static double 价格歧视() {    // 咱们的杀熟算法太粗暴了,应该将客户城市作为随机数生成器的种子。    return new Random()                          // invokespecial           .nextDouble()                         // invokevirtual           + 0.8d;  }}

“商户”类定义了一个成员方法,叫“折后价格”,它接收一个double类型参数及一个“客户”类型参数。
这里“客户”是个接口,定义了一个接口方法“isVIP”。

“奸商”类这个方法,首先调用客户#isVIP,该调用会被编译为invokeinterface指令

  • 若客户是VIP,则调用奸商类的一个名叫“价格歧视”的静态方法。该调用会被编译为invokestatic指令

  • 如客户不是VIP,则通过super调用父类的“折后价格”方法。该调用会被编译为invokespecial指令

在静态方法“价格歧视”会调用Random类的构造器。该调用会被编译为invokespecial指令。然后以这个新建Random对象为调用者,调用Random类中的nextDouble方法。该调用会被编译为invokevirutal指令。

对于invokestatic以及invokespecial而言,Java虚拟机能够直接识别具体的目标方法。

而对于invokevirtual以及invokeinterface而言,在绝大部分情况下,虚拟机需要在执行过程中,根据调用者的动态类型,来确定具体的目标方法。

如虚拟机能确定目标方法有且仅有一个,比如说目标方法被标记为final[3][4],它可不通过动态类型,直接确定目标方法。

4.调用指令的符号引用

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

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

  • 接口符号引用

  • 非接口符号引用

// 在奸商.class的常量池中,#16为接口符号引用,指向接口方法"客户.isVIP()"。#22为非接口符号引用,指向静态方法"奸商.价格歧视()"。$ javap -v 奸商.class ...Constant pool:...  #16 = InterfaceMethodref #27.#29        // 客户.isVIP:()Z...  #22 = Methodref          #1.#33         // 奸商.价格歧视:()D...

执行使用了符号引用的字节码前,JVM需解析这些【符号引用】并替换为【实际引用】。

对【非接口符号引用】,假定该【符号引用】所指向的类为C,则JVM按如下步骤查找:

  • 在C中查找符合名字及描述符的方法

  • 若没找到,搜索C的父类,直至Object类

  • 若还没找到,在C所直接实现或间接实现的接口中搜索,该步搜索得到的目标方法必须是非private、非static且若目标方法在间接实现的接口中,则需满足C与该接口间无其他符合条件的目标方法。若有多个符合条件的目标方法,则返回其中任一。

所以static方法也可通过子类来调用。子类的static方法会隐藏(这不是重写)父类中的同名、同描述符的静态方法。

对于接口符号引用,假定该符号引用所指向的接口为I,则Java虚拟机会按照如下步骤进行查找。

  • 在I中查找符合名字及描述符的方法。

  • 如果没有找到,在Object类中的公有实例方法中搜索。

  • 如果没有找到,则在I的超接口中搜索。这一步的搜索结果的要求与非接口符号引用步骤3的要求一致。

经过上述解析步骤后,符号引用会被解析成实际引用:

  • 对可静态绑定的方法调用,实际引用是个指向方法的指针

  • 对需动态绑定的方法调用,实际引用则是个方法表的索引

5.总结与实践

文介绍了Java以及Java虚拟机是如何识别目标方法的。

在Java方法的:

  • 重载,方法名相同而参数类型不相同的方法间

  • 重写,方法名相同&参数类型也相同的方法间

JVM识别方法的方式除了方法名和参数类型,还有返回类型。

JVM的:

  • 静态绑定:在解析时便能够直接识别目标方法的情况

  • 动态绑定,需在运行过程中根据调用者的动态类型来识别目标方法的情况。由于Java编译器已区分重载方法,因此可认为JVM不存在重载

在class文件中,Java编译器会用符号引用指代目标方法。在执行调用指令前,它所附带的符号引用需要被解析成实际引用。对于可以静态绑定的方法调用而言,实际引用为目标方法的指针。对于需要动态绑定的方法调用而言,实际引用为辅助动态绑定的信息。

Java的重写与Java虚拟机中的重写并不一致,但编译器会通过生成桥接方法来弥补。

读到这里,这篇“Java和JVM的重载和重写方法是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java和JVM的重载和重写方法是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java和JVM的重载和重写方法是什么

本文小编为大家详细介绍“Java和JVM的重载和重写方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java和JVM的重载和重写方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.案例void
2023-06-28

java方法的重写和重载有什么区别

Java方法的重写(Override)和重载(Overload)是两个不同的概念。重写是指在子类中重新定义父类中已有的方法,方法名、参数列表和返回类型都必须相同,目的是为了更改方法的实现逻辑。重写方法需要使用`@Override`注解进行标
2023-08-11

Java中方法重写和方法重载的6个区别

方法重载和方法重写区别:1、定义;2、应用场景;3、方法签名;4、继承与多态;5、异常处理;6、方法访问修饰符;方法重载在同一个类中,方法名相同但参数类型、参数个数不同的方法之间的关系。在子类中,有相同的方法名、参数类型、参数个数并且返回类型兼容的方法称之为方法重写。
Java中方法重写和方法重载的6个区别
2023-10-29

java重载和重写的区别

java重载和重写的区别:(推荐:java视频教程)1、定义区别:①重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。②覆盖(也叫重写)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。即函数
java重载和重写的区别
2018-11-09

JAVA继承、构造方法、重写和重载方法怎么用

本文小编为大家详细介绍“JAVA继承、构造方法、重写和重载方法怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“JAVA继承、构造方法、重写和重载方法怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。构造方
2023-06-30

Java构造方法和方法重载有什么特点

这篇文章主要介绍了Java构造方法和方法重载有什么特点的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java构造方法和方法重载有什么特点文章都会有所收获,下面我们一起来看看吧。类的结构包括 :1. 成员变量2.
2023-06-26

java中重载和重写的区别有哪些

这篇文章主要介绍了java中重载和重写的区别有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1.重载重载指在一个类中,具有多个相同名称的方法,他们的参数列表却不相同(参数
2023-06-25

java重构和重载有什么区别

Java的重构(refactoring)和重载(overloading)是两个不同的概念。1. 重构是指对现有代码的修改,以改善代码的结构、可读性、可维护性和性能等方面的质量。重构通常不改变代码的功能,只是对代码进行优化和改进。重构的目的是
2023-08-30

Java重写AST插件的方法是什么

本篇内容主要讲解“Java重写AST插件的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java重写AST插件的方法是什么”吧!1. 介绍随着Java 6的发布,java编译器已经有了
2023-06-17

java中方法重写的规则是什么

在Java中,方法重写是指子类重写父类中的方法。以下是Java中方法重写的规则:1. 方法重写必须具有相同的方法名称、参数列表和返回类型。2. 子类中重写的方法不能比父类中被重写的方法具有更低的访问权限。例如,如果父类中的方法是public
2023-08-11

java中方法重载什么?

java中方法重载什么?方法重载是指一个类中可以有多个方法具有相同的名字,但这些方法的参数必须不同。好处:只需要记住唯一一个方法名称,就可以实现类似的多个功能。这里需要注意的是参数不同需要满足2个条件,一个是参数的个数不同,一个是参数个数相同,但参数列表中对应
java中方法重载什么?
2016-06-21

Java构造方法的定义和重载

本篇内容介绍了“Java构造方法的定义和重载”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 一、构造方法的定义(一)什么构造方法实例化一个类
2023-06-15

C++函数重载、隐藏与覆盖重写的方法是什么

这篇文章主要介绍了C++函数重载、隐藏与覆盖重写的方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++函数重载、隐藏与覆盖重写的方法是什么文章都会有所收获,下面我们一起来看看吧。1 函数重载定义:C+
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录