2013-07-23 27 views
17
//: c07:Sandwich.java 
// Order of constructor calls. 
// package c07; 
// import com.bruceeckel.simpletest.*; 

import java.util.*; 

class Meal { 
    Meal() { System.out.println("Meal()"); } 
} 

class Bread { 
    Bread() { System.out.println("Bread()"); } 
} 

class Cheese { 
    Cheese() { System.out.println("Cheese()"); } 
} 

class Lettuce { 
    Lettuce() { System.out.println("Lettuce()"); } 
} 

class Lunch extends Meal { 
    Lunch() { System.out.println("Lunch()"); } 
} 

class PortableLunch extends Lunch { 
    PortableLunch() { System.out.println("PortableLunch()");} 
} 

public class Sandwich extends PortableLunch { 
// private static Test monitor = new Test(); 
    private Bread b = new Bread(); 
    private Cheese c = new Cheese(); 
    private Lettuce l = new Lettuce(); 
    public Sandwich() { 
    System.out.println("Sandwich()"); 
    } 
    public static void main(String[] args) { 
    new Sandwich(); 
    /* 
    monitor.expect(new String[] { 
     "Meal()", 
     "Lunch()", 
     "PortableLunch()", 
     "Bread()", 
     "Cheese()", 
     "Lettuce()", 
     "Sandwich()" 
    }); 
    // */ 
    } 
} ///:~ 

這段代碼的輸出是爲了構造的多級繼承調用java中

Meal() 
Lunch() 
PortableLunch() 
Bread() 
Cheese() 
Lettuce() 
Sandwich() 

由於在類中的字段的順序創建聲明它們,爲什麼不

Bread() 
Cheese() 
Lettuce() 

來到上面的列表頂部?

另外,它在這段代碼中試圖做什麼?

monitor.expect(new String[] { 
     "Meal()", 
     "Lunch()", 
     "PortableLunch()", 
     "Bread()", 
     "Cheese()", 
     "Lettuce()", 
     "Sandwich()" 
    }); 

起初我以爲這是一個匿名類,但它看起來不像它。它初始化一個字符串數組嗎?爲什麼它沒有String變量的名稱?請告訴我這裏使用的編程結構的名稱。

+3

但是,這不是您的功課嗎? –

+0

@ChristianMark這個定義不是作業,我在閱讀Thinking in Java時自己也有同樣的問題。我發現這個問題和答案非常有用:) –

回答

19

構造:

public Sandwich() { 
    System.out.println("Sandwich()"); 
} 

是由編譯器翻譯爲:

public Sandwich() { 
    super(); // Compiler adds it if it is not explicitly added by programmer 
    // All the instance variable initialization is moved here by the compiler. 
    b = new Bread(); 
    c = new Cheese(); 
    l = new Lettuce(); 

    System.out.println("Sandwich()"); 
} 

所以,在構造函數中的第一條語句是父類的構造函數的鏈接。事實上,任何構造函數鏈中的第一個語句就是超類構造函數。這就是爲什麼首先調用超類構造函數PortableLunch,由於編譯器添加了(請記住?),因此再次將該調用鏈接到其超類構造函數。

這個構造函數調用的鏈接一直進行到繼承層次結構頂部的類爲止,從而在最後調用Object類構造函數。

現在,在每個超類構造函數執行完畢並且所有超類字段都已初始化之後,直接子類構造函數在調用super()之後開始執行。然後最後回到Sandwitch()構造函數,它現在初始化您的3字段。

所以,基本上你的字段最後被初始化了,因此在打印Sandwitch()之前,它們被打印在最後。

有關實例創建過程的詳細說明,請參閱JLS - §12.5 - Creation of New Class Instance


至於問題的你的第二部分:

monitor.expect(new String[] { 
     "Meal()", 
     "Lunch()", 
     "PortableLunch()", 
     "Bread()", 
     "Cheese()", 
     "Lettuce()", 
     "Sandwich()" 
    }); 

此代碼創建一個無名數組,並在同一時間初始化它的一些字符串文字。它與您創建命名數組的方式類似:

String[] arr = new String[] { "rohit", "jain" }; 
+0

非常感謝你 – user13267

+0

@ user13267。不客氣:) –

+0

如果它們是基元而不是物體,它會是一樣的嗎?例如,如果int b = 5 int c = 10和int l = 15,並且沒有調用「new Sandwich();',編譯器是否仍然爲b,c和l分配內存? – user13267

5

您示例中的對象使用繼承,這會導致調用一系列構造函數。當使用繼承時,從另一個繼承的類(subtype)必須調用它繼承的類的構造函數(super type)。當存在一個類型層次結構時,意味着幾個類在鏈中相互延伸,對超級構造器的調用會傳播到鏈中的第一個類,該類不會從另一個類繼承(忽略Object)。

子類型的超類構造函數必須在執行子類型的構造函數之前調用,因爲它可能依賴於超類型的字段或方法。構造函數在類型層次結構上調用鏈,並且一旦每個構造函數初始化子類型開始其實例化。

一旦類類型層次結構中的超類型構造函數被調用,子類型的字段就被聲明瞭,因爲子類型的構造函數也可能需要它們。在聲明子類型的字段之後,子類型構造函數將執行。

此順序是必要的,因爲子類型可能依賴於在超類型中建立的字段或方法。

Meal() (Top of Class Hierarchy, not including Object) 
Lunch() (Extends Meal) 
PortableLunch() (Extends Lunch) 
Bread() (Field of lunch declared before constructor call) 
Cheese() (Field of lunch declared before constructor call) 
Lettuce() (Field of lunch declared before constructor call) 
Sandwich() (Extends Portable Lunch) 

Here is a really good overview of object creation in Java.

+0

但註釋'新的三明治();'在主要使得它根本沒有輸出。這是爲什麼?麪包,奶酪和生菜是獨立的課程。即使沒有創建三明治的對象,不應該調用其他字段的構造函數,因爲它們已經在三明治類中顯式聲明瞭? – user13267

+0

@ user13267但是他們在哪裏實例化? – zEro

+0

@ user13267這是因爲沒有對象被實例化,所以不會調用所有的構造函數,導致所有的打印語句永遠不會被調用。基本上,執行路徑從不遵循,打印出所有的調試消息。 –

1
new String[] { 
     "Meal()", 
     "Lunch()", 
     "PortableLunch()", 
     "Bread()", 
     "Cheese()", 
     "Lettuce()", 
     "Sandwich()" 
    } 

\上面是anonymous array declaration。在這種情況下,您不需要指定大小。

在初始化實例變量之前,所有的超類構造函數都會被調用。 即,

順序--->

Object(), 
    all your superclass constructors, 
    instance variables of this class in that order 
1

構造首先被調用,那麼它的實例變量進行評估,acording在Java Language Specification指定的順序。這就是爲什麼你

Bread() 
Cheese() 
Lettuce() 

Meal() 
Lunch() 
PortableLunch() 

有關的String []初始化,你認爲是不是一個匿名類,就是一個String對象的創建,它不nesesarily必須分配給一個變量。