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

    內部類訪問局部變量的情況也沒少遇到。也一直知道要給變量加個final修飾符,不然通過不了編譯。但一直也沒深究過爲什麼要加。昨天好奇的上網查了下,並翻閱了下相關的書籍(Core Java 8th),終於算是搞明白了,在這裏簡單說明下。

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 }
 


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

爲了說明內部類訪問局部變量爲什麼要加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 } 


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

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

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


如果beep變量不被標註爲final,那麼就意味着我們可以隨時修改beep的值。假設我們在創建了TimePrinter對象後修改了beep的值,那麼這時我們的內部類所看到的beep的值還是之前通過構造函數傳遞進去的老值,這樣就導致內部類和外部函數對beep值“認識”的不一致。所以final關鍵字的目的就是爲了保證內部類和外部函數對變量“認識”的一致性。

結束語:這個內部類final的問題從最開始學JAVA時就遇到了,期間也有想過爲什麼要加,但最終都沒有深究,就理所當然的認爲是規定了。其實這是一個很不好的習慣。學習東西就要“知其然,知其所以然”,一知半解最是害人。希望大家都可以引以爲鑑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章