1. 程式人生 > 其它 >Java的異常處理概述

Java的異常處理概述

異常處理

防錯程式設計是一個優秀程式中不可或缺的一部分,一個好的軟體,應該將錯誤通過自身的程式進行消化,而不是將錯誤拋給使用者。因此,程式中要用到異常處理。Java看待異常的思想是將異常看成是異常類的物件來處理。

異常的概念

異常也就是導致程式不能正常執行的非正常情況,可以導致程式發生異常的情況有許多,Java自身也定義了一些異常類,比如:ArrayIndxOutOfBoundsException(陣列下標越界),ArithmeticException(算數運算錯誤,如被0除),ClassNotFoundeException(未找到裝載的類),NullPointerException(空指標訪問),NumberFormatException(數字格式錯誤)等。

1.異常的簡介

異常可以分為:檢查性異常和執行時異常。前者是在程式編寫時,系統就自動檢測到了異常,後者是在程式編譯完成後,系統出現的異常,常見的是由於使用者輸入的內容不符合要求而產生的異常。

【舉例】

public class Test {
   public static void main(String[] args) {
      int a=1;
      int b=0;
       System.out.println(a/b); //被除數不能為0
  }
}

上面的程式碼屬於執行時異常,也就是在程式執行後,系統才顯示出異常,顯示的結果為:Exception in thread "main" java.lang.ArithmeticException: / by zero at com.blog.Test.main(Test.java:6)。也就是改程式有ArithmeticException異常。

那麼如何修改程式已達到異常處理的目的呢?Java提供了幾種程式塊來處理異常或可能有異常的程式碼。它們分別是try,catch,finally,throw,throws。

那麼以上程式碼改如何修改系統才不會報出異常呢?可以利用try{}catch(){}來對出現異常的程式碼塊進行處理。修改結果如下:

public class Test {
   public static void main(String[] args) {
      int a=1;
      int b=0;
       try{        
           System.out.println(a/b);
      }catch (ArithmeticException e){
           System.out.println("被除數不可為0");
      }
  }
}

輸出結果為:被除數不可為0。此外,IDEA有快速生成try catch的快捷鍵,步驟是:選中要異常處理的程式碼塊,然後按住Ctrl+Alt+T,就會在出現的列表中發現try catch程式塊。

2.異常的類層次

Java的異常類是處理異常時錯誤的特殊類,每一種異常對應一種特定的執行錯誤。所有的異常類都是系統類庫中Exception類的子類,其繼承結構如下:

 

  • Throwable類:是異常類層次中的最高層,其中第一了一個異常描述串以及可獲取該描述的getMessage()方法。

  • Error類:是JVM系統內部錯誤,程式不能對其進行處理。

  • Exception類:該類就是我們要進行處理的異常類。

異常處理結構

進行異常處理必須使用try程式塊,將可能產生錯誤的程式碼放在try塊中,當JVM執行時發現異常時,會立即停止執行後續程式碼,然後查詢異常處理塊,對catch塊按次序進行匹配檢查,一但找到一個匹配者,就執行該catch塊中的程式碼,且不再檢查後面的catch塊。如果try塊中沒有異常,則忽略catch塊。

1.具體格式:

try{
   語句塊;
}catch(異常型別 參變數名){語句塊;}
finally{語句塊;}

【說明】

  • 可以把try看成是提醒系統,在try塊中的程式可能會出現異常,try塊後可接多個catch塊。

  • 一旦發現異常,try中的剩餘語句不再執行。

  • catch語句塊用於捕捉和處理異常物件。當try塊出現異常時,執行系統會將異常物件傳遞給catch中的參變數,在catch塊中可以通過該物件獲取具體的異常資訊。

  • finally部分是可以沒用的,但如果存在,則無論異常是否發生,finally部分的語句必須執行。即便是try或catch中含有退出方法的語句return,也不能阻止finally中程式碼塊的執行。

2.throw與throws

一般情況下,程式執行過程中發現異常時,系統就會產生一個含有異常的描述資訊和異常的位置的異常物件,產生異常物件的過程就叫做異常的丟擲(throw)。當產生異常物件後,若為編寫異常處理的程式碼,系統將自動返回異常物件的資訊,即異常的描述和位置。而要使程式嚴密且好用,那麼我們就需要對異常進行處理,以接受和處理異常物件。

  • throw

系統自定義的執行異常都可由系統在執行時自動丟擲,這種異常丟擲方式稱為隱式丟擲。而自己設計的異常,需要我們自己通過throw語句丟擲。

【例】丟擲MyException異常

class MyException extends Exception{//自定義的異常類
   String id;//異常標識
   public MyException(String str){//異常類的構造方法
       id=str;
  }
   public String toString(){
       return("異常:"+id);
  }

}
public class Test {
   public static void main(String[] args) {
     try{
         throw new MyException("輸入錯誤");//丟擲一個異常
    }catch (MyException e){//捕捉並處理異常
         System.out.println(e);
    }
  }
}

