https://blog.csdn.net/wjw521wjw521/article/details/77333820
在java編程中,沒用的類定義太多對系統來說也是一個負擔,這時候我們可以通過定義匿名內部類來簡化編程,但匿名內部類訪問外部方法的成員變量時都要求外部成員變量添加final修飾符,final修飾變量代表該變量只能被初始化一次,以后不能被修改。但為什么匿名內部類訪問外部成員變量就不允許他修改了呢?
接下來這個例子應該足夠把這些說清楚了:
示例代碼:
- public class InnerFinalTest {
- private static Test test0= null;
- public static void main(String[] args) {
- new InnerFinalTest().method1();
- System.out.println("-------");
- test0.test();
- }
- public void method1(){
- final Test test = new Test();
- test0 = new Test(){
- @Override
- public void test(){
- System.out.println("匿名內部類:" + test);
- Field[] field = this.getClass().getDeclaredFields();
- for (int i = 0; i < field.length; i++) {
- System.out.println(field[i].getName());
- }
- }
- };
- InnerFinalTest ift = new InnerFinalTest();
- ift.innerFinalTest(test0);
- System.out.println("外部直接訪問變量:"+ test);
- }
- public void innerFinalTest(Test test){
- test.test();
- }
- }
Test類無關緊要,不過還是貼一下他吧
- public class Test {
- public void test(){
- System.out.println("啊啊啊啊啊!" );
- }
- }
說明:
為什么我們要將被匿名內部類訪問的變量定義成final呢?
首先,我們在InnerFinalTest類中定義了一個static變量test0:
private static Test test0= null;
該語句說明test0的生命周期和類一樣
接下來在main方法中調用method1(),在method1()中將我們定義的匿名內部類賦給了test0,這說明如果test0不往別處指的話,我們匿名內部類將被一直引用着,
如同吃了九轉大金丹,與天地同壽,與日月齊光,匿名內部類生命周期和InnerFinalTest類(匿名類的天地)相同了。
但是,method1()調用完了他要釋放資源了,所以method1()方法中:
final Test test = new Test();
test變量也要被釋放了,test沒了,但匿名內部類引用了test,如果java編譯器不搞點小動作,他就沒法玩兒了,因為匿名類的生命周期長,還使用着test,而外部變量先撤了,背后捅了匿名內部類一刀子。。。
匿名內部類說,就防着你這一招呢,所以叫編譯器大哥幫忙搞了個小動作,明修棧道暗度陳倉,編譯的時候,我自己把你給我的變量備份了一份,表面上看是我引用了你的變量,其實在運行期間我就用我自己備份的了。但是別人表面上看不知道我備份了一份,還以為我用的你的,如果不定義成final,變量在外面被修改了,我沒改,那我的結果就會和預期不同,為了防止出現這種情況,所以要被定義成final。
上面實例代碼運行結果:
匿名內部類:Test@40e455bf
this$0
val$test
外部直接訪問變量:Test@40e455bf
-------
匿名內部類:Test@40e455bf
this$0
val$test
我用反射證明了匿名內部類存在外部變量的備份val$test,其中因為變量是默認類型,所以使用getDeclaredFields得到所有匿名內部類運行期間存在的成員屬性,注意,該成員屬性在編碼期間是不存在的,
是編譯器主動為匿名內部類添加的成員屬性,所以可以通過反射在運行期間一窺究竟。
如果去掉匿名內部類對外部變量的引用,如去掉以下代碼:
System.out.println("匿名內部類:" + test);
運行結果中會沒有了val$test,這也再次證明了以上結論:匿名內部類備份了變量。
通過外部變量和內部變量打印內容相同,說明兩個變量test和val$test的變量引用指向的內存區域是相同的,(這里可以參考一下原型模式淺克隆)。指向相同對象,雖然對象不能修改,但對象中的屬性可以修改,而匿名內部類變量和外部變量指向相同,自然值也同步修改了。
總結一下,邏輯應該是這樣的:為了解決生命周期不同的問題,匿名內部類備份了變量,為了解決備份變量引出的問題,外部變量要被定義成final
我們匿名內部類使用final不是怕修改,是怕不能同步修改