1. 程式人生 > >Java內部類引用外部類中的區域性變數為何必須是final問題解析

Java內部類引用外部類中的區域性變數為何必須是final問題解析

       今天編寫一個多執行緒程式,發現在方法內定義內部類時,如果內部類呼叫了方法中的變數,那麼該變數必須申明為final型別,百思不得其解,後來想到應該是生命週期的原因,因為方法內定義的變數是區域性變數,離開該方法,變數就失去了作用,也就會自動被消除,而內部類卻不會離開它所在方法就失去作用,它有更廣的生命週期,下面通過一個例項加以說明:

image

    如例中所示,在外部類Outer中聲明瞭一個內部類TimerPrint,這個類中的方法引用了方法start中的一個區域性變數testTxt

    邏輯上:因為該內部類出現在一個方法的內部,但實際編譯時,內部類編譯為Outer$1TimerPrint.class,這說明,外部類的這個方法和內部類是處於同一級別的。換句話說是非final變數和內部類的生命週期不一樣!start被呼叫後,非final變數也會隨之消失,就會出現內部類引用非法!

    實際做法:java編譯器的行為是這樣的(前提條件是該變數在內部類中被引用): 
    若定義為final,則java編譯器則會在內部類TimerPrint內生成一個外部變數的拷貝,而且可以既可以保證內部類可以引用外部屬性,又能保證值的唯一性。

       若不定義為final,則無法通過編譯!(jdk1.6測試過)。因為編譯器不會給非final變數進行拷貝,那麼內部類引用的變數就是非法的!

下面看經過編譯以後的位元組碼:

外部類編譯後的位元組碼: 
image

內部類編譯後的位元組碼: 
image

image

如果外部類中的變數d沒有被內部類引用,則final為可選的,而且java編譯器將不做特殊處理!!


加一個引數d 並且把它定義為非final型別,編譯以後檔案如下:

編譯後的外部類class: 
image 

編譯後的內部類class: 
image

由上可以看出,在方法內部定義內部類時,內部類如果呼叫了方法內的變數,則該變數必須被final修飾,否則就會因為在呼叫內部類時因為找不到所用的變數而報錯!