JVM如何捕獲異常?

Java異常知識

1.異常的兩大關鍵因素

(1)拋出異常
1.顯式:應用程序手動拋出異常。具體就是使用throw拋出異常
2.隱式:Java虛擬機對於沒法執行的代碼,自動拋出異常
(2)捕獲異常
1.try 代碼塊:用來標記須要進行異常監控的代碼。
2.catch 代碼塊:跟在 try 代碼塊以後,用來捕獲在 try 代碼塊中觸發的某種指定類型的異常。除了聲明所捕獲異常的類型以外,catch 代碼塊還定義了針對該異常類型的異常處理器。在 Java中,try 代碼塊後面能夠跟着多個 catch 代碼塊,來捕獲不一樣類型的異常。Java 虛擬機會從上至下匹配異常處理器。所以,前面的 catch 代碼塊所捕獲的異常類型不能覆蓋後邊的,不然編譯器會報錯。
3.fnally 代碼塊:跟在 try 代碼塊和 catch 代碼塊以後,用來聲明一段一定運行的代碼。它的設計初衷是爲了不跳過某些關鍵的清理代碼,例如關閉已打開的系統資源。在程序正常執行的狀況下,這段代碼會在 try 代碼塊以後運行。不然,也就是 try 代碼塊觸發異常的狀況下,若是該異常沒有被捕獲,fnally 代碼塊會直接運行,而且在運行以後從新拋出該異常。若是該異常被 catch 代碼塊捕獲,fnally 代碼塊則在 catch 代碼塊以後運行。在某些不幸的狀況下,catch 代碼塊也觸發了異常,那麼 fnally 代碼塊一樣會運行,並會拋出 catch 代碼塊觸發的異常。在某些極端不幸的狀況下,fnally 代碼塊也觸發了異常,那麼只好中斷當前 fnally 代碼塊的執行,並往外拋異常。java

2.異常的分類

圖片描述

1.全部異常的父類都是Throwable
2.Error異常是程序的執行狀態沒法恢復的狀態,只能停止線程甚至停止JVM的異常
3.Exception是相對Error沒有這麼嚴重的異常
4.Runtime Exception和Error都屬於不須要檢查的異常
5.除了Runtime Exception和Error的異常都是Check Exception異常
6.Check Exception異常都是須要顯式捕獲的異常

3.Java虛擬機是如何捕獲異常的?

java虛擬機構造異常實例很是昂貴。虛擬機須要生成該異常的棧軌跡。該操做會逐一訪問當前線程的 Java 棧幀,而且記錄下各類調試信息,包括棧幀所指向方法的名字,方法所在的類名、文件名,以及在代碼中的第幾行觸發該異常。
既然異常實例的構造十分昂貴,咱們是否能夠緩存異常實例,在須要用到的時候直接拋出呢?從語法角度上來看,這是容許的。然而,該異常對應的棧軌跡並不是 throw 語句的位置,而是新建異常的位置。
所以,這種作法可能會誤導開發人員,使其定位到錯誤的位置。這也是爲何在實踐中,咱們每每選擇拋出新建異常實例的緣由。

異常處理器
1.來源:每一個方法在編譯的時候都會生成一個異常表。異常表裏面的每個條目都表明一個異常處理器。
2.組成:
(1)from指針,to指針:表明捕獲異常的範圍,就是Try的範圍。
(2)target指針:表明處理器的開始位置,就是catch的起始位置。
(3)捕獲的異常類型。
3.捕獲異常
(1)當程序觸發異常時,Java 虛擬機會從上至下遍歷異常表中的全部條目。當觸發異常的字節碼的索引值在某個異常表條目的監控範圍內,Java 虛擬機會判斷所拋出的異常和該條目想要捕獲的異常是否匹配。若是匹配,Java 虛擬機會將控制流轉移至該條目 target 指針指向的字節碼。
(2)若是遍歷完全部異常表條目,Java 虛擬機仍未匹配到異常處理器,那麼它會彈出當前方法對應的Java 棧幀,而且在調用者(caller)中重複上述操做。在最壞狀況下,Java 虛擬機須要遍歷當前線程 Java 棧上全部方法的異常表。
4.finally代碼的編譯:當前版本 Java 編譯器的作法,是複製 fnally 代碼塊的內容,分別放在 try-catch 代碼塊全部正常執行路徑以及異常執行路徑的出口中。緩存

代碼1:
Try{
Try block
} catch {
Catch block
} finally {
Finally block
}
代碼2:
Try {
Try block
Finally block
} catch {
Catch block
Finally block
} finally{
Finally block
}
代碼1是咱們的Java代碼,代碼2是編譯以後的Java代碼。

注意:若是 catch 代碼塊捕獲了異常,而且觸發了另外一個異常,那麼 fnally 捕獲而且重拋的異常是哪一個呢?答案是後者。也就是說本來的異常便會被忽略掉,這對於代碼調試來講十分不利。spa

5.Java7的 Supressed 異常以及語法糖

針對上節說的會將catch的異常忽略掉,Java7引入了 Supressed 異常處理這個問題。可是使用起來仍是很麻煩(沒有感覺,😔)。
爲此,Java 7 專門構造了一個名爲 try-with-resources 的語法糖,在字節碼層面自動使用Supressed 異常。固然,該語法糖的主要目的並非使用 Supressed 異常,而是精簡資源打開關閉的用法。
資源的關閉操做自己容易觸發異常。因此爲了讓每個資源都關閉,因此每個資源都要寫個try catch 這樣太過繁瑣,因此try-with-resources就很好的解決了這個問題。程序能夠在 try 關鍵字後聲明並實例化實現了 AutoCloseable 接口的類,編譯器將自動添加對應的 close() 操做。在聲明多個AutoCloseable 實例的狀況下,編譯生成的字節碼相似於上面手工編寫代碼的編譯結果。與手工代碼相比,try-with-resources 還會使用 Supressed 異常的功能,來避免原異常「被消失」。
除了 try-with-resources 語法糖以外,Java 7 還支持在同一 catch 代碼塊中捕獲多種異常。實際實現很是簡單,生成多個異常表條目便可。線程

相關文章
相關標籤/搜索