java 多态实现的jvm调用过程_多态:JVM是如何进行方法调用的

在我们平时的工作学习中写java代码时,如果我们在同一个类中定义了两个方法名和参数类型都相同的方法时,编译器会直接报错给我们。还有在代码运行的时候,如果子类定义了一个与父类完全相同的方法的时候,父类的方法就会被覆盖,(也就是我们平时说的重写)。那么,jvm虚拟机是如何精确识别目标方法的。

重载、重写与多态

重载:方法名相同而参数类型不相同的方法之间的关系。

重写:方法名相同并且参数类型也相同的方法之间的关系。

这两个概念我们耳熟能详,那么重载和重写是如何判断的呢?

重载:

重载的方法在编译期间就可以完成识别。java编译器会根据所传入参数的声明类型对方法名相同的方法进行选取。

除了同一个类中,如果A继承了B,A中定义了与B中的非私有方法同名的方法,而且这两个方法的参数类型不同,那么A和B类同样构成了重载

重写:

重写方法的判断是在运行期间才可以完成识别的。

我们都知道多态是java面向对象语言的三大特性之一。而方法的重写,就是最能体现多态的一种方式:它允许子类在继承父类部分特性的同时,拥有自己独特的行为。

举个简单的例子帮大家理解一下多态:

比如我们按下b这个键,在dota中代表的是敌法师的blink技能,在lol中是回城,在网游里又成了背包。对于不同的对象拥有不同的行为,这就是多态。

静态绑定与动态绑定

接下来,我们来看一下jvm是如何识别目标方法的。

刚才我们说到,重载方法的区分在编译阶段已经完成了,那么我们就可以认为在java虚拟机中不存在重载这一概念。因此,重载也可以被称为静态绑定,而重写则被称为动态绑定。

在jvm中,我们有5种方法调用的指令,分别是:

invokestatic:调用静态方法;

invokespecial:调用实例构造方法,私有方法和父类方法,以及使用super关键字调用父类的实例方法或构造器;

invokevirtual:调用虚方法(非私有实例方法);

invokeinterface:调用接口方法,在运行时再确定一个实现此接口的对象;

invokedynamic:在运行时动态解析出调用点限定符所引用的方法之后,调用该方法(jdk1.8lamada表达式);

这里,我们简单介绍一下这几种指令,对于invokestatic指令和invokespecial指令而言,java虚拟机能够直接识别目标方法,也就是我们所说的静态绑定。

invokevirtual和invokeinterface指令则需要在执行的过程中才能找到目标方法,也就是我们所说的动态绑定。

总结一下静态绑定和动态绑定的概念就是:

静态绑定:在程序执行之前就已经被绑定、也就是说再编译阶段就已经知道这个方法是属于哪个类的方法。

1.private修饰的方法,不能被子类调用   2.  被final修饰的方法      3.被static修饰的方法

动态绑定:在运行过程中根据调用者的动态类型来识别目标方法的情况。

动态绑定中,我们会记录方法对应的实际引用的地址,也可以理解为索引值,这里我们把它叫做方法表。

方法表使用了数组的数据结构,每个数组元素指向了当前类以及其祖先类中非私有的实例方法。

这个数据结构,便是java虚拟机实现动态绑定的关键所在

虚方法

通过这些指令的描述,我们发现虚方法和非虚方法直接决定了静态绑定还是动态绑定,也就决定了是直接用父类的方法还是动态地用子类重写的方法。所以,我们有必要去理解虚方法,并能判断哪些是属于虚方法。

我们先一起看两段代码:

代码1:class Dota {private void play() {System.out.println("我喜欢玩dota,哈哈哈~~~");}void startGame() {play();}} class LoLextends Dota {void play() {System.out.println("我喜欢玩lol,哈哈哈~~~~");}} public class Demo1{public static void main(String[]args){new LoL().startGame();}}

代码二:class Dota {void play() {System.out.println("我喜欢玩dota,哈哈哈~~~");} void startGame() {play();}} class LoLextends Dota {void play() {System.out.println("我喜欢玩lol,哈哈哈~~~~");}} public class Demo2{public static void main(String[]args){new LoL().startGame();}}

这里,dota是lol的父类(本人是dotaer,哈哈),这两段代码的唯一不同就是代码1的父类的play方法private修饰的,而代码2中的play()方法不是私有的。接下来,我们看下输出结果:

代码1:我喜欢玩dota,哈哈哈~~~

代码2:1我喜欢玩lol,哈哈哈~~~~

第一段代码直接调用了父类的play()方法,而第二段代码调用了子类的play()方法

大家是不是觉得很奇怪,一个私有的修饰符就能使结果不一样吗?

结合我们之前所说的进行判别,第一段代码应该是静态绑定,第二段代码则是动态绑定

那么,代码1就是非虚函数,代码2是虚函数。接下来,我们来看下虚函数的概念:

