一、前言
我們知道JAVA參數是值傳遞,但是偶然發現java中參數還能加final
修飾符,那麼這個final
修飾符有什麼作用呢?
- 既然java是值傳遞,那麼將參數限定爲
final
與否根本沒有任何意義——就算不限定也不會改變實參的指向。 - 但是存在則必有其合理性,這個合理性還要從匿名內部類說起。
二、匿名內部類
什麼是匿名內部類
-
沒有名字
-
必須繼承一個類或實現一個接口
匿名內部類是語法糖
我們知道java的Lambda表達式是匿名內部類的語法糖,只不過不再綁定this
(this
指向調用它的外部類對象)。但實際上,匿名內部類本身也是一個語法糖。匿名內部類會被編譯爲繼承目標類或實現目標接口的內部類。
生命週期
因爲對象的生命週期是大於方法的生命週期的。所以內部類的對象在方法已經結束以後,可能還會訪問方法,那麼就會出現問題。所以實現的思路就是將匿名內部類使用到的局部變量以構造方法的形式拷貝到內部類中。表現的效果就是——提高了局部變量的生命週期
問題
語法糖帶來便捷性的同時,也容易帶來誤解。比如說很可能寫着寫着就想當然的以爲內部類對象的方法運行的時候外部方法還沒有退出。實際上極大可能性的這個方法已經出棧並且釋放了所有局部變量的內存。所以強制使用final
限定這些拷貝到內部類中的局部變量。約定這些變量只讀不寫
JDK8
JAVA8以後已經默認在匿名內部類訪問的局部變量上final
限定。但是雖然我們不用再顯式用final
修飾了,我們仍然不能去改變這些隱式常量
三、閉包
什麼是閉包
1、百度百科
閉包就是能夠讀取其他函數內部變量的函數。例如在javascript中,只有函數內部的子函數才能讀取局部變量,所以閉包可以理解成“定義在一個內部的函數“。在本質上,閉包是將函數內部和函數外部連接起來的橋樑。
2、簡而言之
閉包就是能夠讀取局部變量的函數
JAVA的閉包
JAVA的匿名內部類也可以稱之爲不完整的閉包。JAVA的局部變量存放在棧中,爲了實現閉包,必須將棧中的局部變量拷貝到匿名內部類對象中(堆中)。因此無論任何技術都無法使得它們雙向綁定,所以必須final
限定,使其永遠不可能觸發不同步的問題
JS的閉包
同樣,JS中函數的局部變量也是存放在棧中。但不同的是,JS中的局部變量在棧退出時會將可能被引用的內存和可能被訪問的內存保存下來。這爲閉包與局部變量雙向綁定的實現提供了可能
只要存在調用內部函數的可能,JavaScript就需要保留被引用的函數。
而且JavaScript運行時需要跟蹤引用這個內部函數的所有變量,直到最後一個變量廢棄,JavaScript的垃圾收集器才能釋放相應的內存空間
爲什麼JAVA不這樣實現?
- 閉包有內存泄漏的風險
- 可能有悖於設計理念吧
- 咱也不知道咱也不敢問啊