例外處理(Exception Handling) 在Java中,程式錯誤的處理是由所謂的「例外處理機制」 (Exception-handling mechanism)來負責。
本章重點 什麼是「例外」 「例外」的種類 如何指定「例外」的處理方式 列印「例外」物件的說明 使用throws敘述 Throwable類別階層 自定「例外」 類別 Java Runtime如何決定「例外」處理常式 Java語言實務 例外處理
什麼是「例外」 影響程式中「指令敘述」正常執行順序的「異常狀態」;例如: 進行除法運算時,分母值為零 存取陣列元素時所用的索引值超出陣列的大小 程式中欲開啟以便讀取資料的檔案尚未存在 記憶體不足 硬碟無法正常運作 Java語言實務 例外處理
異常狀態發生時, Java Runtime會中止目前指令的執行、 產生此異常狀態的「例外」物件 (Exception Object) 並 決定如何進一步處理此異常狀態 Java語言實務 例外處理
「例外」的種類 Checked Exception Runtime Exception Exception UnChecked Exception Runtime Exception Error Java語言實務 例外處理
Runtime Exception及Error由Java系統自行處理。 Checked Exception的處理強制由程式設計師於程式中指定 UrlTest.java:10: Exception java.net.MalformedURLException must be caught , or it must be declared in the throws clause of this method. URL url = new URL(args[0]); Java語言實務 例外處理
如何指定「例外」的處理方式 由下述的程式結構決定: try { // 會產生例外的程式碼 java statements; } //可以有一個以的catch區塊 catch (ExceptionType1 exObject1) { // 處理例外型態一的程式碼 java statements-exception handling; } catch (ExceptionType2 exObject2) { // 處理例外型態二的程式碼 java statements-exception handling; } // … finally {// 例外處理結束前執行的程式碼 //本區塊可省略 java statements; } Java語言實務 例外處理
程式Ex7_1除數為零-程式範例 import java.io.*; class Exception1 { public static void main (String args[]) { int x, y; try { y = 0; x = 44 / y; //本敘述會產生除數為零的Runtime Exception System.out.println(“Actually this line will not be printed”); } catch (ArithmeticException e) { // catch並處理除數為零的錯誤 System.out.println(“Error-->Divided by zero!”); finally { //執行Finally區塊的指令 System.out.println(“The statement in the Finally Block will always be executed!”); } // end of main } // end of class Java語言實務 例外處理
try區塊的指令會產生一個除數為零的「例外」 程式輸出 Error-->Divide by zero! The statement in the Finally Block will always be executed! Java語言實務 例外處理
列印「例外」物件的說明 「例外」物件 的getMessage() 方法會傳回有關該「例外」物件的文字說明: 可能結果: catch (ArithmeticException e) { // catch並處理除數為零的錯誤 System.out.println(“Exception: ” + e.getMessage()); } 可能結果: Exception: / by zero The statement in the Finally Block will always be executed! Java語言實務 例外處理
使用throws敘述 throws關鍵字可以用來指定由外層的方法來來處理例外 使用throws關鍵字的一般規定: type method-name (parameter-list) throws exception-list { // body of method java statements; } Java語言實務 例外處理
程式Ex7_2使用throws-程式範例 import java.net.*; public class UrlTest {public static void main(String [] args) throws MalformedURLException {if (args.length ==1) {URL url = new URL(args[0]); System.out.println ("URL: " + url.toExternalForm() + "\n" + " File: " + url.getFile() + "\n" + " Host: " + url.getHost() + "\n" + " Port: " + url.getPort() + "\n" + " Protocol: " + url.getProtocol() + "\n" + " Reference: " + url.getRef()); } else System.out.println("Usage: UrlTest <URL>"); } } Java語言實務 例外處理
java UrlTest http://www.npic.edu.tw:8080 輸出結果: 執行方式: java UrlTest http://www.npic.edu.tw:8080 輸出結果: URL: http://www.npic.edu.tw File: / Host: www.npic.edu.tw Port: 8080 Protocol: http Reference: null 執行方式: java UrlTest amazon.com 輸出結果: Exception in thread "main" java.net.MalformedURLException: no protocol: amazon.com at java.net.URL.<init>(Unknown Source) at UrlTest.main(UrlTest.java:9) Java語言實務 例外處理
自行控制「例外」的產生 使用throw敘述觸發「例外」 狀態 一般語法: throw ThrowableInstance; ThrowableInstance物件必需是Throwable類別或其子類別的成員 Java語言實務 例外處理
程式Ex7_3 使用throw敘述 輸出結果: 程式Ex7_3 使用throw敘述 import java.io.*; class ThrowDemo { static void main(String args[]) { try { throw new IllegalAccessException(“demo Throw IllegalAccess Exception”); } catch (IllegalAccessException e) { System.out.println(“Exception caught: “ + e.getMessage()); 輸出結果: Exception caught: demo Throw IllegalAcess Exception Java語言實務 例外處理
Throwable類別階層 Object Throwable Error Exception RuntimeException ... Parent-child relation Java語言實務 例外處理
自定「例外」 類別 撰寫自定的「例外」: 首先必須自行定義一個屬於Throwable類別之後代的子類別 然後利用new指令建立一個自定之「例外」類別的物件 最後利用throw敘述觸發「例外」 Java語言實務 例外處理
程式Ex7_4 自定「例外」類別 (1/4) import java.io.*; // 自定例外類別 class InvalidTransferException extends Exception { InvalidTransferException(String emsg) super(emsg); // invoke the constructor of the parent class } } // end of class Java語言實務 例外處理
程式Ex7_4 自定「例外」類別 (2/4) // 轉帳處理類別 class ExceptionDemo { static int balance; // 存放餘額的變數 ExceptionDemo(int b) // 建構子 balance = b; } void transfer(int amount)throws InvalidTransferException if (amount > balance) // 當轉帳金額大於餘額時就產生例外 throw new InvalidTransferException("Transfer Amount greater than balance!"); else balance = balance - amount; // 否則將餘額更新成扣除轉帳金額後之值 } // end of method transfer } // end of class ExceptionDemo Java語言實務 例外處理
程式Ex7_4 自定「例外」類別 (3/4) // 測試自定例外類別的使用 public class MyExceptionDemo { public static void main (String args[]) {// 產生一個處理轉帳的類別並將餘額初值設為1500 ExceptionDemo edemo = new ExceptionDemo(1500); System.out.println("Balance before transfer:" + edemo.balance); try {edemo.transfer(2000); // 進行轉帳2000元的交易 System.out.println("Current Balance:" + ExceptionDemo.balance); } catch (InvalidTransferException e) {System.out.println("Exception caught: " + e.getMessage()); } // end of method main } // end of class MyExceptionDemo Java語言實務 例外處理
程式Ex7_4 自定「例外」類別 (4/4) 執行結果: Balance before transfer:1500 Exception caught: Transfer Amount greater than balance! Java語言實務 例外處理
Java Runtime如何決定「例外」處理常式 基本原則 「由內而外」 「由上而下」 「順序優先」 Java語言實務 例外處理
「由內而外」 當有巢狀式的try區塊時,Java Runtime會先從與最內層的try區塊所對應的catch區塊去尋找適當的「例外」處理常式,若沒有才去尋找外層的catch區塊, try { // 程式區塊一 try // 程式區塊二 { try // 程式區塊三 java statements; } catch(…) { //exception handler 3} catch(…) { //exception handler 2} catch(…) { //exception handler 1} Java語言實務 例外處理
「由上而下」 當方法a呼叫方法b而方法b又呼叫方法c時,若於方法c的執行過程中產生了「例外」,則Java Runtime會依「呼叫堆疊」(Call Stack) 中各方法出現的順序依序從上而下尋找處理常式 method_a // 在method_a的try區塊內呼叫 method_b { try { … } catch (Exception e) {//method_c, method_b及method_a所產生的例外都會在此區塊處理 …} } method_b throws Exception // method_b 呼叫method_c {… } method_c throws Exception //method_c可能會產生例外 {… } Call stack c b a Java語言實務 例外處理
「順序優先」 當一個try區塊對應有一個以上的catch區塊時,Java Runtime會按照各catch區塊在程式中出現的順序尋找合適的處理常式,找到之後就執行該處理常式的指令,其它的catch區塊則跳過不執行 。 try {// java statements … } catch(Exception e) {// Exception handling routine catch(ArithmeticExceeption ae) {// Arithmetic Exception handling routine catch(ArrayIndexOutOfBoundsException aiob) { … } 請注意: 右例簡碼中 catch區塊出現的順序 並不好,編譯時 編譯器會視為有誤。 Java語言實務 例外處理