简介
我们在开发中偶尔会遇到一些小伙伴使用双括号“{{}}”来进行map或者list的初始化,类似下面示例这样
示例
public class MapTest {
Map<String,Object> map = new HashMap<String,Object>(){
{
put("1","one");
}
};
List<Integer> list = new ArrayList<Integer>(){
{
add(1);
add(2);
}
};
}
这样初始化看起来确实挺爽!通俗,简介,易懂 但是越好看的往往越容易踩坑 我们看看编译之后的class文件 发现除了MapTest.class还有两个class文件。 我们把MapTest$1直接放进IDEA中进行反编译,发现MapTest$1继承了hashmap的
class MapTest$1 extends HashMap<String, Object> {
MapTest$1(MapTest this$0) {
this.this$0 = this$0;
this.put("1", "one");
}
}
同理MapTest$2也是这样
class MapTest$2 extends ArrayList<Integer> {
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.ArrayList<java.lang.Integer>
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
{
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
5: aload_0
6: invokespecial
9: aload_0
10: iconst_1
11: invokestatic
14: invokevirtual
17: pop
18: aload_0
19: iconst_2
20: invokestatic
23: invokevirtual
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:
SourceFile: "MapTest.java"
EnclosingMethod:
InnerClasses:
看到了这里,发现内部类持有外部类的引用,并且在构造块中进行初始化 如果我们这样
public class MapTest {
private Map<String,Object> get(){
return new HashMap<String,Object>(){
{
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 Files\Java\jdk1.8.0_121\bin\java.exe" -Dvisualvm.id=1006217305728700 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar=61834:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_121\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar;D:\mysoftware\javabase\target\classes" org.example.MapTest
org.example.MapTest$3
Process finished with exit code 0
可以看到返回的是内部类,如果此内部类被其他类所使用,那么,根据GC的可达性分析算法,外部类是没有办法被回收的,这便会产生内存泄漏问题
总结
如果工程中大量使用双括号法来进行初始化,就会产生大量的class文件 可能会造成内存泄漏
|