小A:“什麼是構造器?”
大B:“首先要注意的是Java的構造器並不是函式,所以他並不能被繼承,這在我們extends的時候寫子類的構造器時比較的常見,即使子類構造器引數和父類的完全一樣,我們也要寫super就是因為這個原因。構造器的修飾符比較的有限,僅僅只有publicprivateprotected這三個,其他的例如任何修飾符都不能對其使用,也就是說構造器不允許被成名成抽象、同步、靜態等等訪問限制以外的形式。因為構造器不是函式,所以它是沒有返回值的,也不允許有返回值。但是這裡要說明一下,構造器中允許存在return語句,但是return什麼都不返回,如果你指定了返回值,雖然編譯器不會報出任何錯誤,但是JVM會認為他是一個與構造器同名的函式罷了,這樣就會出現一些莫名其妙的無法找到構造器的錯誤,這裡是要加倍注意的。”
小A:“在我們extends一個子類的時候經常會出現一些意想不到的問題,你能和我說說一些和構造器有關的嗎?”
大B:“首先說一下Java在構造例項時的順序(不討論裝載類的過程),構造的粗略過程如下1、分配物件空間,並將物件中成員初始化為0或者空,java不允許使用者操縱一個不定值的物件。2、執行屬性值的顯式初始化。3、執行構造器。4、將變數關聯到堆中的物件上。”
小A:“能介紹一下準備知識嗎?以備一會來詳細瞭解這個的流程。”
大B:“this()super()是你如果想用傳入當前構造器中的引數或者構造器中的資料呼叫其他構造器或者控制父類構造器時使用的,在一個構造器中你只能使用this()或者super()之中的一個,而且呼叫的位置只能在構造器的第一行,在子類中如果你希望呼叫父類的構造器來初始化父類的部分,那就用合適的引數來呼叫super(),如果你用沒有引數的super()來呼叫父類的構造器(同時也沒有使用this()來呼叫其他構造器),父類預設的構造器會被呼叫,如果父類沒有預設的構造器,那編譯器就會報一個錯誤,注意這裡,我們經常在繼承父類的時候構造器中並不寫和父類有關的內容,此時如果父類沒有預設構造器,就會出現編譯器新增的預設構造器給你添麻煩的問題了哦!例如:Classbextendsa{publicb(){}}就沒有任何有關父類構造器的資訊,這時父類的預設構造器就會被呼叫。”
舉個SL-275中的例子
publicclassManagerextendsEmployee{
privateStringdepartment;
publicManager(Stringname,doublesalary,Stringdept)
{
super(name,salary);
department=dept;
}
publicManager(Stringn,Stringdept){
super(name);
department=dept;
}
publicManager(Stringdept){//這裡就沒有super(),編譯器會自動地新增一個空引數的預設super構造器,此時如果Employee類中沒有空引數的預設構造器,那就會導致一個編譯錯誤。
department=d;
}
}
大B:“你必須在構造器的第一行放置super或者this構造器,否則編譯器會自動地放一個空引數的super構造器的,其他的構造器也可以呼叫super或者this,呼叫成一個遞迴構造鏈,最後的結果是父類的構造器(可能有多級父類構造器)始終在子類的構造器之前執行,遞迴的呼叫父類構造器。在具體構造類例項的過程中,上邊過程的第二步和第三步是有一些變化的,這裡的順序是這樣的,分配了物件空間及物件成員初始化為預設值之後,構造器就遞迴的從繼承樹由根部向下呼叫,每個構造器的執行過程是這樣的:1、Bind構造器的引數。2、如果顯式的呼叫了this,那就遞迴呼叫this構造器然後跳到步驟5.3、遞迴呼叫顯式或者隱式的父類構造器,除了Object以外,因為它沒有父類。4、執行顯式的例項變數初始化(也就是上邊的流程中的第二步,呼叫返回以後執行,這個步驟相當於在父構造器執行後隱含執行的,看樣子像一個特殊處理)。5、執行構造器的其它部分。”
小A:“好像有點明白了。”
大B:“這裡的步驟很重要哦!從這個步驟中可以很明顯的發現這個例項初始化時的遞迴呼叫過程。”