多态,是同一个对象,在不同时刻表现不同的行为
多态的前提:
- 存在继承
- 子类重写父类方法
- 父类引用指向子类对象:父 f = new 子();(向上转型)
多态的使用
多态实例
1 | class Fu{ |
运行结果:
1 | 10 |
在上面的程序中,使用多态,fu 加载变量num时,先查看父类中是否有这个变量,发现有 num ,则调用;调用子类特有变量 num2 和 ziMethod() 方法,因为 fu 是向上转型的,转到父类后查看没有变量 num2 和 ziMethod(),则编译不通过;加载父类特有的方法 fuMethod() 时,在子类中找不到,向上转型到父类中,查看有次方法,便调用父类的方法 fuMethod();加载子类重写父类的方法时,在子类中找到重写后的方法 showMethod(),则直接调用
总结
指向子类的父类引用变量,向上转型,所以只能调用父类中有的变量和方法,对于父类不存在子类中特有的方法,则不能访问调用;对于子类没有而父类中有的变量和方法,可以访问调用;如果子类中重写了父类中的方法,那么调用的是子类中重写后的方法
多态访问成员特点:
- 构造方法:创建子类对象时访问父类构造方法,对父类数据进行初始化
- 成员变量:编译看左边,运行看左边
- 成员方法:编译看左边,运行看右边
- 静态方法:编译看左边,运行看左边
多态的好处
- 提高了代码的维护性
- 提高了代码的扩展性
多态的弊端
只能使用父类的成员,而不能使用子类的特有功能
解决:向下转型,将父类的引用强制转换为子类的引用
Fu fu = new Fu();
Zi zi = (Zi)fu;
java 多态经典案例
1 | public class A { |
运行结果:
1 | 1--A and A |
在以上程序中,
结果分析
4的结果分析:
很多人以为会输出的是 “B and B”,调用的是B中找show(B)方法!
为什么没有直接在B中找show(B)方法调用呢,因为调用方法,首先看是谁调用的,a2的类型是A,而不是B,所以在查找方法时,先进入A类查找,而B中的方法 show(B),是B类中特有的方法,所以不会调用
- a2是A的引用,但是指向的是B类型
- a2调用方法 show(b),方法参数是B类型
- 查看引用类型A是否有父类,没有父类,然后在引用类型A中看是否有此方法
- A中没有参数为B类型的方法,然后找是否有参数为B的父类的方法
- B有父类A,找到方法 show(A)
- 然后查看a2的指向类型B中是否重写过该方法
- 在B中找到重写A的方法show(A),所以最后调用的是B中的方法,输出 “B and A ”
5的结果分析:
- a2是A的引用,但是指向的是B类型
- a2调用方法 show(c),方法参数是C类型;
- 查看引用类型A是否有父类,没有父类,然后在引用类型A中看是否有此方法
- A中没有参数为C类型的方法,然后找是否有参数为C的父类的方法
- C有父类B,没有参数为B类型的方法;B有父类A,有参数为A类型的方法
- 然后查看a2的指向类型B中是否重写过该方法
- 在B中找到重写A的方法show(A),所以最后调用的是B中的方法,输出 “B and A ”
修改程序
修改1
如果在上面的程序中加上A的父类:Fu ,添加方法show(C obj);其余不变:
1 | class Fu { |
那么,5的结果会跟之前有所不同:
1 | 5--Fu and C |
- a2 是 A 的引用,但是指向的是B类型
- a2 调用方法 show(c),方法参数是C类型;
- 在引用类型A中,查找方法 show(C) 是否存在;
- 找不到方法 show(C),查看引用类型A是否有父类
- 有父类Fu,然后在用用类型A的父类Fu中看是否有此方法
- Fu 中有参数为 C 的方法 show(C)
- 然后查看指向类型 B 中是否有重写后的方法
- B中没有重写方法 show(C),最终调用 Fu 类中的方法 show(C):输出 “ Fu and C ”
修改2
如果在上面的程序中加上A的父类:Fu ,添加方法show(C obj);其余不变:
1 | class Fu { |
那么,5的结果会跟之前又有所不同:
1 | 5--A and C |
- a2 是 A 的引用,但是指向的是B类型
- a2 调用方法 show(c),方法参数是C类型;
- 在引用类型A中,查找方法 show(C) 是否存在;
- 找到方法 show(C),然后查找A的子类:指向类型 B 中是否有重写 show(C) 的方法
- B中没有重写方法 show(C),调用 A 类中的方法 show(C):输出 “ A and C ”
修改3
如果在B中重写方法 show(C),那么结果又会不同:
1 | public String show(C obj) { |
输出 “ B and C ” !!!
在之前的分析中,5中查看 B 中是否有重写后的方法,之前的程序中B中不存在重写方法,现在B中加上重写方法 show(C),所以最终调用的是指向类型 B 的重写方法
可以得出:
在多态中,父类对象作为引用变量,子类对象作为被引用对象类型时,引用变量调用的方法必须是父类(引用对象)中定义过的方法,不能调用父类中没有而子类中有的方法!!!
调用的情况有以下几种:
- 被调用方法仅在父类中被定义,而子类中没有被定义,那么调用的方法是父类中的方法
- 被调用方法在父类中被定义,在子类中被重写,那么调用的是子类中重写后的方法
多态调用顺序
前提:
- 被引用变量类型继承引用变量类型
- 先看引用变量类型和被引用对象类型(指向类型)
- 调用方法必须是引用变量类型对象中存在的方法
调用顺序:
this->引用类型,zi->被引用类型
this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)
在查找方法时,按这四个顺序查找,当某个查找到之后,还要进入被引用类型 zi 中查看是否有重写方法
- 进入 this 查找 show(O)
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show(O)
- 有,调用 zi 的重写后的方法 show(O),结束
- 没有,调用 this 中的方法 this.show(O),结束
- 没有,执行步骤2
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show(O)
- 查找 this 是否有父类 super
- 有,进入 super 中 查找是否存在方法 super.show(O)
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show(O)
- 有,调用 zi 的重写后的方法 show(O),结束
- 没有,调用 super 中的方法 super.show(O),结束
- 没有,执行步骤3
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show(O)
- 没有,执行步骤3
- 有,进入 super 中 查找是否存在方法 super.show(O)
- 查找 this 中是否有 this.show((super)O),(方法参数是调用方法参数的父类的方法)
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show((super)O)
- 有,调用 zi 的重写后的方法 show((super)O),结束
- 没有,调用 this 中的方法 this.show((super)O),结束
- 没有,执行步骤4,(如果是从2到3,那么不会出现这种情况)
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show((super)O)
- 查找 this 中是否有 super.show((super)O)
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show((super)O)
- 有,调用 zi 的重写后的方法 show((super)O),结束
- 没有,调用 super 中的方法 super.show((super)O),结束
- 有,进入被引用类型 zi 中,查找是否存在重写方法 show((super)O)