关于匿名内部类的坑


起因

有一种初始化方法,写起来很爽,比如这样:

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

真相大白,初始化的时候两个大括号的意义也明了了,外部括号是定义了类边界,内部的是普通代码块,作为作用域。

因为是内部类,所以返回出对象之后,如果该对象被一直使用,那么外部类的对象将不会被会回收,这就造成了内存泄露
返回的本质是一个继承了所需要类的内部类,虽然是子类,但是并不能代表父类的一切,比如反射取字段的时候


评论
  目录