如何使用Java異常處理?

異常概述

爲什麼會出現異常?

在使用計算機語言進行項目開發的過程中,即使程序員把代碼寫得盡善盡美,在系統的運行過程中仍然會遇到一些問題,因爲很多問題不是靠代碼能夠避
免的,比如: 客戶輸入數據的格式, 讀取文件是否存在, 網絡是否始終保持通暢等等。

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

異常分類

  • Throwable

    所有異常的父類(都繼承java.lang.Throwable)

    • Error
      Java應用程序語法恢復的嚴重異常,不需要捕獲和處理;發生時,一般通知用戶並終止程序執行(如: JVM系統內部錯誤、 資源耗盡等嚴重情況。 比如: StackOverflowError)

    • Exception

      Java應用程序拋出和處理的非嚴重錯誤,稱爲異常。是所有Java異常的父類。(因編程錯誤或偶然的外在因素導致的一般性問題, 可以使用針對性的代碼進行處理)

      • RuntimeExceotion
        運行時異常,編程時不處理,也可以編譯通過(例如,數組下標越界)

      • CheckedException
        非運行時異常,必須編譯時處理,否則不通過(例如,類找不到)

異常處理的一般思路

一般有兩種解決方法:

  1. 遇到錯誤就終止程序的運行。
  2. 由程序員在編寫程序時, 就考慮到錯誤的檢測、 錯誤消息的提示, 以及錯誤的處理。

運行時異常與編譯時異常

捕獲錯誤最理想的是在編譯期間, 但有的錯誤只有在運行時纔會發生。比如: 除數爲0, 數組下標越界等

運行時異常(RuntimeExceotion)

  1. 是指編譯器不要求強制處置的異常。一般是指編程時的邏輯錯誤,是程序員應該積極避免其出現的異常。 java.lang.RuntimeException類及它的子
    類都是運行時異常。
  2. 對於這類異常,可以不作處理,因爲這類異常很普遍,若全處理可能會對程序的可讀性和運行效率產生影響。

編譯時異常

  1. 是指編譯器要求必須處置的異常。即程序在運行時由於外界因素造成的一般性異常。 編譯器要求Java程序必須捕獲或聲明所有編譯時異常。
  2. 對於這類異常,如果程序不處理,可能會帶來意想不到的結果。

異常對象e,兩個常用方法

  • printStackTrace():用於輸出異常的堆棧信息包括程序運行到當前類的執行流程,顯示方法調用序列(獲取異常類名和異常信息,以及異常出
    現在程序中的位置。返回值void。)
  • getMessage():返回異常信息的字符串

常見異常

  • NullPointerException空指針異常
    屬於運行時異常,調用了未經初始化的對象或不存在的對象,或是訪問或修改null對象的屬性或方法(例如,數組初始化和數組元素初始化混淆)

  • ClassNotFoundException類沒能找到的異常
    原因①的確不存在該類
    ②環境進行了調整(目錄結構發生變化,編譯、運行路徑發生變化)​
    ③修改類名時沒有修改調用該類的其他類​

  • IllegalArgumentException表明向方法傳遞了一個不合法或不正確的參數

  • InputMismatchException由Scanner拋出
    表明Scanner獲取內容與期望類型的模式不匹配,或該內容超出期望類型範圍

  • IllegalAccessException
    應用程序試圖創建一個實例、設置或獲取一個屬性,或者調用一個方法,但當前正在執行的方法無法訪問指定類、屬性、方法或構造方法定義時,拋出

  • ClassCastException試圖將對象強制轉換爲不是實例的子類時拋出異常

  • SQLException提供關於數據庫訪問錯誤或其他信息的異常

  • IOException是失敗或中斷的I/O操作生成的異常的通用類

異常處理

異常處理機制

爲什麼採用異常處理機制?

在編寫程序時,經常要在可能出現錯誤的地方加上檢測的代碼,如進行x/y運算時,要檢測分母爲0,數據爲空,輸入的不是數據而是字符等。 過多的if-else分支會導致程序的代碼加長、臃腫,可讀性差。因此採用異常處理機制。

異常處理的好處?

Java採用的異常處理機制,是將異常處理的程序代碼集中在一起,與正常的程序代碼分開,使得程序簡潔、優雅, 並易於維護。

