
?????????????????????????????????????????????? 馬嘉楠 ??????? 2006-10-19
在上一篇文章《
inconstant constants ( 變化無常的常量 )
? 》的基礎(chǔ)上,我們再來研究一下在 Java 中使用循環(huán)定義會出現(xiàn)哪些問題。
老規(guī)矩,代碼伺候 ^+^
public class ClassX{
???public?static?final?intX?=?2* ClassY.Y;
}
public?class ClassY{
???public?static?final?intY?=ClassZ.Z+?1 ;
}
public class ?ClassZ? extends ClassX{
???public?static?final?int?Z=?X?+?3 ;
}
public class? ClassTest{
???public?static?void main(String[]?args){
??????System.out.println(ClassX.X + ClassY.Y + ClassZ.Z);
???}
}
代碼中的static final變量X,Y,Z,循環(huán)定義。
你可以先想一下,這個值會是多少?看看與實際結(jié)果是否一致。
輸出結(jié)果:


15
現(xiàn)在對ClassTest.java進行一點修改,如下:
public?class ClassTest{
???public?staticvoid main(String[]?args){
??????System.out.println(ClassZ.Z + ClassY.Y + ClassX.X);
???}
}
兩次的輸出結(jié)果會一樣么?
我既然這么問了,你肯定會說不一樣,那你知道原因么?你知道這次的輸出結(jié)果么?
可以先思考一下。
輸出結(jié)果:


8
讓我來告訴你這是怎么回事。
我們可以看見,對于三個staitc final 變量 X,Y,Z,他們的初始化表達式是循環(huán)定義的。在編譯期間不能確定它們的值,所以它們是運行期間常量( runtime constants ),編譯器不會進行常量替換。
而且每個表達式的計算將會依賴于類裝載的順序。
例如,為了計算出例1中的 ClassTest 結(jié)果,我們可以預(yù)見,ClassX 是第一個被裝載的類,但是第一個完成初始化的類卻是 ClassZ.
讓我們一步一步看看都發(fā)生了什么。
1.???X = 2 * ClassY.Y;??????計算X,需要知道ClassY.Y的值,下一步計算Y值
2.???Y = ClassZ.Z + 1;??????計算Y,需要知道ClassZ.Z的值,下一步計算Z值
3.???Z = X + 3;??????????????? 計算Z,需要知道X的值,而此時X的值還沒有被計算出來(又轉(zhuǎn)回來了,居然是個圈,呵呵^+^),所以我們使用X的默認值0。
因此:
??????Z = 3;
??????Y = 4;
??????X = 8;
所以 ClassX.X + ClassY.Y + ClassZ.Z? =? 15
例2當中,也是同一道理
不同的是,ClassZ是第一個被裝載的類,ClassX是第一個完成初始化的類
1. Z = X + 3;
2. X = 2 * ClassY.Y;
3. Y = ClassZ.Z + 1;(此時使用Z的默認值0)
因此:
??????Y = 1;
??????X = 2;
??????Z = 5;
所以 ClassZ.Z + ClassY.Y + ClassX.X = 8
只是簡單的改變的輸出順序,結(jié)果卻截然不同。哪一個才是你想要得結(jié)果呢?
我的例子看起來有點挖空心思鉆牛角尖,但是在大型項目當中,也許就會出現(xiàn)與例子當中相同的循環(huán)定義,如果真的存在的話,那么在紛繁的代碼當中想要發(fā)現(xiàn)循環(huán)定義可不是件容易的事情。
如果獨立看每一個定義的話,似乎都可以進行常量替換,看不出任何問題。但是這樣的代碼在不久的將來就會引發(fā)問題,而且不易被我們所察覺。
在你的應(yīng)用程序當中不經(jīng)意的代碼改變(例如示例代碼中我們只是改變了輸出順序,卻產(chǎn)生了截然不同的結(jié)果),就會導(dǎo)致不同的類裝載順序和計算順序,或者在并發(fā)的線程調(diào)度中,可能也會導(dǎo)致致不同的類裝載順序和計算順序。不幸的是,大多數(shù)編譯器不認為這種代碼是錯誤,也不會對編程人員給出任何警告。
我只是講了一下循環(huán)定義會引發(fā)的問題,我暫時也想不出什么好的解決辦法,只能在編程的過程當中盡量注意啦。
注:有錯誤請告訴我,非常感謝!
馬嘉楠
jianan.ma@gmail.com