本文以Java多态的一些基本特征来谈一下分派调用。 java
在开始,依旧用经常使用方式,例子来引入,看一看下面例子的输出: ide
/** * * @author Sel * * 2014.4.3 */ public class StaticDispatch { public void sayHello(Human guy) { System.out.println("hello, guy!"); } public void sayHello(Man guy) { System.out.println("hello, man!"); } public void sayHello(Women guy) { System.out.println("hello, women!"); } public static void main(String[] args) { Human man = new Man(); Human women = new Women(); StaticDispatch sd = new StaticDispatch(); sd.sayHello(man); sd.sayHello(women); } } class Human { } class Man extends Human { } class Women extends Human { }
hello, guy!
hello, guy!
函数
没错,程序就是你们熟悉的重载(Overload),并且你们也应该能知道输出结果,可是为何输出结果会是这个呢? spa
先来谈一下如下代码的定义: code
Human man = new Man();
其中,变量的静态类型和动态类型在程序中均可以发生变化,而区别是变量的静态类型是在编译阶段就可知的,可是动态类型要在运行期才能够肯定,编译器在编译的时候并不知道变量的实际类型是什么(我的认为可能也是由于要实现多态,因此才会这样设定)。 编译器
如今回到代码中,因为方法的接受者已经肯定是StaticDispatch的实例sd了,因此最终调用的是哪一个重载版本也就取决于传入参数的类型了。 虚拟机
实际上,虚拟机(应该说是编译器)在重载时时经过参数的静态类型来当断定依据的,并且静态类型在编译期就可知,因此编译器在编译阶段就可根据静态类型来断定究竟使用哪一个重载版本。因而对于例子中的两个方法的调用都是以Human为参数的版本。 编译
Java中,全部以静态类型来定位方法执行版本的分派动做,都称为静态分派。 class
再来看动态分派,它和多态的另一个重要体现有很大的关联,这个体现是什么,可能你们也能猜出,没错,就是重写(override)。 变量
例子以下:
/** * * @author Sel * * 2014.4.3 */ public class DynamicDispatch { public static void main(String[] args) { Human man = new Man(); Human women = new Women(); man.sayHello(); women.sayHello(); man = new Women(); man.sayHello(); } } abstract class Human { protected abstract void sayHello(); } class Man extends Human { @Override protected void sayHello() { System.out.println("hello man!"); } } class Women extends Human { @Override protected void sayHello() { System.out.println("hello women!"); } }
hello man!
hello women!
hello women!
其实由两次改变man变量的实际类型致使调用函数版本不一样,咱们就能够知道,虚拟机是根据变量的实际类型来调用重写方法的。
咱们也能够从例子中看出,变量的实际类型是在运行期肯定的,重写方法的调用也是根据实际类型来调用的。
咱们把这种在运行期根据实际类型来肯定方法执行版本的分派动做,称为动态分派。