Java中爲什麼內部類對外部類局部變量有引用時,外部類局部變量一定要有final?

如果沒有final的話,那麼內部類使用的外部類局部變量要用哪個值呢?
使用內部類經常是用在Listener這樣的。
而Listener中的監聽函數被調用時,大部分情況,new內部類所在的函數早就執行完了,此時外部類局部變量早就不存在了。

而加上final後,編譯器是這樣處理內部類的。如果這個外部局部變量是常量,則在內部類代碼中直接用這個常量。如果是類的實例,則編譯器將產生一個內部類的構造參數,將這個final變量傳到內部類裏,這樣即使外部局部變量無效了,還可以使用。

例如:
import java.util.*;

public class Test{
    public void run() {
        final List a=new ArrayList();
        class B{
            public int c;
            public void run(){
                List b;
                b=a;
                System.out.println(b);
            }
        };

        B b=new B();

        b.c=0;
    }
}

使用javap -private Test$1B將看到
Compiled from "Test.java"
class Test$1B extends java.lang.Object{
    public int c;
    private final java.util.List val$a;
    private final Test this$0;
    Test$1B(Test,java.util.List );
    public void run();
}

而下面是使用常量的例子:
public class Test{
    public void run() {
        final int a=100;
        class B{
            public int c;
            public void run(){
                int b;
                b=a;
                System.out.println(b);
            }
        };

        B b=new B();

        b.c=0;
    }
}

javap -private Test$1B:

Compiled from "Test.java"
class Test$1B extends java.lang.Object{
    public int c;
    private final Test this$0;
    Test$1B(Test);
    public void run();
}

javap -private -c Test$1B:

Compiled from "Test.java"
class Test$1B extends java.lang.Object{
public int c;

private final Test this$0;

Test$1B(Test);
Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   aload_1
   6:   putfield        #2; //Field this$0:LTest;
   9:   return

public void run();
Code:
   0:   bipush 100
   2:   istore_1
   3:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:   iload_1
   7:   invokevirtual   #4; //Method java/io/PrintStream.println:(I)V
   10: return

}

另外引用別人的一段話:
內部類並不是直接調用方法傳進來的參數,而是內部類將傳進來的參數通過自己的構造器備份到了自己的內部,自己內部的方法調用的實際是自己的屬性而不是外部類方法的參數。
這樣理解就很容易得出爲什麼要用final了,因爲兩者從外表看起來是同一個東西,實際上卻不是這樣,如果內部類改掉了這些參數的值也不可能影響到原參 數,然而這樣卻失去了參數的一致性,因爲從編程人員的角度來看他們是同一個東西,如果編程人員在程序設計的時候在內部類中改掉參數的值,但是外部調用的時 候又發現值其實沒有被改掉,這就讓人非常的難以理解和接受,爲了避免這種尷尬的問題存在,所以編譯器設計人員把內部類能夠使用的參數設定爲必須是 final來規避這種莫名其妙錯誤的存在。

 

如果熟悉JavaScript的朋友,可以想想JavaScript的閉包特性。

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