2015-11-02 85 views
5
class A {int x = 5;} 
class B extends A {int x = 10;} 
class D { 
    public static void main(String[] args){ 
     A b0 = new B(); 
     System.out.print(b0.x); 
    } 
} 

我很奇怪,爲什麼這個代碼打印的5而不是10靜態實例變量查找的Java

如果我不是寫以下,將變量x的方法,它的工作原理更是我所期待,並打印出10,因爲在編譯時只檢查b0的靜態類型A是否有方法x,然後在運行時使用b0的動態類型B來運行x。

class A {int x() {return 5;}} 
class B extends A {int x() {return 10;}} 
class D { 
    public static void main(String[] args){ 
     A b0 = new B(); 
     System.out.print(b0.x()); 
    } 
} 

我的理論是,實例變量靜態查找不像方法,但我不知道爲什麼會這樣。

謝謝!

回答

4

BA的字段x被覆蓋(隱藏)未覆蓋。回答「爲什麼會這樣」參考文檔herehere。編譯器將根據包含對象的類型從x中選擇兩個實例中的一個。而b0的類型爲A

A b0 = new B(); 

當你定義在另一方面(吸氣),這些方法與父類相同的簽名可以覆蓋方法。另一個令人不愉快的驚喜是,父類中的字段即使是不同類型也會被遮蔽。

成員的陰影被認爲是一種不好的做法,因爲它容易讓開發人員感到困惑。

4

因爲您正在訪問類A中的變量x,因爲b0被定義爲A。它被稱爲隱藏一個變量,而不是你可能會懷疑覆蓋一個變量,這在java中是不可能的。如果您使用typeCast從b0訪問x,則會得到預期的結果。

A b0 = new B(); 
System.out.print(((B)b0).x); 

通過使用類型轉換,你會從類B訪問變量x現在。

瞭解更多信息,你可以通過閱讀JLS 8.3

3

靜態字段不能被繼承,它們不會覆蓋對方,他們的影子對方。當你在你的案例中直接訪問同名的靜態字段時,超類的字段隱藏了另一個字段,並且你有兩個int Xes,但超類中的一個不會隱藏並被選中。更好的是,當你調用同一個方法的另一個實例並訪問相同的靜態字段時,也就是當事情變得非常奇怪時。靜態字段被加在一起,並且最終可能以X爲5 + 5 = 10結束。

另一方面,如果從超類繼承非靜態字段,則不存在子字段值不同的問題,類,因爲子類可以覆蓋非靜態超級成員。

靜態變量和繼承是不好的,它打破了你最不期待它的多態性。 (其實,如果你理解你的語言的概念,你期待它,但其他人可能不會)