1. 程式人生 > >《Java程式設計思想》讀書筆記——構造器初始化順序

《Java程式設計思想》讀書筆記——構造器初始化順序

Java盡力保證:所有變數在使用前都能得到恰當的初始化。對於區域性變數,Java以編譯時錯誤的形式來貫徹這種保證。

public static void main(String[] args) {
    int i;
    System.out.println(i++);
}

執行結果:
Error:(12, 28) java: 可能尚未初始化變數i

類的沒有基本資料成員保證都會有一個初始值,即使資料成員並未顯示賦初值。對於類中的物件引用來說,如果不將其初始化,此引用就會獲得一個特殊值null。

public class Dog {
    public String name;
public int age; public Dog son; } public class Test { public static void main(String[] args) { Dog dog = new Dog(); System.out.println(dog.name); System.out.println(++dog.age); System.out.println(dog.son); } } 執行結果: null 1 null

如果我們在定義類成員變數時沒有為其定義初值,那麼編譯器就會自動為其加上初值。boolean

falseshortintlong0floatdouble0.0char0所以顯示為空。

在類的內部,變數定義的先後順序決定了初始化的順序。即使變數定義散佈於方法定義之間,它們仍舊會在任何方法(包括構造器)被呼叫之前得到初始化。

public class Window {
    public Window(){
        System.out.println("Window()");
    }
}

public class House {
    public Window win = new Window();
    public House(){
        System.
out.println("House()"); } } public class Test { public static void main(String[] args) { House h = new House(); } } 執行結果: Window() House()

從上例中可以看到,成員變數初始化先於構造方法執行。那麼,對於靜態成員來說,情況是否一樣呢?來看看下面例子。

public class Bowl {
    Bowl(int marker){
        System.out.println("Bowl(" + marker + ")");
    }
    void f1(int marker){
        System.out.println("f1(" + marker + ")");
    }
}

public class Table {
    static Bowl bowl1  = new Bowl(1);
    Table(){
        System.out.println("Table()");
        bowl2.f1(1);
    }
    void f2(int marker){
        System.out.println("f2(" + marker + ")");
    }
    static  Bowl bowl2 = new Bowl(2);
}

public class Cupboard {
    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);
    Cupboard(){
        System.out.println("Cupboard()");
        bowl4.f1(2);
    }
    void f3(int marker){
        System.out.println("f3(" + marker + ")");
    }
    static Bowl bowl5 = new Bowl(5);
}

public class Test {
    public static void main(String[] args) {
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }
    static  Table table = new Table();
    static Cupboard cupboard = new Cupboard();
}

執行結果:
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)

初始化順序是先初始化靜態物件(如果它們因前面的物件建立過程而被初始化),而後是非靜態物件

如果大家知道這一點,上面輸出結果就不難解釋了——Test類中,先初始化table,進入table類中先有靜態屬性bowl1,再進入Bowl類中,發現沒有成員變數,直接呼叫Bowl的構造方法,輸出Bowl(1)。再回到Table類中,再有靜態屬性bowl2,同理輸出Bowl(2)。然後呼叫Table類的構造方法,輸出Table()f1(1),此時Test類中的靜態屬性table初始化完成,再根據相同的原則初始化cupboard。**不過這裡值得注意的是,靜態屬性只會初始化一次。**最後執行main()方法裡的內容。

最後,我們來總結一下物件的建立過程,假設有個名為Dog的類:

  1. 即使沒有顯式地使用static關鍵字,構造器實際上也是靜態方法。因此,當首次建立型別為Dog地物件時,或者Dog類地靜態方法/靜態域首次被訪問時,Java直譯器必須查詢類路徑,以定位Dog.class檔案。
  2. 然後載入Dog.class,有關靜態初始化的所有動作都會執行。因此,靜態初始化只在Class物件首次載入的時候進行一次。
  3. 當用new Dog()建立物件的時候,首先將在堆上為Dog物件分配足夠的儲存空間。
  4. 這塊儲存空間會被清零,這就自動地將Dog物件中的所有基本型別資料都設定成了預設值,而引用則被設定成了null。 5、執行所有出現於欄位定義處的初始化動作。 6、執行構造器。