2012-01-12 57 views
6

昨天我遇到了一個有趣的問題,雖然修復很簡單,但我仍然對它的「原因」有些模糊。爲什麼成員對象在超類的構造函數之後初始化?

我有一個類有一個私有成員變量,當它被實例化時被分配,但是如果它被用在一個由超類的構造函數調用的抽象函數中,變量沒有值。該問題的解決方案非常簡單,我只需將變量聲明爲靜態並且它的分配正確。一些代碼來說明這個問題:

class Foo extends BaseClass 
{ 
    private final String bar = "fooBar!"; 
    public Foo() 
    { 
     super(); 
    } 

    @Override 
    public void initialize() 
    { 
     System.out.println(bar); 
    } 
} 

和基礎類:

abstract class BaseClass 
{ 
    public BaseClass() 
    { 
     initialize(); 
    } 

    public abstract void initialize(); 
} 

在這個例子中,當我們調用new Foo();它將輸出(空)而不是預期Foobar的!

由於我們實例化了一個Foo類型的對象,它的成員是否應該在調用其成員(因此它的超類)的構造函數之前不被分配和賦值?這是在Java語言的某處指定的,還是JVM特定的?

感謝您的任何見解!

+2

小心從超類ctor調用子類方法。 – 2012-01-12 23:27:03

+0

該代碼實際上打印fooBar!因爲'bar'變量是最終的,這使得它成爲編譯時常量。如果沒有'final',它會打印出null。 – x22 2012-01-12 23:56:33

+0

@ x22這實際上是不正確的,是什麼引起了這種情況的調查 - 當我最初編寫它時,我相信同樣的事情。 – 2012-01-13 00:05:50

回答

7

bar = "fooBar!";的賦值是內嵌在編譯期間進入構造函數。

超類的構造函數運行before子類的構造函數,因此只有在事後才執行語句。

一般來說,它是bad practice從構造函數調用可重寫的方法。

+0

真棒,感謝您的鏈接,幫助一堆! – 2012-01-12 23:34:06

1

它由Java語言規範定義。將其改爲靜態在現實世界中幾乎不會是可接受的解決方案。

JLS 4.12.5 Initial Values of VariablesJLS 8.3.2 Initialization of Fields

總體而言,這是不好的做法來調用構造函數的非最終方法。原因在於它可以(並且如果該方法是抽象的,那麼肯定會)在尚未初始化的類中調用方法:當執行new Foo()時,構造函數在構造函數之前被調用,因此Foo構造函數Foo.initialize實質上是在一個尚未完全構造的對象上工作。

相關問題