1. 程式人生 > >Java——異常

Java——異常

pri 聲明 業務 string pre mis ice 算術運算 而不是

  Java 的異常機制主要依賴於try 、catch 、finally 、throw 和 throws 五個關鍵字,其中 try 關鍵字後緊跟一個花括號括起來的代碼塊(花括號不能省略),簡稱 try 塊,它裏面放置可能引發異常代碼。 catch 後對應異常類型和一個代碼塊,用於表明該 catch 塊用於處理這種類型代碼塊 。 多個 catch 塊後還可以跟一個 finally 塊,finally塊用於回收在 try 塊裏打開的物理資源,異常機制會保證 finally 塊總會被執行。 throws 關鍵字主要在方法簽名中使用,用於聲明該方法可能拋出的異常;而 throw 用於拋出一個實際的異常, throw 可以單獨作為語句使用,拋出一個具體異常對象。

演示程序中異常:

public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1=input.nextInt();
        System.out.println("請輸入被除數:");
        int num2= input.nextInt();
        System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2));
        System.
out.println("感謝使用本程序!"); }

  在正常情況下,用戶會按照系統的提示輸入整數,除數不為 0 ,運行結果如下:

    技術分享

但是用戶沒按照要求進行輸入,如被除數輸入了“B”,則程序運行時將會發生異常,運行如下:

技術分享

或者輸入 0,則程序運行時也將發生異常,運行如圖:

技術分享

從結果我們可以看出一旦出現異常,程序將會立刻結束,不僅計算機和輸出商的語句不被執行,而且輸出“感謝使用本程序!” 的語句也不執行。那麽我們該如何解決異常呢?如果我們用 if ——else語句來對各種異常情況進行來處理。我們會發現代碼加入了大量的異常情況判斷和處理代碼,把很大一部分時間放在了處理異常代碼上,放在了“堵漏洞”上,減少了編寫業務代碼的時間,必然影響開發效率。

  那麽同樣一段代碼,把可能出現異常的代碼放入 try 語句塊中,並使用 catch 語句塊捕獲異常。

public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1=input.nextInt();
        try {
            System.out.println("請輸入被除數:");
            int num2= input.nextInt();
            System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2));
            System.out.println("感謝使用本程序!");
        } catch (Exception e) {
            System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0");
            e.printStackTrace();
        }
    }

再次輸入以上的三種情況,

第一種:

技術分享

第二種:

技術分享

第三種:

技術分享

如果希望無論是否發生異常,都執行輸出“感謝使用蹦本程序!” 的語句,,那麽只需要在 try——catch 語句塊後加 finally 塊,把該語句放入finally 塊,無論發生異常,finally 開中的代碼總能被執行。

public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1=input.nextInt();
        try {
            System.out.println("請輸入被除數:");
            int num2= input.nextInt();
            System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2));
            
        } catch (Exception e) {
            System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0");
            e.printStackTrace();
        }
        finally{
            System.out.println("感謝使用本程序!");
        }
    }

結果:

技術分享

即使在 try 塊和 catch 塊中存在 return 語句,finally 塊中語句也會被執行。

public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1=input.nextInt();
        try {
            System.out.println("請輸入被除數:");
            int num2= input.nextInt();
            System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2));
            return;     //finally語句塊仍舊會被執行
        } catch (Exception e) {
            System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0");
            return; //finally語句塊仍舊會被執行
        }
        finally{
            System.out.println("感謝使用本程序!");
        }
    }

執行順序:執行 try 塊或 catch 中之前的語句,執行 finally 塊中的語句,執行 try 塊或 catch 中的 return 語句退出。

運行結果:

技術分享

finally 塊中語句不被執行的唯一情況:在異常處理代碼中執行System.exit(1),將退出Java虛擬機。

public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1=input.nextInt();
        try {
            System.out.println("請輸入被除數:");
            int num2= input.nextInt();
            System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2));
        } catch (Exception e) {
            System.out.println("出現錯誤:被除數和除數必須是整數,"+"除數不能為0");
            System.exit(1);
        }
        finally{
            System.out.println("感謝使用本程序!");
        }
    }

運行結果:

技術分享

》多重catch塊

  一段代碼可能會引發多種類型異常,這時,可以在一個 try 語句塊後面跟多個 catch 語句塊,分別處理不同的異常。但排列順序必須是從子類到父類,最後一個一般都是Execption 類,因為所有異常子類都繼承自 Exception類,所以如果將父類異常放到前面,那麽所有的異常都將被捕獲,後面 catch 塊中的子類異常講的不到被執行的機會。當運行時,系統沖上到下分別對每個 catch 語句塊處理的異常的異常類型進行檢測,並執行第一個與異常類型匹配的 catch語句。執行其中的一條 catch 語句之後,其後的 catch 語句都將被忽略。

