java爲什麼匿名內部類的參數引用時final?final局部變量的生命週期

在知乎上看到了一篇帖子
在這裏插入圖片描述

在這個問題下面大家已經吵得不可開交了,看了很多篇文章,被誤導進了很多的坑,發現只有下面兩篇文章是寫的最好的,解釋的很清楚,我把兩篇文章簡單的總結了一下:

1.問題的引出

 
public class Hello {
	public static void main(String[] args) {
		String str="haha";
		new Thread() {
			@Override
			public void run() {
				System.out.println(str);
			}
		}.start();
	}
}

JDK1.8之後做了優化,即不需要將局部變量顯示的聲明爲final也不會發生編譯錯誤,實際上java底層還是加上了final,那麼問題來了,爲什麼匿名內部類訪問局部變量要使用final修飾
這個問題的答案就只有一個: 爲了保持數據的一致性
對匿名內部類字節碼文件進行反編譯

 
public class Hello$1 extends Thread {
	
	private String val$str;
	
	Hello$1(String paramString) {
		this.val$str = paramString;
	}
 
	public void run() {
		System.out.println(this.val$str);
	}
 
}

也就是說匿名內部類之所以可以訪問局部變量,是因爲在底層將這個局部變量的值傳入到了匿名內部類中,並且以匿名內部類的成員變量的形式存在,這個值的傳遞過程是通過匿名內部類的構造器完成的。

這裏所說的數據一致性,對引用變量來說是引用地址的一致性,對基本類型來說就是值的一致性。

爲什麼需要用final保護數據的一致性呢?

因爲將數據拷貝完成後,如果不用final修飾,則原先的局部變量可以發生變化。這裏到了問題的核心了,如果局部變量發生變化後,匿名內部類是不知道的(因爲他只是拷貝了局不變量的值,並不是直接使用的局部變量)。這裏舉個栗子:原先局部變量指向的是對象A,在創建匿名內部類後,匿名內部類中的成員變量也指向A對象。但過了一段時間局部變量的值指向另外一個B對象,但此時匿名內部類中還是指向原先的A對象。那麼程序再接着運行下去,可能就會導致程序運行的結果與預期不同。
在這裏插入圖片描述
可能還有的回答是:

使用final可以保證局部變量的生命週期,因爲在方法中,該方法的局部內部類或匿名內部類創建了,對象存在於堆空間,而局部變量則是存在於棧中,隨着方法結束變量也就消失,而對象卻不一定消失,所以對象引用的就是不存在的變量了,使用final可以在編譯期將final送進常量池中

真有這麼簡單

final修飾的變量確實被當成常量放進了常量池,什麼是生命週期?首先變量是在棧空間,對象在堆空間這是沒有問題的,棧空間裏變量的生命週期就對應着入棧出棧,出棧這個變量就沒了,和是不是final有關係?,而對象在堆上的生命週期有專門的GC回收算法

編譯器在編譯的時候會將這個變量拷貝一份作爲匿名內部類的參數繼續傳遞,並且將這個參數賦給當前匿名類的成員變量,用final的目的就是讓它不可變,保證數據的統一性,所以,即使局部變量釋放了,那個常量值還存在。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章