虚函数:除了静态方法之外,声明为final或者private的实例方法是非虚方法。其它(其他非private方法)实例方法都是虚方法。

当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。

要想调用父类中被重写的方法,则必须使用关键字super。

代码1由于是private修饰,所以为非虚函数,调用了invokespecial指令。

代码2是虚函数,则调用了invokevirtual指令。

再看一个例子

代码3:

AAffA0nNPuCLAAAAAElFTkSuQmCCpublic class Demo2 {    static abstract class Game {}    static class Dota extends Game {}    static class Lol extends Game {}    public void play(Game game) {

System.out.println("hello,game");

}    public void play(Dota dota) {

System.out.println("hello,Dota");

}    public void play(Lol lol) {

System.out.println("hello,lol");

}    public static void main(String[] args) {

Game dota = new Dota();

Game lol = new Lol();

Demo2 sd = new Demo2();

sd.play(dota);

sd.play(lol);

}

}

AAffA0nNPuCLAAAAAElFTkSuQmCC

输出结果:hello,game

hello,game

虽然在这里,play方法是虚方法,是动态绑定,但是调用play方法的是Demo2的实例sd,由于Demo2方法没有子类,所以不需要考虑,则直接执行父类的方法。

总结:

我们介绍了java虚拟机(jvm)是如何执行方法的,我们从我们熟悉的重载和重写切入,了解了静态绑定和动态绑定的概念:

静态绑定:在程序执行之前就已经被绑定、也就是说再编译阶段就已经知道这个方法是属于哪个类的方法。

动态绑定:在运行过程中根据调用者的动态类型来识别目标方法的情况

并且知道了jvm中用方法表来维护非私有实例方法与其索引值的对应关系来实现动态绑定。

接着,我们了解了jvm中调用方法的5个指令,并且通过静态绑定和动态绑定的概念对其进行归类,

静态绑定:

invokestatic:调用静态方法;

invokespecial:调用实例构造方法,私有方法和父类方法;

动态绑定:

invokevirtual:调用虚方法;

invokeinterface:调用接口方法,在运行时再确定一个实现此接口的对象;

invokedynamic:在运行时动态解析出调用点限定符所引用的方法之后,调用该方法;

归类中通过虚函数的概念,使我们对jvm中方法的调用加以更深的了解。

虚函数:除了静态方法之外,声明为final或者private的实例方法是非虚方法。其它(其他非private方法)实例方法都是虚方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

request.getContextPath()取不到值

request.getContextPath()得到项目的名字 如果项目为根目录,则得到一个"",即空的字条串, 如果项目为abc, <%request.getContextPath()% >/ 将得到abc/

java 0l是多少_Java 构造器 - osc_0ltyoebk的个人空间 - OSCHINA - 中文开源技术交流社区...

Java 基础构造器构造器:构造对象.1.构造方法的名字必须和类名字保持一致.2.构造方法没有返回类型.(不是没有返回值!!!!!!!!!!!!!)3.构造方法可以定义参数,也可以不定义,参数默认的是无参构造.4.构造方法也遵守访问修饰符的限制,5.构造方法也支持方法的重载.6.会先执行方法.什么…

编写一个java打印心程序_java – ?同时打印心脏符号

很抱歉问一个愚蠢的问题,我们正在尝试将心脏符号从数据库打印到Java XML文件.但同样的印刷品是“&#xff1f;”不知道我错过了哪里.尝试过char unicode.作为一种练习,我在main方法中尝试使用它.请查找示例java类.public static void main(String[] args) {String t "\u2…

java文件的上传

java使用apache commons-fileupload组件实现文件上传&#xff08;控制文件上传的类型、文件上传的大小&#xff09; 1.导入jar包&#xff08;需要两个&#xff0c;idea中导入依赖&#xff0c;只需要导入common-fileupload&#xff0c;io会自动导入) common-fileupload.jar com…

java 皮鞋_java反射

一、反射的概述反射机制允许java程序加载一个运行时才得知其名称的类&#xff0c;获悉其完整API信息&#xff0c;包括其修饰符、超类、实现的接口&#xff0c;也包括属性和方法的所有信息&#xff1b;二、反射的作用能够分析类能力的程序成为反射(reflective)。反射的功能极其强…

Docker启动MySql后连接报1251处理方法

问题描述&#xff1a; docker成功启动mysql容器后使用navicat连接报1251 - Client does not support authentication protocol requested byserver;consider upgrading MySQL client 处理方法&#xff1a; 首先进入MySQL容器内 docker exec -it mysql bash登录MySQL mysql -…

python enumeration_如何在python中将int转换为Enum?

我想要类似的东西&#xff0c;这样我就可以从一个引用访问值对的任何一部分。香草的版本&#xff1a;#!/usr/bin/env python3from enum import IntEnumclass EnumDemo(IntEnum):ENUM_ZERO 0ENUM_ONE 1ENUM_TWO 2ENUM_THREE 3ENUM_INVALID 4#endclass.print(Passes)print(1…

