關於Java雙括號的初始化凡是確實很方便,特別是在常量文件中,無可替代。如下所示:
Map map = new HashMap() {
{
put("Name", "Unmi");
put("QQ", "1125535");
}
};
好處很明顯就是一目了然。這裡來羅列下此種方法的壞處,如果這個對象要串行化,可能會導致串行化失敗。
1.此種方式是匿名內部類的聲明方式(不懂的下文有詳盡解釋),所以引用中持有著外部類的引用。所以當時串行化這個集合時外部類也會被不知不覺的串行化,當外部類沒有實現serialize接口時,就會報錯。
2.上例中,其實是聲明了一個繼承自Hashset的子類。然而有些串行化方法,例如要通過Gson串行化為json,或者要串行化為xml時,類庫中提供的方式,是無法串行化Hashset或者HashMap的子類的,從而導致串行化失敗。解決辦法:重新初始化為一個Hashset對象:
new HashMap(map);
這樣就可以正常初始化了。
雙括號寫法的原理:
第一層括弧 實際是定義了一個內部匿名類 (Anonymous Inner Class),第二層括弧 實際上是一個實例初始化塊 (instance initializer block),這個塊在內部匿名類構造時被執行。這個塊之所以被叫做“實例初始化塊”是因為它們被定義在了一個類的實例范圍內。
上面代碼如果是寫在 TestDoubleBrace 類中,編譯後你會看到會生成 TestDoubleBrace$1.class 文件,反編譯該文件內容是:
final class com.unmi.TestDoubleBrace$1 extends java.util.HashMap{ //創建了一個 HashMap 的子類 TestDoubleBracke$1
com.unmi.TestDoubleBrace$1();
Code:
0: aload_0
1: invokespecial #8; //Method java/util/HashMap."":()V //{} 中的代碼放到了構造方法中去了
4: aload_0
5: ldc #10; //String Name
7: ldc #12; //String Unmi
9: invokevirtual #14; //Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
12: pop
13: aload_0
14: ldc #18; //String QQ
16: ldc #20; //String 1125535
18: invokevirtual #14; //Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
21: pop
22: return
}
編寫高質量代碼 改善Java程序的151個建議 PDF高清完整版 http://www.linuxidc.com/Linux/2014-06/103388.htm
Java 8簡明教程 http://www.linuxidc.com/Linux/2014-03/98754.htm
Java對象初始化順序的簡單驗證 http://www.linuxidc.com/Linux/2014-02/96220.htm
Java對象值傳遞和對象傳遞的總結 http://www.linuxidc.com/Linux/2012-12/76692.htm
Java對象序列化ObjectOutputStream和ObjectInputStream示例 http://www.linuxidc.com/Linux/2012-08/68360.htm