異常處理機制概述

Java提供的是異常處理的抓拋模型

拋出異常:Java程序的執行過程中如出現異常, 會生成一個異常類對象,該異常對象將被提交給Java運行時系統, 這個過程稱爲拋出(throw)異常。

捕獲異常:如果一個方法內拋出異常, 該異常對象會被拋給調用者方法中處理。 如果異常沒有在調用者方法中處理, 它繼續被拋給這個調用
方法的上層方法。 這個過程將一直繼續下去, 直到異常被處理。這一過程稱爲捕獲(catch)異常。

抓拋模型

過程一:“拋”:

程序在正常執行的過程中,一旦出現異常,就會在異常代碼處生成一個對應異常類的對象。並將此對象拋出。一旦拋出對象以後,其後的代碼就不再執行。

關於異常對象的產生:① 系統自動生成的異常對象 ② 手動的生成一個異常對象,並拋出(throw)

過程二:“抓”:

可以理解爲異常的處理方式:① try-catch-finally ② throws

異常對象如何生成?

  1. 由虛擬機自動生成:程序運行過程中,虛擬機檢測到程序發生了問題,如果在當前代碼中沒有找到相應的處理程序,就會在後臺自動創建一個對應異常類的實例對象並拋出——自動拋出
  2. 由開發人員手動創建: Exception exception = new ClassCastException();——創建好的異常對象不拋出對程序沒有任何影響,和創建一個普通對象一樣

捕獲異常

  1. 如果一個方法內拋出異常, 該異常對象會被拋給調用者方法中處理。 如果異常沒有在調用者方法中處理, 它繼續被拋給這個調用方法的上層方法。 這個過程將一直繼續下去, 直到異常被處理。這一過程稱爲捕獲(catch)異常。
  2. 如果一個異常回到main()方法, 並且main()也不處理, 則程序運行終止。
  3. 程序員通常只能處理Exception, 而對Error無能爲力。

異常處理的方式

1.try-catch-finally

try

捕獲異常的第一步是用try{…}語句塊選定捕獲異常的範圍, 將可能出現異常的代碼放在try語句塊中。

catch (Exceptiontype e)

在catch語句塊中是對異常對象進行處理的代碼。 每個try語句塊可以伴隨一個或多個catch語句, 用於處理可能產生的不同類型的異常對象。

如果明確知道產生的是何種異常, 可以用該異常類作爲catch的參數;也可以用其父類作爲catch的參數。
比 如 : 可 以 用 ArithmeticException 類 作 爲 參 數 的 地 方 , 就 可 以 用RuntimeException類作爲參數, 或者用所有異常的父類Exception類作爲參數。
但不能是與ArithmeticException類無關的異常, 如NullPointerException(catch中的語句將不會執行) 。

finally
  1. 捕獲異常的最後一步是通過finally語句爲異常處理提供一個統一的出口,使得在控制流轉到程序的其它部分以前,能夠對程序的狀態作統一的管理。
  2. 不論在try代碼塊中是否發生了異常事件, catch語句是否執行, catch語句是否有異常, catch語句中是否有return,finally塊中的語句都會被執行。
  3. finally語句和catch語句是任選的
  4. 像數據庫連接、輸入輸出流、網絡編程Socket等資源,JVM是不能自動的回收的,我們需要自己手動的進行資源的釋放。此時的資源釋放,就需要聲明在finally中。
  5. finally唯一不執行的情況——catch語句中出現System.exit(1),直接退出JVM
使用
  try{
 		//可能出現異常的代碼
  
  }catch(異常類型1 變量名1){
  		//處理異常的方式1
  }catch(異常類型2 變量名2){
  		//處理異常的方式2
  }catch(異常類型3 變量名3){
  		//處理異常的方式3
  }
  ....
  finally{
  		//一定會執行的代碼
  }
注意事項
  1. try是必須的,catch和finally可選,但至少要有一個。

  2. try和catch的異常類型必須一致,或是其父類

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

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

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

  6. 常用的異常對象處理的方式: ① String getMessage() ② printStackTrace()

  7. 在try結構中聲明的變量,再出了try結構以後,就不能再被調用

  8. try-catch-finally結構可以嵌套

  9. 多個catch,應按照“從小到大”的順序捕獲,避免對父類大的異常進行捕獲

  10. 有return時,先執行try和catch的,然後在執行finally的;即使try和catch有return,finally代碼仍會執行