public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1=input.nextInt();
        try {
            System.out.println("請輸入被除數:");
            int num2= input.nextInt();
            System.out.println(String.format("%d /%d =%d", num1,num2,num1/num2));
            
        } catch (InputMismatchException e) {
            System.err.println("被除數和除數必須是整數。");
        }catch (ArithmeticException e) {
            System.err.println("除數不能為零。");
        } catch (Exception e) {
            System.err.println("其它未知。");
        }finally{
            System.out.println("感謝使用本程序!");
        }
    }

運行結果:

技術分享

》聲明異常——throws

  如果一個方法體中拋出異常,我們就希望調用者能及時地捕獲異常,那麽如何通知調用著呢?Java 語言中通過關鍵字 throws 聲明某個方法可能拋出的各種異常,throws 可以拋出多個異常,之間用逗號隔開。

public static void main(String[] args) {

        try {
            divide();

        } catch (Exception e) {
            System.err.println("出現錯誤:被除數和除數必須是整數" + "除數不能為零");

        } finally {
            System.out.println("感謝使用本程序!");
        }
    }

    public static void divide() throws Exception {
        Scanner input = new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1 = input.nextInt();
        System.out.println("請輸入被除數:");
        int num2 = input.nextInt();
        System.out.println(String.format("%d /%d =%d", num1, num2, num1 / num2));
    }

  從上面看出 divide() 方法使用 throws 聲明拋出了異常,並在 main 方法裏面調用了此方法,通過 try —— catch 捕獲了異常。如果不通過 try ——catch 處理異常,我們可以繼續使用 throws 拋出異常

public static void main(String[] args) throws Exception {

            divide();

    }

    public static void divide() throws Exception {
        Scanner input = new Scanner(System.in);
        System.out.println("請輸入被除數:");
        int num1 = input.nextInt();
        System.out.println("請輸入被除數:");
        int num2 = input.nextInt();
        System.out.println(String.format("%d /%d =%d", num1, num2, num1 / num2));
    }

  但是使用這種方法需要註意:調用者拋出的異常一定要比被調用拋出的異常更大或等於。

》拋出異常——throw

  除了系統自動拋出異常,在編程過程中,我們往往遇到這樣的情形:有些問題是系統無法自動發現並解決的,如年齡不在正長範圍內、性別輸入不是 “男” 或 “女” 等,此時需要程序員而不是系統來自動拋出異常,把問題提交給調用者去解決。

》使用 throw 在方法內拋出異常
public class Person {

    private String sex;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) throws Exception {
        if ("".equals(sex) || "".equals(sex)) {
            this.sex = sex;
        } else {
            throw new Exception("性別必須是\"男\"或者\"女\"!");
        }
    }

}

public static void main(String[] args) throws Exception {

        Person person = new Person();
        person.setSex("hao");
    }

輸出結果:

技術分享

throw 和 throws 的區別表現在以下三個方面。

  》作用不同 : throw 用於在程序中拋出異常;throws 用於聲明在該方法內拋出了異常。

  》使用位置不同:throw 位於方法體內部,可以作為單獨語句使用;throws 必須跟在方法參數列表的後面,不能單獨使用。

  》內容不同:throw 拋出一個異常對象,而且只能是一個;throws 後面跟異常類,而且可以跟多個以異常類。

》異常的分類

技術分享

  Throwable 類 :所有異常類型都是 Throwable 類的子類,它派生兩個子類,即Error 和 Exception 。

  Error類 :表示緊靠程序本身無法恢復的嚴重錯誤,如內存溢出動態鏈接失敗、虛擬機錯誤。應用程序不應該拋出這種類型的對象(一般是由虛擬機拋出)。假如出現這種錯誤,除了盡力使程序安全退出外,在其他方面是無能為力的。所以在進行程序設計時,應該更關註Exception類。

  Exception 類:由Java 應用程序拋出和處理的嚴重錯誤,如需文件找不到、網絡連接不通或中斷。算術運算出錯(如被零除)、數組下標越界、裝載了一個不存在的類、對 null 對象的操作、類型轉換異常類等。他的各種不同的子類分別對應不同類型的異常。

  運行時異常:包括RuntimeException 及其所有子類,不要求程序必須對它們做出處理。

  Checked 異常(非運行時異常):除了運行時異常外的其他由Exception 繼承來的異常類。程序必須捕獲或者聲明拋出這種異常,否則會出現編譯錯誤,無法通過編譯。處理方式包括來鐘:通過 try——catch 在當前位置捕獲並處理異常;通過throws 聲明拋出異常,交給上一級調用方法處理。

》自定義異常

  當 JDK 中的異常類型不能滿足出現的需要時,可以自定義異常類。

public class GenderException extends Exception {

    public GenderException() {
        super();
    }

    public GenderException(String message, Throwable cause) {
        super(message, cause);
    }

    public GenderException(String message) {
        super(message);
    }

    public GenderException(Throwable cause) {
        super(cause);
    }

}

public class Person {

    private String sex;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) throws Exception {
        if ("".equals(sex) || "".equals(sex)) {
            this.sex = sex;
        } else {
            throw new GenderException("性別必須是\"男\"或者\"女\"!");
        }
    }

}

Java——異常