方法或对象具有多种形态,是面向对象的第三大特征。
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是 同一个接口,使用不同的实例而执行不同操作。
多态是建立在封装和继承之上的。
一、具体体现:
1、方法多态:
通过方法重写(覆盖)和方法重载体现多态。
2、对象多态(核心):
(1) 一个对象的编译类型和运行类型可以不一样。
(2) 编译类型在定义对象时就确定了,不能改变。
(3) 运行类型是可以变化的。
(4) 编译类型看定义时 = 的左边,运行类型看 = 的右边。
例:
Animal animal = new Dog(); //animal 编译类型为Animal ,运行类型为Dog,即父类对象引用可以指向子类对象。
animal = new Cat(); //anomal 运行类型又变成了Cat,编译类型依旧是Animal。
二、细节:
1、多态的前提:两个类存在继承关系 和 多态的向上转型。
向上转型:
本质:父类的引用指向子类的对象。 父类可以不是直接父类。(把子类对象升级到父类,向上转)
语法:父类类型 引用名 = new 子类类型();
特点:可以根据访问权限调用父类的所有成员;
不能调用子类特有成员;
因为在编译阶段,可以调用哪些成员是由编译类型决定的。
最终运行效果看子类的具体实现。
因为运行时,只看运行时类型,不能使用特有成员是因为编译类型限制。
而运行时看实际运行类型,此时肯定时子类,那么就看子类的具体实现。
运行时若属性同名,则优先使用父类属性,方法则优先使用子类方法:
2、多态的向下转型
语法:子类类型 引用名 = (子类类型) 父类引用;
只能强转父类引用,不能强转父类对象。
要求父类的引用必须指向的是当前目标类型的对象。
可以调用子类类型中所有的成员。
通俗来说:
就是父类引用本来指向的就是子类对象,堆中的对应空间本就是子类对象。
然后才能将这个堆空间中原来就是子类对象的父类引用强转成子类对象。
若父类引用本来就指向父类对象,那不能强转成子类对象。强转时,编译不报错,但是运行可能出错。
3、属性重写 (解释1、中为何重名时会使用子类重写方法,不会使用子类同名属性。)
因为属性没有重写概念,所以不会像方法一样,子类重写了父类的方法,
因此访问时,属性只看编译类型,而方法看运行类型。
4、instanceof :用来判断是否是某一类型或该类型的子类
语法: A instanceof B
A为对象引用,B为某种类型
这里 看的是A的运行类型,而不是引用类型。
java
class AA{}
class BB extends AA{}
public static void main(String[] args){
// aa 编译类型为AA,运行类型是 BB
AA aa = new BB();
System.out.println(aa instanceof AA);//true
System.out.println(aa instanceof BB);//true,因为运行类型是BB,所以才为true,若判断编译类型,很显然AA不是BB自身或子类
}
三、动态绑定机制⭐
1、当调用对象方法时,该 方法会和该对象的内存地址(运行类型)绑定。
2、当调用对象属性时,没有动态绑定机制,哪里声明,那里使用。
原理应该在jvm中会有
例:
java
class A{
int i = 10;
int getI(){
return i;
}
int sum(){
return getI() + 10;
}
int sum1(){
return i + 10;
}
}
class B extends A{
int i = 20;
int getI(){
return i;
}
}
public static void main(String[] args){
A a = new B();
//调用a.sum(),发现B类中没有sum()函数
//调用A类中sum()
//A类sum()函数中有getI()函数,动态绑定机制,因为实际对象时B类对象,
//因此调用B类里的getI()
//B类getI()里需要返回变量i,
//属性没有动态绑定,在哪使用就返回哪里的
//所以返回B里的i,即20
//最后返回值为20+10=30
system.out.print(a.sum());//30
//B里没有sum1(),调用A里的sum1()
//用到属性i,那里调用使用哪里的,
//最后返回10 + 10 =20
system.out.print(a.sum1());//20
}
四、多态应用
1、多态数组
可以将数组声明成父类类型,然后接收不同的子类类型对象。
使用时可以直接循环使用共有方法和属性。
想要使用子类特有的方法时,可以先instanceof判断类型,然后向下转型,再调用特有方法。
2、多态参数
方法定义的形参类型为父类类型,实参类型为子类类型。