Think in Java之構造器的真正調用順
- java語言
- 關注:1.89W次
構造器是OOP的重要組成部分,很多人認為它很容易。只不過是new了一個對象而已。而think in java的作者卻告訴我們,其實這並不容易。下面一起學習一下吧!
先看下面這個例子。在你沒看結果之前,你覺得你的答案是對的麼。
package u.t1;
class Meal {
Meal() {
tln("meal");
}
}
class Bread {
Bread() {
tln("Bread");
}
}
class Cheese {
Cheese() {
tln("Cheese");
}
}
class Lettuce {
Lettuce() {
tln("Lettuce");
}
}
class Lunch extends Meal{
Lunch() {
tln("Lunch");
}
}
class PortableLunch extends Lunch{
PortableLunch() {
tln("PortableLunch");
}
}
public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() {
tln("Sandwich");
}
public static void main(String[] args) {
new Sandwich();
}
}
控制枱的打印結果為:
meal
Lunch
PortableLunch
Bread
Cheese
Lettuce
Sandwich
複雜對象調用構造器的順序應該遵循下面的原則:
1、調用基類[即父類]構造器。這個步驟會不斷反覆遞歸下去,首先是構造器這種層次結構的根,然後是下一層導出類[即子類],等等。直到最底層的導出類。[從最上層的meal一直遞歸到PortableLunch]
2、按聲明順序調用成員的初始化方法。[即上面的Bread,Cheese,Lettuce]
3、調用導出類構造器的主體[即Sandwich]
可見,調用類本身是最後完成初始化的,最先完成初始化的是最頂級的基類,所謂沒有父親,哪來的兒子。處於它們中間的是調用類本身擁有的子對象。因為你不可能在子對象初始化之前用本類調用它,所以它一定在本類調用之前,父類調用之後完成初始化的。
那麼這個説法是不是一定成立呢。結果是否定的。你必須知道JVM的編繹原理才可能知道,它究竟是如何工作的`。
我們來看下面這個例子,來解釋為什麼它不一定。因為在繼承和重寫的時候,這種情況變得有點詭異。
深入探究:
package u.t1;
public class ConstrcutorTest2 {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
class Glyph {
void draw() {
tln("Glyph draw()");
}
Glyph() {
tln("Glyph before draw();");
draw();
tln("Glyph after draw();");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
tln("RoundGlyph(),radius:" + radius);
}
void draw() {
tln("(),radius:" + radius);//此處打印是0,而不是1
}
}
控制枱打印結果:
Glyph before draw();
(),radius:0
Glyph after draw();
RoundGlyph(),radius:5
為什麼(),radius:0這裏會是0呢。
默認的1哪去了?值自己會變麼。其實上面的講述並不完整。,而這正是解決謎題的關鍵所在。初始化的實際過程之前,實際在還有一步。
0:在其他任何事物發生之前,將分配對象的存捨得空間初始化為二進制的零。
而它後面的初始化順序就是上面的3步。
調用基類[即父類]構造器。這個步驟會不斷反覆遞歸下去,首先是構造器這種層次結構的根,然後是下一層導出類[即子類],等等。直到最底層的導出類。
按聲明順序調用成員的初始化方法。
調用導出類構造器的主體
也就是説,實際上有4步,知道這些你對對象初始化構造器才可能有個清楚的認識。
JAVA有更多的精髓等着人們去挖掘,而不僅僅是知道如何去使用它。
因為你不知道什麼時候它會出現意想不到的後果,而這個錯誤,可能你根本就想不出來。
編寫構造器時有一條準則:
用盡可能簡單的方法使對象進入正常狀態,如果可以的話,避免調用其它方法。
在構造器內唯一能夠安全調用的那些方法是基類中的final或者private方法,這些方法不能被覆蓋,因此也就不會出現令人驚訝的問題。
你可能無法總是遵循這條準則,但是應該朝着它努力。
- 文章版權屬於文章作者所有,轉載請註明 https://xuezhezhai.com/zh-hk/jsj/java/0ndz5.html