2013-01-18 101 views
15

我知道這個代碼:Java雙大括號初始化始終工作?

Set<String> set = new HashSet<String>() {{ 
    add("test1"); 
    add("test2"); 
}}; 

是真的:構造塊之前正在執行

Set<String> set = new HashSet<String>() { 
    {//initializer 
    add("test1"); 
    add("test2"); 
    } 
}; 

的初始化塊。在上面的例子中,在執行構造函數之前調用add(「test1」)。構造函數可能正在初始化許多實例字段,以便該類可以工作。我想知道爲什麼在構造函數工作之前調用.add()?有沒有導致問題的情況?

+2

這可能會屬於'未指定的行爲'... – 11684

+2

有趣的問題。我沒有真正的答案,但我認爲你在這裏做出了一個錯誤的假設。如果你看看HashSet的構造函數,它會這樣做:'map = new HashMap ();'和add方法執行此操作:'返回map.put(e,PRESENT)== null;'。如果你的假設是正確的,這將導致一個NPE。 –

+0

[認爲這種「模式」是否真的值得麻煩肯定沒錯](http://stackoverflow.com/q/924285/521799) –

回答

17

有一個你忽略的細節解釋了這一點。

首先,讓我們回顧一下步驟3到5的initialization procedure(總結):

3.父類的構造被稱爲
4.實例初始化被稱爲
5.構造體被稱爲

您遺漏的細節是您的表達式不是簡單地創建HashSet類的新實例,而是實際上創建了HashSet的匿名子類的新實例。 (我相信這是在section 15.9.1中指定的。)

由於您沒有聲明構造函數,所以使用了默認的構造函數。但在此之前,超類HashSet的構造函數已完成。

因此,總之,HashSet構造函數在初始化塊運行之前完成。

+0

謝謝,塞繆爾。你完全解釋了那個失蹤的作品。該代碼塊不是初始化HashSet,而是HashSet的匿名子類。 – user926958

+0

@ user926958,在這個例子中錯過了一個簡單的細節。如果你從'Set'或者一個抽象類這樣的接口派生出來,那麼你將需要添加一些方法定義,這會使它在這裏定義的新類更加明顯。 –

6

這種假設是錯誤的:

的初始化塊構造塊之前執行。

因爲在這個特定情況下,初始化代碼塊構造塊的一部分。

docs境界清楚,

Java編譯器副本初始化語句塊到每一個構造函數。因此,這種方法可以用來在多個構造函數之間共享一段代碼。

我覺得你很迷惑靜態初始值設定項。

+0

查看我對原題的評論。在每個構造函數之後而不是在之前都不會出現塊嗎?不是你在說別的,我只是想更好地理解。 –

+0

該命令看起來像構造函數之前的初始化器,至少我從這裏得到它:http://stackoverflow.com/questions/2007666/in-what-order-do-static-initializer-blocks-in-java-run – user926958

+1

我的意思不是粗魯,但這不是發生了什麼; initalizer塊在super之後但在構造函數的其餘部分之前調用,這在大多數情況下會導致問題;他正在做一個HashMap類的內聯擴展,這就是爲什麼這總是起作用。 –

4

實例初始化器在構造對象後立即執行。你基本上是在創建一個HashSet的內聯擴展,然後在創建它之後,你正在向它添加兩個項目。

這是用於測試的模擬對象中的常見使用模式,例如在JMock中,但也有其他方便的用途。

希望這會有所幫助。

+2

構造對象後不執行。它在對象構造期間,在超類構造函數和構造函數體之間執行。 –

3

我認爲這是一個不好的做法,因爲它會創建無意義的子類,這些子類可能會影響應用程序的內存使用情況和性能。無論如何,程序是正確的,因爲超類構造函數在實例初始化器之前被調用。所以當你的初始化器運行時,HashSet構造函數已經運行,所以add的調用將起作用。