为什么方法还没有结束,但局部变量不再存在?

时间:2017-09-01 08:58:42

标签: java

我正在学习核心Java'本书讲述了本地内部类如何从外部方法访问变量。虽然,我对某些事感到困惑。

这本书提供了一段代码:

public void start(int interval, boolean beep)
{
    class TimePrinter implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            System.out.println("At the tone, the time is " + new Date());
            if (beep) Toolkit.getDefaultToolkit().beep();
        }
    }

    ActionListener listener = new TimePrinter();
    Timer t = new Timer(interval, listener);
    t.start();
}

这本书说明了控制流程如何:

  1. 调用start方法。
  2. listener已初始化。
  3. listener引用传递给Timer构造函数。 此时,beep方法的start参数变量不再存在。
  4. 稍后,actionPerformed方法会执行if (beep) ...
  5. 我只是为什么哔哔声不再存在而感到困惑?我认为外部启动方法仍然没有结束},局部变量仍然可以存在......

2 个答案:

答案 0 :(得分:2)

编译器知道beep 有效地最终,因此可以假设它不会改变。然后它将安排构建本地对象,如:

    class TimePrinter implements ActionListener {
        // Capture the local variable in this instance.
        boolean instanceBeep = beep;
        public void actionPerformed(ActionEvent event) {
            System.out.println("At the tone, the time is " + new Date());
            if (instanceBeep) Toolkit.getDefaultToolkit().beep();
        }
    }

但是,这仅在beep参数有效最终时才有效,即它不会更改,甚至可以在不破坏代码的情况下声明final beep

要确认,只需尝试以下内容:

public void start(int interval, boolean beep) {
    class TimePrinter implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            System.out.println("At the tone, the time is " + new Date());
            // Error:(17, 21) java: local variables referenced from an inner class must be final or effectively final
            if (beep) Toolkit.getDefaultToolkit().beep();
        }
    }
    // Add this to see the compiler working it out.
    beep = false;
    ActionListener listener = new TimePrinter();
    Timer t = new Timer(interval, listener);
    t.start();
}

并且看到现在不允许访问beep参数,因为它不再是有效的最终

请参阅this siteeffectively final上的官方Oracle文档。

答案 1 :(得分:1)

如果这是本书的确切引用,那么这本书就错了。你是正确的beep函数的start参数在结束括号}之前不会超出范围。使用本地类没有引起特殊行为。

在这种情况下,一个变量据说是"captured"

变量需要被捕获final or "effectively final" - 即永远不能重新分配。

在您的情况下,您不会修改beep的值,因此它实际上是最终的并且有资格被捕获。

创建TimePrinter对象时,beep的值将复制到其内部状态。这是因为变量beep在需要使用时可能已超出范围。

启动计时器,start方法结束。原始beep现已超出范围。现在将调用每隔几秒钟(取决于intervalactionPerformed。原始的beep 已经超出范围,但我们复制了它的值,因此我们的函数仍然能够按照我们的预期执行。

这几乎是作者试图表达的想法。