Java異常處理

你之因此能優於別人,正是由於你堅持了別人所不能堅持的。
本文相關代碼在個人Github,歡迎Star~
https://github.com/zhangzhibo1014/DaBoJava

異常

異常指不期而至的各類情況,異常是程序中的一些錯誤,但並非全部的錯誤都是異常,而且錯誤有時候是能夠避免的。java

好比說,你的代碼少了一個分號,那麼運行出來結果是提示是錯誤 java.lang.Error ;若是你用 System.out.println(11/0) ,那麼你是由於你用0作了除數,會拋出 java.lang.ArithmeticException 的異常。git

要理解Java異常處理是如何工做的,你須要掌握如下三種類型的異常:程序員

  • 檢查性異常: 最具表明的檢查性異常是用戶錯誤或問題引發的異常,這是程序員沒法預見的。例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
  • 運行時異常: 運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異常能夠在編譯時被忽略。
  • 錯誤: 錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中一般被忽略。例如,當棧溢出時,一個錯誤就發生了,它們在編譯也檢查不到的。

異常都是從 Throwable 類派生出來的,而 Throwable 類是直接從 Object 類繼承而來。github

異常發生的緣由

  • 用戶輸入了非法數據。
  • 要打開的文件不存在
  • 網絡通訊時鏈接中斷,或者JVM內存溢出

異常分類

Exception.png

  • Error:系統內部錯誤,這類錯誤由系統進行處理,程序自己無需捕獲處理。
  • Exception:能夠處理的異常。
  • RuntimeException:能夠捕獲,也能夠不捕獲的異常。
  • 繼承 Exception 的其餘類:必須捕獲,一般在 API 文檔中會說明這些方法拋出哪些異常。

全部的異常類是從 java.lang.Exception 類繼承的子類。而 Exception 異常下又主要分爲兩大類異常,一個是派生於 RuntimeExcption 的異常,一個是除了 RuntimeExcption 體系以外的其餘異常。數組

RuntimeExcption 異常(運行時異常)一般有如下幾種:安全

  • 錯誤的類型轉換
  • 數組訪問越界
  • 訪問 null 指針
  • 算術異常
異常 描述
ArithmeticException 當出現異常的運算條件時,拋出此異常。例如,一個整數"除以零"時,拋出此類的一個實例。
ArrayIndexOutOfBoundsException 用非法索引訪問數組時拋出的異常。若是索引爲負或大於等於數組大小,則該索引爲非法索引。
ArrayStoreException 試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常。
ClassCastException 當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。
IllegalArgumentException 拋出的異常代表向方法傳遞了一個不合法或不正確的參數。
IllegalMonitorStateException 拋出的異常代表某一線程已經試圖等待對象的監視器,或者試圖通知其餘正在等待對象的監視器而自己沒有指定監視器的線程。
IllegalStateException 在非法或不適當的時間調用方法時產生的信號。換句話說,即 Java 環境或 Java 應用程序沒有處於請求操做所要求的適當狀態下。
IllegalThreadStateException 線程沒有處於請求操做所要求的適當狀態時拋出的異常。
IndexOutOfBoundsException 指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。
NegativeArraySizeException 若是應用程序試圖建立大小爲負的數組,則拋出該異常。
NullPointerException 當應用程序試圖在須要對象的地方使用 null 時,拋出該異常
NumberFormatException 當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換爲適當格式時,拋出該異常。
SecurityException 由安全管理器拋出的異常,指示存在安全侵犯。
StringIndexOutOfBoundsException 此異常由 String 方法拋出,指示索引或者爲負,或者超出字符串的大小。
UnsupportedOperationException 當不支持請求的操做時,拋出該異常。

通常來講,RuntimeException 都是代碼邏輯出現問題。微信

RuntimeException(受檢異常,Checked Exception)通常有:網絡

  • 打開一個不存在的文件
  • 沒有找到具備指定名稱的類
  • 操做文件異常
異常 描述
ClassNotFoundException 應用程序試圖加載類時,找不到相應的類,拋出該異常。
CloneNotSupportedException 當調用 Object 類中的 clone 方法克隆對象,但該對象的類沒法實現 Cloneable 接口時,拋出該異常。
IllegalAccessException 拒絕訪問一個類的時候,拋出該異常。
InstantiationException 當試圖使用 Class 類中的 newInstance 方法建立一個類的實例,而指定的類對象由於是一個接口或是一個抽象類而沒法實例化時,拋出該異常。
InterruptedException 一個線程被另外一個線程中斷,拋出該異常。
NoSuchFieldException 請求的變量不存在
NoSuchMethodException 請求的方法不存在

受檢異常是編譯器要求必須處理的異常,必須使用 try catch 處理,或者使用 throw 拋出,交給上層調用者處理。函數

異常的聲明及拋出

throw拋出異常

當程序運行時數據出現錯誤或者咱們不但願發生的狀況出現的話,能夠經過拋出異常來處理。spa

異常拋出語法:

throw new 異常類();

//拋出異常
    public static void fun1() {
        Integer a = 1;
        Integer b = null;
        // 若是 a 或者 b 爲 null 則拋出異常
        if (a == null || b == null) {
            throw new NullPointerException();
        } else {
            System.out.println(a+b);
        }
    }

throws聲明異常

throws 用於聲明異常,表示該方法可能會拋出的異常。若是聲明的異常中包括 checked 異常(受檢異常),那麼調用者必須捕獲處理該異常或者使用 throws 繼續向上拋出。throws 位於方法體前,多個異常之間使用 , 分割。

public static void main(String[] args) throws FileNotFoundException {
        fun2();
    }

    //聲明異常
    public static void fun2() throws FileNotFoundException {
        new FileInputStream("a.txt");
    }

