天天看点

Java多态性之引用变量的动态绑定和静态绑定运行时多态性

运行时多态性

建议参考文章

讲的很好

引用变量的动态绑定和静态绑定

先来看段代码

/**
 * @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追求更精确方法,更新方法的一种思想。