關於構造器中super的使用,書本上這樣寫:
“super是指向父類的引用,如果構造方法沒有顯示地調用父類的構造方法,那么編譯器會自動為它加上一個默認的super()方法調用。如果父類由沒有默認的無參構造方法,編譯器就會報錯,super()語句必須是構造方法的第一個子句。”
首先我要糾正一個我剛剛才發現的印象流錯誤,我之前一直以為,無論有沒有自定義構造器,編譯器為自動為每個類生成一個“方法體為空的無參構造方法”。但上面那段話,指出了,一個類是有可能沒有默認的無參構造方法的。於是我往回翻了書,書上的原文為:
“類可以不定義構造方法,這時編譯器會為類隱含聲明一個方法體為空的無參構造方法。但當類有明確聲明的構造方法時,編譯器就不會自動生成無參構造方法了。”
說明一旦你自定義了構造方法,編譯器就不會幫你自動生成方法體為空的無參構造方法。我之前的印象流是錯的。
我們再來看書本上關於this的描述:
“this表示一個對象的引用,它指向正在執行方法的對象。特別的,在構造方法中通過this關鍵字調用其他構造方法時,必須放在第一行,否則編譯器會報錯。且在構造方法中,只能通過this調用一次其他構造方法。”
綜合上面的描述,會有一個疑問,能否在同一個構造器中同時顯示地使用this和super?
(這個問題在知乎上有討論,詳情請轉義陣地。在這里我直接搬運過來了)
假定這里討論的構造器都沒有顯式的super()調用,則:
- 有顯式this()調用的構造器就會抑制掉該構造器里隱式的super()調用;
- 沒有顯式this()調用的構造器則會得到隱式的super()調用。
this()調用會借助別的重載版本的構造器來做部分初始化,而一連串this()最后來到的構造器必然是沒有顯式this()調用的,這里就會有顯式或隱式的super()調用。舉個例子:
public class Base { private int x; public Base() { // super(); // 這里開頭沒有this()調用,編譯器會合成隱式的super()調用 this.x = 42; } } public class Derived extends Base { private char c; private Object o; public Derived() { this('a'); // 這里開頭有this()調用,編譯器不會合成隱式的super()調用 } public Derived(char c) { this(c, null); // 同上,不會合成super()調用 } public Derived(char c, Object o) { // super(); // 這里開頭沒有this()調用,編譯器會合成隱式的super()調用 this.c = c; this.o = 0; } } 作者:RednaxelaFX 鏈接:https://www.zhihu.com/question/41810504/answer/117132716 來源:知乎 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
在這個例子里,如果有 new Derived(),則一連串的構造器調用會是:
Derived() // 最初的new Derived(char) // 經過this() Derived(char, Object) // 經過this() Base() // 經過super() Object() // 來到Object()
這就是我想說的,一連串this()的最后一個必然不再會在開頭有this()調用,而這個開頭沒有this()調用的構造器就會得到編譯器給隱式合成的super()調用。
這里沒有直接回答“能不能在同一構造器同時顯示調用super()和this()”的問題,但考慮下,如果能的話,最終會有兩次super()調用,也就是說我們沒有理由,在同一個構造器中同時調用super()和this()。
那么強行在同一個構造器中同時調用super()和this()會發生什么呢?
結果是編譯器禁止這樣做,無論把哪個放在構造器的第一行,都會出現以下其中一個錯誤:
Error:(6, 13) java: call to this must be first statement in constructor Error:(6, 14) java: call to super must be first statement in constructor
那么super()的調用究竟完成了什么工作?難道它創建了一個父類對象嗎?
首先,super是指向“父類”的引用,不是像“this”一樣指向一個對象。
由於一個子類的實例會包含其所有基類所聲明的字段(所有噢,包括私有,這些字段也需要初始化),外加自己聲明的字段。而super()是父類封裝對自己聲明的字段進行初始化的手段。
所以,並沒有創建一個父類對象,因為僅僅有那些父類聲明的字段構不成一個完整的實例。
Java對象是這樣組織的(概念上):
一個Derived實例就是一個Derived實例。它的this()會負責初始化自己所聲明的字段的部分,而它通過(遞歸-)調用super()來初始化基類祖先們所聲明的字段的部分。
然后每個類的元數據會包括結構信息,例如說自己的superClass是誰。這里Derived類會知道自己的superClass是Base,而Base知道自己的superClass是Object。
當我們需要執行instanceof、checkcast、arraystore之類的涉及類型檢查的操作時,類的元數據里的結構信息就派上用場了。