Java 異常(一) 異常概述及其架構
一、異常概述
(一)、概述
Java異常是Java提供的一種識別及響應錯誤的一致性機制。異常指的是程式在執行過程中,出現的非正常的情況,最終會導致JVM的非正常停止。
Java異常機制可以使程式中異常處理程式碼和正常業務程式碼分離,保證程式程式碼更加優雅,並提高程式健壯性。在有效使用異常的情況下,異常能清晰的回答 what, where, why 這3個問題:異常型別回答了“什麼”被丟擲,異常堆疊跟蹤回答了“在哪“丟擲,異常資訊回答了“為什麼“會丟擲。
(二)、異常體系
Throwable: 有兩個重要的子類:Exception(異常)和 Error(錯誤),二者都是 Java 異常處理的重要子類,各自都包含大量子類。異常和錯誤的區別是:異常能被程式本身可以處理,錯誤是無法處理。
1、Throwable:Throwable是 Java 語言中所有錯誤或異常的超類。Throwable包含兩個子類: Error 和 Exception。它們通常用於指示發生了異常情況。
Throwable包含了其執行緒建立時執行緒執行堆疊的快照,它提供了printStackTrace()等介面用於獲取堆疊跟蹤資料等資訊。
Throwable常用API:
public void printStackTrace() // 列印異常的詳細資訊。包含了異常的型別,異常的原因,還包括異常出現的位置。
public String getMessage() // 獲取發生異常的原因。
public String toString() // 獲取異常的型別和異常描述資訊。
2、Error:嚴重錯誤Error,無法通過處理的錯誤,只能事先避免。
3、Exception:表示異常,異常產生後程式設計師可以通過程式碼的方式糾正,使程式繼續執行,是必須要處理的。
(三)、異常分類
我們平常說的異常就是指Exception,因為這類異常一旦出現,我們就要對程式碼進行更正,修復程式。
異常主要分為兩大類:編譯期異常和執行期異常。
編譯期異常:checked異常。在編譯期,就會檢查,如果沒有處理異常,則編譯失敗(如日期格式化異常)。
特點: Java編譯器會檢查它。此類異常,要麼通過throws進行宣告丟擲,要麼通過try-catch進行捕獲處理,否則不能通過編譯。例如,CloneNotSupportedException就屬於被檢查異常。當通過clone()介面去克隆一個物件,而該物件對應的類沒有實現Cloneable介面,就會丟擲CloneNotSupportedException異常。被檢查異常通常都是可以恢復的。
執行期異常:runtime異常。在執行時期,檢查異常.在編譯時期,執行異常不會編譯器檢測(不報錯)(如數學異常)。
特點: Java編譯器不會檢查它。也就是說,當程式中可能出現這類異常時,倘若既"沒有通過throws宣告丟擲它",也"沒有用try-catch語句捕獲它",還是會編譯通過。例如,除數為零時產生的ArithmeticException異常,陣列越界時產生的IndexOutOfBoundsException異常,fail-fail機制產生的ConcurrentModificationException異常等,都屬於執行時異常。
雖然Java編譯器不會檢查執行時異常,但是我們也可以通過throws進行宣告丟擲,也可以通過try-catch對它進行捕獲處理。
如果產生執行時異常,則需要通過修改程式碼來進行避免。例如,若會發生除數為零的情況,則需要通過程式碼避免該情況的發生!
二、異常處理
Java異常處理機制用到的幾個關鍵字:try、catch、finally、throw、throws。
- try:用於監聽。將要被監聽的程式碼(可能丟擲異常的程式碼)放在try語句塊之內,當try語句塊內發生異常時,異常就被丟擲。
- catch:用於捕獲異常。catch用來捕獲try語句塊中發生的異常。
- finally:finally語句塊總是會被執行。它主要用於回收在try塊裡開啟的物力資源(如資料庫連線、網路連線和磁碟檔案)。只有finally塊,執行完成之後,才會回來執行try或者catch塊中的return或者throw語句,如果finally中使用了return或者throw等終止方法的語句,則就不會跳回執行,直接停止。
- throw:用於丟擲異常。
- throws:用在方法簽名中,用於宣告該方法可能丟擲的異常。
三、異常注意點
(一)、finally中的異常會覆蓋(消滅)前面try或者catch中的異常
- 不要在fianlly中使用return。
- 不要在finally中丟擲異常。
- 減輕finally的任務,不要在finally中做一些其它的事情,finally塊僅僅用來釋放資源是最合適的。
- 將盡量將所有的return寫在函式的最後面,而不是try ... catch ... finally中。
(二)、多個異常使用捕獲
- 多個異常一次捕獲,多次處理。
try{ // 編寫可能會出現異常的程式碼 }catch(異常型別A e){ 當try中出現A型別異常,就用該catch來捕獲. // 處理異常的程式碼 //記錄日誌/列印異常資訊/繼續丟擲異常 }catch(異常型別B e){ 當try中出現B型別異常,就用該catch來捕獲. // 處理異常的程式碼 //記錄日誌/列印異常資訊/繼續丟擲異常 }
注意:這種異常處理方式,要求多個catch中的異常不能相同,並且若catch中的多個異常之間有子父類異常的關係,那麼子類異常要求在上面的catch處理,父類異常在下面的catch處理。
三、例項
(一)、try - catch用法
try - catch 必須搭配使用,不能單獨使用。
public class ExceptionDemo { public static void main(String[] args) { try { int i = 10 / 0;// 除數不能為0,此行會丟擲 ArithmeticException 異常 System.out.println("i = " + i);// 丟擲異常後,此行不會執行 }catch(ArithmeticException e) { // 捕獲 ArithmeticException 異常 // 在catch 程式碼塊處理異常 e.printStackTrace(); // 異常最詳細資訊 System.out.println("e.getMessage() : " + e.getMessage());// 發生異常的原因 System.out.println("e.toString() : " + e.toString()); // 獲取異常的型別和異常描述資訊 } } }
(二)、finally 用法
try - catch - finally搭配使用,或者 try - finally 搭配使用。
public class ExceptionDemo { public static void main(String[] args) { // try-catch-finally搭配使用 try { int[] arr = {1,2,3}; int i = arr[3];// 陣列索引越界,此行會丟擲 ArrayIndexOutOfBoundsException 異常 System.out.println("i = " + i);// 丟擲異常後,此行不會執行 }catch(ArithmeticException e) { // 捕獲 ArithmeticException System.out.println(e.getMessage());// 發生異常的原因 System.exit(0); // 程式強制退出,finally 程式碼塊不會執行 }finally {// 除了程式強制退出,如(System。exit(0)),無論是否發生異常,finally 程式碼塊總會執行 System.out.println("this is finally"); } // try-finally搭配使用 try { int[] arr = {1,2,3}; int i = arr[3];// 陣列索引越界,此行會丟擲 ArrayIndexOutOfBoundsException 異常 System.out.println("i = " + i);// 丟擲異常後,此行不會執行 }finally { // 無論是否發生異常,finally 程式碼塊總會執行 System.out.println("this is finally"); } } }
注意點:
- try-catch-finally 搭配:這種形式捕獲異常時,開發者可以在 catch 程式碼塊中處理異常(如列印日誌、日誌記錄等),異常處理權在開發者。
- try-finally 搭配:這種形式捕獲異常時,預設丟擲給 JVM 處理,JVM預設處理時呼叫 e.printStackTrace() 方法列印異常詳細資訊。
- finally 程式碼塊:除非程式強制退出,否則無論程式是否發生異常,finally 程式碼塊總會執行。
- finally 中丟擲異常會覆蓋(消滅)前面 try 或者 catch 中的異常,儘量避免在 finally 程式碼塊中丟擲異常。
- 如果 try 中和 finally 中都有 return 語句,程式會先執行 finally 中的 return 語句,然後程式塊執行結束,而不會執行 try 中的 return 語句。所以儘量不在finally中使用 return 語句。
(三)、throw 用法
throw 是用於丟擲異常,將這個異常物件傳遞到呼叫者處,並結束當前方法的執行
public static void main(String[] args) { try { int i = 10 / 0; System.out.println("i = " + i); }catch(ArithmeticException e) { // 丟擲異常,傳遞自定義異常資訊提示 // 預設丟擲給 JVM 處理列印異常詳細資訊 throw new ArithmeticException("除數不能為0"); } }
(四)、throws 用法
public class ExceptionDemo { public static void main(String[] args) { demo(); } public static void demo() throws ArrayIndexOutOfBoundsException{ try { int[] arr = {1,2,3}; int i = arr[3]; System.out.println("i = " + i); }catch(ArrayIndexOutOfBoundsException e) { System.out.println(e.toString()); } } }