2013-11-04 63 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(); 
}