1. 程式人生 > 實用技巧 >【Java】異常處理

【Java】異常處理

異常的定義

異常:在Java語言中,將程式執行中發生的不正常情況稱為“異常”。 (開發過程中的語法錯誤和邏輯錯誤不是異常)

1. 異常的體系結構

Java程式在執行過程中所發生的異常事件可分為兩類:

  1. Error: Java虛擬機器無法解決的嚴重問題。如:JVM系統內部錯誤、資源耗盡等嚴重情況。比如: StackOverflowError和OOM。一般不編寫針對性的程式碼進行處理。
  2. Exception:其它因程式設計錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的程式碼進行處理。例如:
    • 空指標訪問
    • 試圖讀取不存在的檔案
    • 網路連線中斷
    • 陣列角標越界

異常的體系結構

 * java.lang.Throwable
 *      |-----java.lang.Error:一般不編寫針對性的程式碼進行處理。
 *      |-----java.lang.Exception:可以進行異常的處理
 *          |------編譯時異常(checked)不會生成位元組碼檔案
 *                  |-----IOException
 *                      |-----FileNotFoundException
 *                  |-----ClassNotFoundException
 *          |------執行時異常(unchecked,RuntimeException)
 *                  |-----NullPointerException//空指標異常
 *                  |-----ArrayIndexOutOfBoundsException//陣列角標越界
 *                  |-----ClassCastException//型別轉化異常
 *                  |-----NumberFormatException//編碼格式異常
 *                  |-----InputMismatchException//輸入不匹配
 *                  |-----ArithmeticException//算術異常

java中異常類的繼承關係

2. 按照異常發生的時間可以分為兩類

編譯時異常:執行javac.exe命名時,可能出現的異常 是指編譯器不要求強制處置的異常。一般是指程式設計時的邏輯錯誤,是程式設計師應該積極避免其出現的異常。 java. lang. Runtime Exception類及它的子類都是執行時異常。 對於這類異常,可以不作處理,因為這類異常很普遍,若全處理可能會對程式的可讀性和執行效率產生影響
執行時異常:執行java.exe命名時,出現的異常 是指編譯器要求必須處置的異常。即程式在執行時由於外界因素造成的一般性異常。編譯器要求Java程式必須捕獲或宣告所有編譯時異常對於這類異常

,如果程式不處理,可能會帶來意想不到的結果。

3.常見的異常型別

//******************以下是執行時異常***************************
    //ArithmeticException
    @Test
    public void test6(){
        int a = 10;
        int b = 0;
        System.out.println(a / b);
    }
    
    //InputMismatchException
    @Test
    public void test5(){
        Scanner scanner = new Scanner(System.in);
        int score = scanner.nextInt();
        System.out.println(score);
        
        scanner.close();
    }
    
    //NumberFormatException
    @Test
    public void test4(){
        
        String str = "123";
        str = "abc";
        int num = Integer.parseInt(str);    
    }
    
    //ClassCastException
    @Test
    public void test3(){
        Object obj = new Date();
        String str = (String)obj;
    }
    
    //IndexOutOfBoundsException
    @Test
    public void test2(){
        //ArrayIndexOutOfBoundsException
//      int[] arr = new int[10];
//      System.out.println(arr[10]);
        //StringIndexOutOfBoundsException
        String str = "abc";
        System.out.println(str.charAt(3));
    }
    
    //NullPointerException
    @Test
    public void test1(){        
//      int[] arr = null;
//      System.out.println(arr[3]);
        
        String str = "abc";
        str = null;
        System.out.println(str.charAt(0));
        
    }

    //******************以下是編譯時異常***************************
    @Test
    public void test7(){
//      File file = new File("hello.txt");
//      FileInputStream fis = new FileInputStream(file);
//      
//      int data = fis.read();
//      while(data != -1){
//          System.out.print((char)data);
//          data = fis.read();
//      }
//      
//      fis.close();
        
    }

異常的處理

1. java異常處理的抓拋模型

過程一:"拋":程式在正常執行的過程中,一旦出現異常,就會在異常程式碼處生成一個對應異常類的物件。並將此物件丟擲。一旦丟擲物件以後,其後的程式碼就不再執行。

關於異常物件的產生:

① 系統自動生成的異常物件

② 手動的生成一個異常物件,並丟擲(throw)

過程二:"抓":可以理解為異常的處理方式:① try-catch-finally ② throws

2.異常處理方式一:try-catch-finally

2.1 使用說明:

