要抓還是要丟?


此 文件已有 新版本

假設今天你受命開發一個程式庫,例如一個存取檔案的程式好了,你也許會這麼寫:
import java.io.*;
public class FileUtil {
    public static String readText(String file) {
        String text = null;
        FileReader reader = null;
        try {
            reader = new FileReader(file);
            ...
        }
        catch(FileNotFoundException ex) {
            System.out.println("找不到檔案" + file);
        }
        return text;
    }
    ....
}

由 於操作檔案讀取的過程中,有許多方法可能丟出受檢例外(Checked Exception),你可能如上使用try..catch加以處理,在catch中以主控台方式輸出錯誤訊息,但問題在於,你並不知道你的程式庫會用在 什麼環境,是文字模式?視窗模式?或是Web應用程式?直接在catch中寫死處理處理例外或輸出錯誤訊息的方式,是一件不符合需求的方式。

如果你的方法設計流程中,發生例外時,當時的上下文環境並不知道該如何 處理(例如你並不知道程式庫會用在什麼環境下時),那麼你可以丟出例外,讓呼叫你的方法的客戶端來處理。例如:
import java.io.*;
public class FileUtil {
    public static String readText(String file) throws FileNotFoundException {
        String text = null;
        FileReader reader = new FileReader(file)
        ...
        return text;
    }
    ....
}

操作物件的過程中如果丟出受檢例外,而你不使用try..catch處 理,則表示目前環境資訊不足以處理例外,必須由屆時使用該方法的客戶端依當時環境資訊進行處理,為了告訴編譯器這件事實,則必須在方法上使用throws 宣告會丟出例外,那麼編譯器才會讓你通過編譯。

如果是非受檢例外(Unchecked Exception),由於你可以自行選擇是否處理例外,不使用try..catch處理時也不用特別在方法上使用throws宣告,當你不處理非受檢例 外,例外自動往外傳播。

當例外發生時,你可以使用try..catch處理當時環境下所可以作的錯誤處理,對於當時環境下無法決定如何處理的部份,可以丟出去給屆時的客戶端處 理。如果你想先處理部份事項再丟出,則一個例子如下:
import java.io.*;
public class FileUtil {
    public static String readText(String file) throws IOException {
        String text = null;
        FileReader reader = null;
        try {
            reader = new FileReader(file);
            ...
        }
        catch(FileNotFoundException ex) {
            // Logging 或 ex.printstackTrace()
            // 其它處理
            throw ex;
        }
        finally {
            if(reader != null) {
                try {
                    reader.close();
                }
                catch(IOException ex) {
                    // Logging 或 ex.printstackTrace()
                    // 其它處理
                    throw ex;
                }
            }
        }

        return text;
    }
    ....
}

在進行完部份錯誤處理之後,你可以使用throw將例外再丟出,當你在流程中丟出例外,就 直接跳離原有的流程。如果流程正常執行,最後一定要執行的動作可以放在finally區塊中。

一個有趣的問題是,如果程式撰寫的流程中先return了,而你也有寫finally區塊,那finally區塊還會執行嗎?答案是肯定的, finally區塊會先執行完後,再執行return。例如下面這個程式會先顯示「finally...」再顯示「1」:
public class Main {   
    public static int test(boolean flag) {
        try {
            if(flag) {
                return 1;
            }
        }
        finally {
            System.out.println("finally...");
        }
        return 0;
    }
   
    public static void main(String[] args) {
        System.out.println(test(true));
    }
}