11

例如,如果我有一个变量int x = 1,并且我在主线程中声明了一个 runnable,并且我想将 x 传递给 runnable 的run()方法,则必须声明它final。为什么?

final int x = 0;//<----must be final...
private class myRun implements Runnable {

    @Override
    public void run() {
        x++;//
    }

}
4

3 回答 3

11

因为这就是语言规范所说的。根据 Guy Steele的说法,这种选择背后的基本原理是程序员希望int x = 0方法中的声明会导致堆栈分配存储,但是如果您可以new myRun()从方法返回 a (或者让 amyRun在函数返回之后持久化)并且您之后可以对其进行修改,然后x必须进行堆分配以获得您期望的语义。

他们本可以做到这一点,事实上其他语言已经这样做了。但是 Java 设计者决定改为要求您标记xfinal以避免要求实现堆分配看起来像堆栈分配的存储。

(我应该注意:这不是特定于Runnable. 它适用于任何匿名内部类。)

于 2012-07-11T02:42:53.210 回答
10

因为如果可以更改它们,可能会导致很多问题,请考虑以下问题:

public void count()
{
    int x;

    new Thread(new Runnable()
    {
        public void run()
        {
            while(x < 100)
            {
                x++;
                try
                {
                    Thread.sleep(1000);
                }catch(Exception e){}
            }
        }
     }).start();

     // do some more code...

     for(x = 0;x < 5;x++)
         for(int y = 0;y < 10;y++)
             System.out.println(myArrayElement[x][y]);
 }

这是一个粗略的例子,但您可以看到很多无法解释的错误可能发生在哪里。这就是为什么变量必须是最终的。这是针对上述问题的简单解决方法:

public void count()
{
    int x;

    final int w = x;

    new Thread(new Runnable()
    {
        public void run()
        {
            int z = w;

            while(z < 100)
            {
                z++;
                try
                {
                    Thread.sleep(1000);
                }catch(Exception e){}
            }
        }
     }).start();

     // do some more code...

     for(x = 0;x < 5;x++)
         for(int y = 0;y < 10;y++)
             System.out.println(myArrayElement[x][y]);
 } 

如果您想要更完整的解释,它有点像同步。Java 想要阻止您从多个线程中引用一个对象。这里是关于同步的一点点:

希望这有帮助!

于 2012-07-11T02:45:30.917 回答
2

多线程的一个大“问题”,也是使用它的全部原因,是同时发生了多件事。突然之间,您的线程访问的任何不是线程本地的变量的值都可以随时更改。因此,您可能只是使用以下代码打印数字 1-10:

int x = 0;  //supposing that this was allowed to be non-final...
   private class myRun implements Runnable{

    @Override
    public void run() {
        for (int i=0; i<10; i++ ) {
            System.Out.Println( x++ );
        }
    }
}

但实际上,如果该类中的其他代码更改了 x 的值,您最终可能会打印 230498 - 230508。x 的值可能会在循环中间发生变化。如果您不能依赖x某个值或保留您之前分配给它的值,那么在您的代码中使用它是徒劳的。如果变量的内容可以随时更改,为什么要使用变量?

Java 不仅完全禁止您使用它,还要求您制作它final。您可以“承诺”永远不会更改x另一个线程的值,但是为什么不final首先做到这一点并让编译器帮助您呢?当然,您只能访问分配给 的初始值x,但是仅仅能够访问变量的初始值总比不能使用它要好,这将有效地切断线程利用其余数据的能力你的班。

于 2012-07-11T02:50:50.450 回答