本謎題測試的是你對某些規(guī)則的掌握程度,這些規(guī)則用于聲明從方法中拋出并被catch語句塊所捕獲的異常。下面的三個程序每一個都會打印些什么?不要假設(shè)它們都可以通過編譯:
import java.io.IOException;
public class Arcane1 {
public static void main(String[] args) {
try {
System.out.println("Hello world");
} catch(IOException e) {
System.out.println("I’ve never seen
println fail!");
}
}
}
public class Arcane2 {
public static void main(String[] args) {
try {
// If you have nothing nice to say, say nothing
} catch(Exception e) {
System.out.println("This can’t
happen");
}
}
}
interface Type1 {
void f() throws CloneNotSupportedException;
}
interface Type2 {
void f() throws InterruptedException;
}
interface Type3 extends Type1, Type2 {
}
public class Arcane3 implements Type3 {
public void f() {
System.out.println("Hello world");
}
public static void main(String[] args) {
Type3 t3 = new Arcane3();
t3.f();
}
}
第一個程序,Arcane1,展示了被檢查異常的一個基本原則。它看起來應(yīng)該是可以編譯的:try子句執(zhí)行I/O,并且catch子句捕獲IOException異常。但是這個程序不能編譯,因為println方法沒有聲明會拋出任何被檢查異常,而IOException卻正是一個被檢查異常。語言規(guī)范中描述道:如果一個catch子句要捕獲一個類型為E的被檢查異常,而其相對應(yīng)的try子句不能拋出E的某種子類型的異常,那么這就是一個編譯期錯誤[JLS 11.2.3]。
基于同樣的理由,第二個程序,Arcane2,看起來應(yīng)該是不可以編譯的,但是它卻可以。它之所以可以編譯,是因為它的catch子句檢查了Exception。盡管JLS在這一點上十分含混不清,但是捕獲Exception或Throwble的catch子句是合法的,不管與其相對應(yīng)的try子句的內(nèi)容為何。盡管Arcane2是一個合法的程序,但是catch子句的內(nèi)容永遠(yuǎn)的不會被執(zhí)行,這個程序什么都不會打印。
第三個程序,Arcane3,看起來它也不能編譯。方法f在Type1接口中聲明要拋出被檢查異常CloneNotSupportedException,并且在Type2接口中聲明要拋出被檢查異常InterruptedException。Type3接口繼承了Type1和Type2,因此,看起來在靜態(tài)類型為Type3的對象上調(diào)用方法f時,有潛在可能會拋出這些異常。一個方法必須要么捕獲其方法體可以拋出的所有被檢查異常,要么聲明它將拋出這些異常。Arcane3的main方法在靜態(tài)類型為Type3的對象上調(diào)用了方法f,但它對CloneNotSupportedException和InterruptedExceptioin并沒有作這些處理。那么,為什么這個程序可以編譯呢?
上述分析的缺陷在于對“Type3.f可以拋出在Type1.f上聲明的異常和在Type2.f上聲明的異?!彼龅募僭O(shè)。這并不正確,因為每一個接口都限制了方法f可以拋出的被檢查異常集合。一個方法可以拋出的被檢查異常集合是它所適用的所有類型聲明要拋出的被檢查異常集合的交集,而不是合集。因此,靜態(tài)類型為Type3的對象上的f方法根本就不能拋出任何被檢查異常。因此,Arcane3可以毫無錯誤地通過編譯,并且打印Hello world。
總之,第一個程序說明了一項基本要求,即對于捕獲被檢查異常的catch子句,只有在相應(yīng)的try子句可以拋出這些異常時才被允許。第二個程序說明了這項要求不會應(yīng)用到的冷僻案例。第三個程序說明了多個繼承而來的throws子句的交集,將減少而不是增加方法允許拋出的異常數(shù)量。本謎題所說明的行為一般不會引發(fā)難以捉摸的bug,但是你第一次看到它們時,可能會有點吃驚。
import java.io.IOException;
public class Arcane1 {
public static void main(String[] args) {
try {
System.out.println("Hello world");
} catch(IOException e) {
System.out.println("I’ve never seen
println fail!");
}
}
}
public class Arcane2 {
public static void main(String[] args) {
try {
// If you have nothing nice to say, say nothing
} catch(Exception e) {
System.out.println("This can’t
happen");
}
}
}
interface Type1 {
void f() throws CloneNotSupportedException;
}
interface Type2 {
void f() throws InterruptedException;
}
interface Type3 extends Type1, Type2 {
}
public class Arcane3 implements Type3 {
public void f() {
System.out.println("Hello world");
}
public static void main(String[] args) {
Type3 t3 = new Arcane3();
t3.f();
}
}
第一個程序,Arcane1,展示了被檢查異常的一個基本原則。它看起來應(yīng)該是可以編譯的:try子句執(zhí)行I/O,并且catch子句捕獲IOException異常。但是這個程序不能編譯,因為println方法沒有聲明會拋出任何被檢查異常,而IOException卻正是一個被檢查異常。語言規(guī)范中描述道:如果一個catch子句要捕獲一個類型為E的被檢查異常,而其相對應(yīng)的try子句不能拋出E的某種子類型的異常,那么這就是一個編譯期錯誤[JLS 11.2.3]。
基于同樣的理由,第二個程序,Arcane2,看起來應(yīng)該是不可以編譯的,但是它卻可以。它之所以可以編譯,是因為它的catch子句檢查了Exception。盡管JLS在這一點上十分含混不清,但是捕獲Exception或Throwble的catch子句是合法的,不管與其相對應(yīng)的try子句的內(nèi)容為何。盡管Arcane2是一個合法的程序,但是catch子句的內(nèi)容永遠(yuǎn)的不會被執(zhí)行,這個程序什么都不會打印。
第三個程序,Arcane3,看起來它也不能編譯。方法f在Type1接口中聲明要拋出被檢查異常CloneNotSupportedException,并且在Type2接口中聲明要拋出被檢查異常InterruptedException。Type3接口繼承了Type1和Type2,因此,看起來在靜態(tài)類型為Type3的對象上調(diào)用方法f時,有潛在可能會拋出這些異常。一個方法必須要么捕獲其方法體可以拋出的所有被檢查異常,要么聲明它將拋出這些異常。Arcane3的main方法在靜態(tài)類型為Type3的對象上調(diào)用了方法f,但它對CloneNotSupportedException和InterruptedExceptioin并沒有作這些處理。那么,為什么這個程序可以編譯呢?
上述分析的缺陷在于對“Type3.f可以拋出在Type1.f上聲明的異常和在Type2.f上聲明的異?!彼龅募僭O(shè)。這并不正確,因為每一個接口都限制了方法f可以拋出的被檢查異常集合。一個方法可以拋出的被檢查異常集合是它所適用的所有類型聲明要拋出的被檢查異常集合的交集,而不是合集。因此,靜態(tài)類型為Type3的對象上的f方法根本就不能拋出任何被檢查異常。因此,Arcane3可以毫無錯誤地通過編譯,并且打印Hello world。
總之,第一個程序說明了一項基本要求,即對于捕獲被檢查異常的catch子句,只有在相應(yīng)的try子句可以拋出這些異常時才被允許。第二個程序說明了這項要求不會應(yīng)用到的冷僻案例。第三個程序說明了多個繼承而來的throws子句的交集,將減少而不是增加方法允許拋出的異常數(shù)量。本謎題所說明的行為一般不會引發(fā)難以捉摸的bug,但是你第一次看到它們時,可能會有點吃驚。