《Java程式設計思想》讀書筆記——構造器初始化順序
阿新 • • 發佈:2018-12-11
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
false
,short
、int
、long
為0
,float
、double
為0.0
,char
為0
所以顯示為空。
在類的內部,變數定義的先後順序決定了初始化的順序。即使變數定義散佈於方法定義之間,它們仍舊會在任何方法(包括構造器)被呼叫之前得到初始化。
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的類:
- 即使沒有顯式地使用static關鍵字,構造器實際上也是靜態方法。因此,當首次建立型別為Dog地物件時,或者Dog類地靜態方法/靜態域首次被訪問時,Java直譯器必須查詢類路徑,以定位Dog.class檔案。
- 然後載入Dog.class,有關靜態初始化的所有動作都會執行。因此,靜態初始化只在Class物件首次載入的時候進行一次。
- 當用new Dog()建立物件的時候,首先將在堆上為Dog物件分配足夠的儲存空間。
- 這塊儲存空間會被清零,這就自動地將Dog物件中的所有基本型別資料都設定成了預設值,而引用則被設定成了null。 5、執行所有出現於欄位定義處的初始化動作。 6、執行構造器。