1. 程式人生 > >[01] 異常的概念和處理

[01] 異常的概念和處理

只需要 point cnblogs 尋求 牛客網 不能 多個 ioe except


1、異常和錯誤

Java作為面向對象的語言,自然把系統發生的不正確的事件也封裝成了Java對象。比如一個不存在的對象,我們卻試圖調用它的方法,自然是行不通的,這個不正確的事件,也就被封裝成為了我們常見的NullPointerException對象。
即是說,在Java程序的運行過程中,如果發生了意外事件(發生了錯誤或異常),則該意外會被封裝成為一個對象,並把它提交給運行時的系統,尋求相應的代碼來處理。意外事件在Java中分為兩類,即錯誤異常;而把這個意外對象的生成和提交過程,我們稱之為拋出。
在Java中:
  • 錯誤 - 不受控的,程序無法處理的
  • 異常 - 容易排查的,可以處理的
技術分享

2、異常的體系

技術分享萬物皆可拋,Throwable就是Java語言中所有錯誤和異常的父類,其兩個子類:Error(錯誤)、Exception(異常)
Error是程序無法處理的,比如OutOfMemoryError、ThreadDeath等,出現如此情況我們往往無能為力,只能交給JVM自行處理,大多數情況下,JVM會選擇終止線程(真是簡單粗暴...);而Exception就是程序可以處理的異常,也是我們重點要理解的部分。

Exception的分類

