Java 方法中最终变量声明的效果如何?

Java 方法中最终变量声明的效果如何?,java,concurrency,methods,final,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(); Runnabl

简单服务器的经典示例:

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();
      }
   }
}

为什么要将
套接字
声明为
最终
?这是因为处理请求的新
线程可能会引用方法中的
套接字
变量,并导致某种
并发修改异常

您需要声明它为final,而不仅仅是应该。否则,编译器将无法在匿名可运行类实现中使用它。

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


由于
connection
是一个局部变量,因此无法在线程之间共享它。由于它不是在线程之间共享的,所以您需要将它设置为final,因此它是否是局部变量并不重要(它更像是一个常量)。

在这种情况下,变量必须是final,才能在匿名
可运行的
实现中使用


这是因为当变量已经超出范围并因此消失时,该对象将存在。对象获取变量的副本。为了隐藏这一点,变量必须是final,这样就没有人可以期望一个副本中的更改对另一个副本可见。

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

实际上,对于线程,这里对线程安全性的贡献很小;线程之间的最终变量将没有可见性问题。但是,这根本不能保证线程安全。

请考虑以下示例:

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不是final,当您到达
b.goo()
时,它将指向垃圾,因为c将被垃圾收集—在方法调用结束后是一个局部变量。

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

我知道匿名类的这种限制已经有一段时间了,但我一直不太明白为什么。从目前的回答来看,没有其他人真正做到这一点。一些谷歌搜索出现在下面,我认为这很好地解释了这一点

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

归功于:


当然不明显,我认为编译器确实应该对开发人员隐藏一些东西。

您是否暗示最终变量不会被垃圾收集?这是错误的。问题不在于垃圾收集。内部类无论如何都有一个对变量的强引用。语言设计者可以允许在内部类中使用非最终局部变量。但是,这可能会让开发人员感到困惑(请参阅Michaels response),因为不清楚引用的赋值是否被内部类看到。@Pindatjuh-我的意思是,当方法结束时,最终变量不会被垃圾收集@Eyal-你是对的,语言本来可以设计得更好,等等。但是,当前的设计就是我们所拥有的……它们只有在引用计数为零时才是GCed,这可能是在方法的末尾。在您的示例中,仅当返回的
B
为时,
c
才为GCed,因为
goo()
中存在引用可能性。另外,
c
需要初始化为某个值(null或instance),因为
final
变量没有默认值。答案很好,有点技术性。我想补充一下为什么它需要这种保证:因为内部类可能是GCed的,如果局部变量以后发生更改,那么更改需要反映到内部类实例中,这就是GCed:如果变量是final,就避免了一个问题。
声明方法变量final意味着它的值不能更改;只能设置一次
。不是真的。这意味着对对象的引用不能更改。该值仍然可以更改多次。(除非对象是基本体)