2011-09-06 129 views
5
public class Base { 
public Base() { 
    x = 0; 
    bar(); 
} 

public Base(int x) { 
    this.x = x; 
    foo(); 
} 

public void foo() { 
    System.out.println("Base.foo : " + x); 
} 

private void bar() { 
    System.out.println("Base.bar:" + x.toString()); 
} 

protected Integer x; 
} 

    public class Derived extends Base { 
     public Derived() { 
     bar(); 
     } 
     public Derived(int x, int y) { 
     super(x); 
     this.y = y; 
     } 
     public void foo() { 
     System.out.println("Derived.foo : " + x + ", " + y); 
     } 
     public void bar() { 
     System.out.println("Derived.bar:" + x.toString() + ", " + y.toString()); 
     } 
     private Integer y; 


     public static void main(String[] args) { 
     Base b = new Derived(10, 20); 
     } 
} 

爲什麼它打印"Derived.foo:" 10, nulll而不是20而不是空? y是一個Derived的私有變量,它的初始值是20.它在它的範圍內..所以爲什麼它是空的?Java-爲什麼它打印出null?

回答

7

因爲超級構造函數首先被調用(super(x))。這個超級構造函數調用foo方法。然後Derived構造函數將y初始化爲20(this.y = y)。所以當foo被調用時,y尚未初始化。

在構造函數中調用可重寫方法是一種不好的做法,因爲正如您剛纔注意到的那樣,它可以在部分構造的對象上調用重寫的方法。

+0

+1。很好解釋。 – Mikaveli

+2

+1,特別是對於不良練習警告:_it可以在部分構建的objects_上調用重寫的方法。 –

2

println來自法foo這是從Base的構造,這是從Derived所謂的構造函數(通過super)初始化y之前調用。因此預期爲空值。

1

當您進入超類y的構造函數尚未實例化。所以致電foo()將有一個nully

2

這樣做是因爲超類(Base)構造函數在設置y成員變量之前調用Derived.foo()

因此,這裏是操作的基本順序:

main(...) 
Derived(10,20) (start constructor) 
Base(10) (start constructor) 
this.x = x 
foo() (calls Derived.foo() which prints the message you see) 

this.y = y 
1

它是由Derived.foo()由超級構造函數調用觸發打印之前與之後20初始化之後。所以在打印時它仍然是空的。

1

所有的答案確實是正確的,但下次你可以在打印某些東西的地方放一個斷點。 然後您可以通過讀取正在調用的部分簡單地檢查調用堆棧並查看代碼。

這樣你就可以發現y不會被初始化。

祝你好運! 羅埃爾

+0

我需要學習如何做到這一點,謝謝! – Numerator

2

如果按照從構造函數中調用它使得它更清晰:

  1. 調用Derived(int x, int y)構造
  2. 調用超級構造Base(int x)
  3. 設置x變量
  4. 調用覆蓋foo()方法
  5. 執行println()
  6. 然後y變量

正如你所看到的,你嘗試打印出來的值後的變量y是越來越設置。這就是爲什麼你看到null未初始化的值。

從構造函數調用可重寫的方法是一個常見的錯誤,因爲它可以允許未初始化的變量在對象完全構建之前轉義。

1

因爲在基類構造函數中調用foo()時y尚未初始化。

鏈變爲 main - > Derived(x,y) - > Base(x) - > initialise x,foo()。但是,由於您正在Derived中開始調用,它覆蓋了foo(),所以派生的foo()實際上由解釋器執行。