首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 異常處理核心:throws 與 throw 完整指南

     編輯:Java 相關知識

前言:異常處理的兩把關鍵刷

Java 異常機制中,throws(聲明拋出)與 throw(拋出實例)是方法簽名與方法體內的分工。搞清二者差異、檢查型異常規則、自定義異常最佳實踐,是寫出健壯代碼的基石。

核心差異速覽

關鍵字位置作用後跟內容
throws方法聲明聲明方法可能拋出的異常類型,轉嫁處理責任給調用者異常類列表(用逗號分隔)
throw方法體內實際拋出一個異常對象實例,中斷當前執行流程new ExceptionType() 實例

throws:聲明拋出——契約式設計

語法

修飾符 返回類型 方法名(參數列表) throws ExceptionType1, ExceptionType2 { ... }

規則鐵律

  1. 檢查型異常(繼承 Exception 但非 RuntimeException):必須用 throws 聲明或 try-catch 捕獲,否則編譯報錯。
  2. 非檢查型異常(Error、RuntimeException 及子類):可選聲明,編譯器不強制,運行時由 JVM 自動拋出。
  3. 覆寫方法:子類拋出的異常必須是父類聲明異常的同類或子類(里氏替換原則)。
  4. 調用鏈傳遞:若方法 A 調用 B,B throws IOException,A 必須 catch 或繼續 throws。

代碼示範

import java.io.*;

public class ThrowsDemo {
    // 聲明拋出受檢異常
    static void readFile(String path) throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream(path); // 可能拋 FileNotFoundException
        int b;
        while ((b = fis.read()) != -1) { // 可能拋 IOException
            System.out.print((char) b);
        }
        fis.close();
    }
    
    public static void main(String[] args) {
        try {
            readFile("data.txt"); // 調用者必須處理
        } catch (FileNotFoundException e) {
            System.err.println("檔案不存在:" + e.getMessage());
        } catch (IOException e) {
            System.err.println("讀取失敗:" + e.getMessage());
        }
    }
}

throw:拋出實例——主動觸發

語法

throw new ExceptionType("錯誤描述");

核心規則

  • 只能拋出 Throwable 或其子類實例。
  • throw 後的代碼不可達,編譯器會報錯。
  • 常用於參數校驗、業務規則違反、將捕獲異常轉換為業務異常。

代碼示範:參數校驗 + 自定義異常


// 自定義業務異常(繼承 RuntimeException 為非受檢,或 Exception 為受檢)
class BusinessException extends RuntimeException {
    private final int code;
    public BusinessException(int code, String msg) { super(msg); this.code = code; }
    public int getCode() { return code; }
}

public class ThrowDemo {
    static int divide(int a, int b) {
        if (b == 0) {
            // 主動拋出業務異常
            throw new BusinessException(400, "除數不能為零");
        }
        if (b < 0) {
            // 拋出受檢異常,需在方法聲明 throws
            throw new IllegalArgumentException("除數不能為負數"); // 非受檢,可不聲明
        }
        return a / b;
    }
    
    public static void main(String[] args) {
        try {
            int r = divide(10, 0);
        } catch (BusinessException e) {
            System.out.println("業務錯誤 [" + e.getCode() + "]:" + e.getMessage());
        }
    }
}

兩者協作:經典模式

模式一:捕獲轉換 + throws 透傳

// 底層方法:捕獲 IO 異常,轉為業務異常拋出
void saveUser(User u) throws BusinessException {
    try {
        db.insert(u);
    } catch (SQLException e) {
        // 記錄完整堆疊,拋出業務語義異常
        throw new BusinessException(500, "用戶保存失敗", e); // 支持 cause 鏈
    }
}

// 上層調用:繼續 throws 或 catch 處理
void register(User u) throws BusinessException {
    saveUser(u); // 透傳
}

模式二:try-with-resources 自動關閉 + 異常掩蓋處理

void process(File file) throws AppException {
    try (InputStream in = Files.newInputStream(file.toPath())) {
        // 處理流
    } catch (IOException e) {
        // 若 try 塊也拋異常,close() 拋出的異常會被 suppressed
        throw new AppException("處理失敗", e);
    }
}

異常設計最佳實踐

1. 異常分層


Exception (受檢:需聲明/捕獲)
 ├─ BusinessException (業務邏輯錯誤:參數非法、狀態不符)
 ├─ ResourceException (資源不可用:DB斷連、第三方超時)
 └─ SystemException (系統級錯誤:配置缺失、環境異常)

RuntimeException (非受檢:編程錯誤、不可恢復)
 ├─ IllegalArgumentException
 ├─ IllegalStateException
 └─ NullPointerException

2. 原則

  • 受檢異常:調用者能夠且應當恢復處理的場景(如檔案不存在、網絡超時)。
  • 非受檢異常:編程錯誤(參數非法)、不可恢復的系統故障(OOM、棧溢出)。
  • 不要吞沒異常:catch (Exception e) {} 禁止;至少記錄日誌或轉換拋出。
  • 保留原因鏈:throw new XxxException("msg", cause)。
  • 異常即文檔:方法 Javadoc 標註 @throws 條件與含義。

常見誤區

誤區正確做法
所有方法都 throws Exception精確聲明具體異常類型,便於調用者針對性處理
用異常控制業務流程用條件判斷、Optional、Result 模式;異常僅用於「異常」情況
catch 後只打印堆疊記錄結構化日誌、轉換業務異常、或補償重試
自定義異常不攜帶上下文攜帶錯誤碼、關鍵參數、原因鏈,支援快速定位

總結

  • throws 是方法契約,聲明「我可能拋什麼」,轉嫁責任。
  • throw 是執行動作,實際製造異常對象,中斷流程。
  • 受檢 vs 非受檢:可恢復用受檢,編程錯誤用非受檢。
  • 自定義異常體系 + 原因鏈 + 結構化日誌 = 可維護的錯誤處理。

掌握這兩把刷,你的代碼將在正確性、可讀性、可維護性上躍升一個台階。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved