重写与重载 / 动静态分派调用(JVM字节码底层逐步解析,吃鸡例子简单易懂)

面试混分巨兽!网络千遍一律标准答案如下:

(1)两者都是面向对象中实现多态的方式,两者与父类方法名都必须相同,两者都允许子类根据自己需要重新实现父类方法。
(2)重写是运行时的多态,重载是编译时的多态。
(3)重写必须保持与父类方法一样的参数列表,一样的返回值。重载是返回值类型不同或者参数列表不同(参数列表的类型,个数等等)。
背题去面试的足够了。下面开始讲重写和重载的原理,会有点苦涩:

我曾经也背书式学习,深受其害,直到后来有空了,带着求知精神深入学习了JVM,才融会贯通很多知识点。

在这里插入图片描述

从这个简单的例子可以看出,子类继承了父类的方法,可以重写输出自己定义的‘说话’;也可以修改父类方法void返回值类型为string,重载了say方法。下面看看字节码文件:

在这里插入图片描述

编译后发现有三个class字节码文件,一个主类。一个父类一个子类。

我们这里只探究重写和重载,所以直接看子类:

先看重写的方法

在这里插入图片描述

再到重载的方法:

在这里插入图片描述

发现在这里,它们两个方法的字节码还是大同小异的,就是一个很普通的方法执行,没有特别的标志和符号或者操作指令说明是重写还是重载。真正不同的应该是Main方法,也就是调用的地方。它必须知道调用的哪个具体方法,重载的那个还是重写的那个。

代码:
在这里插入图片描述
字节码:
在这里插入图片描述
在这里插入图片描述

可以看见重写的方法入参类型跟父类的入参类型是一样的,重载的入参类型则是int类型,这里就是根据返回值类型或者入参类型或者参数个数去区分。但是子类两个方法都叫say,却能够准确的找到具体哪个say去调用。这是为什么?继续深一步跟踪:

这里要加一个概念,就是JVM中方法的调用分两种

(1)解析调用:一般就是static静态方法

(2)分派调用:

				### 动态调用又分:静态分派调用(重载)和动态分派调用(重写)
***静态分派调用***就是重载的方法,它之所以说是静态分派,就是因为它在程序的编译阶段就已经通过参数和方法名或者返回值就能确定调用子类的哪个重载的具体方法,是在编译期确定的,所以叫静态分派。如果有多个都相符合的,比如一个重载方法入参是char ,一个是long,一个是Object.当你传 “a” 的时候,就会准确找到int,当没有入参类型是char的重载方法时,才会迁就一点去找long或者Obeject的(中间会有强转型和如果是对象类型包装类型的装箱)。
比如你玩吃鸡,一开始跳伞你就确定了地图上要捡哪一把枪,目标明确。但是当你想找一把mini狙找不到,那么98K你也将就着先用,因为都是相同类型,就算找到空投里面有AWM,但是你还是用mini顺手,所以你还是将就用着,有最合适的就换。
***动态分派调用***就是重写的方法或者子类调用父类的方法,它在编译期间没办法根据方法名和参数或者返回值确定具体方法,而是在运行时才去主动找要哪个方法。JVM有一个调用过程:

(1)invoke字节码操作指令调用方法时,先去找操作数栈中顶部的栈帧元素指向的对象的类型(方法就是栈里面一个个栈帧),这里可以想象成先从子类找有没有重写这个方法。
(2)假如这个栈帧中是描述符和简单名称都相符合的方法,则进行访问权限的校验,如果校验通过,则直接返回这个方法的直接引用。
(3)如果不存在,则根据继承的父子关系对父类继续进行查找(描述符和简单名称都相符,并且权限允许),如果一个父类不存在,则继续找父类,继续父类的父类。
(4)如果全部父类都没有,返回一个抽象方法未实现异常(AbstractMethodError)。

比如玩吃鸡的时候,你开局跳伞时根本没有想着一定要打什么武器,而是在作战的时候,根据地形和战况再选择武器,攻房用冲锋,近战喷子,远打狙,守桥时用汽油之类。

好了,重写重载就说到这里,如果我有说的不对的地方欢迎提出来,这都是我根据自己的学习理解总结的,欢迎大家讨论,鞠躬!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章