2010-05-09 54 views
7

一个简单的服务器的经典例子:方法中最终变量声明的作用是什么?

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

回答

13

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

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

+0

非常感谢。 – Finbarr 2010-05-09 20:28:02

2

你需要声明它是最终的,不仅应该。否则,编译器不能在匿名Runnable类实现中使用它。

0

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

由于connection是一个局部变量,所以不可能在线程之间共享它。由于它不在线程之间共享,因此您需要将它设置为final,因此它不是一个局部变量(它可以看作更像一个常量值)。

0

它并不打算解决ConcurrentModificationException。任何在方法嵌套类(例如匿名内部类)中使用的局部变量都必须声明为final。从上周在这里看到一个类似的讨论:

method local innerclasses accessing the local variables of the method

其实在线程的情况下,有一个小的贡献,在这里线程安全;线程之间的最终变量将没有可视性问题。但是,这并不能保证线程安全。

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为最后,当你到达b.goo(),它会指向垃圾,因为是c。将垃圾收集 - 结束后的局部变量方法调用。

+0

你是否暗示最终变量不会被垃圾收集?这是错误的。 – Pindatjuh 2010-05-09 20:11:00

+0

问题不是垃圾收集。无论如何,内部类对变量有很强的参考。语言设计者可以允许在内部类中使用非最终局部变量。然而,这可能会让开发人员感到困惑(参见Michaels的回应),因为不清楚内部课程是否能够看到参考书目的分配。 – 2010-05-09 20:12:50

+0

@Pindatjuh - 我暗示,当方法结束时,最终变量不会被垃圾收集*。 @Eyal - 你说得对,语言本来可以更好的设计,等等。但是,目前的设计是我们所拥有的... – 2010-05-10 07:58:57

2

声明一个方法变量final意味着它的值不能改变;它只能设置一次。这在这方面如何适用?

我已经知道这个限制与匿名类一段时间,但我从来没有完全明白为什么。我发现到目前为止,没有其他人真的做出任何回应。一些谷歌搜索出现在下面,我认为解释它很好。

匿名局部类可以使用本地 变量,因为编译器 自动给出类 私有实例字段来保存类使用的每个局部变量的副本 。 编译器还向每个构造函数添加隐藏的 参数到 初始化这些自动创建的 私有字段。因此,本地类 实际上并不访问本地 变量,而仅仅是它们自己的私人 副本。 正确工作的唯一方法是如果本地 变量被声明为最终的,以便 他们保证不会更改。 有了这个保证, 本地类是保证它的 内部副本的变量 准确地反映实际本地 变量。

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

肯定不会明显,我觉得编译器真的应该从开发商难言之隐。

+0

很棒的回答,技术性很强。我喜欢添加**为什么**它需要这种保证:因为内部类可能会被GCed,并且如果稍后局部变量发生变化并且需要将变化反映到内部类实例中,那么就是GCed:一个问题如果变量是最终的,则避免。 – Pindatjuh 2010-05-10 16:09:49

+0

'声明方法变量final意味着它的值不能改变;它只能设置一次。不是真的。这意味着对象的**引用**不能被改变。价值依然可以改变几次。 (除非对象是原始的) – mercury0114 2016-04-18 07:29:15

相关问题