【语法拾零】方法内部类所访问的局部变量为什么必须是final的?

final局部变量也可称为局部常量,赋值以后不再改变。

Java内部类是位于类内部的类,方法内部类即位于类方法内部的类。

比如,Inner是Outer的foo()方法内部类:

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局部变量:

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must be declared final.

并且,局部变量需要在内部类定义之前赋值:

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()内的局部变量已经出栈销毁。

问题是,为什么在bar()中能够访问foo()中本该已经销毁了的final局部变量呢?

利用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局部变量在内部类定义之前赋了值,且不可变,在编译时即可生成该局部变量的常量副本。

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

你可能感兴趣的:(内部类,局部变量,生命周期,方法内部类,final)