1. 静态分派
public class StaticDispatch {
static abstract class Human {
}
static class Man extends Human {
}
static class Woman extends Human {
}
public void sayHello(Human guy) {
System.out.println("hello,guy!");
}
public void sayHello(Man guy) {
System.out.println("hello,gentleman!");
}
public void sayHello(Woman guy) {
System.out.println("hello,lady!");
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
StaticDispatch sr = new StaticDispatch();
sr.sayHello(man);
sr.sayHello(woman);
}
}
运行结果为:
hello,guy!
hello,guy!
分析:
Human 称为变量的 静态类型/外观类型;
Man 称为变量的 实际类型/运行时类型;
两种类型都可能发生变化,区别是:
静态类型的变化仅仅在使用时发生,变量本身的静态类型不会发生改变,并且最终的静态类型是在编译期可知的;
实际类型变化的结果在运行期才可确定,编译器在编译程序的时候并不知道一个对象的实际类型是什么;
// 实际类型变化
Human human = (new Random()).nextBoolean() ? new Man() : new Woman();
// 静态类型变化
sr.sayHello((Man) human);
sr.sayHello((Woman) human);
对象human的实际类型是可变的,编译期间它完全是个”薛定谔的人“,到底是Man还是Woman,必读等到程序运行到这行的时候才能确定。
虚拟机(准确地说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定的依据的。
所有依赖静态类型来决定方法执行版本的分派动作,都称为静态分派。
静态分派的典型代表就是方法重载。
静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的(编译器来执行)。
2. 静态分配目标的过程
Javac编译器虽然能确定出方法的重载版本,但在很多情况下这个重载版本并不是”唯一的“,往往只能确定一个”相对更合适的“版本。
重载方法匹配优先级:
public class Overload {
public static void sayHello(Object arg) {
System.out.println("hello Object");
}
public static void sayHello(int arg) {
System.out.println("hello int");
}
public static void sayHello(long arg) {
System.out.println("hello long");
}
public static void sayHello(Character arg) {
System.out.println("hello Character");
}
public static void sayHello(char arg) {
System.out.println("hello char");
}
public static void sayHello(char... arg) {
System.out.println("hello char ...");
}
public static void sayHello(Serializable arg) {
System.out.println("hello Serializable");
}
public static void main(String[] args) {
sayHello('a');
}
}
char -> int -> long -> Character -> Serializable -> 父类(若有多个父类,继承关系中从下往上开始搜索)-> 可边长参数。
此代码演示了编译期间选择静态分派目标的过程,这个过程也是Java语言实现方法重载的本质。