2012-03-27 68 views
30

我有一個類,Super規定Java中靜態變量繼承的規則是什麼?

public class Super { 
    public static String foo = "foo"; 
} 

我也有另一個類,Sub擴展Super

public class Sub extends Super { 
    static { 
     foo = "bar"; 
    } 

    public static void main (String[] args) { 
     System.out.println(Super.foo); 
    } 
} 

當我運行它,它打印出bar
我的第三個(也是最後一次)類Testing

public class Testing { 
    public static void main (String[] args) { 
     System.out.println(Super.foo); 
     System.out.println(Sub.foo); 
     System.out.println(Super.foo); 
    } 
} 

此打印:

foo 
foo 
foo 

爲什麼foo內容取決於什麼類你訪問它,我不明白。誰能解釋一下?

+2

我的意思是說,當我從'Testing'訪問它時,它返回的東西與我從'Sub'訪問它時不同。 – jmgrosen 2012-03-27 21:43:33

+0

'@jmgrosen:'啊,現在和你在一起。 – 2012-03-27 21:46:00

+3

FWIW,注意你上面的內容和'Sub'包含的內容之間的重要區別'public static String foo =「bar」;'(因此你得到「foo」,「bar」,「foo」as你可能期望)。 – 2012-03-27 21:48:44

回答

33

我不明白爲什麼foo的內容會根據您訪問的類別而有所不同。

基本上它是一個類型的初始化的問題。當Sub被初始化時,foo的值被設置爲"bar"。但是,在您的Testing類中,對Sub.foo的引用實際上編譯爲對Super.foo的引用,因此它不會初始化爲Sub,因此foo永遠不會變爲"bar"

如果你改變你的測試代碼:

public class Testing { 
    public static void main (String[] args) { 
     Sub.main(args); 
     System.out.println(Super.foo); 
     System.out.println(Sub.foo); 
     System.out.println(Super.foo); 
    } 
} 

然後它會打印出「bar」的四倍,因爲第一條語句將迫使Sub進行初始化,這將改變foo值。這不是從什麼地方訪問它的問題。

+2

這似乎是正確的,但是你碰巧知道爲什麼'Sub.foo'被編譯成'Super.foo'? – jmgrosen 2012-03-27 21:43:58

+4

@jmgrosen:是的 - 因爲只有一個變量,由'Super'聲明。正如maerics所說,它可以通過「Sub」訪問,但這並不意味着它是一個不同的變量。 – 2012-03-27 21:46:31

+0

對於最後的成員也是一樣的情況 – 2013-12-19 07:53:31

27

Java中的靜態變量不會被繼承,它們只存在於聲明它們的類中;然而,通過引用定義靜態變量的類的實例或子類(或子類實例),它們可以隱式地訪問。 (靜態變量處理是Java語言的幾個棘手的部分之一,恕我直言。)

在您的示例代碼中,Sub類有一個靜態初始化而變化的Super.foo值(儘管看起來像它改變其自己「foo」變量的繼承實例)。但是,Testing類實際上並未加載Sub類(可能通過某種編譯技巧?),因此它不運行靜態初始化程序,因此「foo」的值永遠不會被更改。

+1

所有這些隱式訪問和隱式轉換對於學習都是不利的。 ** PSA:**有一個選項只能顯示編碼和編譯。 – NoName 2017-06-23 14:17:51

0

靜態成員不在java中繼承,因爲它們是類的屬性,它們在類區域中加載。他們與對象創作無關。但是隻有子類可以訪問其父類的靜態成員。

0

無論您在哪裏更改靜態變量的值,它都與SubSuper中的變量foo相同。無論您創建並修改多少個new Subnew Super對象,都無關緊要。的變化將隨處可見,因爲Super.fooSub.fooobj.foo共享同一塊storage.This的可與原始類型也:

class StaticVariable{ 
     public static void main(String[] args){ 
      System.out.println("StaticParent.a = " + StaticParent.a);// a = 2 
      System.out.println("StaticChild.a = " + StaticChild.a);// a = 2 

      StaticParent sp = new StaticParent(); 
      System.out.println("StaticParent sp = new StaticParent(); sp.a = " + sp.a);// a = 2 

      StaticChild sc = new StaticChild(); 
      System.out.println(sc.a);// a = 5 
      System.out.println(sp.a);// a = 5 
      System.out.println(StaticParent.a);// a = 5 
      System.out.println(StaticChild.a);// a = 5 
      sp.increment();//result would be the same if we use StaticParent.increment(); or StaticChild.increment(); 
      System.out.println(sp.a);// a = 6 
      System.out.println(sc.a);// a = 6 
      System.out.println(StaticParent.a);// a = 6 
      System.out.println(StaticChild.a);// a = 6 
      sc.increment(); 
      System.out.println(sc.a);// a = 7 
      System.out.println(sp.a);// a = 7 
      System.out.println(StaticParent.a);// a = 7 
      System.out.println(StaticChild.a);// a = 7 
     } 
} 
class StaticParent{ 
     static int a = 2; 
     static void increment(){ 
      a++; 
     } 
} 
class StaticChild extends StaticParent{ 
     static { a = 5;} 
} 

可以通過對象指的是靜態變量/方法(如sc.a)或通過它的類名(如StaticParent.a)。最好使用ClassName.staticVariable來強調變量/方法的靜態特性,併爲編譯器提供更好的優化機會。