final局部變量也可稱爲局部常量,賦值之後再也不改變。 java
Java內部類是位於類內部的類,方法內部類即位於類方法內部的類。 shell
好比,Inner是Outer的foo()方法內部類: ide
public class Outer { public Other foo() { final int a = 123; final String str = "hello"; final Other other = new Other(); final class Inner extends Other { @Override public void bar() { int a1 = a; String str1 = str; Other other1 = other; } } return new Inner(); } public static void main(String[] args) { Other other = new Outer().foo(); other.bar(); } } class Other { public void bar() {} }
Java語言規範中說方法內部類只能訪問該方法的final局部變量: this
Any local variable, formal parameter, or exception parameter used but not declared in an inner class must be declared final.
而且,局部變量須要在內部類定義以前賦值: spa
Any local variable used but not declared in an inner class must be definitely assigned before the body of the inner class.
之因此這麼規定,一個理由是:方法執行結束,方法內的局部變量已經銷燬,而方法內部類的對象仍可能存在。好比上例中,other.bar()執行時,foo()內的局部變量已經出棧銷燬。 code
問題是,爲何在bar()中可以訪問foo()中本該已經銷燬了的final局部變量呢? orm
利用javap獲得Outer$1Inner.class的字節碼: 對象
public void bar(); Code: Stack=1, Locals=4, Args_size=1 0: bipush 123 2: istore_1 3: ldc #24; //String hello 5: astore_2 6: aload_0 7: getfield #14; //Field val$other:LOther; 10: astore_3 11: return LineNumberTable: line 12: 0 line 13: 3 line 14: 6 line 15: 11
在foo()方法中定義的3個final局部變量:a,str和other,在Inner的bar()方法中分別被替換成了字面量和Outer$1Inner.class常量池中的常量。 生命週期
const #14 = Field #1.#15; // Outer$1Inner.val$other:LOther; const #15 = NameAndType #7:#8;// val$other:LOther; const #16 = Method #3.#17; // Other."<init>":()V const #17 = NameAndType #9:#18;// "<init>":()V const #18 = Asciz ()V; const #19 = Asciz LineNumberTable; const #20 = Asciz LocalVariableTable; const #21 = Asciz this; const #22 = Asciz LOuter$1Inner;; const #23 = Asciz bar; const #24 = String #25; // hello
因爲final局部變量在內部類定義以前賦了值,且不可變,在編譯時便可生成該局部變量的常量副本。 ip
雖然方法局部變量的本體已經銷燬,它們的靜態副本卻保留了下來,供方法內部類的對象在合適的時候調用。也能夠理解爲,局部變量的生命週期經過副本的形式延長了。