Java卷之異常框架處理篇


 一、簡介

  在Java程序執行過程中, 總是會發生不被期望的事件, 阻止程序按照程序員預期正常運行, 這就是Java程序出現的異常。在企業級開發中, 容易導致各種各樣的小bug, 嚴重影響產品的運行和用戶體驗。所以大多數開發團隊都有自己的異常處理的規則和方法。如果你是一個團隊的新手,你可能會驚訝於這些方法與你之前使用過的那些方法有多么不同。

二、Java異常體系

   Java所有異常的父類都是java.lang.Throwable無論是內部的異常還是自定義異常。只有直接或者間接集成java.lang.Throwable類,JVM才會認為這是異常對象並且處理。

  Java異常體系中Error為錯誤,較Exception嚴重。Error不是由我們程序自身導致的,是JVM運行錯誤導致的,所以暫時不是我們討論的范圍。Exception則是異常的基類,又可以分為"運行時異常"與"編譯時異常"(又稱為"非檢查異常和檢查異常")。

  非檢查異常RuntimeException; 在編譯階段無法檢查,如ArithmeticException(除0引發)、InputMismatchException(輸入的數據不能被轉換為int類型引發)。引發非檢查異常大多數原因是編碼錯誤,應該檢查程序。

  檢查異常(IOException),在編譯時可以檢查, 需要異常處理。處理方式有二種、(1)函數簽名中throws拋出異常 (2)tryCatch語句捕獲。

 

 

  三 、異常現象

     下面的代碼會演示2個異常類型:ArithmeticException 和 InputMismatchException。前者由於整數除0引發,后者是輸入的數據不能被轉換為int類型引發。    

 1 package it.check.exception;
 2 import java.util.Scanner;
 3 /*
 4  *  初步測試Java的“非檢查異常”、如ArithmeticException(除0引發)、InputMismatchException(輸入的數據不能被轉換為int類型引發)
 5  *  “非檢查異常”在編譯時不會提示信息、在運行是則會拋出異常。通常不需要try{} catch(){} finally{}處理
 6  *  
 7  * **/
 8 public class ArithmeticExceptionRun {
 9 
10     public static void main(String[] args) {
11           System . out. println( "----歡迎使用命令行除法計算器----" ) ;
12           CMDCalculate ();
13 
14     }
15      public static void CMDCalculate ()
16      {
17            Scanner scan = new Scanner ( System. in );
18            int num1 = scan .nextInt () ; //阻塞、等待輸入
19            int num2 = scan .nextInt () ;
20            int result = devide (num1 , num2 ) ;
21            System . out. println( "result:" + result) ;
22            scan .close () ;
23      }
24      public static int devide (int num1, int num2 ){
25            return num1 / num2 ;
26      }
27 
28 }

 

/**
     * ----歡迎使用命令行除法計算器----
2
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at it.check.exception.ArithmeticExceptionRun.devide(ArithmeticExceptionRun.java:25)
    at it.check.exception.ArithmeticExceptionRun.CMDCalculate(ArithmeticExceptionRun.java:20)
    at it.check.exception.ArithmeticExceptionRun.main(ArithmeticExceptionRun.java:12)
     * 
     * */
     /**
      * ----歡迎使用命令行除法計算器----
a
Exception in thread "main" java.util.InputMismatchException
    at java.util.Scanner.throwFor(Scanner.java:864)
    at java.util.Scanner.next(Scanner.java:1485)
    at java.util.Scanner.nextInt(Scanner.java:2117)
    at java.util.Scanner.nextInt(Scanner.java:2076)
    at it.check.exception.ArithmeticExceptionRun.CMDCalculate(ArithmeticExceptionRun.java:18)
    at it.check.exception.ArithmeticExceptionRun.main(ArithmeticExceptionRun.java:12)
      * */

  

 

異常追蹤棧; 異常在函數中產生,函數存在調用棧,main 調用CMDCalculate方法、CMDCalculate方法在調用device。當發生/0 異常時, 當這些被影響的函數以異常信息輸出時、形成異常追蹤棧、由device->CMDCalculate->main 棧頂向棧底回朔。

以上例子為非檢查異常、接着是檢查異常, 有二種處理方式。

方式一: 使用tryCatchFinally語句捕獲

public class BufferDemo {

    public static void main(String[] args) {
       bufferRead("E:"+File.separatorChar+"a.txt");
    }

