【語法拾零】方法內部類所訪問的局部變量爲何必須是final的?

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

雖然方法局部變量的本體已經銷燬,它們的靜態副本卻保留了下來,供方法內部類的對象在合適的時候調用。也能夠理解爲,局部變量的生命週期經過副本的形式延長了。

相關文章
相關標籤/搜索