天天看點

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