From Gossip@caterpillar

Java Gossip: throw、throws

當程式發生錯誤而無法處理的時候,會丟出對應的例外物件,除此之外,在某些時刻,您可能會想要自行丟出例外,例如在例外處理結束後,再將例外丟出,讓下一層例外處理區塊來捕捉,若想要自行丟出例外,您可以使用"throw"關鍵字,並生成指定的例外物件,例如:
throw new ArithmeticException();

舉個例子來說明,在Java的除法中,允許除數為浮點數0.0,所得到的是Infinity,即無窮數,如果您想要自行檢驗除零錯誤,可以自行丟出例外,最接近這個條件的是ArithmeticException,當除數為整數且為0時,就會引發這個例外,您可以如下丟出例外:

  • UseThrow.java
public class UseThrow { 
public static void main(String[] args) {
double dblzero = 0.0;
try {
System.out.println("浮點數除以零: "
+ (100 / dblzero));
if(dblzero == 0)
throw new ArithmeticException();
}
catch(ArithmeticException e) {
System.out.println("發生除零例外");
}
}
}

執行結果:
浮點數除以零: Infinity
發生除零例外


每個例外都必須有一個"catch"區塊來捕捉,在巢狀的try...catch時,必須注意該例外是由何者引發並由何者捕捉,例如:
  • UseThrow.java
public class UseThrow { 
public static void main(String[] args) {
try {
try {
throw new ArrayIndexOutOfBoundsException();
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(
"ArrayIndexOutOfBoundsException/內層try-catch");
}

throw new ArithmeticException();
}
catch(ArithmeticException e) {
System.out.println("發生ArithmeticException");
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(
"ArrayIndexOutOfBoundsException/外層try-catch");
}
}
}

執行結果:

ArrayIndexOutOfBoundsException/內層try-catch
發生ArithmeticException 

在這個程式中,ArrayIndexOutOfBoundsException由內層try-catch丟出並捕捉,由於內層 已經捕捉了例外,所以外層的try-catch中之ArrayIndexOutOfBoundsException並不會捕捉到內層所丟出的例外,但如果 內層的try-catch並沒有捕捉到這個例外,則外層try-catch就有機會捕捉這個例外,例如:

  • UseThrow.java
public class UseThrow { 
public static void main(String[] args) {
try {
try {
throw new ArrayIndexOutOfBoundsException();
}
catch(ArithmeticException e) {
System.out.println(
"ArrayIndexOutOfBoundsException/內層try-catch");
}

throw new ArithmeticException();
}
catch(ArithmeticException e) {
System.out.println("發生ArithmeticException");
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(
"ArrayIndexOutOfBoundsException/外層try-catch");
}
}
}

執行結果:
ArrayIndexOutOfBoundsException/外層try-catch

程式中會訂定許多方法(Method),這些方法中可能會因某些錯誤而引發例外,但您不希望直接在這個方法中處理這些例外,而希望呼叫這個它的方法來統一處理,這時候您可以使用"throws"關鍵字來宣告這個方法將會丟出例外,例如:

private void arrayMethod(int[] arr)
                   throws ArrayIndexOutOfBoundsException,
                          ArithmeticException {
    // 實作
}

 注意如果會丟出多種可能的例外時,中間使用逗點分隔;當有方法上使用"throws"宣告例外時,意味著呼叫該方法的呼叫者必須處理這些例外,而被呼叫方法可以保持程式邏輯的簡潔,下面這個範例是"throws"的一個簡單示範:

  • UseThrows.java
public class UseThrows { 
public static void main(String[] args) {
try {
throwsTest();
}
catch(ArithmeticException e) {
System.out.println("捕捉例外");
}
}

private static void throwsTest()
throws ArithmeticException {
System.out.println("這只是一個測試");
// 程式處理過程假設發生例外
throw new ArithmeticException();
}
}

執行結果:
這只是一個測試
 捕捉例外

簡單的說,您要不就在方法中直接處理例外,要不就在方法上宣告該方法會丟回例外,由呼叫它的呼叫者來處理例外,另一方面,在方法上使用 "throws"宣告可丟出的例外,也表示了您只能丟出所宣告類型的例外,其它的例外您必須在方法中處理完,或是重新包裝為所宣告的例外再丟出。

如果使用繼承時,在父類別的某個方法上宣告了throws某些例外,而在子類別中重新定義該方法時,您可以:
  • 不處理例外(重新定義時不設定throws)
  • 可僅throws父類別中被重新定義的方法上之某些例外
  • 可throws被重新定義的方法上之例外之子類別

但是您不可以:
  • throws出額外的例外
  • throws被重新定義的方法上之例外之父類別