
?????????????????????????????????????????????? 馬嘉楠 ??????? 2006-10-19
在上一篇文章《
inconstant constants ( 變化無常的常量 )
? 》的基礎(chǔ)上,我們?cè)賮硌芯恳幌?font color="#000000">在 Java 中使用循環(huán)定義會(huì)出現(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)定義。
你可以先想一下,這個(gè)值會(huì)是多少?看看與實(shí)際結(jié)果是否一致。
輸出結(jié)果:


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


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