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外的語句都會被正常執行,錯誤結果如下:

根據結果可以看到我們輸入字符串"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語句塊,釋放系統資源的占用,比如網絡連接、數據庫連接、文件關閉等;

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM