1. 程式人生 > >隱藏和覆蓋的區別和用法

隱藏和覆蓋的區別和用法

靜態方法 類的變量 靜態 如何 區別 參數類型 子類重寫 pre 父類

講隱藏和覆蓋之前先看兩個概念:靜態類型動態類型

任何一個引用變量都有兩個類型:一個叫靜態類型,也就是定義該引用變量的類型;另一個叫動態類型,也就是該引用實際指向的對象類型。

比如對於兩個類A和類B,有:A a=new B();

那麽,引用a的靜態類型就是A,動態類型就是B。

java中引用的靜態類型在編譯的時候就可以確認,但是編譯器無法得知這個引用的動態類型;只有當程序運行時,通過RTTI就可以檢查出引用的動態類型。

再介紹一下,java中綁定的概念:對於一個程序,可以有很多的方法。這些方法的名稱、參數類型和參數數量都可能相同或者不同,那麽在調用一個方法的時候,如何將一個方法和該方法所在的類關聯起來,這就是綁定。java中的綁定分為靜態綁定和動態綁定。

靜態綁定:所有依賴於靜態類型來將某方法和該方法所在的類關聯起來的動作都是靜態綁定。因為靜態綁定在程序運行前發生,所有又叫前期綁定。

動態綁定:所有依賴於動態類型來將某方法和該方法所在的類關聯起來的動作都是動態綁定。因為動態綁定是在程序運行時,通過RTTI實現,所以又叫後期綁定。

舉例:假如有一個父類Father和一個子類Son,子類重寫了父類中的某個方法method()。有以下語句:

Father father=new Son();

father.method();

對於這個例子,靜態綁定的過程是:java文件編譯時,編譯器檢查出引用father的靜態類型是Father類,由於將method()方法和父類Father關聯起來。也就是說,程序運行前編譯器是無法檢查出引用father的動態類型的,所以會直接調用靜態類型中對應的方法。

而動態綁定的過程是:當這個java程序運行起來了,RTTI檢查出引用father的動態類型是Son類時,會將method()方法和子類Son關聯起來,也就是決定調用動態類型Son類中的method()方法。具體過程為:①JVM提取對象的實際類型的方法表;②JVM搜索方法簽名;③調用方法。

另外,要補充的是:java中類的屬性也都是靜態綁定的。這是因為靜態綁定是有很多的好處,它可以讓我們在編譯期就發現程序中的錯誤,而不是在運行期。這樣就可以提高程序的運行效率!而對方法采取動態綁定是為了實現多態。

----------------------------------------------------------------------------------------------------------------分割線------------------------------------------------------------------------------------------------------------------

下面來說一下,java中的隱藏和覆蓋的概念。我們知道,當子類繼承父類時,除了繼承父類所有的成員變量和成員方法之外,還可以聲明自己的成員變量和成員方法。那麽,如果父類和子類的成員變量和方法同名會發生什麽?假設有一個父類Father和一個子類Son。父類有一個成員變量a=0;有一個靜態成員變量b=0;有一個成員方法a,輸出0;有一個靜態成員方法b,輸出0。子類分別重寫這些變量和方法,只是修改變量的值和方法的輸出,全部改為1. 我們再聲明一個靜態類型是父類,動態類型是子類的引用:

Father father=new Son();

通過這個引用訪問子類的變量和調用子類的方法,那麽,會有以下結論:

1、所有的成員變量(不管是靜態還是非靜態)都只進行靜態綁定,所以JVM的綁定結果會是:直接訪問靜態類型中的成員變量,也就是父類的成員變量,輸出0.

2、對於靜態方法,也是只進行靜態綁定,所以JVM會通過引用的靜態類型,也就是Father類,來進行綁定,結果為:直接調用父類中的靜態方法,輸出0.

3、對於非靜態方法,會進行動態綁定,JVM檢查出引用father的動態類型,也就是Son類,綁定結果為:調用子類中的靜態方法,輸出1.

對於1和2這兩種情況,子類繼承父類後,父類的屬性和靜態方法並沒有被子類抹去,通過相應的引用可以訪問的到。但是在子類中不能顯示地看到,這種情況就稱為隱藏。

而對於3這種情況,子類繼承父類後,父類的非靜態方法被子類重寫後覆蓋上去,通過相應的引用也訪問不到了(除非創建父類的對象來調用)。這種情況稱為覆蓋。

總結一下,就是,子類繼承父類後:

父類的成員變量只會被隱藏,而且支持交叉隱藏(比如靜態變量被非靜態變量隱藏)。父類的靜態方法只會被靜態方法隱藏,不支持交叉隱藏。父類的非靜態方法會被覆蓋,但是不能交叉覆蓋。

代碼測試如下:

package test;
/* 隱藏和覆蓋的區別 */
public class HideAndCover {
    public static void main(String[] args) {
        Father father=new Son();
        System.out.println(father.a);
        System.out.println(father.b);
        father.c();
        father.d();
    }

}
//聲明父類
class Father{
    static int a=0;
    int b=0;
    void c() {
        System.out.println(0);
    }
    static void d() {
        System.out.println(0);
    }
}
//聲明子類
class Son extends Father{
    static int a=1;
    int b=1;
    void c() {
        System.out.println(1);
    }
    static void d() {
        System.out.println(1);
    }
}

運行結果為:

0

0

1

0

最後附上多態的三個必要條件:

①繼承

②重寫

③父類引用指向子類對象

——over。

隱藏和覆蓋的區別和用法