Java非法向前引用變量

轉載自:https://blog.csdn.net/xdugucc/article/details/78239920

在學習《深入理解java虛擬機》一書中,關於類的初始化一章中提到了一句:靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,定義在它之後的變量,在前面的靜態語句塊中可以賦值,但是不能訪問。

並給出一個示例:

public class MyClass {
	static {
		i = 0; //給變量賦值可以正常通過
		System.out.println(i); //編譯器提示非法向前引用
	}
	static int i = 1;
}

看了之後有兩點疑問

1.我們知道類變量和靜態語句塊的初始化順序是按照出現順序的,在執行i = 0的時候,還未執行static int i = 1,爲什麼不會報變量未定義的錯?

2.既然i = 0沒有問題,爲什麼不可以訪問?

帶着這些問題去看了網上的一些關於向前引用的博客後,有了一些自己的理解。

看三個例子:

第一個例子:

public class MyClass {
	    void method()  
	    {  
	        System.out.println(myvar);  
	    }  
	    String myvar = "var value";  
	    
	    public static void main(String[] args) {
			new MyClass().method();
		}
}
//output:var value

在這個例子中,可以看到生成對象調用method()方法,method方法裏是可以訪問到myvar變量的,這是因爲在new出對象時,會首先爲成員變量分配存儲空間並初始化。在執行method()方法時,在堆中的MyClass對象中已經有了一個名爲myvar的引用並初始化指向了字符串“var char”。所以可以訪問這個變量並打印出來。

第二個例子:

public class MyClass {
    int method() {return n; }  
    int m = method();  
    int n = 1;
}
//output:
//1
//0

這個例子,打印n和m的話依次輸出1和0。現在分析對象創建的過程來解釋這樣的結果。new MyClass()執行後,首先會在堆中劃出一片區域存儲對象,並且爲成員變量賦默認的初始值(類似於類加載過程中的準備階段爲靜態變量非final變量賦默認值),然後再按照成員變量的出現順序進行初始化(這個時候可以運行java代碼),等所有成員變量初始化完成之後調用構造方法完成對象的創建。

現在根據以上一段的分析,m和n一開始都是0,然後開始初始化,執行m=method(); 這個時候n還未初始化還是默認值0,所以返回值賦給m,m是0。再來初始化n,執行int n = 1後,n由0變成1。所以對象創建完成之後m=0,n=1。輸出結果也就不難解釋了。

第三個例子:

public class MyClass {
 
    int m = n;   //非法向前引用
    {
    	n = 0;
    	System.out.println(n); //非法向前引用
    }
    int n = 1;   
}

在編譯這段代碼時,編譯期無法通過,提示非法向前引用。似乎這個例子中,int m=n和上個例子中int m = method(); method() {return n;}n的效果是一樣的。其實,如果編譯能夠通過,確實效果是一樣的。按照我們的分析,int m = n和代碼塊中println(n)是沒有問題的,可以給m賦還未初始化n的默認值0以及打印n的默認值0,但是java卻不允許我們這樣做。仔細想想就會明白,這是java出於安全性考慮的:如果字段會進行初始化,一定要防止在初始化前程序中訪問默認值(可以賦值但不可訪問),這會導致很多意想不到的麻煩

在構造器多態行爲一文http://blog.csdn.net/xdugucc/article/details/78220518中,我們已經見過這種做法帶來的問題。所以編譯期會對變量還未完全初始化卻在前面被引用的現象進行檢查並規避,但是如果像第二個例子中的int m = method(); method() {return n;}的現象,編譯器卻無能爲力,因爲在編譯期它無法知道是什麼樣的值賦值給了m。所以編譯器並不會報錯,但是我們要避免這樣的現象發生。

現在再來看本文一開始的例子就很清晰了,雖然舉的三個例子是基於對象的創建過程,而一開始的例子是基於類加載的過程,但是意思確實相通的。就是對於有初始值的變量,要避免在初始化之前訪問變量的默認值。但是賦值確是可以的,因爲最終變量還是會進行初始化。

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