2013-11-04 93 views
6

我想向try {} catch {}塊和堆棧跟蹤如何工作的內部工作。`try/catch`如何在細節中工作

我讀this great article about exception handling anti-patterns,發現以下段落:

catch (NoSuchMethodException e) { 
    throw new MyServiceException("Blah: " + 
     e.getMessage()); 
} 

這破壞了原始異常的堆棧跟蹤,並永遠是錯的。

之後,我才意識到,我並不真的知道如何try/catch作品。我的理解如下。請看例子:

void top() { 
    try { 
     f(); 
    } catch (MyException ex) { 
     handleIt(); 
    } finally { 
     cleanup(); 
    } 
} 

void f() { 
    g(); 
} 

void g() { 
    throw new MyException(); 
} 

當我打電話top(),調用鏈top -> f -> g 留下了兩個stack frames調用堆棧上(對於topf功能)。當在g, 中引發異常時,程序會啓動執行堆棧,直到找到處理異常的try/catch塊爲止。同時它釋放堆棧幀並將堆棧跟蹤信息附加到一些可以傳遞到catch的「魔術」對象,並且可以打印堆棧跟蹤。

它如何知道被調用的函數被try/catch塊「包圍」?這個信息綁定到堆棧幀嗎?就像一個指向錯誤處理塊的指針(某些開關選擇一個匹配的塊catch)和一個指向finally塊的指針?爲什麼e.getMessage()在上述示例中具有破壞性(請參閱評論)?

注意,我知道如何使用try/catch語句和例外情況,我想知道它是如何工作

+0

嗯,你說得對,我沒有注意到'throw' :)但剩餘的問題仍然有效 –

回答

6

「它怎麼知道被調用的函數被try/catch塊」包圍「?「

每種方法的代碼包含Exception Table其描述了方法的所有的try-catch塊。

當一個過程(函數,方法)被調用時,當前堆棧幀被附加與調用的地址指令,以便在正確的指令下恢復執行該幀(在調用指令之後)

當執行throw語句時,JVM examines each stack frame找出該幀是否可以處理該異常,方法包含一個包含調用指令的try-catch塊,並且塊的異常類型是一個超類型(或相同)的第排除異常。如果找到這樣的幀,那麼該幀將從try-catch塊指向的指令中恢復執行。

3

當引發異常時,完整的調用堆棧信息不附加到某個魔術對象,而是附加到創建的異常對象。當異常「冒泡」時不會發生這種情況 - 它在創建時發生並且始終包含完整的調用鏈。

被調用函數不需要知道它被try-catch-block包圍,它只是創建一個包含調用鏈並將其傳遞給調用方法的Exception-object。這個方法必須決定它是否處理Exception,因爲它被某個catch子句捕獲,或者如果它進一步傳遞它。在捕獲到達調用鏈頂端並且VM處理它們之前,不會被捕獲到的異常 - 通常是通過打印堆棧跟蹤並終止。

關於e.getMessage - 實例: 完整的堆棧信息包含僅在原始異常。在給定的例子中,原始的Exception對象e被丟棄,只有包含的消息被傳遞給新創建的Exception-object。並且該Exception僅「知道」其自己的調用堆棧,因此附加到e的原始信息丟失。

0

較低級別的方法只是拋出異常,我們應該在上層處理它們。考慮你的例子。它應該是這樣的

void top() { 
try { 
    f(); 
} catch (MyException ex) { 
    handleIt(); 
} finally { 
    cleanup(); 
} 
} 

void f() throws MyException { 
try{ 
    g(); 
}catch(MyException e){ 
    throws new MyException("Error in g()",e); 
} 

} 

void g() throws MyException{ 
throw new MyException(); 
}