我們知道距境,實(shí)例變量初始化和代碼塊初始化問(wèn)題在構(gòu)造函數(shù)執(zhí)行之前。
每個(gè)類至少都有一個(gè)構(gòu)造函數(shù)祭阀,在編譯生成字節(jié)碼時(shí)鹉戚,構(gòu)造函數(shù)會(huì)被命名成<init>方法,參數(shù)類弄和順序不變 专控。
我們知道抹凳,java在實(shí)例化類之前,必須先實(shí)例化其超類伦腐,以保存實(shí)例的完整性赢底。事實(shí)上,這一點(diǎn)是在構(gòu)造函數(shù)中保證的柏蘑,
構(gòu)造的函數(shù)的第一條語(yǔ)句必須是調(diào)用超類的語(yǔ)句或是類中定義的其他函數(shù)幸冻。如果我們沒(méi)有顯示地調(diào)用超類的構(gòu)造函數(shù),也沒(méi)有調(diào)用類中的其它構(gòu)造函數(shù)咳焚,
則編譯器會(huì)自動(dòng)為我們生成對(duì)超類構(gòu)造函數(shù)的調(diào)用洽损。如:
public class Student {
public static void main(String[] args) {
System.out.println(111);
}
}
上面的語(yǔ)句沒(méi)有顯示地調(diào)用超類構(gòu)造函數(shù),但是我們可以查看編譯后的字節(jié)碼革半,編譯器自動(dòng)為我們生成了調(diào)用超類的代碼趁啸,如下:
上面紅色箭頭就是調(diào)用超類Object類的構(gòu)造函數(shù)强缘。
特別地,如果我們?cè)谝粋€(gè)構(gòu)造函數(shù)中調(diào)用另外一個(gè)構(gòu)造函數(shù)不傅,如下:
public class Student {
private int i;
/**
* 在無(wú)參構(gòu)造函數(shù)中調(diào)用有參構(gòu)造函數(shù)
* 對(duì)于這種情況旅掂,只允許在 Student(int i) 中調(diào)用超類的構(gòu)造函數(shù)
*/
public Student() {
//super(); //在這里調(diào)用父類構(gòu)造方法會(huì)報(bào)錯(cuò),
this(1);
}
public Student(int i) {
this.i = i;
}
public static void main(String[] args) {
System.out.println(111);
}
}
總結(jié):實(shí)例化一個(gè)類的對(duì)象的過(guò)程是一個(gè)典型的遞歸過(guò)程访娶,實(shí)例化類的對(duì)象時(shí)商虐,會(huì)先實(shí)例化該類的父類,
如果該父類還有父類崖疤,那么會(huì)一直往上遞歸直到Object類秘车,先實(shí)例化Object類,再往下遞歸實(shí)例化劫哼,直到目標(biāo)類叮趴。
實(shí)例化每個(gè)類時(shí),遵循如下順序权烧,先執(zhí)行實(shí)例變量初始化和實(shí)例代碼塊初始化眯亦,再執(zhí)行構(gòu)造函數(shù)初始化。
也就是説般码,編譯器會(huì)將實(shí)例變量和實(shí)例代碼塊放到類的構(gòu)造函數(shù)中去妻率,并且放到超類的調(diào)用構(gòu)造函數(shù)語(yǔ)句之后,構(gòu)造函數(shù)本身代碼之前 板祝。
綜合實(shí)例:
實(shí)例變量初始化宫静、實(shí)例代碼塊初始化以及構(gòu)造函數(shù)初始化
//父類
class Foo {
int i = 1;
Foo() {
System.out.println(i); //輸出2 ----(1)
int x = getValue();
System.out.println(x); //輸出0,根據(jù)多態(tài)券时,調(diào)用的是子類的getValue()孤里,而此時(shí)子類的構(gòu)造函數(shù)還沒(méi)被調(diào)用 ----(2)
}
{
i = 2;
}
protected int getValue() {
return i;
}
}
//子類
class Bar extends Foo {
int j = 1;
Bar() {
j = 2;
}
{
j = 3;
}
@Override
protected int getValue() {
return j;
}
}
public class ConstructorExample {
public static void main(String... args) {
Bar bar = new Bar();
System.out.println(bar.getValue()); //輸出2 ----(3)
}
}
我們可以將Foo類的構(gòu)造函數(shù)和Bar類的構(gòu)造函數(shù)等價(jià)地分別變?yōu)槿缦滦问剑?/p>
Foo() {
i = 1;
i = 2;
System.out.println(i); //輸出2 ----(1)
int x = getValue();
System.out.println(x); //輸出0,根據(jù)多態(tài)橘洞,調(diào)用的是子類的getValue()扭粱,而此時(shí)子類的構(gòu)造函數(shù)還沒(méi)被調(diào)用 ----(2)
}
Bar() {
Foo();
j = 1;
j = 3;
j = 2;
}
在通過(guò)使用Bar類的構(gòu)造方法new一個(gè)Bar類的實(shí)例時(shí),首先會(huì)調(diào)用Foo類構(gòu)造函數(shù)震檩,因此(1)處輸出是2琢蛤,這從Foo類構(gòu)造函數(shù)的等價(jià)變換中可以直接看出。
(2)處輸出是0抛虏,為什么呢博其?因?yàn)樵趫?zhí)行Foo的構(gòu)造函數(shù)的過(guò)程中,由于Bar重載了Foo中的getValue方法迂猴,所以根據(jù)Java的多態(tài)特性可以知道慕淡,
其調(diào)用的getValue方法是被Bar重載的那個(gè)getValue方法。但由于這時(shí)Bar的構(gòu)造函數(shù)還沒(méi)有被執(zhí)行沸毁,因此此時(shí)j的值還是默認(rèn)值0峰髓,因此(2)處輸出是0傻寂。
最后,在執(zhí)行(3)處的代碼時(shí)携兵,由于bar對(duì)象已經(jīng)創(chuàng)建完成疾掰,所以此時(shí)再訪問(wèn)j的值時(shí),就得到了其初始化后的值2徐紧,這一點(diǎn)可以從Bar類構(gòu)造函數(shù)的等價(jià)變換中直接看出静檬。
參考:https://blog.csdn.net/justloveyou_/article/details/72466416