內部類中引用的變量必須要聲明爲final的原因

 

Cannot refer to a non-final variable mylis inside an inner class defined in a different method

查了下看到有人說原因如下:


局部匿名類在源代碼編譯後也是要生成對應的class文件的(一般會是A$1.class這種形式的文件),那麼這個二進制文件是獨立於其外圍類(A.class)的,就是說它無法知道A類中方法的變量。但是A$1.class又確實要訪問A類對應方法的局部變量的值。。。怎麼辦呢?於是乾脆就要求“匿名內部類調用的方法內局部變量必須爲final”,這樣A$1.class訪問A類方法局部變量部分就直接用常量來表示
這是一個編譯器設計的問題,如果你瞭解java的編譯原理的話很容易理解。
首先,內部類被編譯的時候會生成一個單獨的內部類的.class文件,這個文件並不與外部類在同一class文件中。
當外部類傳的參數被內部類調用時,從java程序的角度來看是直接的調用例如:
public void dosome(final String a,final int b){
class Dosome{public void dosome(){System.out.println(a+b)}};
Dosome some=new Dosome();
some.dosome();
}
從代碼來看好像是那個內部類直接調用的a參數和b參數,但是實際上不是,在java編譯器編譯以後實際的操作代碼是
class Outer$Dosome{
public Dosome(final String a,final int b){
this.Dosome$a=a;
this.Dosome$b=b;
}
public void dosome(){
System.out.println(this.Dosome$a+this.Dosome$b);
}
}}
從以上代碼看來,內部類並不是直接調用方法傳進來的參數,而是內部類將傳進來的參數通過自己的構造器備份到了自己的內部,自己內部的方法調用的實際是自己的屬性而不是外部類方法的參數。
這樣理解就很容易得出爲什麼要用final了,因爲兩者從外表看起來是同一個東西,實際上卻不是這樣,如果內部類改掉了這些參數的值也不可能影響到原參數,然而這樣卻失去了參數的一致性,因爲從編程人員的角度來看他們是同一個東西,如果編程人員在程序設計的時候在內部類中改掉參數的值,但是外部調用的時候又發現值其實沒有被改掉,這就讓人非常的難以理解和接受,爲了避免這種尷尬的問題存在,所以編譯器設計人員把內部類能夠使用的參數設定爲必須是final來規避這種莫名其妙錯誤的存在。



JVM中每個進程都會有多個根,每個static變量,方法參數,局部變量,當然這都是指引用類型.基礎類型是不能作爲根的,根其實就是一個存儲地址.垃圾回收器在工作時先從根開始遍歷它引用的對象並標記它們,如此遞歸到最末梢,所有根都遍歷後,沒有被標記到的對象說明沒有被引用,那麼就是可以被回收的對象(有些對象有finalized方法,雖然沒有引用,但JVM中有一個專門的隊列引用它們直到finalized方法被執行後才從該隊列中移除成爲真正沒有引用的對象,可以回收,這個與本主題討論的無關,包括代的劃分等以後再說明).這看起來很好但是在內部類的回調方法中,s既不可能是靜態變量,也不是方法中的臨時變量,也不是方法參數,它不可能作爲根,在內部類中也沒有變量引用它,它的根在內部類外部的那個方法中,如果這時外面變量s重指向其它對象,則回調方法中的這個對象s就失去了引用,可能被回收,而由於內部類回調方法大多數在其它線程中執行,可能還要在回收後還會繼續訪問它.這將是什麼結果?而使用final修飾符不僅會保持對象的引用不會改變,而且編譯器還會持續維護這個對象在回調方法中的生命週期.所以這纔是final變量和final參數的根本意義.


發佈了25 篇原創文章 · 獲贊 3 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章