通过对一道习题来浅层理解多态的成员方法的调用
新学习了java的多态性相关的内容,找到一道经典习题来练手,初看时一头雾水,在查阅了相关资料后逐渐理解。
代码和概念部分参考自(其余部分为个人理解,可能有误,敬请指正):
深入理解java多态性
解题思路为个人对概念的理解,可能由理解错误的地方,欢迎指正。
题目如下:
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{}
public class Test1{
public static void main(String[] args){
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));//1
System.out.println(a1.show(c));//2
System.out.println(a1.show(d));//3
System.out.println(a2.show(b));//4
System.out.println(a2.show(c));//5
System.out.println(a2.show(d));//6
System.out.println(b.show(b));//7
System.out.println(b.show(c));//8
System.out.println(b.show(d));//9
}
}
网上查阅相关资料了解到,要解决这道题必须要先理解下面这句话:
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
拿以下这句代码,说下我自己对这句话的理解:
A a2 = new B();
这行代码的作用是将A类的引用变量来引用一个B类的对象,a此时通过a2.方法名调用成员方法时,首先看A类是否有此方法,如果没有,直接编译不通过:
符号: 方法 show(D)
位置: 类型为A的变量 a1
Test1.java:46: 错误: 找不到符号
System.out.println(a2.show(b));
当A类中有此方法,分两种情况:
1.在B类中对此方法进行的重写,则此时调用B的方法
2.在B类中没有对此方法的重写,则此时调用父类的方法,即A类的方法
简单来说就是左边决定能否调用方法,右边决定调用谁得方法
还有一个是看到了有人可以通过以下调用的优先级来解决:
优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)
通过此方法也可解决此问题,但这个优先级是如何得来得还没有理解,此时先记住。
下面来一条一条得来分析:
1.
A a1 = new A();
B b = new B();
System.out.println(a1.show(b));//1
a1为A类的引用,引用的也是一个A类的对象,直接调用A的成员方法即可。而A有show方法有两个,一个参数接收D类的引用,而另一个接收A类的引用。但是调用时传递的实参是B类的引用b,编译运行得到A and A可以得知是调用了show(A obj)。此时将传参看成是一条赋值语句即可理解:
首先看show(D obj)传参过程看成是D obj = b;很明显因为D不是B类的父类,D类的引用变量obj并不能引用B类的对象
然后看show(A obj)传参过程看成是A obj = b;A是B的父类,A类的引用变量obj可以引用B类的对象,传参成功,所以最终调用show(A obj),即打印
A and A
A a1 = new A();
C c = new C();
System.out.println(a1.show(c));//2
分析同1,最终a1.show©会调用A类的show(A obj)方法,打印
A and A
A a1 = new A();
D d = new D();
System.out.println(a1.show(d));//3
同1,不同的此时实参为D类引用d,调用A类show(D obj)方法,打印
A and D
A a2 = new B();
B b = new B();
System.out.println(a2.show(b));//4
a2是一个A类的引用变量,引用一个B类的对象,所以要优先调用B类的成员方法,此时很容易理解成直接调用B的show(B obj)方法,从而打印
B and B,很明显,这样理解忽略了上面的这句话:
但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法
A类中只有两个方法:show(A obj);和show(D obj);
很明显B类中的show(B obj)并不是这两个方法的覆写,而是B类的一个特有的方法,A类引用无法调用到。
此时正确的调用是执行B类中的成员方法show(A obj)(由1的分析可以得知B类引用实参可以传入A类型引用的形参)该方法为A类中的show(A obj)的覆写。最终打印B and A
(如果此时在A类中加入一个方法show(B obj),则a2.show(b)就会执行B类中的show(B obj)方法,因为此时该方法不再是B类的特有方法,而是对A类方法的覆写,所以可以调用)
A a2 = new B();
C c = new C();
System.out.println(a2.show(c));//5
分析同4, a2.show(c)执行B方法中的show(A obj)打印B and A
A a2 = new B();
D d = new D();
System.out.println(a2.show(d));//6
分析同4,A类中存在show(D obj)方法而B类中没有对show(D obj)的覆写,所以最终执行A类中的show(D obj)方法,即打印A and D。
此处有一点小问题:
根据4的分析,既然d也可以传入show(A obj)中,此时为什么不调用B类中的show(A obj),而是调用父类A的show(D obj)。
通过做以下调整来理解:
① 首先将A类中的两个方法前后位置互换,结果不变,所以要调用哪个跟顺序无关,
② 在B类增加show(D obj),此时会调用B的show(D obj)
③ 去掉②增加的方法,将A类中的show(D obj)改为show(B obj)此时会执行B方法的show(B obj)
④ A类中只留一个show(A obj)方法,其余方法删去,此时会执行B中的show(A obj)
个人对此的理解:
当实参d能同时满足多个形参列表时,如show(A obj) show(B obj) show(D obj)到底调用哪个方法的优先级如下(由大到小)
show(Object)---->show(super(Object))------>show(super(super(Object)))…
即先找与自己相符合的,如果没有再找父类,如果还没有,再找父类的父类…
B b = new B();
System.out.println(b.show(b));//7
直接执行B类的show(B obj)即打印B and B
B b = new B();
C c = new C();
System.out.println(b.show(c));//8
执行B类的show(B obj); 至于为什么不执行show(A obj),分析同6中
此处一点小问题的分析
首先会再B中找有没有show(C obj),没找到,然后看B的父类A类中有没有此方法,也没有,此时再在B中看:有没有参数为C的父类B的方法 即show(B obj) 找到了 即执行show(B obj)的代码。
(如果此时去掉show(B obj),则会继续找有没有show(A obj),从而执行show(A obj))
B b = new B();
D d = new D();
System.out.println(b.show(d));//9
执行A类的show(D obj)方法;
同上先B类中有没有show(D obj)方法,没有找到,然后看A类中有没有此方法,找到了所以执行A类中的show(D obj),即打印A and D
关于上面提到的优先级:
优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)
还是不太理解,以4为例,先贴上一个原博客是如何分析的
A a2 = new B();
B b = new B();
System.out.println(a2.show(b));//4
比如④,a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(B obj)方法,没有找到,于是到A的super(超类)找,而A没有超类,因此转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(A obj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(A obj)方法,因此最终锁定到类B的show(A obj),输出为"B and A”。
————————————————
版权声明:本文为优快云博主「thinkGhoster」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/thinkGhoster/article/details/2307001
以上是初学Java多态性后对这道题目个人理解,可能有些错误与描述不清楚的地方,敬请指正。之后抽时间再对上面的优先级做一些深入的了解,然后再了解以下从内存层面上各条语句是如何操作的,了解完后再重做这道题应该会有一个全新的理解。