1. 前言
在程式設計中,常常會遇到這樣的情況:方法碰到錯誤,但不知道該如何正確處理。例如,方法要讀取檔案,但不清楚當檔案不存在時該怎麼辦:詢問使用者?結束程式?改讀別的檔案?在這種情況下,方法可以「轉交責任」給呼叫者——也就是把例外「往上拋」到呼叫鏈的下一層。
拋出例外是一種機制,允許方法不必自行處理錯誤,而是告訴呼叫端:「我這裡出了問題,請你自己決定怎麼解決!」
類比:想像你是客服中心人員。客戶打來詢問一個你不知道答案的問題。與其胡亂猜測,你會說:「請稍候,我幫您轉接專家。」你就是把問題「往上轉交」。
關鍵字 throws:如何運作
在 Java 中,拋出例外是透過在方法宣告中使用關鍵字 throws 來完成。
語法:
tip_vozvrashchaemogo_znacheniya imyaMetoda(...) throws ExceptionType
{
// 方法的程式碼
}
在 throws 後面要註明此方法可能拋出的例外型別。這就像是對其他開發者的警示:「注意!這個方法可能會拋出某種型別的例外,請做好準備!」
範例:
public void readFile(String filename) throws FileNotFoundException
{
FileReader reader = new FileReader(filename); // 可能會拋出 FileNotFoundException
// ...
}
這裡,readFile 方法不自行處理錯誤,而是宣告:「我可能會拋出 FileNotFoundException——讓呼叫我的人自己決定怎麼辦。」
2. 呼叫端應該如何因應?
如果你要呼叫一個在宣告中有 throws 的方法,你有兩種選擇:
- 用 try-catch 處理例外
- 也把例外再往上拋(在自己的方法上加上 throws)
作法一:用 try-catch 處理
public static void main(String[] args)
{
try
{
readFile("data.txt");
}
catch (FileNotFoundException e)
{
System.out.println("找不到檔案:" + e.getMessage());
}
}
在這裡我們捕捉例外並自行決定該怎麼做(例如向使用者輸出訊息)。
作法二:往上拋出
public static void main(String[] args) throws FileNotFoundException
{
readFile("data.txt");
}
現在,處理錯誤的責任就落在呼叫 main 的一方(通常是 JVM;如果錯誤未被處理,程式將會以錯誤訊息結束)。
3. 範例:帶有拋出例外的檔案讀取
包含 IOException 並在 main 層級處理的完整範例:
import java.io.*;
public class FileDemo
{
// 方法宣告自己可能拋出 IOException
public static void printFirstLine(String filename) throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line = reader.readLine();
System.out.println("第一行:" + line);
reader.close();
}
public static void main(String[] args)
{
try
{
printFirstLine("nofile.txt");
}
catch (IOException e)
{
System.out.println("讀取檔案時發生錯誤:" + e.getMessage());
}
}
}
方法 printFirstLine 不知道在檔案不存在時該怎麼辦——它只是把例外往上拋。在 main 中我們捕捉錯誤並輸出訊息。
4. 實用細節
何時以及為什麼要拋出例外?
- 當方法無法或不應決定如何處理錯誤(例如函式庫程式碼)。
- 當錯誤處理取決於情境(有時結束程式,有時改讀別的檔案)。
- 為了避免在不需要的地方以過多的 try-catch 使程式碼變得雜亂。
Best practice: 當你無法有意義地處理錯誤時,就把例外拋出。不要只為了「走流程」而捕捉例外!
可以拋出多種例外
public void process() throws IOException, SQLException
{
// ...
}
Checked 與 unchecked 例外:複習
Checked exceptions(例如 IOException、SQLException)——編譯器要求你要么處理它們、要么拋出它們。
Unchecked exceptions(例如 NullPointerException、IllegalArgumentException)——編譯器不要求處理,不需要在 throws 中列出。
5. 拋出例外時的常見錯誤
錯誤 1:忘記為 throws 宣告 checked 例外。
如果方法可能拋出 checked 例外,但你沒有在 throws 中宣告、也沒有用 try-catch 處理——編譯器會報錯。
錯誤 2:捕捉了例外卻不處理。
寫 catch (Exception e) {} 是不良實務!如果不知道該怎麼處理,寧可把例外再往上拋。
錯誤 3:拋出過於寬泛的型別。
如果方法只可能拋出 FileNotFoundException,就不要寫 throws Exception——這會讓程式碼更難理解。
錯誤 4:在 throws 中列出 unchecked 例外。
寫 throws NullPointerException 沒有意義——編譯器不要求,而且對錯誤處理沒有幫助。
GO TO FULL VERSION