当从实例方法返回没有引用其封闭类的匿名类时,它会引用this
.为什么?
请考虑以下代码:
package so; import java.lang.reflect.Field; public class SOExample { private static Object getAnonymousClassFromStaticContext() { return new Object() { }; } private Object getAnonymousClassFromInstanceContext() { return new Object() { }; } public static void main(String[] args) throws NoSuchFieldException, SecurityException { Object anonymousClassFromStaticContext = getAnonymousClassFromStaticContext(); Object anonymousClassFromInstanceContext = new SOExample().getAnonymousClassFromInstanceContext(); Field[] fieldsFromAnonymousClassFromStaticContext = anonymousClassFromStaticContext.getClass().getDeclaredFields(); Field[] fieldsFromAnonymousClassFromInstanceContext = anonymousClassFromInstanceContext.getClass().getDeclaredFields(); System.out.println("Number of fields static context: " + fieldsFromAnonymousClassFromStaticContext.length); System.out.println("Number of fields instance context: " + fieldsFromAnonymousClassFromInstanceContext.length); System.out.println("Field from instance context: " + fieldsFromAnonymousClassFromInstanceContext[0]); } }
这是输出:
Number of fields static context: 0 Number of fields instance context: 1 Field from instance context: final so.SOExample so.SOExample$2.this$0
每种方法虽然看似调用相同的代码,但却在做一些不同的事情.在我看来,实例方法返回一个嵌套类,而静态方法返回一个静态嵌套类(作为静态成员,它显然不能有引用this
).
鉴于没有提及封闭类的事实,我看不出这方面的好处.
幕后发生了什么?
匿名/内部类背后有一个设计原则:内部类的每个实例都属于外部类的实例.
抛弃对内部类的引用会改变垃圾收集的行为:它的实现方式,只要内部类存活,外部类就不能被垃圾收集.
这支持了在没有外部类的情况下内部类不能存在的想法.
应用程序可能依赖于此行为,例如通过创建临时文件并在析构函数中删除它.这样,只有当所有内部类都消失时才会删除该文件.
这也意味着无法更改当前行为,因为更改它可能会破坏现有应用程序.
因此,当您不需要引用时,应始终将内部类标记为静态,因为这可能会导致一些不错的内存泄漏.
编辑: 我想说的内容的例子(抱歉可怕的代码质量):
class Ideone { static Object[] objects = new Object[2]; public static void main (String[] args) throws java.lang.Exception { M1(); M2(); System.gc(); } static void M1() { objects[0] = new Foo().Bar(); } static void M2() { objects[1] = new Foo().Baz(); } } class Foo { static int i = 0; int j = i++; public Foo() { System.out.println("Constructed: " + j); } Object Bar() { return new Object() { }; } static Object Baz() { return new Object() { }; } protected void finalize() throws Throwable { System.out.println("Garbage collected " + j); } }
输出:
构造:0
构造:1
垃圾收集1
如您所见,第一个Foo 不是垃圾收集,因为仍然存在"内部实例".要使此行为起作用,内部类需要引用.
当然,这可能也有不同的实现.但我想说,保持参考是一个有目的的设计决策,因此"内部实例"不会比它的父母更活跃.
BTW:Java语言引用非常隐秘地说明了这一点(对于不访问外部类的内部类,没有例外):
类或接口O的直接内部类C的实例i与O的实例相关联,称为i的直接封闭实例.在创建对象时确定对象的直接封闭实例(如果有)(第15.9.2节).