2010-02-10 179 views
4

給出:繼承和類成員

class A 
{ 
    String s = "A"; 
} 

class B extends A 
{ 
    String s = "B"; 
} 

public class C 
{ 
    public static void main(String[] args){ new C().go();} 
    void go() 
    { 
     A a = new B(); 
     System.out.println(a.s); 
    } 
} 

問:

什麼是JVM背後的機制,當運行這些代碼? a怎麼打印回「A」。

+0

除了其他回答之外,請注意,沒有向B的s成員使用而不將其向下轉換爲B的訪問,例如, ((B)a).s – 2010-02-10 16:22:30

回答

4

字段引用不受多態性,所以在編譯時,編譯器引用的字段,因爲您的局部變量的類型爲A的

換句話說,現場的行爲就像是在方法的Java超載行爲,而不是Java壓倒一切的行爲。

+0

謝謝你的回答。如果你可以,我想從你的回答中澄清以下內容:[1]在運行時和編譯時,「a」有什麼不同。 [2]代碼中的哪一點會發生實際的重載機制? – Krolique 2010-02-10 17:11:48

+0

錯誤,更正:[2] ....是否會出現實際字段「重載」機制? =) – Krolique 2010-02-10 17:22:40

+0

@Krolique,A實際上在編譯時沒有任何東西(它只是一個定義,它不是執行代碼)。當你在類B中定義一個變量s時發生重載。在這一點上,你有兩個在兩個不同類中具有相同名字的字段。編譯器是決定在代碼中引用哪一個的編譯器。它不是動態的。 – Yishai 2010-02-10 17:38:44

1

這不是多態(標記爲)。

Java有virtual methods而不是虛擬成員變量 - 即您不覆蓋屬性 - 您將其隱藏。

+1

這不是一個屬性,它是一個領域。 – 2010-02-10 16:10:07

+0

和一個成員變量。術語並不嚴格 – Bozho 2010-02-10 16:10:55

+0

謝謝你的回答。我錯過了虛擬(繼承?)方法和成員之間的細微區別。 – Krolique 2010-02-10 17:13:37

2

您可能期望字段被重寫,如方法,基於對象的運行時類型的動態分派。

這不是Java的工作原理。字段沒有被覆蓋,它們是隱藏的。這意味着類B的對象具有名爲「s」的兩個字段,但是其中哪些被訪問取決於上下文。

至於爲什麼會這樣:重寫字段沒有任何意義,因爲當類型不同時沒有任何有用的方法使其工作,並且當類型相同時(如你可以使用超類字段)。就個人而言,我認爲它應該只是一個編譯器錯誤。

+0

謝謝你的回答。看看我是否正確掌握了這個概念。上下文隱含了聲明類型? 可以說,如果有可能upcast,我做了以下:B b = new A();那麼在b.s中應該打印「B」? – Krolique 2010-02-10 17:08:42

+0

既然這是不可能的,爭論它「應該」打印什麼是沒有意義的,但是是的,這是基本的想法。 – 2010-02-10 17:12:25

+0

謝謝,我認爲這個說明很多! – Krolique 2010-02-10 23:30:20

0

雖然成員變量是從基類繼承的,但它們不是多態調用的(即動態調用不適用於成員變量)。

所以,a.s將引用基類中的成員而不是派生類。

話雖如此,代碼並不遵循面向對象的原則。根據業務用例,類的成員需要是私有/保護的(不是公共或默認的),並且您需要提供公共方法來獲取和設置成員的值。

+0

謝謝你的回答。在所有正常情況下,我會照你的建議去做。 – Krolique 2010-02-10 17:14:57