體會

體會1:使用try-catch-finally處理編譯時異常,是得程序在編譯時就不再報錯,但是運行時仍可能報錯。相當於我們使用try-catch-finally將一個編譯時可能出現的異常,延遲到運行時出現。

體會2:開發中,由於運行時異常比較常見,所以我們通常就不針對運行時異常編寫try-catch-finally了。針對於編譯時異常,我們說一定要考慮異常的處理。

示例
public class IndexOutExp {
    public static void main(String[] args) {
        String friends[] = { "lisa", "bily", "kessy" };
        try {
            for (int i = 0; i < 5; i++) {
            	System.out.println(friends[i]);
            }
        } catch (ArrayIndexOutOfBoundsException e) {
        	System.out.println("index err");
        }
        System.out.println("\nthis is the end");
    }
}

結果:

程序IndexOutExp.java運行結果: java IndexOutExp
lisa
bily
kessy
index err
this is the end

2.throws(聲明拋出異常)

  1. 如果一個方法(中的語句執行時)可能生成某種異常, 但是並不能確定如何處理這種異常, 則此方法應顯示地聲明拋出異常, 表明該方法將不對這些異常進行處理,而由該方法的調用者負責處理。
  2. 在方法聲明中用throws語句可以聲明拋出異常的列表, throws後面的異常類型可以是方法中產生的異常類型, 也可以是它的父類。
  3. "throws + 異常類型"寫在方法的聲明處。指明此方法執行時,可能會拋出的異常類型。一旦當方法體執行時,出現異常,仍會在異常代碼處生成一個異常類的對象,此對象滿足throws後異常類型時,就會被拋出。異常代碼後續的代碼,就不再執行!
示例
public void readFile(String file) throws FileNotFoundException {
    // 讀文件的操作可能產生FileNotFoundException類型的異常
    FileInputStream fis = new FileInputStream(file);
}
圖解拋出異常過程

在這裏插入圖片描述

重寫方法聲明拋出異常的原則
  1. 在多態的情況下,對methodA()方法的調用-異常的捕獲按父類聲明的異常處理。
  2. 子類重寫的方法拋出的異常類型不大於父類被重寫的方法拋出的異常類型
體會

體會1:try-catch-finally:真正的將異常給處理掉了。throws的方式只是將異常拋給了方法的調用者。 並沒有真正將異常處理掉。

手動拋出異常(throw)

Java異常類對象除在程序執行過程中出現異常時由系統自動生成並拋出, 也可根據需要使用人工創建並拋出。

使用

  1. 首先要生成異常類對象, 然後通過throw語句實現拋出操作(提交給Java運行環境)。

    IOException e = new IOException();
    throw e;
    
  2. 可以拋出的異常必須是Throwable或其子類的實例。 下面的語句在編譯時將會產生語法錯誤:
    throw new String("want to throw");

開發中如何選擇使用try-catch-finally 還是使用throws?

  1. 如果父類中被重寫的方法沒有throws方式處理異常,則子類重寫的方法也不能使用throws,意味着如果子類重寫的方法中有異常,必須使用try-catch-finally方式處理。

  2. 執行的方法a中,先後又調用了另外的幾個方法,這幾個方法是遞進關係執行的。我們建議這幾個方法使用throws的方式進行處理。而執行的方法a可以考慮使用try-catch-finally方式進行處理。

自定義異常

如何自定義異常類?

  1. 繼承於現有的異常結構:RuntimeException 、Exception

  2. 提供全局常量:serialVersionUID

  3. 提供重載的構造器

注意事項

  1. 一般地,用戶自定義異常類都是RuntimeException的子類。

  2. 自定義異常類通常需要編寫幾個重載的構造器。

  3. 自定義異常需要提供serialVersionUID

  4. 自定義的異常通過throw拋出。

  5. 自定義異常最重要的是異常類的名字,當異常出現時,可以根據名字判斷異常類型。

示例

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

拋出自定義異常

throw new MyException("出現了自定義異常");

小結

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章