重载:
先看个例子
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 man){
System.out.println("Hello, man!");
}
public void sayHello(Woman man){
System.out.println("Hello, woman!");
}
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 = new Man() ;
对于上面这句,Human称为变量的静态类型,后面的Man是变量的实际类型。
(1)静态类型类型的变化在编译器就可知,实际类型变化的结果在运行期才可确定。
(2)虚拟机在重载时时通过参数的静态类型而不是实际类型来决定使用哪一个重载版本。
(3)所有依赖静态类型来定位方法来执行版本的分派动作称为静态分派,静态分派的典型应用是方法重载。静态分派发生在编译阶段。
(4)确定版本并不是唯一的,往往只能确定一个更加合适的版本。
自身--自动转型--自动装箱--接口/父类(多个优先级一样,提示类型模糊,拒绝编译)--Object--变长参数
重写:
还是拿上一个例子:
public class StaticDispatch {
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{
@Override
protected void sayHello() {
System.out.println("Man say hello!");
}
}
static class Woman extends Human{
@Override
protected void sayHello() {
System.out.println("Woman say hello!");
}
}
public static void main(String[] args) {
Human man = new Man() ;
Human woman = new Woman();
man.sayHello();
woman.sayHello();
man = new Woman();
man.sayHello();
}
}
输出结果:
Man say hello!
Woman say hello!
Woman say hello!
动态分派:类在方法区建立一个虚方法表(invokevirtual指令),使用虚方法表索引来代替元数据查找以提高性能。
虚方法表中存放各个方法的实际入口地址。
如果某个方法在子类没有被重写,则子类的虚方法表里面的地址入口和父类相同方法的地址入口一致。
如果被重写,子类方法表中的地址将会被替换为指向子类实现版本的入口地址。