Java宣告和丟擲異常:throws宣告異常、throw丟擲異常、throw和throws的區別

2020-07-16 10:05:17
Java 中的例外處理除了包括捕獲異常和處理異常之外,還包括宣告異常和拋出異常,可以通過 throws 關鍵字在方法上宣告該方法要拋出的異常,然後在方法內部通過 throw 拋出異常物件。本節詳細介紹在 Java 中如何宣告異常和拋出異常。

throws 關鍵字和 throw 關鍵字在使用上的幾點區別如下
  • throws 用來宣告一個方法可能丟擲的所有異常資訊,throw 則是指拋出的一個具體的異常型別。
  • 通常在一個方法(類)的宣告處通過 throws 宣告方法(類)可能拋出的異常資訊,而在方法(類)內部通過 throw 宣告一個具體的異常資訊。
  • throws 通常不用顯示地捕獲異常,可由系統自動將所有捕獲的異常資訊拋給上級方法; throw 則需要使用者自己捕獲相關的異常,而後再對其進行相關包裝,最後將包裝後的異常資訊丟擲。

throws 宣告異常

當一個方法產生一個它不處理的異常時,那麼就需要在該方法的頭部宣告這個異常,以便將該異常傳遞到方法的外部進行處理。可以使用 throws 關鍵字在方法的頭部宣告一個異常,其具體格式如下:
returnType method_name(paramList) throws Exception 1,Exception2,…{…}

其中,returnType 表示返回值型別,method_name 表示方法名,Exception 1,Exception2,… 表示異常類。如果有多個異常類,它們之間用逗號分隔。這些異常類可以是方法中呼叫了可能拋出異常的方法而產生的異常,也可以是方法體中生成並拋出的異常。

例 1

建立一個 readFile() 方法,該方法用於讀取檔案內容,在讀取的過程中可能會產生 IOException 異常,但是在該方法中不做任何的處理,而將可能發生的異常交給呼叫者處理。在 main() 方法中使用 try catch 捕獲異常,並輸出異常資訊。程式碼如下:
import java.io.FileInputStream;
import java.io.IOException;

public class Test04 {
    public void readFile() throws IOException {
        // 定義方法時宣告異常
        FileInputStream file = new FileInputStream("read.txt"); // 建立 FileInputStream 範例物件
        int f;
        while ((f = file.read()) != -1) {
            System.out.println((char) f);
            f = file.read();
        }
        file.close();
    }

    public static void main(String[] args) {
        Throws t = new Test04();
        try {
            t.readFile(); // 呼叫 readFHe()方法
        } catch (IOException e) {
            // 捕獲異常
            System.out.println(e);
        }
    }
}
以上程式碼,首先在定義 readFile() 方法時用 throws 關鍵字宣告在該方法中可能產生的異常,然後在 main() 方法中呼叫 readFile() 方法,並使用 catch 語句捕獲產生的異常。

注意:在編寫類繼承程式碼時要注意,子類在覆蓋父類別帶 throws 子句的方法時,子類的方法宣告中的 throws 子句不能出現父類別對應方法的 throws 子句中沒有的異常型別,因此 throws 子句可以限制子類的行為。也就是說,子類方法拋出的異常不會超過父類別定義的範圍。

throw 拋出異常

throw 語句用來直接拋出一個異常,後接一個可拋出的異常類物件,其語法格式如下:
throw ExceptionObject;

其中,ExceptionObject 必須是 Throwable 類或其子類的物件。如果是自定義異常類,也必須是 Throwable 的直接或間接子類。例如,以下語句在編譯時將會產生語法錯誤:
throw new String("拋出異常");    // 因為String類不是Throwable類的子類

當 throw 語句執行時,它後面的語句將不執行,此時程式轉向呼叫者程式,尋找與之相匹配的 catch 語句,執行相應的例外處理程式。如果沒有找到相匹配的 catch 語句,則再轉向上一層的呼叫程式。這樣逐層向上,直到最外層的例外處理程式終止程式並列印出呼叫棧情況。

例 2

在某倉庫管理系統中,要求管理員的使用者名稱需要由 8 位以上的字母或者數位組成,不能含有其他的字元。當長度在 8 位以下時拋出異常,並顯示異常資訊;當字元含有非字母或者數位時,同樣拋出異常,顯示異常資訊。程式碼如下:
import java.util.Scanner;

public class Test05 {
    public boolean validateUserName(String username) {
        boolean con = false;
        if (username.length() > 8) {
            // 判斷使用者名稱長度是否大於8位元
            for (int i = 0; i < username.length(); i++) {
                char ch = username.charAt(i); // 獲取每一位字元
                if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
                    con = true;
                } else {
                    con = false;
                    throw new IllegalArgumentException("使用者名稱只能由字母和數位組成!");
                }
            }
        } else {
            throw new IllegalArgumentException("使用者名稱長度必須大於 8 位!");
        }
        return con;
    }

    public static void main(String[] args) {
        Test05 te = new Test05();
        Scanner input = new Scanner(System.in);
        System.out.println("請輸入使用者名稱:");
        String username = input.next();
        try {
            boolean con = te.validateUserName(username);
            if (con) {
                System.out.println("使用者名稱輸入正確!");
            }
        } catch (IllegalArgumentException e) {
            System.out.println(e);
        }
    }
}
如上述程式碼,在 validateUserName() 方法中兩處拋出了 IllegalArgumentException 異常,即當使用者名稱字元含有非字母或者數位以及長度不夠 8 位時。在 main() 方法中,呼叫了 validateUserName() 方法,並使用 catch 語句捕獲該方法可能拋出的異常。

執行程式,當使用者輸入的使用者名稱包含非字母或者數位的字元時,程式輸出異常資訊,如下所示。
請輸入使用者名稱:
[email protected]#
java.lang.IllegalArgumentException: 使用者名稱只能由字母和數位組成!

當使用者輸入的使用者名稱長度不夠 8 位時,程式同樣會輸出異常資訊,如下所示。
請輸入使用者名稱:
admin
java.lang.IllegalArgumentException: 使用者名稱長度必須大於 8 位!