天天看點

Java異常與異常處理簡單使用

  異常就是程式運作過程中阻止目前方法或作用域繼續執行的問題;

  任何程式都不能保證完全正常運作,當發生異常時,需要我們去處理異常,特别是一些比較重要的場景,異常處理的邏輯也會比較複雜,比如:給使用者提示、儲存目前使用者操作或改動、未完成的業務復原、釋放程式占用的資源等。

  在Java中,Throwable異常類是所有異常類的祖先,任何異常類都繼承于Throwable類;

  Throwable類主要有兩個子類:Error類、Exception類

  Error異常類是系統異常,比如:虛拟機錯誤(VirtualMachineError)、線程死鎖(ThreadDeath)等,Error類異常一旦發生,程式将會崩潰

  Exception是開發中我們最常見的一般異常,這種異常原因可能是程式代碼編寫錯誤,環境問題,使用者輸入錯誤等異常

  Exception異常一般分為:運作時異常(RuntimeException)也稱為非檢查異常、檢查異常;

  非檢查異常常見的有:輸出空指針時的異常,數組下标越界異常,類型轉換異常,算術異常(比如0作為分母)等,運作時異常會由Java虛拟機自動捕獲,自動抛出,一般是我們寫的代碼本身有問題,需要改進我們的代碼來解決

  檢查異常的原因有可能是:檔案異常(不存在或者權限)、資料庫連接配接異常、網絡連接配接異常等,這種異常系統不會自動捕獲,需要我們手動添加捕獲處理的語句

  

  我們通常使用try-catch以及try-catch-finally代碼塊來處理異常

  try代碼塊中是可能發生異常的語句,當程式确實發生異常了,try塊中程式會中止執行,并且抛出異常給catch塊進行處理,catch根據需要去處理異常、記錄錯誤日志等,看一個簡單示例:

1 import java.util.Scanner;
 2 
 3 public class Ceshi {
 4     public static void main(String[] args){
 5         try{
 6             System.out.println("請輸入一個整數:");
 7             Scanner input = new Scanner(System.in);
 8             int a = input.nextInt();
 9             System.out.println("您輸入的是:" + a);
10         }catch(Exception e){
11             System.out.println("輸入異常");
12             e.printStackTrace();    //列印異常資訊
13         }
14         System.out.println("程式執行結束");
15     }
16 }      

這是一個最簡單的異常處理,通過Scanner擷取使用者輸入,當使用者正确輸入時程式正常執行,當然catch塊不會被執行,但是使用者如果輸入的不是整數,那麼就會抛出異常給catch塊,可以利用printStackTrace()方法列印具體的異常,注意無論程式是否異常try-catch外的語句都會被正常執行,錯誤結果如下:

Java異常與異常處理簡單使用

根據結果可以看到我們輸入字元串"3s"之後,抛出了異常并且提示輸入異常,最後但是try-catch後面的語句正常執行,抛出的e.printStackTrace()會在最後被列印出來,可以看出來是Ceshi.java第就行發生了異常産生了終止,那麼就是在a接收輸入這一行語句中發生的異常,那麼在這一行之後的所有try塊中的語句便終止執行

另外如果try中的代碼會抛出好幾個類型的異常,那麼我們需要多個catch塊來處理,并且加上finally進行善後處理工作

1 import java.util.Scanner;
 2 import java.util.InputMismatchException;
 3 import java.lang.ArithmeticException;
 4 public class Ceshi {
 5     public static void main(String[] args){
 6         Scanner input = new Scanner(System.in);
 7         try{
 8             System.out.println("請輸入分子:");
 9             int a = input.nextInt();
10             System.out.println("您輸入分母:");
11             int b = input.nextInt();
12             System.out.println("計算結果是:" + a*1.0/b);
13         }catch(InputMismatchException e){
14             System.out.println("請輸入整數");
15             e.printStackTrace();    //列印異常資訊
16         }catch(ArithmeticException e){
17             System.out.println("分母不能為0");
18             e.printStackTrace();    //列印異常資訊
19         }catch(Exception e){
20             System.out.println("其他未知異常");
21             e.printStackTrace();
22         }finally{
23             input.close();
24         }
25         System.out.println("程式執行結束");
26     }
27 }      

以上的處理就比較合理了,首先保證輸入是整數,如果都是整數那麼分母為0也會抛出異常,最後如果還有我們考慮不到的異常,那麼就通過Exception父類抛出異常,catch異常塊從上到下一般是是由小到大或者由子類到父類的異常類抛出,就是從作用範圍來說從細節到整體,Exception異常類抛出必須放在最後面,這樣能抛出我們開發中遇到的所有異常,另外finally塊建議帶上,當遇到異常時,他可以釋放前面還未操作的系統資源,比如例子中的關閉輸入,這樣能提高程式的健壯性,如果try和catch中有傳回值,那麼finally中的語句會在try和catch語句塊中的return傳回值傳回到調用者之前,獲得該傳回值,我們可以在程式中輸出他們,但是放在try-catch-finally外傳回值時在finally是無法擷取到的,隻能擷取前面的變量值

  Java中方法異常抛出,因為很多代碼我們會寫到方法中,為了便于管理,我們可以在專門的方法中處理異常,是以我們可以将方法中的異常向上抛出,可以寫一個方法來簡單抛出異常,代碼如下:

