在Java中存在重载与重写,面试中经常会出现与之相关的问题,今天从字节码来看看他们的实现。
简单代码
简单的测试代码如下图:
其中有继承关系的三个类Animal、Mamml(哺乳动物)、Tiger,主要用来实现重写,AssignTest中有三个test方法用来实现重载,大家可以猜猜打印结果!
运行结果
运行结果如下图:
根据打印结果可以看到两个test方法执行的都是test(Animal)这个方法,两个name()方法却分别执行的Mammal与Tiger的两个类方法,代码比较简单,所以产生的结果也比较直接简单,接下来我们用字节码来分析分析!
方法字节码
main方法字节码指令如下图:
字节码指令最重要的部分已经用红框圈出,其中第26行与31行为test方法,可以看到方法的参数都是Animal,说明在编译期就已经指定以具体的方法,从idea编辑器也可以看出来,只有test(Animal)方法被使用,这个在编译期就确定执行方法叫做方法的静态分派,重载就属于静态分派。
可是在35行与38行同样是执行Animal.name()方法,那么为什么会与上面不一样呢?这里就要说到invokevirtual这条指令了,invokevirtual指令会找到方法栈顶的第一个元素它所指向对象的实际类型,在这个实际类型中找对应的方法,如果存在就执行,否则继续往上找,直到找到为止,这就叫方法的动态分配。
这这个例子中,虽然编译时表示的是执行Animal的name方法,可是从前面初始化的指令可以看到仍然是初始化具体的类型,所以他执行具体的方法。
综合代码
在AssignTest在加一个父类,实现重载与重写,同时把name()方法放到了test方法中,代码如下图:
运行的结果为Mammal、animal、Tiger、animal,说明执行的具体方法并没有发生变化,那么直接看字节码如下图:
可以看到字节码指定的方法还是AssignSuperTest.test(Animal)方法,不过我们从结果可以知道还是执行的AssignTest.test(Animal)方法,AssignTest.test(Animal)的字节码指令如下图:
AssignTest.test(Animal)在编译期并不知道传进来的Animal具体是什么类型,所以只能在运行期才能通过去查找具体的类型执行!
总结
从字节码来说,方法的重载实际上重载的几个方法已经是相互独立的方法,这与我们平时所理解是一致的(方法通过方法名与参数列表区分),而重写则不同,重写的目的是为了实现子类与父类不同的表现,是多态。
以最后一个例子为例,在test方法中执行name(),由于Animal具体的类型不知道,所以不知道会执行具体哪个name()方法。
所以方法重载是静态的,是编译器行为,是在写代码期或者编译器可以确定执行哪个方法,而方法重写是动态的,是运行期行为,只有在执行期才知道是具体哪个类型来执行方法。
只要理解了上面两句话在面试中遇到这类题就能够轻松解决。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!