Java異常處理機制 —— 深刻理解與開發應用

  本文爲原創博文,轉載請註明出處,侵權必究!java

  Java異常處理機制在平常開發中應用頻繁,其最主要的不外乎幾個關鍵字:try、catch、finally、throw、throws,以及各類各樣的Exception。本篇文章主要在基礎的使用方法上,進一步分析在開發中如何使用異常機制更合理。程序員

  • try-catch-finally

    try-catch-finally塊的用法比較簡單,使用頻次也最高。try塊中包含可能出現異常的語句(固然這是人爲決定的,try理論上能夠包含任何代碼),catch塊負責捕獲可能出現的異常,finally負責執行必須執行的語句,這裏的代碼不管是否發生了異常,都會被執行。ide

    針對這部分,由於很基礎,因此就提幾點比較關鍵的建議:函數

      一、當你在寫try-catch語句的時候,腦子裏是知道本身要去針對哪一種異常進行處理的,不要只是以防萬一,加了個catch(Exception e),這是毫無心義的。而且,一個try塊中可能有多個異常,對於每一類異常,要分別寫一個catch進行捕獲。       工具

      二、針對可能出現異常的語句進行try-catch,大段代碼的try-catch會很是不利於維護代碼時定位異常可能發生的位置,對於確定不會發生異常的穩定的代碼,不須要放在try塊中。spa

      三、try-catch雖然在功能上,能夠成爲流程控制的工具,達到條件分支的效果。但相比於if-else語句,java的異常處理機制基於面向對象的思想,使用過程當中須要更多的時間與空間的開銷,因此不要用異常機制去作基本的條件判斷,只有在程序會由於異常而中斷時進行捕獲和處理。線程

      四、finally塊中永遠不要寫return語句,由於finally塊中老是最後執行,他會改變預期在try和catch塊中的返回值(舉個例子,你在catch中捕獲了一個異常並拋出e,又在finally語句中return true,這樣你拋出的異常就"消失"了,由於當前函數的執行結果已經從拋出異常 轉變成 return true)。另外,在使用資源對象與流對象時,finally塊必須對資源對象、流對象進行關閉。3d

  • Java異常體系結構

    Java異常體系的基類是Throwable,它主要有兩個子類:Error 和 Exception。其結構以下圖:
指針

      

    上圖中,Error是指程序沒法處理的錯誤,多指系統內部比較嚴重的錯誤。大多數這類錯誤與開發人員無關,咱們關注的主要是Exception。code

    Exception主要分爲兩塊:運行時異常和非運行時異常。RuntimeException及其子類都稱爲運行時異常;除此以外, 全部Exception的子類異常都是非運行時異常。

    運行時異常多指程序邏輯上出現問題(也就是咱們本身寫代碼邏輯出了問題),常見的錯誤包括 ClassCastException:類型轉換異常、NullPointerException:空指針異常、IndexOutOfBoundsException:越界異常...這些異常均可以經過程序邏輯處理來避免(好比加一個判斷語句判斷是否越界、是否屬於某類型、是否爲null),因此編譯器把這些工做交給了程序員來把控,在編譯期即便手動拋出了一個運行時異常不去捕獲,編譯器也會經過。於是這類異常也叫作"未檢查異常"(uncheck)。一樣屬於未檢查異常的還有全部的Error。即上圖中,全部藍色框表示未檢查異常,橙色框表示"檢查異常"(check)。對於檢查異常,在可能發生異常的位置須要用try-catch塊去捕獲並處理,若是不處理它,就會一直向上層調用拋出,直到被處理爲止。

  • throw 與 throws

    throws關鍵字主要在方法簽名中使用,用於聲明該方法可能拋出的異常。throws 能夠理解成是一種通知行爲,沒有實際的拋出異常的動做,而僅僅是告訴調用他的上層函數,這裏可能會拋出這個異常;

    throw用於在函數體內語句中,表示拋出一個實際的異常的實際動做,若是在函數內沒有捕獲並處理,那麼將會一直向上拋出這個異常直到被main()/Thread.run()拋出。

    當一個函數throws聲明函數可能拋出一個非運行時異常(檢查異常)時,那麼即便這個函數內部不顯示使用throw,調用它的上層函數也必須包含處理這個異常的代碼。舉個例子:

public class Main {

    public static void main(String[] args){

        exceptionTest();
    }
    static int exceptionTest() throws IOException {
        
        return 0;    
    }
}

    上述代碼中調用的exceptionTest函數聲明拋出一個IOException屬於檢查異常,哪怕exceptionTest函數中不可能拋出這個異常,調用它的函數也必須對此異常作出捕獲處理。如今main函數中沒有相關的處理邏輯,因此會編譯錯誤,以下圖:

    而對運行時異常,就是另外一種狀況了:

 1 public class Main {
 2 
 3     public static void main(String[] args){
 4 
 5         int i = divideTest(0);
 6         System.out.println(i);
 7     }
 8     static int divideTest(int b) throws ArithmeticException {  
 9         
10         int i = 5/b;
11         return i;    
12     }
13 }

    一樣在main函數沒有處理異常的邏輯,此次聲明拋出的異常是ArithmeicException,他屬於運行時異常(RuntimeException),因此編譯器對聲明的拋出置之不理:

        

    雖然編譯期經過,但在運行時程序仍然會自動拋出運行時異常,並一直向上拋出到Main函數。而Main()中沒有對該異常的捕獲處理,因此主線程終止。

    結論:我目前的理解是,throws一個運行時異常是沒有任何實際意義的,除非你爲了遵循某個統一的規範而這樣作。throws 存在的意義主要是將可能的非運行時異常交給編譯器把關,讓編譯器監督開發人員對這些異常進行捕獲處理。

    另外,當你須要自定義一個異常時,若是須要在編譯期檢查,並在上層統一處理,那麼直接繼承Exception成爲一個檢查異常;若是不須要編譯期檢查,拋出異常表示程序異常須要直接中斷,那麼繼承RuntimeException成爲一個運行時異常便可。

相關文章
相關標籤/搜索