請(qǐng)考慮下面的兩個(gè)類:
public class Strange1 {
public static void main(String[] args) {
try {
Missing m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
public class Strange2 {
public static void main(String[] args) {
Missing m;
try {
m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
Strange1和Strange2都用到了下面這個(gè)類:
class Missing {
Missing() { }
}
如果你編譯所有這三個(gè)類,然后在運(yùn)行Strange1和Strange2之前刪除Missing.class文件,你就會(huì)發(fā)現(xiàn)這兩個(gè)程序的行為有所不同。其中一個(gè)拋出了一個(gè)未被捕獲的NoClassDefFoundError異常,而另一個(gè)卻打印出了Got it! 到底哪一個(gè)程序具有哪一種行為,你又如何去解釋這種行為上的差異呢?
程序Strange1只在其try語(yǔ)句塊中提及Missing類型,因此你可能會(huì)認(rèn)為它捕獲NoClassDefFoundError異常,并打印Got it!另一方面,程序Strange2在try語(yǔ)句塊之外聲明了一個(gè)Missing類型的變量,因此你可能會(huì)認(rèn)為所產(chǎn)生的NoClassDefFoundError異常不會(huì)被捕獲。如果你試著運(yùn)行這些程序,就會(huì)看到它們的行為正好相反:Strange1拋出了未被捕獲的NoClassDefFoundError異常,而Strange2卻打印出了Got it!怎樣才能解釋這些奇怪的行為呢?
如果你去查看Java規(guī)范以找出應(yīng)該拋出NoClassDefFoundError異常的地方,那么你不會(huì)得到很多的指導(dǎo)信息。該規(guī)范描述道,這個(gè)錯(cuò)誤可以“在(直接或間接)使用某個(gè)類的程序中的任何地方”拋出[JLS 12.2.1]。當(dāng)VM調(diào)用Strange1和Strange2的main方法時(shí),這些程序都間接使用了Missing類,因此,它們都在其權(quán)利范圍內(nèi)于這一點(diǎn)上拋出了該錯(cuò)誤。
于是,本謎題的答案就是這兩個(gè)程序可以依據(jù)其實(shí)現(xiàn)而展示出各自不同的行為。但是這并不能解釋為什么這些程序在所有我們所知的Java實(shí)現(xiàn)上的實(shí)際行為,與你所認(rèn)為的必然行為都正好相反。要查明為什么會(huì)是這樣,我們需要研究一下由編譯器生成的這些程序的字節(jié)碼。
如果你去比較Strange1和Strange2的字節(jié)碼,就會(huì)發(fā)現(xiàn)幾乎是一樣的。除了類名之外,的差異就是catch語(yǔ)句塊所捕獲的參數(shù)ex與VM本地變量之間的映射關(guān)系不同。盡管哪一個(gè)程序變量被指派給了哪一個(gè)VM變量的具體細(xì)節(jié)會(huì)因編譯器的不同而有所差異,但是對(duì)于和上述程序一樣簡(jiǎn)單的程序來(lái)說(shuō),這些細(xì)節(jié)不太可能會(huì)差異很大。下面是通過(guò)執(zhí)行javap -c Strange1命令而顯示的Strange1.main的字節(jié)碼:
0: new
3: dup
4: invokespecial #3; //Method Missing."":()V
7: astore_1
8: goto 20
11: astore_1
12: getstatic #5; // Field System.out:Ljava/io/PrintStream;
15: ldc #6; // String "Got it!"
17: invokevirtual #7;//Method PrintStream.println: (String); V
20: return
Exception table:
from to target type
0 8 11 Class java/lang/NoClassDefFoundError
Strange2.main相對(duì)應(yīng)的字節(jié)碼與其只有一條指令不同:
11: astore_2
這是一條將catch語(yǔ)句塊中的捕獲異常存儲(chǔ)到捕獲參數(shù)ex中的指令。在Strange1中,這個(gè)參數(shù)是存儲(chǔ)在VM變量1中的,而在Strange2中,它是存儲(chǔ)在VM變量2中的。這就是兩個(gè)類之間的差異,但是它所造成的程序行為上的差異是多么地大呀!
public class Strange1 {
public static void main(String[] args) {
try {
Missing m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
public class Strange2 {
public static void main(String[] args) {
Missing m;
try {
m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
Strange1和Strange2都用到了下面這個(gè)類:
class Missing {
Missing() { }
}
如果你編譯所有這三個(gè)類,然后在運(yùn)行Strange1和Strange2之前刪除Missing.class文件,你就會(huì)發(fā)現(xiàn)這兩個(gè)程序的行為有所不同。其中一個(gè)拋出了一個(gè)未被捕獲的NoClassDefFoundError異常,而另一個(gè)卻打印出了Got it! 到底哪一個(gè)程序具有哪一種行為,你又如何去解釋這種行為上的差異呢?
程序Strange1只在其try語(yǔ)句塊中提及Missing類型,因此你可能會(huì)認(rèn)為它捕獲NoClassDefFoundError異常,并打印Got it!另一方面,程序Strange2在try語(yǔ)句塊之外聲明了一個(gè)Missing類型的變量,因此你可能會(huì)認(rèn)為所產(chǎn)生的NoClassDefFoundError異常不會(huì)被捕獲。如果你試著運(yùn)行這些程序,就會(huì)看到它們的行為正好相反:Strange1拋出了未被捕獲的NoClassDefFoundError異常,而Strange2卻打印出了Got it!怎樣才能解釋這些奇怪的行為呢?
如果你去查看Java規(guī)范以找出應(yīng)該拋出NoClassDefFoundError異常的地方,那么你不會(huì)得到很多的指導(dǎo)信息。該規(guī)范描述道,這個(gè)錯(cuò)誤可以“在(直接或間接)使用某個(gè)類的程序中的任何地方”拋出[JLS 12.2.1]。當(dāng)VM調(diào)用Strange1和Strange2的main方法時(shí),這些程序都間接使用了Missing類,因此,它們都在其權(quán)利范圍內(nèi)于這一點(diǎn)上拋出了該錯(cuò)誤。
于是,本謎題的答案就是這兩個(gè)程序可以依據(jù)其實(shí)現(xiàn)而展示出各自不同的行為。但是這并不能解釋為什么這些程序在所有我們所知的Java實(shí)現(xiàn)上的實(shí)際行為,與你所認(rèn)為的必然行為都正好相反。要查明為什么會(huì)是這樣,我們需要研究一下由編譯器生成的這些程序的字節(jié)碼。
如果你去比較Strange1和Strange2的字節(jié)碼,就會(huì)發(fā)現(xiàn)幾乎是一樣的。除了類名之外,的差異就是catch語(yǔ)句塊所捕獲的參數(shù)ex與VM本地變量之間的映射關(guān)系不同。盡管哪一個(gè)程序變量被指派給了哪一個(gè)VM變量的具體細(xì)節(jié)會(huì)因編譯器的不同而有所差異,但是對(duì)于和上述程序一樣簡(jiǎn)單的程序來(lái)說(shuō),這些細(xì)節(jié)不太可能會(huì)差異很大。下面是通過(guò)執(zhí)行javap -c Strange1命令而顯示的Strange1.main的字節(jié)碼:
0: new
3: dup
4: invokespecial #3; //Method Missing."
7: astore_1
8: goto 20
11: astore_1
12: getstatic #5; // Field System.out:Ljava/io/PrintStream;
15: ldc #6; // String "Got it!"
17: invokevirtual #7;//Method PrintStream.println: (String); V
20: return
Exception table:
from to target type
0 8 11 Class java/lang/NoClassDefFoundError
Strange2.main相對(duì)應(yīng)的字節(jié)碼與其只有一條指令不同:
11: astore_2
這是一條將catch語(yǔ)句塊中的捕獲異常存儲(chǔ)到捕獲參數(shù)ex中的指令。在Strange1中,這個(gè)參數(shù)是存儲(chǔ)在VM變量1中的,而在Strange2中,它是存儲(chǔ)在VM變量2中的。這就是兩個(gè)類之間的差異,但是它所造成的程序行為上的差異是多么地大呀!

