所有最终变量都是由匿名类捕获的吗?

ajb*_*ajb 17 java anonymous-class

我以为我知道答案,但是在一小时左右的搜索后我找不到任何确认.

在这段代码中:

public class Outer {

    // other code

    private void method1() {
        final SomeObject obj1 = new SomeObject(...);
        final SomeObject obj2 = new SomeObject(...);
        someManager.registerCallback(new SomeCallbackClass() {
            @Override
            public void onEvent() {
                 System.out.println(obj1.getName());
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

假设registerCallback将其参数保存在某处,以便匿名子类的对象将存活一段时间.显然,这个对象必须保持一个引用,obj1以便onEvent在调用时可以工作.

但是鉴于该对象没有使用obj2,它是否仍然保持引用obj2,以便obj2在对象存在时不能进行垃圾收集?我的印象是所有可见final(或有效的最终)局部变量和参数都被捕获,因此只要对象存活就无法进行GC,但我找不到任何说明这种或那种方式的东西.

它是依赖于实现的吗?

JLS中有一节可以解答这个问题吗?我无法在那里找到答案.

And*_*ner 12

关于匿名类应如何从其封闭范围中捕获变量,语言规范几乎没有什么可说的.

我能找到的语言规范中唯一特别相关的部分是JLS Sec 8.1.3:

使用但未在内部类中声明的任何局部变量,形式参数或异常参数必须声明为final或者是有效的final(§4.12.4),否则在尝试使用时会发生编译时错误.)

(匿名类是内部类)

它没有指定匿名类应该捕获哪些变量,或者应该如何实现捕获.

我认为从中推断实现不需要捕获内部类中未引用的变量是合理的; 但它并没有说它们不能.


And*_*eas 10

只有obj1被捕获.

从逻辑上讲,匿名类是作为普通类实现的,如下所示:

class Anonymous1 extends SomeCallbackClass {
    private final Outer _outer;
    private final SomeObject obj1;
    Anonymous1(Outer _outer, SomeObject obj1) {
        this._outer = _outer;
        this.obj1 = obj1;
    }
    @Override
    public void onEvent() {
         System.out.println(this.obj1.getName());
    }
});
Run Code Online (Sandbox Code Playgroud)

请注意,匿名类始终是内部类,因此它将始终保持对外部类的引用,即使它不需要它也是如此.我不知道编译器的后期版本是否已经优化了,但我不这么认为.这是内存泄漏的潜在原因.

使用它变成:

someManager.registerCallback(new Anonymous1(this, obj1));
Run Code Online (Sandbox Code Playgroud)

正如可以看到,参考值obj1复制(通过按值).

技术上没有理由obj1成为最终的,无论是声明的final还是有效的(Java 8+),除非它不是并且你改变了值,副本不会改变,导致错误,因为你期望值改变鉴于复制是隐藏的动作.为了防止程序员混淆,他们认为obj1必须是最终的,所以你永远不会对这种行为感到困惑.

  • 注意,无论是否使用,都会捕获封闭实例的"this". (3认同)
  • 值得一提的是,自Java 8以来,必须明确声明`final`的限制已被取消.JLS现在谈论"有效'最终'"变量. (2认同)