     /** 使用緩沖技術讀取數據
      * 
      * */
    public static void bufferRead(String path) {
        BufferedInputStream bi=null;
        byte[] buf=new byte[1024];
        int length=0;
        try {
             bi=new BufferedInputStream(new FileInputStream(path));
             while((length=bi.read(buf))!=-1){
                 System.out.println(new String(buf,0,length));
             }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
                try {
                    if(bi!=null){
                    bi.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
  
}

 

方式二:throws拋出,由調用者處理。函數使用throws拋出異常可能是(1)函數本身不知道怎么處理異常 (2)把異常交給調用者處理(捕獲等)更加合適。

public class BufferDemo {

    public static void main(String[] args) {
      
        try {
            bufferRead("E:"+File.separatorChar+"a.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    
    }

     /** 使用緩沖技術讀取數據
     * @throws IOException 
     * 
      * 
      * */
    public static void bufferRead(String path) throws IOException{
        BufferedInputStream bi=null;
        byte[] buf=new byte[1024];
        int length=0;
        bi=new BufferedInputStream(new FileInputStream(path));
        while((length=bi.read(buf))!=-1){
        System.out.println(new String(buf,0,length));
        bi.close();
             }
        
    }
  
}

 四、異常鏈化

         在一些大型的,模塊化的軟件開發中,一旦一個地方發生異常,則如骨牌效應一樣,將導致一連串的異常。假設B模塊完成自己的邏輯需要調用A模塊的方法,如果A模塊發生異常,則B也將不能完成而發生異常,但是B在拋出異常時,會將A的異常信息掩蓋掉,這將使得異常的根源信息丟失。異常的鏈化可以將多個模塊的異常串聯起來,使得異常信息不會丟失。

異常鏈化:以一個異常對象為參數構造新的異常對象。新的異對象將包含先前異常的信息。這項技術主要是異常類的一個帶Throwable參數的函數來實現的。這個當做參數的異常,我們叫他根源異常(cause)。

查看Throwable類源碼,可以發現里面有一個Throwable字段cause,就是它保存了構造時傳遞的根源異常參數。這種設計和鏈表的結點類設計如出一轍,因此形成鏈也是自然的了。

public class Throwable implements Serializable {
    private Throwable cause = this;
   
    public Throwable(String message, Throwable cause) {
        fillInStackTrace();
        detailMessage = message;
        this.cause = cause;
    }
     public Throwable(Throwable cause) {
        fillInStackTrace();
        detailMessage = (cause==null ? null : cause.toString());
        this.cause = cause;
    }
    
    //........
} 

  

下面是一個例子,演示了異常的鏈化:從命令行輸入2個int,將他們相加,輸出。輸入的數不是int,則導致getInputNumbers異常,從而導致add函數異常,則可以在add函數中拋出一個鏈化的異常。給出鏈化例子。

 

 1 public static void main(String[] args)
 2 {
 3     
 4     System.out.println("請輸入2個加數");
 5     int result;
 6     try
 7     {
 8         result = add();
 9         System.out.println("結果:"+result);
10     } catch (Exception e){
11         e.printStackTrace();
12     }
13 }
14 //獲取輸入的2個整數返回
15 private static List<Integer> getInputNumbers()
16 {
17     List<Integer> nums = new ArrayList<>();
18     Scanner scan = new Scanner(System.in);
19     try {
20         int num1 = scan.nextInt();
21         int num2 = scan.nextInt();
22         nums.add(new Integer(num1));
23         nums.add(new Integer(num2));
24     }catch(InputMismatchException immExp){
25         throw immExp;
26     }finally {
27         scan.close();
28     }
29     return nums;
30 }
31 
32 //執行加法計算
33 private static int add() throws Exception
34 {
35     int result;
36     try {
37         List<Integer> nums =getInputNumbers();
38         result = nums.get(0)  + nums.get(1);
39     }catch(InputMismatchException immExp){
40         throw new Exception("計算失敗",immExp);  /////////////////////////////鏈化:以一個異常對象為參數構造新的異常對象。
41     }
42     return  result;
43 }
44 
45 /*
46 請輸入2個加數
47 r 1
48 java.lang.Exception: 計算失敗
49     at practise.ExceptionTest.add(ExceptionTest.java:53)
50     at practise.ExceptionTest.main(ExceptionTest.java:18)
51 Caused by: java.util.InputMismatchException
52     at java.util.Scanner.throwFor(Scanner.java:864)
53     at java.util.Scanner.next(Scanner.java:1485)
54     at java.util.Scanner.nextInt(Scanner.java:2117)
55     at java.util.Scanner.nextInt(Scanner.java:2076)
56     at practise.ExceptionTest.getInputNumbers(ExceptionTest.java:30)
57     at practise.ExceptionTest.add(ExceptionTest.java:48)
58     ... 1 more
59 
60 */

 


免責聲明!

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



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