如果我有一个变量int x = 1
,比如说,我在主线程中声明了一个runnable,并且我想将x传递给runnable的run()
方法,则必须将其声明为{{1} }。为什么呢?
final
答案 0 :(得分:10)
因为这就是语言规范所说的。 According to Guy Steele,这个选择背后的基本原理是程序员会期望方法中的声明int x = 0
导致堆栈分配的存储,但是如果你可以从方法中返回new myRun()
(或者否则让myRun
持续超过函数的返回值,然后你可以修改它,然后x
必须被堆分配而不是你想要的语义。
他们本可以做到这一点,事实上其他语言就是这样做的。但Java设计者决定要求将x
标记为final
,以避免要求实现堆分配看起来像堆栈分配的存储。
(我应该注意:这不是Runnable
特有的。它适用于任何匿名内部类。)
答案 1 :(得分: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希望阻止您从多个线程引用一个Object。这里有一些关于同步的内容:
希望这有帮助!
答案 2 :(得分: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
的初始值,但只是能够访问变量的初始值比完全不能使用它更好,这将有效地切断线程利用它的能力。来自其他同学的数据。