起因
有一种初始化方法,写起来很爽,比如这样:
Map<String,String> map = new HashMap<String,String>(){
{
put("name","timeliar");
put("name","timeliar");
}
};
这种写法千万注意,会造成内存泄露,原因以下再说
然而这种写法有坑,坑来自 Java 基础的不扎实。
原本天真的以为,这种写法是直接初始化一个类,然后直接调用对象的方法。
但是用反射提取字段信息的时候,出现了问题
@Test
public void testInner() throws IllegalAccessException {
BarUserLink link = new BarUserLink(){
{
setBarId(1L);
setUserId(1L);
}
};
BarUserLink link1 = new BarUserLink();
link.setBarId(1L);
link.setUserId(1L);
System.out.println("匿名内部类反射取字段");
Field[] fields = link.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.getName() + " ==> " + field.get(link));
}
System.out.println("正常new对象反射取字段");
fields = link1.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.getName() + " ==> " + field.get(link));
}
}
输出:
匿名内部类反射取字段
this$0 ==> date.timeliar.graduation.web.TestQuickInit@27abe2cd
正常new对象反射取字段
barId ==> 1
userId ==> 1
解释
查阅了相关资料 (主要是 Google) 之后,尝试得出一个结论,这种写法返回的其实是一个继承了该类的匿名内部类,
实验检验:
@Test
public void innerClass(){
BarUserLink link = new BarUserLink(){
{
System.out.println("this -> "+this.getClass());
System.out.println("super -> "+this.getClass().getSuperclass());
}
};
}
输出:
this -> class date.timeliar.graduation.web.TestQuickInit$4
super -> class date.timeliar.graduation.web.model.po.BarUserLink
真相大白,初始化的时候两个大括号的意义也明了了,外部括号是定义了类边界,内部的是普通代码块,作为作用域。
因为是内部类,所以返回出对象之后,如果该对象被一直使用,那么外部类的对象将不会被会回收,这就造成了内存泄露
返回的本质是一个继承了所需要类的内部类,虽然是子类,但是并不能代表父类的一切,比如反射取字段的时候