throw 和 throws的區別

  • throw 用於拋出異常對象,後面跟的是異常對象;throw 用在函數內。
  • throws 用於拋出異常類,後面跟的異常類名,能夠跟多個,用逗號隔開。throws 用在函數上

捕獲異常

一般拋出異常後,還須要將異常捕獲。使用 trycatch 語句塊來捕獲異常,有時候還會用到 finally

對於上述三個關鍵詞所構成的語句塊,try 語句塊是必不可少的,catchfinally 語句塊能夠根據狀況選擇其一或者全選。你能夠把可能發生錯誤或出現問題的語句放到 try 語句塊中,將異常發生後要執行的語句放到 catch 語句塊中,而 finally 語句塊裏面放置的語句,無論異常是否發生,它們都會被執行。

捕獲異常對於系統而言,其開銷很是大,因此應儘可能減小該語句塊中放置的語句。

public class Demo1 {
    public static void main(String[] args) {
        try{
        //要檢查的程序語句
            System.out.println("I am try block.");

            Class<?> tempClass = Class.forName("");
            // 聲明一個空的Class對象用於引起「類未發現異常」
            System.out.println("Bye! Try block.");

        }catch (ClassNotFoundException e) {
        //異常發生時的處理語句
            System.out.println("I am catch block.");

            e.printStackTrace();
            //printStackTrace()的意義在於在命令行打印異常信息在程序中出錯的位置及緣由

            System.out.println("Goodbye! Catch block.");
        }finally {
        //必定會執行的語句
            System.out.println("I am finally block.");
        }
    }
}

注意下面事項

  • catch 不能獨立於 try 存在。
  • try/catch 後面添加 finally 塊並不是強制性要求的。
  • try 代碼後不能既沒 catch 塊也沒 finally 塊。
  • try, catch, finally 塊之間不能添加任何代碼。
  • finally 頗有用,主要用戶關閉資源。不管是否發生異常,資源都必須進行關閉

捕獲多個異常

在一段代碼中,可能會因爲各類緣由拋出多種不一樣的異常,而對於不一樣的異常,咱們但願用不一樣的方式來處理它們,而不是籠統的使用同一個方式處理,在這種狀況下,可使用異常匹配,當匹配到對應的異常後,後面的異常將再也不進行匹配。

public class Demo2 {
    public static void main(String[] args) {
        try{
            new FileInputStream("");
        } catch (FileNotFoundException e) {
            System.out.println("IO 異常");
        } catch (Exception e) {
            System.out.println("發生異常");
        }
    }
}

在處理異常時,並不要求拋出的異常同 catch 所聲明的異常徹底匹配,子類的對象也能夠匹配父類的處理程序。好比異常 A 繼承於異常 B,那麼在處理多個異常時,必定要將異常 A 放在異常 B 以前捕獲,若是將異常 B 放在異常 A 以前,那麼將永遠匹配到異常 B,異常 A 將永遠不可能執行,而且編譯器將會報錯。

父類的異常捕獲語句不能夠寫在子類異常捕獲語句前面

Exception1.png

自定義異常

自定義一個異常類很是簡單,只須要讓它繼承 Exception 或其子類就行。在自定義異常類的時候,建議同時提供無參構造方法和帶字符串參數的構造方法,後者能夠爲你在調試時提供更加詳細的信息。

Java 中你能夠自定義異常。編寫本身的異常類時須要記住下面的幾點。

  • 全部異常都必須是 Throwable 的子類。
  • 若是但願寫一個檢查性異常類,則須要繼承 Exception 類。
  • 若是你想寫一個運行時異常類,那麼須要繼承 RuntimeException 類。
public class Demo3 {
    public static void main(String[] args) {
        int number = 5;
        for(int i = 4; i > -1; i--) {
            if (i == 0) {
                throw new MyException("這是一個異常");
            }
            System.out.println(number / i);
        }
    }
}

//自定義異常類 ,繼承於ArithmeticException
class MyException extends ArithmeticException {
    //實現無參構造器
    public MyException() {

    }
    //實現參數構造器,可將提示信息做爲異常結果輸出
    public MyException(String msg) {
        super(msg);
    }
}

異常堆棧

當異常拋出後,咱們能夠經過異常堆棧追蹤程序的運行軌跡,以便咱們更好的 DEBUG

public class Demo4 {
    public static void method1() {
        method2();
    }

    public static void method2() {
        throw new NullPointerException();
    }

    public static void main(String[] args) {
        try{
            method1();
        } catch (Exception e) {
            // 打印堆棧軌跡
            e.printStackTrace();
        }
    }
}

打印結果:
java.lang.NullPointerException
    at Demo4.method2(Demo4.java:10)
    at Demo4.method1(Demo4.java:6)
    at Demo4.main(Demo4.java:15)

經過上面的異常堆棧軌跡,在對比咱們方法的調用過程,能夠得出異常信息中首先打印的是距離拋出異常最近的語句,接着是調用該方法的方法,一直到最開始被調用的方法。 從下往上看 ,就能夠得出程序運行的軌跡。

何時定義try,何時定義throws呢?

  • 功能內部若是出現異常,若是內部能夠處理,就用 try
  • 若是功能內部處理不了,就必須聲明出來,讓調用者處理

總結

異常的捕獲、拋出和異常處理是維持代碼健壯性的重要條件。靈活使用異常及處理,不只能最大限度的避免出錯,也能增長軟件的容錯機制。

相關代碼記錄於GitHub中,歡迎各位夥伴 Star

有任何疑問 微信搜一搜 [程序猿大博] 與我聯繫~

若是以爲對您有所幫助,請 點贊收藏 ,若有不足,請評論或私信指正!謝謝~

相關文章
相關標籤/搜索