1. 程式人生 > >匿名類訪問區域性變數時,為什麼區域性變數必須加final

匿名類訪問區域性變數時,為什麼區域性變數必須加final

匿名內部類就是在物件的方法體內部定義的類。我們都知道方法中的匿名內部類是能夠訪問同一個方法中的區域性變數的,但是為什麼區域性變數要加上一個final呢? 

原因就是因為匿名內部類物件的生命週期可能會超過區域性變數的生命期。區域性變數的生命週期是當該方法被呼叫時,該方法中的區域性變數在棧中被建立,當方法呼叫結束時(執行完畢),退棧,這些區域性變數就會死亡。但是內部類物件是建立在堆中的,其生命週期跟其它類一樣,只有當jvm用可達性分析法發現這個物件通過GCRoots節點已經不可達,然後進行gc才會死亡。所以完全有可能存在的一個現象就是一個方法已經呼叫結束(區域性變數已死亡),但該內部類的物件仍然活著,也就是說內部類建立的物件的生命期會超過區域性變數,

這就會出現內部類的物件要訪問區域性變數時訪問失敗,而java為了保證這種情況不會發生,就用了一種折中的方案:內部類要訪問區域性變數,區域性變數必須加final,那為什麼區域性變數定義成final就可以了呢?

首先我們來看final這個關鍵字修飾變數的特點:當final修飾一個基本資料型別的變數時,這個基本型別的變數在初始化的時候就應該賦值,並且初始化完成之後其值就不能再發生改變了;當final修飾一個引用型別的變數時,這個引用型別的變數在初始化的時候就應該賦值並且之後不能再發生改變,但是引用型別的變數指向的物件的堆記憶體的值是可以改變的。基於final修飾變數的以上兩個特點,java就把區域性內部類物件要訪問的final型區域性變數,複製

過來變成該內部類物件中的一個成員變數,這樣即使棧中區域性變數(含final)已死亡,但由於它是final的,其值是不會發生改變的,因而匿名內部類物件在區域性變數死亡後,照樣可以訪問自己內部維護的一個值跟區域性變數一樣的成員變數,從而解決這個問題。

ps:java8中匿名類訪問區域性變數時,區域性變數已經可以不用加final了,但其實這個區域性變數還是final的,只不過不用顯式的加上而已,推測可能是編譯機制發生了改變。