例如,如果我有一个变量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++;//
}
}
因为这就是语言规范所说的。根据 Guy Steele的说法,这种选择背后的基本原理是程序员希望int x = 0
方法中的声明会导致堆栈分配存储,但是如果您可以new myRun()
从方法返回 a (或者让 amyRun
在函数返回之后持久化)并且您之后可以对其进行修改,然后x
必须进行堆分配以获得您期望的语义。
他们本可以做到这一点,事实上其他语言已经这样做了。但是 Java 设计者决定改为要求您标记x
为final
以避免要求实现堆分配看起来像堆栈分配的存储。
(我应该注意:这不是特定于Runnable
. 它适用于任何匿名内部类。)
因为如果可以更改它们,可能会导致很多问题,请考虑以下问题:
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 想要阻止您从多个线程中引用一个对象。这里是关于同步的一点点:
希望这有帮助!
多线程的一个大“问题”,也是使用它的全部原因,是同时发生了多件事。突然之间,您的线程访问的任何不是线程本地的变量的值都可以随时更改。因此,您可能只是使用以下代码打印数字 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
,但是仅仅能够访问变量的初始值总比不能使用它要好,这将有效地切断线程利用其余数据的能力你的班。