概述
多态是面向对象世界中三大特性(封装、继承、多态)之一,是Java非常重要的部分之一。
什么是多态?
多态就是一个引用变量所指向的具体对象和通过这个引用变量执行的方法在编译时期是不能确定的,需要在运行时才能确定。一个引用变量具体指向那个对象、通过这个引用变量执行方法调用那个类的方法都需要在程序执行期间才能确定。
多态的分类
- 编译时多态,实现原理是方法重载。
- 运行时多态,实现原理是方法重写。
编译时多态
在编译时就已经确定调用那个方法。接下来通过一段代码来说明。
package basic;
/*多态之编译时多态*/
public class BasicTest2 {
public static void main(String[] args) {
BasicTest2 basicTest2 = new BasicTest2();
Human man = new Man();
Human woman = new Woman();
basicTest2.hello(man);
basicTest2.hello(woman);
}
public void hello(Human human) {
System.out.println("hello,Human");
}
public void hello(Man man) {
System.out.println("hello, Man");
}
public void hello(Woman woman) {
System.out.println("hello, Woman");
}
}
class Human { }
class Man extends Human { }
class Woman extends Human{ }
代码运行结果
hello,Human
hello,Human
为什么会选择参数类型为Human的重载方法呢?为了解决这个问题我们先补充一给知识点。
Human man = new Man()
,这个断码片段中,Human称之为变量的静态类型,Man称之为变量的实际类型。
重载时,是通过参数的静态类型来作为判断依据的。并且静态类型是在编译时期就可知的,所以编译阶段就可以根据参数的静态类型来确定使用那个重载方法。
运行时多态的前提条件
- 继承
- 方法重写
- 向上转型
运行时多态
我们仍然根据一段代码来了解。
package basic;
public class BasicTest3 {
static class BMW {}
static class QQ {}
static class Human{
public void driveQQ(QQ qq) {
System.out.println("Human can drive QQ");
}
public void driveBMW(BMW bmw) {
System.out.println("Human can drive BMW");
}
}
static class Man extends Human {
public void driveQQ(QQ qq) {
System.out.println("Man can drive QQ");
}
}
public static void main(String[] args) {
Human human = new Human();
Human man = new Man();
human.driveQQ(new QQ());
man.driveQQ(new QQ());
man.driveBMW(new BMW());
}
}
代码运行结果
Human can drive QQ
Man can drive QQ
Human can drive BMW
普通方法、finally、super都是调用的invokespecial指令,super有点特殊,先讲前两种,都是在编译时期就已经确定了方法的直接地址。先在方法区里面寻找该方法的直接地址。(现在自己类方法表里面找,没找到再去离得最近的超类方法表里面找,直至Object)
super.方法是去离该类最近的超类方法表里面寻找该方法,如果没有找到,则取另外一个超类里面寻找该方法,若最终没有找到则报错。
多态方法。用的invokeVirtual指令。在编译器不能确定其直接地址,需要在运行期确定。先通过编译器确定其符号引用(静态类型的全限定名.方法名),运行期去静态类型类虚方法表里面查询其索引地址,然后去实际类型类虚方法表里面找到该方法的直接地址。(虚方法表里面都是可见方法,不包含private方法等)