一、什么是多态
一个对象变量可以指示多种实际类型。当程序运行时,才能确定该变量引用的哪个类的对象,从而虚拟机自动选择调用哪个方法,这也被称为动态绑定。
二、理解方法的调用过程
我们以x.f(args)为例,A x = new B()。
1.编译器查看对象变量的声明类型和方法名
x声明为A类,编译器将列举A类及其父类中可以被继承的所有名为 f 的方法。由于可能存在重载,名为 f 但参数列表不同的方法可能有多个。
至此编译器获得了所有可能被调用的候选方法。
2.编译器查看调用该方法时提供的参数列表,进行重载解析
如果在候选方法中有与提供的参数列表完全匹配的方法,则选择该方法;
如果没有完全匹配的方法,进行隐式类型转换( 如int可转为double,低精度向高精度转;Son可转为Father,向上转型)可以匹配,则选择该方法。
如果没有一个方法与提供的参数列表匹配,或转型后有多个匹配,都会报错。
至此,编译器获得了需要调用的方法名和参数列表。
3.当程序运行时,动态绑定调用方法。
查找x引用的对象的实际类型,这里为B类。
首先查找x的实际类型B类中有没有具有该参数列表的方法,若有,直接调用该方法。
否则查找其父类A类中查找,以此类推。
为了节约开销,虚拟机预先为每个类创建了一个方法表,只需要查找这个方法表即可。
对于static、private、final方法,可以准确的知道它调用的哪个类,所以采用的是静态绑定。(思考一下为什么)
4.拿段代码感受一下
1 class A { 2 public String show(D obj){ 3 return ("A and D"); 4 } 5 6 public String show(A obj){ 7 return ("A and A"); 8 } 9 10 } 11 12 class B extends A{ 13 public String show(B obj){ 14 return ("B and B"); 15 } 16 17 public String show(A obj){ 18 return ("B and A"); 19 } 20 21 } 22 23 class C extends B{} 24 class D extends B{} 25 26 public class E{ 27 public static void main(String [] args){ 28 A a1 = new A(); 29 A a2 = new B(); 30 B b = new B(); 31 C c = new C(); 32 D d = new D(); 33 System.out.println(a1.show(b)); //① 34 System.out.println(a1.show(c)); //② 35 System.out.println(a1.show(d)); //③ 36 System.out.println(a2.show(b)); //④ 37 System.out.println(a2.show(c)); //⑤ 38 System.out.println(a2.show(d)); // ⑥ 39 System.out.println(b.show(b)); //⑦ 40 System.out.println(b.show(c)); //⑧ 41 System.out.println(b.show(d)); //⑨ 42 } 43 }
这段代码挺有意思,值得好好体会。在网上看到有的朋友写到,多态的三个条件:继承,重写,父类变量引用子类对象,且多态总伴随着动态绑定。我个人体会这是动态绑定的三个条件,多态也不一定会伴随动态绑定,倒是动态绑定一直伴随着多态。