Java 提供了一種健壯且面向對象的方法來處理稱爲 Java異常處理的異常狀況。java
異常是在程序執行期間可能發生的錯誤事件,它會破壞其正常流程。異常可能源於各類狀況,例如用戶輸入的錯誤數據,硬件故障,網絡鏈接故障等。程序員
每當執行 Java 語句時發生任何錯誤,都會建立一個異常對象,而後 JRE嘗試查找異常處理程序來處理該異常。若是找到了合適的異常處理程序,則將異常對象傳遞處處理程序代碼以處理異常,稱爲捕獲異常。若是未找處處理程序,則應用程序將異常拋出給運行時環境,而且 JRE 終止程序。面試
Java 異常處理框架僅用於處理運行時錯誤,異常處理框架不處理編譯時錯誤。算法
java 異常處理中使用了四個關鍵字。編程
throw:有時咱們明確地想要建立異常對象,而後將其拋出以中止程序的正常處理。throw 關鍵字用於向運行時拋出異常以進行處理。數組
throws:當咱們在方法中拋出任何已檢查的異常而且不對其進行處理時,咱們須要在方法簽名時使用 throws 關鍵字,以使調用方程序知道該方法可能拋出的異常。調用方方法能夠處理這些異常,也可使用throws
關鍵字將其傳播到其調用方方法。咱們能夠在 throws 子句中提供多個異常,它也能夠與 main()方法一塊兒使用。網絡
try-catch:咱們在代碼中使用 try-catch 塊進行異常處理。try 是塊的開始,catch 是 try 塊的末尾,用於處理異常。咱們可使用 try 捕獲多個 catch 塊,而且 try-catch 塊也能夠嵌套。catch 塊須要一個應爲 Exception 類型的參數。框架
finally:finally 塊是可選的,只能與 try-catch 塊一塊兒使用。因爲異常會暫停執行過程,所以咱們可能會打開一些不會關閉的資源,所以可使用 finally 塊。不管是否發生異常,finally 塊都會始終執行。ide
Java 異常是分層的,繼承用於對不一樣類型的異常進行分類。Throwable
是 Java 異常層次結構的父類,它有兩個子對象– Error
和Exception
。異常進一步分爲檢查異常和運行時異常。函數
Error是超出應用程序範圍的特殊狀況,沒法預見並從中恢復,例如硬件故障,JVM 崩潰或內存不足錯誤。
Checked Exception 是咱們能夠在程序中預期並嘗試從程序中恢復的異常狀況,例如 FileNotFoundException。咱們應該捕獲該異常,並向用戶提供有用的消息,並正確記錄下來以進行調試。Exception
是全部 「檢查的異常」 的父類。
Runtime Exception是由錯誤的編程引發的,例如,嘗試從 Array 中檢索元素。在嘗試檢索元素以前,咱們應該首先檢查數組的長度,不然它可能ArrayIndexOutOfBoundException
在運行時拋出。RuntimeException
是全部運行時異常的父類。
Exception及其全部子類均未提供任何特定方法,而且全部方法均在基類 Throwable 中定義。
getMessage()
方法便可返回異常消息。void printStackTrace() –此方法將堆棧跟蹤信息打印到標準錯誤流,此方法已重載,咱們能夠傳遞 PrintStream 或 PrintWriter 做爲參數,以將堆棧跟蹤信息寫入文件或流。
若是您在單個 try 塊中捕獲了不少異常,則您會注意到 catch 塊代碼看起來很是醜陋,而且主要由用於記錄錯誤的冗餘代碼組成,請記住,Java 7 的功能之一就是多捕獲塊咱們能夠在單個 catch 塊中捕獲多個異常。具備此功能的 catch 塊以下所示:
catch(IOException | SQLException | Exception ex){ logger.error(ex); throw new MyException(ex.getMessage()); }
在大多數狀況下,咱們使用 finally 塊只是爲了關閉資源,有時咱們忘記關閉它們並在資源耗盡時獲取運行時異常。這些異常很難調試,咱們可能須要調查使用該類型資源的每一個位置,以確保咱們將其關閉。所以,java 7 的改進之一是 try-with-resources,咱們能夠在 try 語句自己中建立資源,並在 try-catch 塊內使用它。當執行從 try-catch 塊執行時,運行時環境會自動關閉這些資源。具備這種改進的 try-catch 塊示例爲:
try (MyResource mr = new MyResource()) { System.out.println("MyResource created in try-with-resources"); } catch (Exception e) { e.printStackTrace(); }
一、檢查異常應在代碼中使用 try-catch 塊進行處理,不然方法應使用 throws 關鍵字使調用者知道該方法可能拋出的檢查異常。未經檢查的異常不須要在程序中處理,也不須要在方法的 throws 子句中說起。
2.、Exception是全部Checked 異常的超類,而RuntimeException
是全部Unchecked 的異常的超類。請注意,RuntimeException 是 Exception 的子類。
三、Checked 異常是須要在代碼中處理的錯誤方案,不然您將得到編譯時錯誤。例如,若是您使用 FileReader 讀取文件,則可能會拋出該文件FileNotFoundException
,咱們必須將其在 try-catch 塊中捕獲,或再次將其拋出給調用方方法。Unchecked 異常一般是由不良的編程引發的,例如,在調用對象引用中的方法而不確保其不爲 null 時,會引起 NullPointerException。例如,我能夠編寫一種方法來刪除字符串中的全部元音。確保不傳遞空字符串對象是調用者的責任。我可能會更改處理這些狀況的方法,但理想狀況下,調用方應注意這一點。
throws 關鍵字與方法一塊兒使用,以聲明該方法可能拋出的異常,而 throw 關鍵字用於中斷程序流,並將異常對象移交給運行時進行處理。
咱們能夠擴展Exception
類或它的任何子類來建立咱們的自定義異常類。自定義異常類能夠具備本身的變量和方法,可用於將錯誤代碼或其餘與異常相關的信息傳遞給異常處理程序。
自定義異常的一個簡單示例以下所示。
package com.journaldev.exceptions; import java.io.IOException; public class MyException extends IOException { private static final long serialVersionUID = 4664456874499611218L; private String errorCode="Unknown_Exception"; public MyException(String message, String errorCode){ super(message); this.errorCode=errorCode; } public String getErrorCode(){ return this.errorCode; } }
Java 中的 OutOfMemoryError 是 java.lang.VirtualMachineError 的子類,當 JVM 堆內存不足時,它會被 JVM 拋出。咱們能夠經過修改 java 選項提供更多內存來解決此錯誤。
$>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m
一些常見的主線程異常狀況是:
final 和 finally 是 Java 中的關鍵字,而 finalize 是一種方法。
final 關鍵字能夠與類變量一塊兒使用,以使它們不能被從新分配; class 能夠避免經過類進行擴展; final
關鍵字能夠與方法避免被子類覆蓋;
finally 關鍵字能夠與 try-catch 塊一塊兒使用,以提供將始終執行的語句即便出現某些異常,一般最終仍是會用來關閉資源。
finalize()方法在對象被銷燬以前由垃圾回收器執行,這是確保關閉全部全局資源的好方法。
在這三個中,只有finally 與 Java 異常處理有關。
當 main()方法引起異常時,Java Runtime 將終止程序並在系統控制檯中打印異常消息和堆棧跟蹤。
咱們能夠有一個空的 catch 塊,但這是最糟糕的編程示例。咱們永遠不該該有空的 catch 塊,由於若是異常被該塊捕獲,咱們將沒有有關該異常的信息,調試它將是一場噩夢。至少應該有一條日誌記錄語句,以將異常詳細信息記錄在控制檯或日誌文件中。
與 Java 異常處理有關的一些最佳實踐是:
在這裏,咱們將研究與 Java 異常相關的一些編程問題。
1). 下面的程序有什麼問題?
package com.journaldev.exceptions; import java.io.FileNotFoundException; import java.io.IOException; public class TestException { public static void main(String[] args) { try { testExceptions(); } catch (FileNotFoundException | IOException e) { e.printStackTrace(); } } public static void testExceptions() throws IOException, FileNotFoundException{ } }
上面的程序沒法編譯,而且您會收到錯誤消息,「The exception FileNotFoundException is already caught by the alternative IOException」。這是由於 FileNotFoundException 是 IOException 的子類,有兩種方法能夠解決此問題。
第一種方法是對兩個異常都使用單個 catch 塊。
try { testExceptions(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }
另外一種方法是從多捕獲塊中刪除 FileNotFoundException。
try { testExceptions(); }catch (IOException e) { e.printStackTrace(); }
您能夠根據狀況選擇任何一種方法。
2). 下面的程序有什麼問題?
package com.journaldev.exceptions; import java.io.FileNotFoundException; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException1 { public static void main(String[] args) { try { go(); } catch (IOException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); } } public static void go() throws IOException, JAXBException, FileNotFoundException{ } }
該程序將編譯錯誤,由於 FileNotFoundException 是 IOException 的子類,所以 FileNotFoundException 的 catch 塊不可訪問,而且您將收到錯誤消息 「 Unreachable catch block for FileNotFoundException. It is already handled by the catch block for IOException」。
您須要修復 catch 塊順序才能解決此問題。
try { go(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); }
請注意,JAXBException 與 IOException 或 FileNotFoundException 不相關,能夠放置在以上 catch 塊層次結構中的任何位置。
3). 下面的程序有什麼問題?
package com.journaldev.exceptions; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException2 { public static void main(String[] args) { try { foo(); } catch (IOException e) { e.printStackTrace(); }catch(JAXBException e){ e.printStackTrace(); }catch(NullPointerException e){ e.printStackTrace(); }catch(Exception e){ e.printStackTrace(); } } public static void foo() throws IOException{ } }
該程序將沒法編譯,由於 JAXBException 是一個已檢查的異常,而且 foo()方法應拋出此異常以捕獲調用方法。您將收到錯誤消息 「 JAXBException 沒法訪問的捕獲塊。不會從 try 語句主體中引起此異常。
要解決此問題,您將必須刪除 JAXBException 的 catch 塊。
注意,捕獲 NullPointerException 是有效的,由於它是未經檢查的異常。
4). 下面的程序有什麼問題?
package com.journaldev.exceptions; public class TestException3 { public static void main(String[] args) { try{ bar(); }catch(NullPointerException e){ e.printStackTrace(); }catch(Exception e){ e.printStackTrace(); } foo(); } public static void bar(){ } public static void foo() throws NullPointerException{ } }
這是一個技巧性的問題,代碼沒有問題,它將成功編譯。咱們老是能夠捕獲 Exception 或任何未經檢查的異常,即便它不在方法的 throws 子句中也是如此。
一樣,若是方法(foo)在 throws 子句中聲明未經檢查的異常,則在程序中處理該異常不是強制性的。
5). 下面的程序有什麼問題?
package com.journaldev.exceptions; import java.io.IOException; public class TestException4 { public void start() throws IOException{ } public void foo() throws NullPointerException{ } } class TestException5 extends TestException4{ public void start() throws Exception{ } public void foo() throws RuntimeException{ } }
上面的程序沒法編譯,由於子類中的start()方法簽名不一樣。要解決此問題,咱們能夠將子類中的方法特性更改成與超類徹底相同,也能夠從子類方法中刪除throws子句,以下所示。
@Override public void start(){ }
6). 下面的程序有什麼問題?
package com.journaldev.exceptions; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException6 { public static void main(String[] args) { try { foo(); } catch (IOException | JAXBException e) { e = new Exception(""); e.printStackTrace(); }catch(Exception e){ e = new Exception(""); e.printStackTrace(); } } public static void foo() throws IOException, JAXBException{ } }
上面的程序沒法編譯,由於多捕獲塊中的異常對象是最終對象,咱們沒法更改其值。因爲「沒法分配多捕獲塊的參數e」,將致使編譯時錯誤。
咱們必須刪除對新異常對象的「 e」分配以解決此錯誤。
「不積跬步,無以致千里」,但願將來的你能:有夢爲馬 隨處可棲!加油,少年!
關注公衆號:「Java 知己」,天天更新Java知識哦,期待你的到來!