我们在开发中偶尔会遇到一些小伙伴使用双括号“{{}}”来进行map或者list的初始化,类似下面示例这样
示例public class MapTest {
Map map = new HashMap(){
{
put("1","one");
}
};
List list = new ArrayList(){
{
add(1);
add(2);
}
};
}
这样初始化看起来确实挺爽!通俗,简介,易懂
但是越好看的往往越容易踩坑
我们看看编译之后的class文件
发现除了MapTest.class还有两个class文件。
我们把MapTest$1直接放进IDEA中进行反编译,发现MapTest$1继承了hashmap的
class MapTest$1 extends HashMap{ MapTest$1(MapTest this$0) { this.this$0 = this$0; this.put("1", "one"); } }
同理MapTest$2也是这样
class MapTest$2 extends ArrayList{ MapTest$2(MapTest this$0) { this.this$0 = this$0; this.add(1); this.add(2); } }
如果工程中大量使用双括号法来进行初始化,就会产生大量的class文件
再使用javap看下内容
javap -v MapTest$1.class
> javap -v MapTest$2.class Classfile /D:/mysoftware/javabase/target/classes/org/example/MapTest$2.class Last modified 2021-11-9; size 657 bytes MD5 checksum 0be8f98cffeb87ca4a8fa953e4ef7b95 Compiled from "MapTest.java" class org.example.MapTest$2 extends java.util.ArrayListminor version: 0 major version: 52 flags: ACC_SUPER Constant pool: #1 = Fieldref #5.#23 // org/example/MapTest$2.this$0:Lorg/example/MapTest; #2 = Methodref #6.#24 // java/util/ArrayList." ":()V #3 = Methodref #25.#26 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #4 = Methodref #5.#27 // org/example/MapTest$2.add:(Ljava/lang/Object;)Z #5 = Class #28 // org/example/MapTest$2 #6 = Class #29 // java/util/ArrayList #7 = Utf8 this$0 #8 = Utf8 Lorg/example/MapTest; #9 = Utf8 #10 = Utf8 (Lorg/example/MapTest;)V #11 = Utf8 Code #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 InnerClasses #16 = Utf8 Lorg/example/MapTest$2; #17 = Utf8 Signature #18 = Utf8 Ljava/util/ArrayList ; #19 = Utf8 SourceFile #20 = Utf8 MapTest.java #21 = Utf8 EnclosingMethod #22 = Class #30 // org/example/MapTest #23 = NameAndType #7:#8 // this$0:Lorg/example/MapTest; #24 = NameAndType #9:#31 // " ":()V #25 = Class #32 // java/lang/Integer #26 = NameAndType #33:#34 // valueOf:(I)Ljava/lang/Integer; #27 = NameAndType #35:#36 // add:(Ljava/lang/Object;)Z #28 = Utf8 org/example/MapTest$2 #29 = Utf8 java/util/ArrayList #30 = Utf8 org/example/MapTest #31 = Utf8 ()V #32 = Utf8 java/lang/Integer #33 = Utf8 valueOf #34 = Utf8 (I)Ljava/lang/Integer; #35 = Utf8 add #36 = Utf8 (Ljava/lang/Object;)Z { final org.example.MapTest this$0; //持有外部类的引用 descriptor: Lorg/example/MapTest; flags: ACC_FINAL, ACC_SYNTHETIC org.example.MapTest$2(org.example.MapTest); descriptor: (Lorg/example/MapTest;)V flags: Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:Lorg/example/MapTest; 5: aload_0 6: invokespecial #2 // Method java/util/ArrayList." ":()V 9: aload_0 10: iconst_1 11: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 14: invokevirtual #4 // Method add:(Ljava/lang/Object;)Z 17: pop 18: aload_0 19: iconst_2 20: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 23: invokevirtual #4 // Method add:(Ljava/lang/Object;)Z 26: pop 27: return LineNumberTable: line 14: 0 line 16: 9 line 17: 18 line 18: 27 LocalVariableTable: Start Length Slot Name Signature 0 28 0 this Lorg/example/MapTest$2; 0 28 1 this$0 Lorg/example/MapTest; } Signature: #18 // Ljava/util/ArrayList ; SourceFile: "MapTest.java" EnclosingMethod: #22.#0 // org.example.MapTest InnerClasses: #5; //class org/example/MapTest$2
看到了这里,发现内部类持有外部类的引用,并且在构造块中进行初始化
如果我们这样
public class MapTest {
private Map get(){
return new HashMap(){
{
put("1","one");
put("2","two");
put("3","tree");
}
};
}
public static void main(String[] args) {
System.out.println(new MapTest().get().getClass().getName());
}
}
"C:Program FilesJavajdk1.8.0_121binjava.exe" -Dvisualvm.id=1006217305728700 "-javaagent:D:Program FilesJetBrainsIntelliJ IDEA 2020.1.3libidea_rt.jar=61834:D:Program FilesJetBrainsIntelliJ IDEA 2020.1.3bin" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_121jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_121jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_121jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_121jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_121jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_121jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_121jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_121jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_121jrelibextnashorn.jar;C:Program FilesJavajdk1.8.0_121jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_121jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_121jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_121jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_121jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_121jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_121jrelibjce.jar;C:Program FilesJavajdk1.8.0_121jrelibjfr.jar;C:Program FilesJavajdk1.8.0_121jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_121jrelibjsse.jar;C:Program FilesJavajdk1.8.0_121jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_121jrelibplugin.jar;C:Program FilesJavajdk1.8.0_121jrelibresources.jar;C:Program FilesJavajdk1.8.0_121jrelibrt.jar;D:mysoftwarejavabasetargetclasses" org.example.MapTest org.example.MapTest$3 Process finished with exit code 0
可以看到返回的是内部类,如果此内部类被其他类所使用,那么,根据GC的可达性分析算法,外部类是没有办法被回收的,这便会产生内存泄漏问题
总结如果工程中大量使用双括号法来进行初始化,就会产生大量的class文件
可能会造成内存泄漏