在異常中,又分為兩種,CheckedException(受檢異常)和UncheckedExcpeiton(不受檢異常),其中:
  • CheckedException 要求我們必須處理(要不然為什麽叫CheckedExcpetion呢),使用 try catch 語句塊,否則在編譯階段就無法通過(所以稱之為編譯期異常
    • 如大名鼎鼎的 IOException,要求我們必須捕捉處理

  • UncheckedException 發生在運行期(所以稱之為運行期異常 RuntimeException),具有不確定性,主要是程序的邏輯問題所引起的,難以排查
    • 如 NullPointerException(空指針異常)、IndexOutOfBoundsException(數組越界異常)


3、異常的處理

3.1 try catch

對於可能發生異常的代碼,我們要使用 try 語句塊來進行包裹,與 try 相呼應的還要有 catch 語句塊。即 try 用來檢測不安全的代碼,用來發現異常,而一旦某條語句出現了異常,則從此處中止,後面的代碼不再執行,而是直接跳轉到異常處理的代碼塊中,即提到的 catch 語句塊。
所以:
  • try 包括需要檢測的代碼
  • catch 發生異常時進行捕獲,並進行處理

  1. public static void main(String[] args) {
  2. Date date = null;
  3. try {
  4. long time = date.getTime
    ();
  5. System.out.println(time);
  6. } catch (NullPointerException e) {
  7. System.out.println("空指針異常發生了,好像我應該做些什麽");
  8. }
  9. }

(註意:這裏只是使用運行期異常作為示例,實際上RuntimeException是不需要在編譯階段專門進行異常處理的,即不寫try catch也行)

如上例因為 line5 試圖調用空對象的一個方法,所以會發生 NullPointerException,所以 line6 會跳過不執行,而直接執行 catch 塊中的內容,即 line9,打印輸出 “空指針異常發生了,好像我應該做些什麽
整個過程就像打棒球,發生異常就像投手丟出了棒球,這個棒球就是異常;而捕獲異常就像接住棒球。技術分享技術分享

3.2 多個catch

catch 緊跟在 try 語句之後,也就是用來進行異常處理的部分,實際上 catch 可以寫多個,分別用來捕獲不同類型的異常,必須從小到大(即從子類到父類)的順序進行捕獲,否則會出現編譯錯誤,且某異常只被捕捉一次,即第一個符合的 catch 處捕獲。
  1. public static void main(String[] args) {
  2. Date date = null;
  3. int[] arr = new int[]{0, 1, 2};
  4. try {
  5. int i = arr[5];
  6. System.out.println(i);
  7. long time = date.getTime();
  8. System.out.println(time);
  9. } catch (NullPointerException e) {
  10. System.out.println("空指針異常發生了,好像我應該做些什麽");
  11. } catch (IndexOutOfBoundsException e) {
  12. System.out.println("數組越界了");
  13. } catch (Exception e) {
  14. System.out.println("發生了異常");
  15. }
  16. }

我們把之前的例子改一下,如上,按照剛才我們提到的,這裏可能出現數組越界和空指針異常,所以我們寫了兩個catch,可能還有其他我們沒想到的異常,所以我們再捕捉了一個異常的父類Exception。這個例子最後只會輸出 "數組越界了",因為異常只被捕捉一次,且後面的代碼不再執行。
  1. public static void main(String[] args) {
  2. Date date = null;
  3. int[] arr = new int[]{0, 1, 2};
  4. try {
  5. int i = arr[5];
  6. System.out.println(i);
  7. long time = date.getTime();
  8. System.out.println(time);
  9. } catch (Exception e) { //錯誤的寫法,異常捕獲的順序只能從子類到父類
  10. System.out.println("發生了異常");
  11. } catch (NullPointerException e) {
  12. System.out.println("空指針異常發生了,好像我應該做些什麽");
  13. } catch (IndexOutOfBoundsException e) {
  14. System.out.println("數組越界了");
  15. }
  16. }

註意catch異常的順序,如上例中的寫法是無法通過編譯的,因為Exception是所有異常的父類,而catch塊必須按照從子類到父類的順序進行編寫。

3.3 finally

在異常處理中,還有一個語句塊叫 finally,可以不寫,一旦寫上,那麽有且只能有一個finally語句塊,該部分的代碼內容總是會執行的。一般是跟在最後的catch塊之後。
  1. public static void main(String[] args) {
  2. Date date = null;
  3. int[] arr = new int[]{0, 1, 2};
  4. try {
  5. int i = arr[5];
  6. System.out.println(i);
  7. long time = date.getTime();
  8. System.out.println(time);
  9. } catch (NullPointerException e) {
  10. System.out.println("空指針異常發生了,好像我應該做些什麽");
  11. } catch (IndexOutOfBoundsException e) {
  12. System.out.println("數組越界了");
  13. } catch (Exception e) {
  14. System.out.println("發生了異常");
  15. } finally {
  16. System.out.println("我總是要執行的");
  17. }
  18. }

如上例中,除了輸出 “數組越界了”,還始終會輸出 “我總是要執行的”。不論程序是否發生異常,finally代碼塊總是會執行,所以finally一般也用來關閉資源
需要註意的是,之前提到finally是可選的,即可以只有try和catch,同時要知道的是:
  • 可以只有try和finally
  • 可以只有try和catch
  • 不能只有try

3.4 異常處理中的return

在Java語言的異常處理中,finally塊的作用就是為了保證無論出現什麽情況,finally塊裏的代碼一定會執行。而return意味著結束了對當前函數的調用並跳出這個函數體,那麽如果try塊中或者catch塊中出現了return,finally又如何是好?
我們看這樣一道題,下面的函數最終會返回多少?
  1. 鏈接:https://interview.nowcoder.com/questionTerminal/5a6ea98ed42347fe81c950a1a206dc7e?toCommentId=77575
  2. 來源:牛客網
  3. public static int func (){
  4. try{
  5. return 1;
  6. }catch (Exception e){
  7. return 2;
  8. }finally{
  9. return 3;
  10. }
  11. }

只需要記住,無論如何finally語句都要執行(除非調用了System.exit()方法。同時,如果:
  • finally中沒有return,那麽不會影響try或catch中return的結果,但是finally還是會執行的
  • finally中有return語句,那麽會覆蓋掉try或catch中的return的結果,再進行返回

  1. public static int fun() {
  2. int i = 0;
  3. try {
  4. return i;
  5. } catch (Exception e) {
  6. return -1;
  7. } finally {
  8. ++i;
  9. }
  10. }

如上例,(前提是finally中沒有return,否則會覆蓋值當執行到return時,結果會被保存等待finally執行完畢後返回,這個時候無論finally內部如何改變這個值,都不會影響返回結果。最終返回0。


[01] 異常的概念和處理