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
虽然方法局部变量的本体已经销毁,它们的静态副本却保留了下来,供方法内部类的对象在合适的时候调用。也能够理解为,局部变量的生命周期经过副本的形式延长了。