本文共 2599 字,大约阅读时间需要 8 分钟。
首先是两个概念:
比如我们有这样一段代码
class Human {}public class Man extends Human { public static void main(String[] args) { Human man = new Man(); }}
我们就称变量 man 的静态类型为 Human,实际类型为 Man。
在编译期所有依赖静态类型来定位方法执行版本的分派动作称为静态分派,其典型应用是方法重载(根据参数的静态类型来定位目标方法)。
静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机执行的。静态类型在编译期是可知的;
在运行期根据实际类型确定方法执行版本的分派动作称为动态分派,其典型应用是方法重写。
本文以Java多态的一些基本特征来谈一下分派调用。
在开始,依旧用常用方式,例子来引入,看一看下面例子的输出:
输出结果:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748/**
*
* @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),而且大家也应该能知道输出结果,但是为什么输出结果会是这个呢?
先来谈一下以下代码的定义:
1Human man =
new
Man();
我们把 Human 称为变量的 静态类型 , Man 称为变量的 实际类型 。
其中,变量的静态类型和动态类型在程序中都可以发生变化,而区别是变量的静态类型是在编译阶段就可知的,但是动态类型要在运行期才可以确定,编译器在编译的时候并不知道变量的实际类型是什么(个人认为可能也是因为要实现多态,所以才会这样设定)。
现在回到代码中,由于方法的接受者已经确定是StaticDispatch的实例sd了,所以最终调用的是哪个重载版本也就取决于传入参数的类型了。
实际上,虚拟机(应该说是编译器)在重载时时通过参数的静态类型来当判定依据的,而且静态类型在编译期可知,所以编译器在编译阶段就可根据静态类型来判定究竟使用哪个重载版本。于是对于例子中的两个方法的调用都是以Human为参数的版本。
Java中,所有以静态类型来定位方法执行版本的分派动作,都称为静态分派。
再来看动态分派,它和多态的另外一个重要体现有很大的关联,这个体现是什么,可能大家也能猜出,没错,就是重写(override)。
例子如下:
1234567891011121314151617181920212223242526272829303132333435363738394041424344/**
*
* @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变量的实际类型导致调用函数版本不同,我们就可以知道,虚拟机是根据变量的实际类型来调用重写方法的。
我们也可以从例子中看出,变量的实际类型是在运行期确定的,重写方法的调用也是根据实际类型来调用的。
我们把这种在运行期根据实际类型来确定方法执行版本的分派动作,称为动态分派。
单分派和多分派
单分派:根据一个宗量对目标方法进行选择。动态分派属于单分派多分派:根据多余一个宗量对目标方法进行选择。静态分派属于多分派
转载地址:http://lzpui.baihongyu.com/