Springboot访问jsp页面但是却变成下载该页面

Springboot访问jsp页面但是却变成下载该页面 缺少jsp解析的依赖&#xff0c;加上即可 <!--jsp解析器依赖--><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency> …

控制台出现Failed to bind properties under ‘spring.datasource‘ to javax.sql.DataSource

网上查了下&#xff0c;没有找打相关的报错解决办法&#xff0c;所以在解决问题后&#xff0c;整理到网上&#xff0c;帮助有需要的朋友。 springboot整合druid时&#xff0c;引入了druid的数据源&#xff0c;在配置文件application.yml中配置了相关配置 initialSize: 5minIdl…

java开发属于itsm吗_【行业】IT服务管理(ITSM):IT行业变革的思考(1)

ITSM&#xff0c;ITIL这些词越来越热门。有人认为这是一种新的技术&#xff0c;有人认为ITSM不过是MIS的一种应用&#xff0c;也有人认为无非是网管&#xff0b;工作流。其实ITSM的出现应该放在整个IT行业发展的过程中来看。可以说&#xff0c;ITSM的出现映射着IT行业的新一轮变…

linux 所有命令无法使用

配置nginx时&#xff0c;错误export之后linux 所有命令无法使用 出现这个问题是因为系统的环境变量没有正确配置造成的&#xff0c;造成这个原因有很多&#xff0c;比如系统升级&#xff0c;比如不正当操作等导致环境变量被覆盖修改&#xff0c;解决的方式有两种。 其一&…

java junit4 for_java-自动将测试从JUnit 3迁移到JUnit 4的最佳方法?

这是我用来执行furtelwart建议的实际正则表达式&#xff1a;// Add TestReplace:^[ \t](public void test)With:Test\n $1Regular Expression: onCase sensitive: onFile name filter:*Test.java// Remove double Tests on already Test annotated filesReplace:^[ \t]Test\n[ …

linux中安装jdk安装(rpm安装)

jdk安装&#xff08;rpm安装&#xff09; 1、下载jek rpm地址http://www.oracle.com/technetwork/java/javase/downloads/index.html 2、如果有安装openjdk 则卸载 [rootkuangshen ~]# java -version 检测当前系统是否存在java环 境&#xff01; java version "1.8.0_121…

java 数字组合_java解决数字组合问题

问题描述&#xff1a;输入1~9以内的几个数字&#xff0c;输出这些数字的多重组合。输入&#xff1a;1,2,3输出&#xff1a;123121323123问题分析&#xff1a;当输入为1输出1当输入为1 2输出1212当输入为1 2 3输出123121323123那么可以分析如下当输入1 2 3 4时做了以下处理把…

iis php的mime映射_iis MIME 映射添加.php文件映射类型肿么定义

进入控制面板&#xff0c;选择程序和功能&#xff0c;打开或关闭Windows 功能&#xff0c;找到Internet informationservices&#xff0c;分别开启FTP服务器、Web管理工具和万维网服务组件&#xff0c;其中万维网服务的子组件也依次开启&#xff0c;”其中最重要的开启应用程序…

usd php 换算,货币在PHP中从INR转换为USD

我想在PHP中将货币从INR转换为USD,这是我迄今为止尝试过的代码.但它不起作用.我错过了什么&#xff1f;function currency($from_Currency,$to_Currency,$amount) {$amount urlencode($amount);$from_Currency urlencode($from_Currency);$to_Currency urlencode($to_Curren…

Node.js介绍、优势、用途

一、Node.js介绍 Node.js是一个javascript运行环境。它让javascript可以开发后端程序&#xff0c;实现几乎其他后端语言实现的所有功能&#xff0c;可以与PHP、Java、Python、.NET、Ruby等后端语言平起平坐。 Nodejs是基于V8引擎&#xff0c;V8是Google发布的开源JavaScript引…

php数组有没有类似next方法,PHP 数组current跟next用法

PHP 数组current和next用法1.current$transport array(foot, bike, car, plane);$mode current($transport); //$mode foot;$mode next($transport); // $modebike;$mode current($transport); //$mode bike;$mode prev($transport); // $mode foot;$mode end($trans…

RROR in main Module not found: Error: Can‘t resolve ‘index.js‘

第一种方式&#xff0c;使用新的命令 webpack打包的时候报错 webpack版本升级了 这个命令不能用了 用这个就可以&#xff1a; webpack ./index.js -o ./dist./dist是打包文件生成的目录 另一种方式 更改webpack版本&#xff0c;降低版本&#xff0c;还有webpak这个命令 降…

swfupload 实例 php,文件上传之SWFUpload插件(代码)

下面通过一段代码给大家演示下&#xff0c;主要分为1.前台文件index.html和 2.后台文件upload.php。具体代码如下所示&#xff1a;1.前台文件index.htmlSWFUploadvar swfu;window.onload function() {var settings {flash_url : "swfupload/swfupload.swf",upload_…