Java向前引用容易出错的地方
???? 所謂向前引用,就是在定義類、接口、方法、變量之前使用它們,例如,
?
class MyClass {void method(){System.out.println(myvar);}String myvar = "var value"; }?
?
???? myvar在method方法后定義,但method方法可以先使用該變量。在很多語言,如C++,是需要提前定義的,而Java已經允許了向前引用。不過在使用向前引用時可能會容易犯一些錯誤。例如,下面的代碼。
?
class MyClass {int method() {return n; }int m = method();int n = 1; }???? 如果簡單地執行下面的代碼,毫無疑問會輸出1.
?
System.out.println(new MyClass().method());
???? 不過使用下面的代碼輸出變量m,卻得到0.
?
?
System.out.println(new MyClass().m);??? 那么這是真么回事呢?
?
??? 實際上,從java編譯器和runtime的工作原理可以得知。在編譯java源代碼時只是進行了詞法、語法和語義檢測,如果都通過,會生成.class文件。不過這時MyClass中的變量并沒有被初始化,編譯器只是將相應的初始化表達式(method()、1)記錄在.class文件中。
??? 當runtime運行MyClass.class時,首先會進行裝載成員字段,而且這種裝載是按順序執行的。并不會因為java支持向前引用,就首先初始化所有可以初始化的值。首先,runtime會先初始化m字段,這時當然會調用method方法,在method方法中利用向前引用技術使用了n。不過這時的n還沒有進行初始化呢。runtime為了實現向前引用,在進行初始化所有字段之前,還需要將所有的字段添加到符號表中。以便在任何地方(但需要滿足java的調用規則)都可以引用這些字段,不過由于還沒有初始化這些字段,所以這時符號表中所有的字段都使用默認的值。int類型的字段默認值自然是0了。所以在初始化int m = method()時,method方法訪問的n實際上是在進行正式初始化之前已經被添加到符號表中的字段n,而不是后面的int n = 1執行的結果。但將MyClass改成如下的形式,結果就完全不同了。
?
class MyClass {int method() {return n; }int n = 1;int m = method(); }?
?
?
???? 現在執行下面的代碼,會輸出1.
?
?
System.out.println(new MyClass().m);??? 究其原因,是引用初始化m時調用method方法,該方法中使用的n已經是初始化完的了,而不是最初放到符號表中的值。
?
??? 綜合上述,runtime在運行.class文件時,每個作用域(方法、接口、類等帶語言元素都有自己的作用域)的符號表都會被至少訪問兩次,第一次會將所有的字段(這里只考慮類的初始化)放到符號表中,暫時不考慮初始化只,放到符號表中只是相當于一個索引,好讓其他地方引用該字段時可以找到它們,例如,method方法中引用n時就會到符號表中尋找n,不過這時的n只是int類型的默認值。等到第二次訪問n就是真正初始化n的時候(int n = 1)。這是將符號表中存儲的字段n的值更新為實際的初始化值(1)。所以如果引用n放生在正式初始化n之前,當然輸出的是0。
? ? 那么可能有人會問,先訪問一下n,再訪問m,這時m的值是否為1呢?答案仍然是0。因為在創建MyClass對象時m和n的初始化工作已經完成,它們的值已成事實,除非再次設置,否則不可改變了。
?
MyClass myClass = new MyClass();System.out.println(myClass.n); // 輸出1System.out.println(myClass.m); // 仍然輸出0? ? 對于靜態成員,仍然符合這一規則。 ?
class MyClass {static int method() {return n; }static int m = method(); // 直接訪問m,仍然會輸出0static int n = 1; }?
?
?????
?
?
?
轉載于:https://www.cnblogs.com/snake-hand/p/3157157.html
總結
以上是生活随笔為你收集整理的Java向前引用容易出错的地方的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信公众平台开发入门教程[2019版]
- 下一篇: 安卓初学者必看实例,(手机GPS简单编程