try{
        //可能出現異常的程式碼
    
}catch(異常型別1 變數名1){
        //處理異常的方式1
}catch(異常型別2 變數名2){
        //處理異常的方式2
}catch(異常型別3 變數名3){
        //處理異常的方式3
}
 ....
finally{
        //一定會執行的程式碼
}

說明:

  1. finally是可選的。

  2. 使用try將可能出現異常程式碼包裝起來,在執行過程中,一旦出現異常,就會生成一個對應異常類的物件,根據此物件的型別,去catch中進行匹配

  3. 一旦try中的異常物件匹配到某一個catch時,就進入catch中進行異常的處理。一旦處理完成,就跳出當前的try-catch結構(在沒寫finally的情況。繼續執行其後的程式碼)

  4. catch中的異常型別如果沒子父類關係,則誰宣告在上,誰宣告在下無所謂。
    catch中的異常型別如果滿足子父類關係,則要求子類一定宣告在父類的上面。否則,報錯

  5. 常用的異常物件處理的方式: ① String getMessage() ② printStackTrace()

  6. 在try結構中宣告的變數,再出了try結構以後,就不能再被呼叫

  7. try-catch-finally結構可以巢狀

如何看待程式碼中的編譯時異常和執行時異常?

  1. 使用try-catch-finally處理編譯時異常,是得程式在編譯時就不再報錯,但是執行時仍可能報錯。相當於我們使用try-catch-finally將一個編譯時可能出現的異常,延遲到執行時出現。
  2. 開發中,由於執行時異常比較常見,所以我們通常就不針對執行時異常編寫try-catch-finally了。==針對於編譯時異常,我們說一定要考慮異常的處理==。

2.2. finally的再說明:

  1. finally是可選的
  2. finally中宣告的是一定會被執行的程式碼。即使catch中又出現異常了,try中return語句,catch中return語句等情況。
  3. 像資料庫連線、輸入輸出流、網路程式設計Socket等資源,JVM是不能自動的回收的,我們需要自己手動的進行資源的釋放。此時的資源釋放,就需要宣告在finally中。

3. 異常處理方式二:

"throws + 異常型別"==寫在方法的宣告處==。指明此方法執行時,可能會丟擲的異常型別。 一旦當方法體執行時,出現異常,仍會在異常程式碼處生成一個異常類的物件,此物件滿足throws後異常型別時,就會被丟擲。異常程式碼後續的程式碼,就不再執行!

4. 對比兩種處理方式

try-catch-finally:真正的將異常給處理掉了。 throws的方式只是將異常拋給了方法的呼叫者。==並沒真正將異常處理掉==。

5. 開發中應該如何選擇兩種處理方式?

  • 如果父類中被重寫的方法沒throws方式處理異常,則子類重寫的方法也不能使用throws,意味著如果子類重寫的方法中異常,必須使用try-catch-finally方式處理。
  • 執行的方法a中,先後又呼叫了另外的幾個方法,這幾個方法是遞進關係執行的。我們建議這幾個方法使用throws的方式進行處理。而執行的方法a可以考慮使用try-catch-finally方式進行處理。

    補充: 方法重寫的規則之一: 子類重寫的方法丟擲的異常型別不大於父類被重寫的方法丟擲的異常型別

手動丟擲異常物件

1.使用說明

在程式執行中,除了自動丟擲異常物件的情況之外,我們還可以手動的throw一個異常類的物件。

2.經典面試題

throw 和 throws區別: throw 表示丟擲一個異常類的物件,生成異常物件的過程。宣告在方法體內。 throws 屬於異常處理的一種方式,宣告在方法的宣告處。

3.程式碼示例

class Student{
    
    private int id;
    
    public void regist(int id) throws Exception {
        if(id > 0){
            this.id = id;
        }else{
            //手動丟擲異常物件
//          throw new RuntimeException("您輸入的資料非法!");
//          throw new Exception("您輸入的資料非法!");
            throw new MyException("不能輸入負數");

        }       
    }

    @Override
    public String toString() {
        return "Student [id=" + id + "]";
    }
        
}

自定義異常類

1.如何自定義異常類

  1. 繼承於現的異常結構:RuntimeException 、Exception
  2. 提供全域性常量:serialVersionUID(對類的唯一標識)
  3. 提供過載的構造器

2.程式碼示例

public class MyException extends Exception{
    
    static final long serialVersionUID = -7034897193246939L;
    
    public MyException(){
        
    }
    
    public MyException(String msg){
        super(msg);
    }
}