Exceptions容許您順利處理程序運行時發生的意外狀況。要演示Java虛擬機處理異常的方式,請考慮一個名爲NitPickyMath的類。它提供了對整數執行加法,減法,乘法,除法和餘數的方法。NitPickyMath在溢出,下溢和被零除的條件下拋出已檢查的異常。Java虛擬機將在整數除零上拋出一個ArithmeticException,但不會在溢出和下溢上拋出任何異常。方法拋出的異常定義以下:java
class OverflowException extends Exception { } class UnderflowException extends Exception { } class DivideByZeroException extends Exception { }
捕獲和拋出異常的簡單方法是remainder
類的方法NitPickyMath
:程序員
static int remainder(int dividend, int divisor) throws DivideByZeroException { try { return dividend % divisor; } catch (ArithmeticException e) { throw new DivideByZeroException(); } }
該remainder
方法僅在傳遞兩個 int 參數時執行餘數運算。若是餘數運算的除數爲零,則餘數運算拋出一個ArithmeticException
。這個方法捕獲了這個ArithmeticException
並拋出一個DivideByZeroException
。DivideByZeroException
和ArithmeticException
之間的差異是 DivideByZeroException
是一個 檢查 異常,而且ArithmeticException
是 未經檢查 。由於ArithmeticException
是非受檢異常,因此方法不須要在throws子句中聲明此異常,即便它可能會拋出它。任何屬於 Error 或者 RuntimeException
子類的異常都是非受檢異常。(ArithmeticException
是 RuntimeException
的子類。)經過捕獲ArithmeticException
而後拋出 DivideByZeroException
,該 remainder
方法強制其客戶端處理除零異常的可能性,經過捕獲它或在本身的throws子句中聲明 DivideByZeroException
。這是由於已檢查的異常,例如 DivideByZeroException
,拋出方法必須由方法捕獲或在方法的throws子句中聲明。未經檢查的異常(例如 ArithmeticException
,不須要在throws子句中捕獲或聲明)。sql
javac
爲該 remainder
方法生成如下字節碼序列:架構
The main bytecode sequence for remainder: 0 iload_0 // Push local variable 0 (arg passed as divisor) 1 iload_1 // Push local variable 1 (arg passed as dividend) 2 irem // Pop divisor, pop dividend, push remainder 3 ireturn // Return int on top of stack (the remainder) The bytecode sequence for the catch (ArithmeticException) clause: 4 pop // Pop the reference to the ArithmeticException // because it isn't used by this catch clause. 5 new #5 <Class DivideByZeroException> // Create and push reference to new object of class // DivideByZeroException. DivideByZeroException 8 dup // Duplicate the reference to the new // object on the top of the stack because it // must be both initialized // and thrown. The initialization will consume // the copy of the reference created by the dup. 9 invokenonvirtual #9 <Method DivideByZeroException.<init>()V> // Call the constructor for the DivideByZeroException // to initialize it. This instruction // will pop the top reference to the object. 12 athrow // Pop the reference to a Throwable object, in this // case the DivideByZeroException, // and throw the exception.
該 remainder
方法的字節碼序列有兩個獨立的部分。第一部分是該方法的正常執行路徑。這部分從pc偏移0到3。第二部分是catch子句,它從pc偏移4到12。併發
主字節碼序列中的 irem 指令可能會拋出一個 ArithmeticException
。若是發生這種狀況,Java虛擬機知道經過查找表中的異常來跳轉到實現catch子句的字節碼序列。捕獲異常的每一個方法都與一個異常表相關聯,該異常表在類文件中與方法的字節碼序列一塊兒傳遞。每一個try塊捕獲的每一個異常在異常表中都有一個條目。每一個條目都有四條信息:起點和終點,要跳轉到的字節碼序列中的pc偏移量,以及正被捕獲的異常類的常量池索引。 remainder 類的 NitPickyMath 方法的異常表以下所示:app
Exception table: from to target type 0 4 4 <Class java.lang.ArithmeticException>
上面的異常表指示從pc偏移0到3(包括0),表示 ArithmeticException
將被捕獲的範圍。在標籤「to」下面的表中列出的是try塊的端點值,它老是比捕獲異常的最後一個pc偏移量多一。在這種狀況下,端點值列爲4,捕獲到異常的最後一個pc偏移量爲3。此範圍(包括0到3)對應於在 remainder
的try塊內實現代碼的字節碼序列。若是 ArithmeticException
在pc偏移量爲0和3之間(包括0和3)之間拋出,則表中列出的"to"就是跳轉到的pc偏移量。分佈式
若是在執行方法期間拋出異常,Java虛擬機將在異常表中搜索匹配的條目。若是當前程序計數器在條目指定的範圍內,而且拋出的異常類是由條目指定的異常類(或者是指定異常類的子類),則異常表條目匹配。Java虛擬機按照條目在表中的顯示順序搜索異常表。找到第一個匹配項後,Java虛擬機會將程序計數器設置爲新的pc偏移位置並繼續執行。若是未找到匹配項,Java虛擬機將彈出當前堆棧幀並從新拋出相同的異常。當Java虛擬機彈出當前堆棧幀時,它有效地停止當前方法的執行並返回調用此方法的方法。可是,不是在前一個方法中繼續正常執行,而是在該方法中拋出相同的異常,這會致使Java虛擬機經歷搜索該方法的異常表的相同過程。ide
Java程序員能夠使用throw語句拋出異常,例如 remainder
中的一個子句catch( ArithmeticException
),其中一個 DivideByZeroException
建立並拋出。執行拋出的字節碼以下表所示:微服務
athrow 指令從堆棧中彈出頂部字節,而且會認爲它是一個 Throwable
子類的引用(或 Throwable
自己)。拋出的異常是彈出對象引用定義的類型。高併發
下面的applet演示了一個執行一系列字節碼的Java虛擬機。模擬中的字節碼序列由javac生成。
類的playBall方法以下所示:
class Ball extends Exception { } class Pitcher { private static Ball ball = new Ball(); static void playBall() { int i = 0; while (true) { try { if (i % 4 == 3) { throw ball; } ++i; } catch (Ball b) { i = 0; } } } }
javac爲該 playBall
方法生成的字節碼以下所示:
0 iconst_0 // Push constant 0 1 istore_0 // Pop into local var 0: int i = 0; // The try block starts here (see exception table, below). 2 iload_0 // Push local var 0 3 iconst_4 // Push constant 4 4 irem // Calc remainder of top two operands 5 iconst_3 // Push constant 3 6 if_icmpne 13 // Jump if remainder not equal to 3: if (i % 4 == 3) { // Push the static field at constant pool location #5, // which is the Ball exception itching to be thrown 9 getstatic #5 <Field Pitcher.ball LBall;> 12 athrow // Heave it home: throw ball; 13 iinc 0 1 // Increment the int at local var 0 by 1: ++i; // The try block ends here (see exception table, below). 16 goto 2 // jump always back to 2: while (true) {} // The following bytecodes implement the catch clause: 19 pop // Pop the exception reference because it is unused 20 iconst_0 // Push constant 0 21 istore_0 // Pop into local var 0: i = 0; 22 goto 2 // Jump always back to 2: while (true) {} Exception table: from to target type 2 16 19 <Class Ball>
該 playball
方法永遠循環。每四次循環,playball拋出 Ball
並抓住它,只是由於它頗有趣。由於try塊和catch子句都在無限循環中,因此樂趣永遠不會中止。局部變量 i 從0開始,每次遞增遞增循環。當 if
語句出現 true
時,每次 i 等於3 時都會發生 Ball
異常,拋出異常。
Java虛擬機檢查異常表並發現確實存在適用的條目。條目的有效範圍是2到15(包括二者),異常在pc偏移12處拋出。條目捕獲的異常是類 Ball ,拋出的異常是類 Ball
。鑑於這種完美匹配,Java虛擬機將拋出的異常對象推送到堆棧,並繼續在pc偏移19處執行catch子句,這裏僅將 int i 重置爲0,而且循環從新開始。
要驅動模擬,只需按「步驟」按鈕。每次按下「Step」按鈕都會使Java虛擬機執行一個字節碼指令。要開始模擬,請按「重置」按鈕。要使Java虛擬機重複執行字節碼而不須要進一步操做,請按「運行」按鈕。而後,Java虛擬機將執行字節碼,直到按下「中止」按鈕。applet底部的文本區域描述了要執行的下一條指令。快樂點擊。
不單單是虛擬機,針對Java的Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分佈式、高併發、性能調優、微服務等架構技術,我這邊都給大夥兒找了點資料,但願能幫助到有須要的人,記得私信我喲
怎麼領取 → 進粉絲羣:963944895,私聊管理員便可
既然看到這裏了,以爲筆者寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!