运行时多态性
建议参考文章
讲的很好
引用变量的动态绑定和静态绑定
先来看段代码
/**
* @SuperClass是父类,SubClass是子类。父类和子类都有method和add两种方
*/
public class SuperClass {
public void method(SuperClass superObj){
System.out.println("SuperClass.method(SuperClass)");
}
public int add(SuperClass superObj){
System.out.println("SuperClass.add(SuperClass)");
return 0;
}
}
class SubClass extends SuperClass{
public void method(SubClass subObj){
System.out.println("SubClass.method(SubClass)");
}
public int add(SuperClass superObj){
System.out.println("SubClass.add(SuperClass)");
return 0;
}
public int add(SubClass subObj){
System.out.println("SubClass.add(SubClass)");
return 0;
}
}
/**
* @调用这对父类子类,看看method和add方法的绑定结果
*/
public class test {
public static void main(String[] args){
SuperClass superObj = new SuperClass();
SubClass subObj = new SubClass();
SuperClass superRef1 = superObj;
SuperClass superRef2 = subObj;
superRef1.method(subObj);
superRef1.method(superObj);
superRef2.method(subObj);
superRef2.method(superObj);
System.out.println("--------------------------------------");
superRef1.add(subObj);
superRef1.add(superObj);
superRef2.add(subObj);
superRef2.add(superObj);
}
}
运行结果是:
SuperClass.method(SuperClass)
SuperClass.method(SuperClass)
SuperClass.method(SuperClass)
SuperClass.method(SuperClass)
--------------------------------------
SuperClass.add(SuperClass)
SuperClass.add(SuperClass)
SubClass.add(SuperClass)
SubClass.add(SuperClass)
分析
先看method方法的实际调用
从结果上看出,method全部调用的父类,说明对于superRef1和superRef2来说,调用方法的确定发生在编译阶段。因为引用变量定义为父类,因此调用父类的method。
再看add方法的实际调用
superRef1调用父类的add,superRef2调用的是子类的add。这和编译阶段的两个变量的定义不同,说明add的实际调用发生在运行阶段。
我们之前说过,重写发生在运行阶段。在父类方法中,add发生重写。因此,可以推测,在编译过程中系统判定add方法发生重写,因此暂定调用父类add方法,但是没有绑定,直到运行阶段才实际绑定。
结论
对没有重写的方法的实际调用,发生在编译阶段。
对发生重写的方法的实际调用,发生在运行阶段。
再来看下add方法的调用
已经知道add的绑定发生在运行阶段,为什么superRef2.add(SubClass)也是调用的子类的add(SuperClass)方法,而不是add(SuperClass)呢?
这是因为在编译阶段supreRef2.add已经实现了对父类的add方法初次绑定,但是在编译时系统判定add方法发生重写,需要等到运行时才真正绑定,而在运行时superRef2已经变成了子类,系统去调用子类中被重写的add,也就是子类中的add(SuperClass),才出现这样的结果。
有些拗口,那么可以直接跳过,看下边的结论
结论
一切引用变量的方法的绑定都在编译阶段完成。如果没有或者参数不匹配,就会直接报错
但是具体到非静态方法(静态方法不会出现运行时多态)的实际调用时出现以下两种情况:
1、方法没有更精确的版本(重写就是提供了更精确的版本),那么一切方法的确定将在编译阶段完成。
2、如果方法有更精确的版本,那么方法的实际调用就会推到运行阶段完成,但是和编译阶段的预绑定又具有一致性。
出现的原因可能是java的内在优化机制,也体现了java追求更精确方法,更新方法的一种思想。