2010-05-09 94 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

相關問題