Java進階 ——— 局部內部類訪問局部變量爲何必須加final關鍵字

疑問

在Java中,局部內部類若是調用了方法中的變量,那麼該變量必須申明爲final類型,若是不申明,則編譯就會出錯。java

這裏的內部類指的是方法內部類或匿名內部類,不包含靜態內部類和成員內部類bash

這裏經過一個例子類分析函數

public class InnerClass {
  
	private int defaultAge = 5;
  局部變量 age,必須添加final關鍵字,這裏先不加
	public void addAge( int age){
  
    //局部內部類
		class NewAge{
			private int getAge(){
				return age + defaultAge;
			}
		}

		NewAge newAge = new NewAge();
		System.out.print(newAge.getAge());
	}
}
複製代碼

強行不加final,編譯,則會報錯:ui

Error:(16, 12) 錯誤: 從內部類中訪問本地變量age; 須要被聲明爲最終類型this

分析

  • 緣由

1.生命週期不一樣: 爲何必須局部變量加final關鍵字呢?由於局部變量直接存儲在棧中,當方法執行結束,非final的局部變量就被銷燬,而局部內部類對局部變量的引用依然存在,當局部內部類要調用局部變量時,就會出錯,出現非法引用。簡單來講,就是非final的局部變量的生命週期比局部內部類的生命週期短,是否是直接能夠拷貝變量到局部內部類?這樣內部類中就可使用並且不擔憂生命週期問題呢?也是不能夠的,由於直接拷貝又會出現第二個問題,就是數據不一樣步 2.數據不一樣步:內部類並非直接使用傳遞進來的參數,而是將傳遞進來的參數經過本身的構造器備份到本身內部,表面看是同一個變量,實際調用的是本身的屬性而不是外部類方法的參數,若是在內部類中,修改了這些參數,並不會對外部變量產生影響,僅僅改變局部內部類中備份的參數。可是在外部調用時發現值並無被修改,這種問題就會很尷尬,形成數據不一樣步。因此使用final避免數據不一樣步的問題spa

  • 原理

那爲何添加final修飾的局部變量,就能夠被局部內部類引用呢? 若定義爲final,則java編譯器則會在內部類NewAge內生成一個外部變量的拷貝,並且能夠既能夠保證內部類能夠引用外部屬性,又能保證值的惟一性 也就是拷貝了一個變量的副本,提供給局部內部類,這個副本的生命週期和局部內部類同樣長,而且這個副本不能夠修改,保證了數據的同步 注意在Java8 中,被局部內部類引用的局部變量,默認添加final,因此不須要添加final關鍵詞.net

  • 字節碼

若是有興趣,能夠看看編譯後的字節碼,即.class文件code

延伸 如何查看Java字節碼blog

class InnerClass$1NewAge {
    //能夠看到,局部內部類中的使用的age,是經過構造函數傳遞進來,並非直接引用外部變量。
    InnerClass$1NewAge(InnerClass var1, int var2) {
        this.this$0 = var1;
        this.val$age = var2;
    }

    private int getAge() {
        return this.val$age + InnerClass.access$000(this.this$0);
    }
}

複製代碼

InnerClass類編譯後,在文件夾會出現InnerClass.class和InnerClass$1NewAge.class,這說明外部類的方法 和內部類處於同一級。生命週期

結論

局部內部類引用局部變量,不添加final,會出現生命週期不一樣,致使非法引用問題,並且直接拷貝會出現數據不一樣步問題,因此使用final,保證了合法引用,並且數據不可修改

相關文章
相關標籤/搜索