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

2022-09-03 01:05:31

我以为我知道这个问题的答案,但经过一个小时左右的搜索,我找不到任何确认。

在此代码中:

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());
            }
        });
    }
}

假设 将其参数保存在某个位置,以便匿名子类的对象将存在一段时间。显然,此对象必须维护对 的引用,以便在调用它时可以正常工作。registerCallbackobj1onEvent

但是,假设对象不使用,它是否仍然保持对 的引用,因此在对象存活期间不能被垃圾回收?我的印象是,所有可见的(或实际上是最终的)局部变量和参数都被捕获了,因此只要对象还活着,就无法进行GC'ed,但我找不到任何以某种方式表达的东西。obj2obj2obj2final

它是否依赖于实现?

JLS中是否有一节可以回答这个问题?我无法在那里找到答案。


答案 1

语言规范对匿名类应如何从其封闭作用域中捕获变量几乎没有什么可说的。

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

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

(匿名类是内部类)

它没有指定有关匿名类应捕获哪些变量或应如何实现该捕获的任何内容。

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


答案 2

仅捕获。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());
    }
});

请注意,匿名类始终是内部类,因此它将始终维护对外部类的引用,即使它不需要它。我不知道编译器的更高版本是否已经优化了这一点,但我不这么认为。这是内存泄漏的潜在原因。

它的使用变成:

someManager.registerCallback(new Anonymous1(this, obj1));

如您所见,的参考值被复制(按值传递)。obj1

从技术上讲,无论是声明的还是有效的最终(Java 8+),都没有理由成为最终版本,除非它不是并且您更改了值,则副本不会更改,从而导致错误,因为您期望该值会更改,因为复制是一个隐藏的操作。为了防止程序员混淆,他们决定这必须是最终的,所以你永远不会对这种行为感到困惑。obj1finalobj1


推荐