輸出結果:異常:輸入錯誤

【說明】

(1)一個throw語句只能丟擲一個異常物件。

(2)throw語句會改變程式的執行流程。一般來說,執行完throw語句後會結束其所在方法的執行。

(3)throw語句必須是某個語法結構的最後一條語句。當throw後面還有語句時,會顯示錯誤:該程式碼為不可達的程式碼


  • throws

在方法的宣告部分可以加上throws子句——告知JRE該方法可能會丟擲某些異常。格式:

public void doSomething() throws IOException, ArithmeticException

{
  //方法體
}

【說明】

關鍵字throws後面可以跟多個異常,逗號隔開,沒有先後順序,甚至可以是方法體中並未丟擲的異常。


  • throw語句與throws子句區別:一個是丟擲異常,另一個是宣告方法中將產生什麼異常。

自定義異常

看到自定義異常,我就有個疑問:為什麼系統已經有了異常處理類還要來搞個自定義異常呢?估計老師上課時講過,估計我上課時走神了。那麼原因是什麼呢?其實,稍微想一想就會知道大概原因:由於異常的情況是千奇百怪的,而系統給我們提供的異常類卻是有限的,所以當我們的catch無法捕捉這個系統中沒有的異常型別時,我們就需要自己來設計一個異常類,來返回具體的異常資訊,比如說異常的型別或異常程式碼塊所處的位置。

1.自定義異常類設計

自定義的異常類要通過繼承Exception類來實現,自定義異常類中一般包括異常標識,構造方法和toString()方法。這三者的作用分別是:記錄異常程式碼塊的資訊,給異常標識賦值,輸出描述的異常。

【例】一個簡單的自定義異常類

class MyException extends Exception{
   String id;//異常標識
   public MyException(String str){
       id=str;
  }
   public String toString(){
       return("異常:"+id);
  }
   
}

【說明】自定義的異常類和普通類一樣,都是可以被繼承的。前面我們說過,catch捕獲異常的順序按的是catch塊由上到下的次序,一但找到匹配的catch塊,後面的catch塊不再匹配。所以,我們應該按照由子類到父類的順序來捕獲異常。

2.丟擲異常

異常的本質是物件,所以throw關鍵字後面跟的是new運算子來建立一個異常物件。 不管是自定義的還是系統本身的異常物件,都需要由try catch語句塊來對異常進行劃分區域,捕捉和處理。而自己定義的異常類,需要在try語句塊的末尾,通過throw來建立異常物件(可以建立多個自定義異常類,但要注意一個throw只能建立一個),並由catch對try中的所有可能出現的異常物件進行捕捉和處理。

3.方法的異常宣告

如果某一方法中有異常丟擲,有兩種選擇:一是在方法內對異常進行捕捉處理,;二是在方法中不處理異常,將異常處理交給外部呼叫程式,通常在方法頭使用throws子句列出方法可能出現哪些異常。

習題

【例1】設計一個方法計算一元二次方程的根

//設計一個方法計算一元二次方程的根
public class Ex9_1 {
   static double[] root(double a,double b,double c) throws IllegalArgumentException {
       //此為一個求一元二次方程根的方法,在方法聲明後,有throws子句宣告的異常類
       double[] x = new double[2];
       if (a == 0)
           throw new IllegalArgumentException("a 不能為0");//通過throw丟擲異常,在新建的異常類的括號中,輸入異常標識。
       else {
           double dic = b * b - 4 * a * c;
           if (dic < 0)
               throw new IllegalArgumentException("此方程無根");//丟擲此異常後,此方法的後續程式碼不再執行
           x[0] = (-b + Math.sqrt(dic) / (2 * a));
           x[1] = (-b - Math.sqrt(dic) / (2 * a));
           return x;
      }
  }

   public static void main(String[] args) {
       try{
           double x[]=root(0,5,3);//建立一個double型別的陣列,並通過root方法的返回值對該陣列進行賦值
           System.out.println("方程的根為:"+x[0]+"和"+x[1]);
      }catch (IllegalArgumentException e){
           System.out.println(e);//返回該異常物件的資訊
      }
  }
          }

輸出結果:java.lang.IllegalArgumentException: a 不能為0

總結

異常處理的作用主要有兩點:(1)通過程式自身來處理異常(2)讓程式遇到異常時仍能繼續執行其他程式碼。總之,都是為了提高使用者對程式的使用體驗。在catch語句塊中,不只是可以將異常的資訊返回,還可以根據異常的型別,對程式進行修改,使得異常被處理。此外,在多重catch塊中,最好加上一個catch(Exception e)以避免有異常情況被忽略。