Java “try/catch”在细节上是如何工作的

Java “try/catch”在细节上是如何工作的,java,exception,exception-handling,stack,Java,Exception,Exception Handling,Stack,我想了解try{}catch{}块和堆栈跟踪是如何工作的 我在阅读时发现以下段落: 这会破坏原始异常的堆栈跟踪,并且总是错误的 在那之后,我意识到我真的不知道try/catch是如何工作的。我的理解如下。考虑这个例子: void top() { try { f(); } catch (MyException ex) { handleIt(); } finally { cleanup(); } } void f()

我想了解
try{}catch{}
块和堆栈跟踪是如何工作的

我在阅读时发现以下段落:

这会破坏原始异常的堆栈跟踪,并且总是错误的

在那之后,我意识到我真的不知道
try/catch
是如何工作的。我的理解如下。考虑这个例子:

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

void f() {
    g();
}

void g() {
    throw new MyException();
}
当我调用
top()
时,调用链
top->f->g
在调用堆栈上留下两个(用于
top
f
函数)。在
g
中引发异常时, 程序将执行堆栈冒泡,直到找到处理异常的
try/catch
块。同时,它释放堆栈帧并将堆栈跟踪信息附加到一些“神奇”对象,这些对象可以传递到
catch
,并且可以打印堆栈跟踪


它如何知道被调用的函数被try/catch块“包围”了?此信息是否绑定到堆栈帧?比如,指向错误处理块的指针(一些开关选择匹配的
catch
块),以及指向
finally
块的指针?为什么
e.getMessage()
在上述示例中具有破坏性(请参见注释)


注意,我知道如何使用try/catch和exceptions,我想知道它在内部是如何工作的。

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

被调用函数不需要知道它被try-catch块包围,它只创建一个包含调用链的异常对象,并将其传递给调用方法。这个方法必须决定它是处理异常,因为它被某个catch子句捕获,还是进一步传递它。在到达调用链的顶端并且VM处理它们之前,不会捕获的异常会冒泡——通常是通过打印堆栈跟踪并终止

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

低级方法只是抛出异常,我们应该在高级处理它们。考虑一下你的例子。应该是这样的

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();
}
它如何知道被调用函数被try/catch块“包围”

每个方法的代码都包含描述该方法的所有try-catch块的代码

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


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

异常将从最初抛出它的方法向上传播到调用堆栈,直到调用堆栈中的方法捕获它。如果方法a调用B,B调用C,则调用堆栈如下所示:

A
B
C
当方法C返回时,调用堆栈仅包含A和B。 异常从最初抛出它的方法向上传播到调用堆栈,直到调用堆栈中的方法捕获它


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

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

如果在catch块内抛出异常而未捕获该异常,则catch块将中断,就像try块一样


Hm,你说得对,我没有注意到抛出的问题,但其余的问题仍然有效
A
B
C