2016-09-30 178 views
2

我已經問過關於這個主題的幾個問題,但似乎每次我得到答案時,我都有更多問題。 這個問題是我的另一個問題的延續:Initialization in polymorphism of variables實例變量和構造函數

不管怎麼說,考慮下面的例子。

class A{  //1 
    int a = 1; //2 
}    

我聽說過這個概念上的樣子,

class A {   //1 
    int a = 0;  //2 

    A() {   //3 
     super();  //4 
     a = 1;  //5 
    } 

據我瞭解,這是因爲在每次創建對象時,實例對象被初始化爲它的默認值。

  1. 如果我把初始化塊說,

    System.out.print(i); 
    

    右下面的兩個例子線2條,頂部將打印1和底部將打印0。據我所知,初始化塊前執行構造函數。那麼這僅僅是對構造函數的概念表示嗎?或者,當默認構造函數被調用時,代碼實際上是否會改變?有人能爲我澄清這一點嗎?

  2. 它爲什麼會這樣?在我的另一個問題中,它似乎只會導致混淆哪個變量被調用。無法將實例變量聲明爲a = 1並在整個類中使用?這不應該讓它變得更簡單嗎?

+0

檢查http://stackoverflow.com/questions/19561332/in-what-order-do-static-blocks-and-initialization-blocks-execute-when-using-inhe – TheLostMind

+0

提示:除了詢問問題。考慮一個人**真的**這樣的事情應該做的事**:學習Java語言規範,例如https://docs.oracle.com/javase/specs/jls/ se8/html/jls-8.html#jls-8.3.2我在說的是:似乎你想要走到最深處。那麼不要相信人們在這裏給你答案(太多)。學習閱讀規格(在要求其他人教你之前或之前) – GhostCat

回答

1

正如你所說,你問題中兩個類之間的等價關係只是概念上的。實際上,如果一個非靜態數據字段有一個初始化值,它在調用構造函數之前被初始化。初始化塊由編譯器複製到每個構造函數的開頭(在super行之後),所以它在字段的初始化之後並在構造函數代碼本身之前執行。

1

你的例子之間的區別是操作的順序。在你的第一個例子,與初始化塊,你說,順序是:

  1. 分配1至a(在聲明)
  2. 輸出a(在初始化塊)

。 ..但在你的例子例子,它是

  1. 分配0(默認值)a(實際上在聲明)
  2. 輸出a(在初始化塊)
  3. 分配1至a(構造函數中)

理解實例初始化對我來說,關鍵是這樣的:實例初始化代碼字面上複製到構造  —所有這些,包括由編譯器默認的  —。它以源代碼順序複製,並且在構造函數中的任何內容之前(包括super)。

下面是一個更完整的例子。考慮這個類:

class Example { 
    // Instance field with initializer 
    private int i = 5; 

    // Instance initialization block 
    { 
     System.out.println(this.i); 
    } 

    // constructor 1 
    Example() { 
     System.out.println(this.i * 2); 
    } 

    // constructor 2 
    Example(int _i) { 
     this.i = _i; 
     System.out.println(this.i * 3); 
    } 
} 

也就是說,就好像是這個已經編譯成字節碼正是

class Example { 
    // Instance field 
    private int i; 

    // constructor 1 
    Example() { 
     // begin copied code 
     this.i = 5; 
     System.out.println(this.i); 
     // end copied code 
     System.out.println(i * 2); 
    } 

    // constructor 2 
    Example(int _i) { 
     // begin copied code 
     this.i = 5; 
     System.out.println(this.i); 
     // end copied code 
     this.i = _i; 
     System.out.println(this.i * 3); 
    } 
} 

在上述兩種情況下,Oracle的Java 8輸出完全相同的字節碼(通過使用javap -c Example觀看編譯後):

 
Compiled from "Example.java" 
class Example { 
    Example(); 
    Code: 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."":()V 
     4: aload_0 
     5: iconst_5 
     6: putfield  #2     // Field i:I 
     9: getstatic  #3     // Field java/lang/System.out:Ljava/io/PrintStream; 
     12: aload_0 
     13: getfield  #2     // Field i:I 
     16: invokevirtual #4     // Method java/io/PrintStream.println:(I)V 
     19: getstatic  #3     // Field java/lang/System.out:Ljava/io/PrintStream; 
     22: aload_0 
     23: getfield  #2     // Field i:I 
     26: iconst_2 
     27: imul 
     28: invokevirtual #4     // Method java/io/PrintStream.println:(I)V 
     31: return 

    Example(int); 
    Code: 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."":()V 
     4: aload_0 
     5: iconst_5 
     6: putfield  #2     // Field i:I 
     9: getstatic  #3     // Field java/lang/System.out:Ljava/io/PrintStream; 
     12: aload_0 
     13: getfield  #2     // Field i:I 
     16: invokevirtual #4     // Method java/io/PrintStream.println:(I)V 
     19: aload_0 
     20: iload_1 
     21: putfield  #2     // Field i:I 
     24: getstatic  #3     // Field java/lang/System.out:Ljava/io/PrintStream; 
     27: aload_0 
     28: getfield  #2     // Field i:I 
     31: iconst_3 
     32: imul 
     33: invokevirtual #4     // Method java/io/PrintStream.println:(I)V 
     36: return 
} 
1

您對如何將int a = 1轉換爲con的說明結構是正確的,但它不是全部。

  • 如果,除了a,有與初始化其他實例字段,所有的初始化都收集到,作爲構造的一部分運行
  • 如果,除了現場初始化你有一般的單塊目標初始化程序塊,它們的內容與字段初始化程序一起被收集到同一個塊中。

舉例來說,如果你有

class A { 
    { 
     System.out.println(a); 
    } 
    int a = 1; 
    { 
     System.out.println(a); 
     System.out.println(b); 
    } 
    int b = 2; 
    { 
     System.out.println(b); 
    } 
    public A() { 
     // Code of A 
    } 
} 

然後之前Code of A代碼塊看起來是這樣的:

System.out.println(a); 
a = 1; 
System.out.println(a); 
System.out.println(b); 
b = 2; 
System.out.println(b); 
// Code of A 

應該清楚了吧,爲什麼零初始化塊印刷之前int a = 1在初始化程序之前的塊中:初始化塊不會與字段初始值設定項分開處理,它們的代碼按照它們出現的相同順序混合在一起在源代碼中。

0

如果沒有其他設置,實例變量可立即使用默認變量:對象被設置爲null,並將基本類型設置爲0,false等。

您有3種選擇來設定在Java中的實例變量的值:

1)聲明和實例立即

class A { 
    int i = 1; 
} 

2)實例化它在一個實例初始化塊

class A { 
    int a; // it is default value 0 at this point 
    { a = 1; } //instance initializer block 
} 

3)在構造函數中實例化它

class A{ 
    int a; // it is default value 0 at this point 
    A() { 
     a = 1; 
    } 
}  

在一個對象的實例化,Java將

  1. 先初始化變量爲它的默認,如果不是由用戶來完成的,
  2. 然後它會經過的順序任何實例初始化塊,他們出現,最後
  3. 它將進入構造函數。