深入理解Java重载与重写(2)

  (1)静态类型:Human称为变量的静态类型Static Type或者外观类型Apparent Type;静态类型的变化只会在使用时发生,变量本身的静态类型不会被改变。

  (2)实际类型:Man/Women称为变量的实际类型Actual Type;实际类型变化结果在运行期才能确定,编译器并不知道对象的实际类型(可以看《Thinking in Java》中相关的描述)

  (3)有一种说法就是:重载是静态的,重写是动态的,所以重写算是多态性的体现,而重载不算是。各人有各人的理解,但是我觉得是这句话是显然不能够成立的,为什么呢?虽然重载最后确定使用哪个方法是完全通过参数类型与个数就能被确定的,但是Human man = new Man()类似于这样的cast转型,最后参数的类型也是“动态”的,cast之后才知道的。具体的还是先看下面的解释吧,之后相信大家都会有个大概的概念(可能个人理解也会有偏差,望指正!)。

  (4) 事实上,就是定义了两个静态类型相同、实际类型不同的变量。而重载方法是通过参数的静态类型Human而不是实际类型Women/Man作为判断。静态类型在编译期可知的,所以会选择sayHello(Human guy)方法输出。


 

2、静态分派与动态分派

  (1)静态分派(《Thinking In Java》中称之为静态绑定(前期绑定)):所有依赖静态类型来定位方法执行版本(版本即哪一个方法)的分派动作,静态分派的最典型的应用就是方法重载。

  (2)动态分派(《Thinking In Java》中称之为动态绑定(后期绑定)):在运行期根据实际类型确定执行版本的分派过程称为动态分派,这是重写的实际本质,在重写过程中并不是唯一的版本,而是选择更加合适的版本(如果有多个父类,那么接近上层的优先级越低)。

  

3、多分派类型与单分派类型

  (1)多分派类型:根据一个以上的宗量(方法的接受者与方法的参数统称为方法的宗量)进行方法的选择方法的分派类型。其中静态分派属于多分派类型。即Father father = new Son(); father.overloadMethod(param),中overloadMethod()方法的选择是要根据静态类型Father与方法的参数param共同确定的

  (2)单分派类型:动态分配属于单分派类型,即只会根据实际类型Son选择方法。

  那么废话不多说,其实可以总结起来就是:Java语言是一门静态多分派,动态单分派的语言。

二、VM中动态分派的实现

  静态分派相对好理解,只要确定静态类型、参数以及参数个数就能知道最后是哪个版本被执行,但是动态分派就相对难理解,但是结合VM的相关知识理解,其实也就那么回事。

  (1)高能权威解释:

    动态分配并不会频繁操作去搜索合适的目标方法,而是通过在类的方法区中建立一个虚方法表(Virtual Method Table=vtable)来代替元数据查找以提高性能。Father与Son的虚方法表如下图(Vtable中存放各个方法的实际入口地址)

  (2)个人粗俗解释:

    动态分派(我)懒得每次都要浪费时间去找要被执行的目标方法,VM大哥本来就已经提供给了我方法表这么一个好东西,只要我加以利用(虚表中存放方法的实际入口)就能实现了找到目标方法。

  先看下图,之后解释便会一目了然:

深入理解Java重载与重写

  解释1:如果子类没有重写方法,执行的是父类的方法:这是因为子类中没有被重写,那么子类的虚方法表里面的地址入口与父类相同方法的地址入口是一致的,都指向父类的实现入口。即:Father与Son的choiceA与choiceB方法都指向了Father相同方法中的入口地址。

  解释2:如果子类重写父类方法,那么执行的是子类的方法,这是因为子类方法表中地址将会被替换为指向子类实现版本的入口地址。即:Son的choiceA与choiceB方法被重写了,则指向了Son实现版本的入口地址,并没有指向Father的箭头。

  解释3:Father与Son从Object继承来的方法都没有重写,故都会指向Object的数据类型;

Linux公社的RSS地址:https://www.linuxidc.com/rssFeed.aspx

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d626fb0522d37bc12b9fa28ca61a9e7c.html