概述
多态是面向对象世界中三大特性(封装、继承、多态)之一,是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
上述代码需要通过两次分派才能得到答案。
首先是静态分派,也就是编译阶段的选择。这时选择目标方法的依据有两点:一是静态类型是Man还是Woman,二是方法参数是QQ还是BMW。最后编译阶段结果是指向了Human.driveQQ(QQ)、Human.driveQQ(QQ)、Human.driveBMW(BMW)方法的符号引用。
其次是动态分派,更具实际类型在方法区找到该类型的方法表,(JVM257页),然后再找到放发表中的被调用方法,最后执行方法。