Day42--異常向上丟擲
-
方法呼叫棧的概念
- 在Java程式執行過程中,方法之間會相互呼叫,當一個方法呼叫另一個方法時,就會形成一個方法呼叫棧。棧底是
main
方法(程式的入口),隨著方法的呼叫,新的方法被壓入棧頂。例如,main
方法呼叫methodA
,methodA
又呼叫methodB
,那麼此時棧頂是methodB
,中間是methodA
,棧底是main
方法。
- 在Java程式執行過程中,方法之間會相互呼叫,當一個方法呼叫另一個方法時,就會形成一個方法呼叫棧。棧底是
-
異常向上層丟擲的過程
- 當在一個
try
塊中發生異常,並且沒有合適的catch
塊來處理這個異常時,異常會沿著方法呼叫棧向上傳播。也就是說,異常會從當前方法丟擲,回到呼叫該方法的上層方法中。 - 例如,假設有三個方法
method1
、method2
和method3
,method3
在執行過程中出現了異常,並且沒有處理這個異常,那麼這個異常會從method3
丟擲,回到呼叫method3
的method2
中。如果method2
也沒有處理這個異常,異常會繼續向上傳播到呼叫method2
的method1
中,以此類推,直到異常被處理或者到達main
方法。如果異常一直傳播到main
方法都沒有被處理,那麼程式就會終止,並在控制檯列印出異常資訊。
- 當在一個
-
示例程式碼說明
- 假設有以下程式碼結構:
public class ExceptionPropagation { public static void method3() { int[] arr = {1, 2, 3}; System.out.println(arr[3]); // 這裡會丟擲ArrayIndexOutOfBoundsException異常 } public static void method2() { method3(); } public static void method1() { try { method2(); } catch (ArithmeticException e) { System.out.println("捕獲到ArithmeticException"); } } public static void main(String[] args) { method1(); } }
-
以下是結合上述程式碼詳細說明在方法呼叫棧中各個呼叫方法的位置所在以及位置變化情況:
初始狀態
程式從
main
方法開始執行,此時方法呼叫棧中只有一個元素,即main
方法處於棧底位置,此時棧的情況如下:棧內位置 方法名 棧底 main
main
方法呼叫method1
時當執行到
main
方法裡呼叫method1
的語句時,method1
方法被壓入棧中,此時method1
位於棧頂,main
方法在棧底,呼叫棧結構變為:棧內位置 方法名 棧頂 method1
棧底 main
method1
呼叫method2
時在
method1
方法內部執行呼叫method2
的語句後,method2
方法被壓入棧頂,此時棧內從上到下依次是method2
、method1
、main
,結構如下:棧內位置 方法名 棧頂 method2
中間 method1
棧底 main
method2
呼叫method3
時接著,
method2
方法裡呼叫method3
,method3
會被壓入棧頂,此時棧內元素順序變為method3
在最頂部,然後依次是method2
、method1
、main
,具體如下:棧內位置 方法名 棧頂 method3
第二層 method2
第三層 method1
棧底 main
method3
中丟擲異常後在
method3
方法執行到System.out.println(arr[3]);
丟擲ArrayIndexOutOfBoundsException
異常時,由於method3
本身沒有處理這個異常,異常開始沿著方法呼叫棧向上傳播。此時method3
的執行會立即停止,它依然處於棧頂位置,但即將要被彈出棧,棧的情況暫時還是:棧內位置 方法名 棧頂 method3
第二層 method2
第三層 method1
棧底 main
異常傳播到
method2
時隨著異常向上傳播,
method3
會被彈出棧(因為它已經執行不下去了,且異常要傳遞給上層方法),此時method2
成為棧頂,棧內剩下method2
、method1
、main
,結構如下:棧內位置 方法名 棧頂 method2
第二層 method1
棧底 main
不過因為
method2
同樣沒有處理該異常的程式碼,它也沒辦法繼續執行下去,同樣即將被彈出棧,異常繼續向上傳播。異常傳播到
method1
時method2
被彈出棧後,method1
變為棧頂,此時棧內只有method1
和main
,如下:棧內位置 方法名 棧頂 method1
棧底 main
但
method1
裡的catch
塊是用來捕獲ArithmeticException
的,無法捕獲當前的ArrayIndexOutOfBoundsException
異常,所以method1
也執行不下去了,會被彈出棧,異常繼續往main
方法傳播。異常傳播到
main
方法時method1
被彈出棧後,此時整個方法呼叫棧中就只剩下main
方法了,它既是棧頂也是棧底,棧的結構變為:棧內位置 方法名 棧頂(也是棧底) main
由於
main
方法也沒有處理這個異常的程式碼,程式最終會因為這個未處理的異常而終止執行,此時方法呼叫棧也就隨之銷燬了。總之,在方法呼叫及異常傳播過程中,方法呼叫棧會動態變化,新呼叫的方法會被壓入棧頂,當出現異常且方法無法處理時,方法會從棧頂依次彈出,異常不斷向上層方法所在的棧位置傳播,直到被處理或者導致程式終止,棧中元素的這種變化順序體現了方法間的呼叫關係以及異常回溯的過程。
異常會向上層丟擲,指的是異常物件本身從當前丟擲異常的方法位置,沿著呼叫棧的層次結構,依次回到呼叫它的上層方法中。
這個疑問的來源:
解釋 Java 中的異常處理機制,包括try-catch-finally
語句塊的執行流程。
- 首先執行
try
塊中的程式碼,如果在try
塊中沒有發生異常,則跳過catch
塊,直接執行finally
塊(如果有finally
塊),然後繼續執行try-catch-finally
語句塊之後的程式碼。 - 如果在
try
塊中發生了異常,則立即停止try
塊中剩餘程式碼的執行,根據異常的型別去匹配catch
塊中的異常型別,如果匹配成功,則執行對應的catch
塊中的程式碼,處理完異常後,再執行finally
塊(如果有)。如果沒有匹配到合適的catch
塊,則異常會向上層丟擲,直到被處理或者導致程式終止。 finally
塊中的程式碼無論是否發生異常都會被執行,通常用於釋放資源等操作,如關閉檔案流、資料庫連線等,哪怕try
塊中使用了return
語句,finally
塊依然會執行,並且如果finally
塊中有return
語句,會覆蓋try
或catch
塊中的return
結果哦(這是個容易混淆的點,也可以提及一下)。
你上面說的“如果沒有匹配到合適的catch
塊,則異常會向上層丟擲”,這是啥意思?什麼叫向上層丟擲