`try / catch`如何在细节上工作

时间:2013-11-04 10:05:01

标签: java exception exception-handling stack

我想了解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和异常,我想知道它是如何工作的中。

4 个答案:

答案 0 :(得分:8)

“怎么知道被调用的函数被try / catch块”包围“了?”

每个方法的代码都包含Exception Table,它描述了该方法的所有try-catch块。

当调用过程(函数,方法)时,当前堆栈帧附加调用指令的地址,以便在正确的指令(调用指令后的下一个)恢复该帧的执行。

执行throw语句时,JVM examines each stack frame将查找该帧是否可以处理异常。它可以包含一个包含调用指令的try-catch块,并且块的异常类型是抛出异常的超类型(或相同)。如果找到这样的帧,则帧将从try-catch块指向的指令恢复其执行。

答案 1 :(得分:3)

抛出异常时,完整的调用堆栈信息不会附加到某个魔术对象,而是附加到创建的异常对象。当异常“冒泡”时不会发生这种情况 - 它在创建时发生并且它总是包含完整的调用链。

被调用的函数不需要知道它被try-catch-block包围,它只是创建一个包含调用链的Exception对象并将其传递给调用方法。此方法必须决定它是否处理异常,因为它被某些catch子句捕获或者如果它进一步向上传递它。在它们到达调用链的顶部并且VM处理它们之前没有被捕获的异常 - 通常通过打印堆栈跟踪并终止。

关于e.getMessage - 示例: 完整的堆栈信息仅包含在原始异常中。在给定示例中,丢弃原始异常对象e,仅将包含的消息传递给新创建的Exception对象。而且,Exception只“知道”自己的调用堆栈,因此附加到e的原始信息将丢失。

答案 2 :(得分: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();
}

答案 3 :(得分:0)

异常会从最初引发该异常的方法一直传播到调用堆栈,直到调用堆栈中的某个方法捕获到该异常为止。如果方法A调用B,而方法B调用C,则调用堆栈如下所示:

A
B
C

方法C返回时,调用堆栈仅包含A和B。 异常会从最初引发异常的方法一直传播到调用堆栈,直到调用堆栈中的方法捕获到异常为止。

引发异常时,该方法将在“ throw”语句之后立即停止执行。 “ throw”语句之后的任何语句都不会执行。当异常被“ catch”块捕获到某个地方时,程序将恢复执行。

如果某个方法调用另一个引发已检查异常的方法,则该调用方法将被强制传递该异常或将其捕获。捕获异常是通过try-catch块完成的,如果在try块内执行的任何调用的方法或语句未引发任何异常,则catch块将被忽略。 ,例如从除法中,调用方法callDivide的程序流被中断,就像除法中的程序流一样。程序流在可以捕获引发的异常的调用堆栈中的catch块处恢复。

如果在catch块中引发了异常并且未捕获到该异常,则catch块将被中断,就像try块一样。

http://tutorials.jenkov.com/java-exception-handling/basic-try-catch-finally.html https://www.geeksforgeeks.org/flow-control-in-try-catch-finally-in-java/