标签:
先来一段百度来的定义和概念
什么是多态:
面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。
多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
多态的作用:消除类型之间的耦合关系。
接着看到了这样一段题目
class A ...{ public String show(D obj)...{ return ("A and D"); } public String show(A obj)...{ return ("A and A"); } } class B extends A...{ public String show(B obj)...{ return ("B and B"); } public String show(A obj)...{ return ("B and A"); } } class C extends B...{} class D extends B...{} A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println(a1.show(b)); ① System.out.println(a1.show(c)); ② System.out.println(a1.show(d)); ③ System.out.println(a2.show(b)); ④ System.out.println(a2.show(c)); ⑤ System.out.println(a2.show(d)); ⑥ System.out.println(b.show(b)); ⑦ System.out.println(b.show(c)); ⑧ System.out.println(b.show(d)); ⑨ Answer: ① A and A ② A and A ③ A and D ④ B and A ⑤ B and A ⑥ A and D ⑦ B and B ⑧ B and B ⑨ A and D
上述的概念似乎并不清楚,并且也无助于理解多态这样一个概念。结尾我们将进行上述问题的详细解析。
进行下述实践:
Parent aParent = new Child();
aParent在进行动态绑定时的方法匹配范围为Parent Class中未被Child Class覆盖的method,以及Child Class中override的所有方法。Child Class中所有不包含在Parent Class中的方法在向上转型的时候会全部被除去(实际上也不可能出现这种情况,加入method在Parent中不存在的话,aParent.method()是无法通过编译的,更谈何执行)。
ps:可以形象的认为此处aParent作为Child的实例进行了向上类型转换(Parent), 除去了Parent Class中未定义的方法,并对Parent Class中的方法进行了override。
此时多态的特性主要体现在Child Class中重写过的方法:
Class Parent{
....
override_method(){
}
....
}
class Child extends Parent....{ .... overrride_method(){ System.out.print("I‘m child.") } ... } class Nephew extends Parent....{ .... overrride_method(){ System.out.print("I‘m nephew.") } ... }
Parent aParent = new Child(); Parent bParent = new Nephew(); aParent.override_method(); bParent.override_method(); OUTPUT: I‘m child. I‘m nephew.
在Parent的引用下调用同一个方法,不同的子类实例就会产生不同的方法表现,是比较直观的多态体现。
再一步深入该过程的细节:
1.在方法调用时发生了什么?
按照搜索结果,我们可以得知,override_method的调用过程体现了动态绑定的概念。
首先引入方法签名的概念:
即一个对象的方法定义唯一辨识包含两部分:方法名和参数列表。它们共同组成一个方法的唯一标识。
overload:同一个类中方法名相同,但是参数列表不同的方法定义称为方法重载(overload)
override: 与超类中的方法名,参数列表完全相同的子类中的方法定义称为覆盖(override)
ps: 如果子类中的方法定义仅仅是方法名与超类中的方法相同,则并不会覆盖超类中的方法实现。其效果类似于方法重载的结果。
2.对象方法的执行过程:
1.假设x声明为C类的对象,即C x = new C(); 调用x.f(参数p),那么编译器将会一一列举所有C类中名为f的方法和C的超类中访问属性为public且名为f且未被C中的f覆盖的方法。
2.接下来,编译器进行调用方法时提供的参数列表的检查,如果所有名为f的方法中存在完全匹配的参数类型,那么就选择这个方法,这个过程称为重载解析,然而可能存在并没有完全匹配的参数类型的情况,编译器将对传入的参数进行类型转换,如int可以转成double,子类转成父类(此行为将会检索整个向上的继承链)。如果进行转换后没有匹配的,或者发现经过转换后有多个方法与之匹配,则会发生错误。
3.采用动态绑定调用方法时,虚拟机一定调用的是x所引用对象的实际类型最合适的那个类的方法。举个栗子:Class D extends C, C x = new D(); 进行x.f(String)调用,那么首先如果D中定义了f(String)那么直接调用D中的f(String),如果D中没有定义,那么将在超类C中寻找f(String),如果还没有找到,依次类推向上搜索整个继承链。
4.有动态绑定,那么对应的就有静态绑定:所有private、static、final方法或者constructor方法都是编译器能够准确知道调用的是哪一个方法,称之为static binding。
接下来我们看一下开头那个问题:
1.a1.show(b)
声明引用的类型为A,实际对象类型也是A,参数列表为 B 类型,包含show方法为show(D),show(A),此时没有完全匹配调用show(B)的,进行传入参数的类型向上转换,B的超类为A,传入参数列表中的B转换为A,匹配show(A),调用show(A)
返回A and A。
2.a1.show(c)
声明引用的类型为A,实际对象类型也是A,参数列表为 C 类型,包含show方法为show(D),show(A),此时没有完全匹配调用show(C)的,进行传入参数的类型向上转换,C的超类为B,B并无匹配项,继续向上转换得到A,传入参数列表中的B转换为A,匹配show(A),调用show(A).
返回A and A
3.a1.show(d)
同上。
返回A and A。
4.a2.show(b)
声明引用为A类型,实际实例类型为B,调用的方法为show(B),首先我们来看A中的show,show(D),show(A),在看B中的show(B),show(A)其中show(A)override了A中的show(A),show(B)方法在A中不存在,排除。所以备选为A.show(D),B.show(A),传入参数B向上转换成A,匹配B.show(A)
返回B and A
5.a2.show(c)
同上
6.a2.show(d)
声明引用为A类型,实际实例类型为B,调用的方法为show(B),首先我们来看A中的show,show(D),show(A),在看B中的show(B),show(A)其中show(A)override了A中的show(A),show(B)方法在A中不存在,排除。所以备选为A.show(D),B.show(A),匹配A.show(D)
返回A and D
7.b.show(b)
声明引用为B,实际类型为b,调用的方法为show(B),直接调用B.show(B)
返回B and B。
8.b.show(c)
声明引用为B,实际类型为b,调用的方法为show(C),B和超类A中无方法匹配,C向上转换为B,匹配B.show(B)
返回B and B
9.b.show(d)
声明引用为B,实际类型为b,调用的方法为show(D),B中方法无匹配,但B的超类A中匹配方法a.show(D)
返回A and D
标签:
原文地址:http://www.cnblogs.com/coonver/p/5368683.html