Java內部類訪問局部變量時的final問題

說先咱們來看一段示例代碼:
java

01 public void start(int interval, final boolean beep) {
02
03     // Inner Class
04     class TimePrinter implements ActionListener {
05
06         @Override
07         public void actionPerformed(ActionEvent event) {
08             Date now = new Date();
09             System.out.println(「At the tone, the time is 「 + now);
10             if (beep) {
11                 Toolkit.getDefaultToolkit().beep();
12             }
13         }
14     }
15     //
16     ActionListener listener = new TimePrinter();
17     Timer t = new Timer(interval, listener);
18     t.start();
19 }
ide

 

咱們在start函數中定義了一個內部類TimePrinter,其中訪問了函數的局部變量beepwordpress

爲了說明內部類訪問局部變量爲何要加final關鍵字,咱們先來看一下JAVA對內部類的實現。函數

假設上述代碼中start函數所在的類的名稱爲TalkingClock。則編譯上述代碼的時候,JAVA編譯器會把TimePrinter內部類編譯爲一個獨立的class文件。其形式以下(類名爲:外部類$內部類):學習

01 class TalkingClock$1TimePrinter {
02     // 添加的構造函數,參數爲外部類對象的引用和該內部類訪問的局部變量的引用(這裏爲boolean類型)
03     TalkingClock$1TimePrinter(TalkingClock, boolean);
04     // 內部類原有的函數
05     public void actionPerformed(java.awt.event.ActionEvent);
06     // 局部變量的引用
07     final boolean val$beep;
08     // 外部類對象的引用
09     final TalkingClock this$0;
10 }this

 

經過上述類的定義,咱們能夠看出內部類在構造的時候,會被編譯器自動傳入外部類對象的一個引用,同時也會傳入內部類訪問的局部變量的引用,這也就解釋了內部類對象爲何能夠訪問外部類的成員變量和函數還有局部變量了。可是因爲這些工做是在編譯時進行的,JAVA虛擬機並無什麼所謂的內部類的概念,在JAVA虛擬機看來,該內部類和外部類是兩個獨立的class文件。咱們知道,一個類的私有函數和成員變量是不能被其餘類訪問的。那麼內部類又是如何訪問外部類的私有成員變量和函數呢?spa

咱們假設外部類中有一個私有的int型的變量counter。咱們想在內部類中訪問它。其實在編譯的時候,爲了內部類能夠訪問外部類的私有變量,JAVA 編譯器還偷偷作了一些額外的工做。編譯器除了上文提到的會生成一個內部類類,同時還會修改咱們的外部類代碼。其修改以下:orm

1 class TalkingClock {
2     // 編譯器自動添加的函數,用來訪問私有成員變量counter
3     static int access$0(TalkingClock);
4     // 原有的函數
5     public void start();
6     // 私有成員變量
7     private int counter
8 }
對象


能夠看出,爲了訪問counter私有成員變量,編譯器偷偷的爲咱們添加了一個access$0的靜態函數,它接收一個TalkingClock對象的引用,並返回該對象內的coutner的值。而且編譯器會把咱們在內部類中用到counter的地方都替換爲TalkingClock.access$0(this$0)get

好了,如今咱們已經瞭解了內部類的實現機制,那咱們最後來看一下訪問局部變量爲何要求局部變量添加final關鍵字。

仍是以咱們最開始的那個start函數爲例。咱們來看一下該函數的可能的執行過程:

若是beep變量不被標註爲final,那麼就意味着咱們能夠隨時修改beep的值。假設咱們在建立了TimePrinter對象後修改了beep的值,那麼這時咱們的內部類所看到的beep的值仍是以前經過構造函數傳遞進去的老值,這樣就致使內部類和外部函數對beep值「認識」的不一致。因此final關鍵字的目的就是爲了保證內部類和外部函數對變量「認識」的一致性。

結束語:這個內部類final的問題從最開始學JAVA時就遇到了,期間也有想過爲何要加,但最終都沒有深究,就理所固然的認爲是規定了。其實這是一個很很差的習慣。學習東西就要「知其然,知其因此然」,只知其一;不知其二最是害人。但願你們均可以引覺得鑑。

轉載自 http://lidejiasw.wordpress.com/2011/06/08/java%E5%86%85%E9%83%A8%E7%B1%BB%E8%AE%BF%E9%97%AE%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E6%97%B6%E7%9A%84final%E9%97%AE%E9%A2%98/

相關文章
相關標籤/搜索