【Java學習】java實例對象的編譯時類型和運行時類型(更正版)

原文鏈接:https://blog.csdn.net/qq_23419401/article/details/52064871

爲什麼要區分編譯時類型和運行時類型?
看這樣一句代碼:Person p=new Women()(Women類繼承自Person類)那麼,假如p的屬性修飾符爲public 訪問屬性時得到的是Person類的屬性還是Women類的屬性,方法調用又是哪個類?答案:會得到Person類的屬性,調用Women類的方法。爲什麼會這樣呢?這裏就需要知道什麼是編譯時類型和運行時類型,java程序狀態會分爲編譯和運行這兩種狀態,編譯時,JVM會在棧中靜態創建基本數據變量,和引用數據變量的引用,回到剛剛那句代碼,顯然,p這個引用就是在編譯時創建的,那麼,p的編譯時類型就是Person了,當運行這句java代碼時,JVM在堆中爲p新建一塊內存,對應new Women()這句代碼,所以p的運行時類型就是Women。有這樣一條規則,對象調用編譯時類型的屬性和運行時類型的方法。下面先用代碼表示這樣的結果,然後再說明我個人的一些理解。

code1:

public class TestDemo1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Person p=new Women();
        System.out.println("p.name:"+p.name);
        p.show();
    }

}
class Person{
    public String name;
    public Person()
    {
        name="person";
    }
    public void show()
    {
        System.out.println("class person's show()");
    }
}

class Women extends Person
{
    public String name;
    public Women()
    {
        name="women";
    }
    public void show()
    {
        System.out.println("class women's show()");
    }
}

結果如下:

從代碼運行結果可以看出,p調用的屬性屬於Person類,而調用的方法是Women類的,驗證了上面的規則–對象調用編譯時類型的屬性和運行時類型的方法

個人理解
這裏屬於我個人的理解,可能有錯,以後發現會重新修正
根據上述規則
根據繼承的特點我們可以知道,子類會繼承父類非私有的屬性和方法,也就是說,父類的(非私有)屬性也會出現在子類中,當然,這是顯而易見的,然而關鍵在於,如果子類重新定義了這一屬性,會怎麼樣呢?實際上,父類的屬性並不會被覆蓋,爲了方便起見,我把從父類繼承來的屬性記爲– 屬性<父類> 而自己重新定義的同名屬性爲–屬性<子類> 這樣,在子類中,會有兩個屬性 即:屬性<父類> 屬性<子類>,那麼如何調用呢?–解答:<>中的內容對應着調用該屬性的對象的編譯時類型,編譯時類型爲父類,調用屬性<父類> ,另一種情況就是調用子類的屬性了。下面用圖來表示:

Class A中定義屬性a,Class B繼承自A,重新定義了屬性a,此時,B中有編譯時類型爲A的屬性a和編譯時類型爲B的屬性a,Class C繼承自B,自己重新定義了屬性a,這時,C具有三種編譯時類型的屬性a。這樣就好看多了,不知道應該調用的屬性是哪個類的,就只要分析自己的編譯時類型就可以了,調用方法其實不用在意,直接調用運行時類型的方法即可(運行時類型還是比較容易看的)。
就上圖的例子我們用代碼測試如下

code2
 

public class TestDemo2 {

    public static void main(String[] args) {
        // 編譯時類型爲A,輸出應該是A
        System.out.println("編譯時類型爲A,輸出應該是A");
        A a=new A();
        System.out.println(a.name);
        A ab=new B();
        System.out.println(ab.name);
        A ac=new C();
        System.out.println(ac.name);
        // 編譯時類型爲B,輸出應該是B
        System.out.println("編譯時類型爲B,輸出應該是B");
        B b=new B();
        System.out.println(b.name);
        B bc=new C();
        System.out.println(bc.name);
        // 編譯時類型爲C,輸出應該是C
        System.out.println("編譯時類型爲C,輸出應該是C");
        C c=new C();
        System.out.println(c.name);
    }

}
class A
{
    String name="A";
}
class B extends A
{
    String name="B";
}
class C extends B
{
    String name="C";
}

運行結果如下:

下面這張圖可以幫助看一下,總結的很到位:

該文章非本人所寫,因爲原文有兩處錯誤,作爲強迫症的小白,更正了一下,作爲轉載,如有冒犯,請聯繫刪除,原文鏈接:https://blog.csdn.net/qq_23419401/article/details/52064871

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章