最终变量声明在方法中的作用是什么?

时间:2010-05-09 19:39:39

标签: java concurrency methods final

简单服务器的经典示例:

class ThreadPerTaskSocketServer {
   public static void main(String[] args) throws IOException {
      ServerSocket socket = new ServerSocket(80);
      while (true) {
          final Socket connection = socket.accept();
          Runnable task = new Runnable() {
              public void run() {
                 handleRequest(connection);
              }
          };
          new Thread(task).start();
      }
   }
}

为什么要将Socket声明为final?是因为处理请求的新Thread可以返回方法中的socket变量并导致某种ConcurrentModificationException

6 个答案:

答案 0 :(得分:13)

在这种情况下,变量必须是最终的,才能在匿名Runnable实施中使用。

这是因为当变量已经超出范围并因此消失时,该对象将存在。该对象获取变量的副本。为了隐藏它,变量必须是最终的,这样任何人都不能指望一个副本中的变化对另一个副本可见。

答案 1 :(得分:3)

考虑这个例子:

class A {
  B foo() {
    final C c;
    return new B() {
      void goo() {
        // do something with c
      }
    }
  }
}
// somewhere else in the code
A a = new A();
B b = a.foo();
b.goo();

如果c not final,当你到达b.goo()时,它会指向垃圾,因为那个c会被垃圾收集 - 一个方法调用结束后的一个局部变量

答案 2 :(得分:2)

你需要宣布它是最终的,不仅应该。没有它,编译器就无法在匿名的Runnable类实现中使用它。

答案 3 :(得分:2)

声明方法变量final意味着它的值不能改变;它只能设置一次。在这种情况下,这是如何适用的?

我已经知道有一段时间匿名课程的这种限制,但我从来都不明白为什么。我发现到目前为止,没有其他人能真正做到这一点。一些谷歌搜索出现在下面,我认为很好地解释它。

  

匿名本地类可以使用本地   变量因为编译器   自动给出一个班级   用于保存副本的私有实例字段   该类使用的每个局部变量。   编译器还添加了隐藏   每个构造函数的参数   初始化这些自动创建   私人领域。因此,一个本地班   实际上并不访问本地   变量,但仅仅是它自己的私有   他们的副本。唯一的方法就是这样   工作正确是如果当地的   变量被声明为final,所以   他们保证不会改变。   有了这个保证到位,   当地的班级确信它的   变量的内部副本   准确反映当地的实际情况   变量

信用: http://renaud.waldura.com/doc/java/final-keyword.shtml#vars

当然不是很明显,我认为编译器应该隐藏在开发人员身上。

答案 4 :(得分:0)

线程之间不共享局部变量。 (局部变量是激活记录的一部分,每个线程都有自己的激活记录)。

由于connection是局部变量,因此无法在线程之间共享它。由于它不在线程之间共享,因此你需要将其设为final,因此它是一个局部变量并不重要(它可以看作更像是一个常量值)。

答案 5 :(得分:0)

它无意解决ConcurrentModificationException。必须将方法嵌套类(例如匿名内部类)中使用的任何局部变量声明为final。请参阅上周的类似讨论:

method local innerclasses accessing the local variables of the method

实际上,对于线程,这里对线程安全有一点贡献;线程之间的最终变量不会出现可见性问题。但是,这并不能保证线程安全。