1.什么是多态
多态:同一事物在不同时刻显示出的不同形态。在语言中多态性是指允许不同子类型的对象对同一消息做出不同的反应。
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
比如桌上有三杯酒,从表面上看你是看不出来的,只知道是酒,只有资深的品酒师尝过后才能区分出来
酒 a = 剑南春
酒 b = 五粮液
酒 c = 酒鬼酒
2.多态的必要条件
- 子类必须继承父类
- 子类必须重写父类的方法
- 父类引用指向子类对象,即:父类类名 引用名称 = new 子类类名();
3.多态中的类型转换
Java多态中,有两种类型转换:向上转型和向下转型
- 向上转型 : 通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换
- 向下转型 : 通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换
向上转型
向上转型,也叫做自动类型转换,子类型赋值给父类型(父类型的引用指向子类型),构成多态
父类类型 引用名称 = new 子类类名();
当使用多态方式调用方法时,该引用名称只能访问父类中的属性和方法。编译器首先检查父类中是否有该方法,如果没有,则编译错误。如果有,再去调用子类的同名(重写)方法。
向下转型
向下转型,也叫做强制类型转换,父类型赋值给子类型
当使用多态时,并且访问子类独有的属性或方法时,则必须进行向下转型
当进行向下转型时,建议先使用 instance of 关键字进行判断,判断合法时,在转为对应的类型,否则可能会出现类型转换异常 java.lang.ClassCastException。
向上转型时,父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用的.
向下转型则是为了,通过父类强制转换为子类,从而来调用子类独有的方法
package demo1;
/**
* @description:
* @author: ljx
* @time: 2020/6/15 15:24
*/
class Wine {
public void fun1(){
System.out.println("Wine 的Fun.....");
fun2();
}
public void fun2(){
System.out.println("Wine 的Fun2...");
}
}
class JNC extends Wine{
/**
* @desc 这个方法是子类独有方法
* 父类中不存在该方法,向上转型后,父类是不能引用该方法的
* @param a
* @return void
*/
public void fun1(String a){
System.out.println("JNC 的 Fun1...");
fun2();
}
/**
* 子类重写父类方法
* 指向子类的父类引用调用fun2时,必定是调用该方法
*/
public void fun2(){
System.out.println("JNC 的Fun2...");
}
}
public class Jicheng2 {
public static void main(String[] args) {
Wine a = new JNC();
a.fun1();
// a.fun1("aaaa");这样调用失败,不能调用子类独有方法
JNC b = (JNC)a; //向下转型
b.fun1("aaaaaaaa");
}
}
//-------------------------------------------------
//Wine 的Fun.....
//JNC 的Fun2...
//JNC 的 Fun1...
//JNC 的Fun2...
多态的好处:定义一个方法时传入参数可以是父类,实际进行运行时传入的参数可以是各个不同子类,各个子类自己实现自己的功能。对外提供的接口一致。避免了重复代码的编写
4.多态中的调用关系
首先,遵循
this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)
这个优先级规则。
然后,在遵循之前的优先级规则进行判断的期间,遵循
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法
这个规则。
eg:
package demo1;
/**
* @description:
* @author: ljx
* @time: 2020/6/15 13:39
*/
public class Jicheng {
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("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
// System.out.println(a2.get());
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
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");
}
public void get(){
System.out.println("==============");
}
}
class C extends B{
}
class D extends B{
}
运行结果:
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D
分析:
1. 1,2,3这三个,a1都是A的对象的引用,此时不存在多态使用,所以都先在A类中找方法,this代表A。A.show(b),A中没有,A超类是object,更没有,所以调用this.show((A)b)可以找到对应方法show(A a)所以打印结果 A and A;
2.与1操作差不多,向上转型需要两次,第一次转换成B,再转换成A,this.show((A)c),可以找到对应方法show(A a)所以打印结果 A and A;
3.在A中可以找到this.show(D d)方法,直接调用,A and D
4. a2对象是父类引用指向子类对象,标准的多态使用方法,this 仍然代表A,这个经过this.show(b)找不到,A的超类肯定也不存在这类方法,调用this.show((A)b)有,但是同时B类对该方法进行了重写,所以调用子类的方法,显示 B and A;
5.分析过程同4
6.在A中可以找到this.show(D d)方法,直接调用,A and D
7.在B中找到this.show(B b),直接调用
8.b.show(c),this 代表B,最后调用this.show((B)c)
9.this代表B,this.show(d),没找到,super.show(d)即A.show(d)找到了