1 public void divide(int a,int b) throws Exception {
2     if(b == 0){
3         throw new Exception("除數不能為零!");
4     }else{
5         System.out.println("結果為:" + a*1.0/b);
6     }
7 }      

當該方法被調用時,那麼如果發生異常,異常将抛出到調用的語句塊中,我們可以在調用的時候進行處理,比如:

1 public void complte() {
2     try{
3         divide(5,0);    //此時發生異常,調用方法将異常抛出到這裡
4     }catch(Exception e){
5         System.out.println(e.getMessage());    //此處捕獲異常,将方法中定義的異常資訊抛出
6     }
7 }      

這樣就把方法中的異常抛出并進行了處理,另外我們還可以不在complte方法中抛出,還可以向上抛出,由上面調用該方法時抛出異常,代碼如下:

public void complte() throws Exception {
    /**
     * 省略方法中的代碼
     */
    divide(5,0);    //将裡面的異常抛出到調用complte方法的位置
}      

這樣的話異常繼續向上抛出,最終還是按照第二段代碼的方式來處理異常,是以用throws關鍵字聲明此方法向上抛出異常,用throw關鍵字來抛出異常

  自定義異常

  除了利用系統的異常我們還可以自定義異常,以便适應我們情景的需要,簡單定義個異常類:

1 public class CeshiException extends Exception {
 2 
 3     public CeshiException(){
 4         
 5     }
 6     
 7     public CeshiException(String message){
 8         super(message);
 9     }
10 }      

注意,自定義異常類必須繼承于Exception異常類,裡面定義了一個有參數的構造方法來自定義異常資訊,無參的構造方法是為了執行個體化類時,預設不會發生錯誤,那麼我們可以在方法中具體來使用這個自定義異常類了:

1 public class ChainTest {
 2 
 3     /**
 4      * test1():抛出自定義異常
 5      * test2():調用test1(),捕獲自定義異常,并且包裝成運作時異常,抛出新異常
 6      * main方法中,調用test2(),嘗試捕獲test2()方法抛出的異常
 7      */
 8     public static void main(String[] args) {
 9         ChainTest ct = new ChainTest();
10         try{
11             ct.test2();
12         }catch(Exception e){
13             e.printStackTrace();
14         }
15     }
16 
17     public void test1() throws CeshiException{
18         throw new CeshiException("原始自定義異常抛出");
19     }
20     
21     public void test2(){
22         try {
23             test1();
24         } catch (CeshiException e) {
25             // TODO Auto-generated catch block
26             RuntimeException newExc = 
27                 new RuntimeException("抛出新運作時異常");
28             newExc.initCause(e);    //引用原始異常方法,異常鍊
29             throw newExc;
30         }
31     }
32 }      

根據代碼可以看出,main方法調用test2方法并捕獲test2方法抛出的異常,而test2方法中運作test1并捕獲test1方法中抛出的自定義異常,并且自己也抛出一個新的運作時異常抛出到main方法中,而test1方法通過聲明自定義異常類實作了抛出自定義異常類中的異常方法,将異常抛出到test2中,這樣就好比一連串的異常抛出和異常處理,同時結合了自定義異常,這樣就形成了一個小型的異常鍊,就好像鍊式反應一樣去抛出異常

  最後,總結一下,通過try-catch來處理異常,并不能避免錯誤的存在性,而是盡量提高程式的健壯性,減小程式錯誤而帶來的安全風險和損失,我們不能一味的用try-catch來屏蔽錯誤,我們應該采用合理的邏輯算法來解決程式設計的不足,try-catch隻是一個作為輔助使用,不可以過分依賴;

  在多重catch塊之後,最好加個catch(Exception e){}來處理其他可能會被遺漏的未知的異常,對于不太确定的異常,可以加上try-catch來處理潛在的風險;

  對于異常一定要盡量去處理,千萬不要隻是簡單地使用e.printStackTrace();來列印錯誤資訊,這樣就失去了異常處理的意義;

  具體如何處理異常,應該根據不同的業務需求和異常類型來确定;

  最後要善于在try-catch塊後面添加finally語句塊,釋放系統資源的占用,比如網絡連接配接、資料庫連接配接、檔案關閉等;

  什麼時候怎麼使用異常,還需要自己以後在開發中慢慢的熟悉