【问题标题】:Java - Difference between throwing an Exception and catching and rethrowing ExceptionJava - 抛出异常与捕获和重新抛出异常之间的区别
【发布时间】:2012-03-25 13:44:38
【问题描述】:
我对捕获并重新抛出异常而不是一开始就抛出异常有什么好处感到困惑。
例如
private void testMethod() throws Exception
{
//some bad code here
}
对比:
private void testMethod() throws Exception
{
try
{
//some bad code here
}
catch (Exception e)
{
throw e;
}
} //end testMethod()
这是为了保留错误消息的堆栈跟踪吗?我尝试设置一个示例,但没有看到两者之间有任何不同的输出。
感谢您的帮助。
【问题讨论】:
标签:
java
exception
try-catch
throws
【解决方案1】:
您的两个代码示例之间的行为没有区别。 (特别是,堆栈跟踪是在创建异常时记录的,而不是在抛出异常时记录的,因此重新抛出的异常仍将具有原始堆栈跟踪)。因此,人们通常使用更简单的成语。
这并不是说重新抛出没有它的用途。例如,如果你想处理除 FooBarExceptions 之外的所有异常,你可以这样写:
try {
// bad code
} catch (FooBarException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
或者,如果处理异常的决定比简单地检查其类型更复杂,您可以简单地捕获它,如果事实证明您无法处理它,则重新抛出:
for (int attempts = 0; attemps < 6; attempts++) {
try {
return crankyMethod();
} catch (Exception e) {
if (fatal(e)) {
throw e;
} else {
// try again
continue;
}
}
}
值得注意的是,当人们说重新抛出时,有些人的意思是抛出不同的异常,如下例所示:
for (int i = 0; i < array.length; i++) {
try {
process(array[i]);
} catch (Exception e) {
throw new RuntimeException("Could not process element at index " + i, e);
}
}
这种模式的优点是用可能相关的附加信息来装饰原始异常(在上面的示例中:无法处理哪些数据)。请注意,原始异常会传递给新异常的构造函数,因此不会丢失其堆栈跟踪。
【解决方案2】:
捕获然后重新抛出的主要好处之一是将异常包装在另一个特定于域的异常中,因此调用代码不必担心特定于实现的问题,并且可以捕获特定于库的异常。
在您给出的示例中没有真正的好处,但是在其他库中可以看到这种好处的一个很好的例子——作为一个例子,我们可以看看 Hibernate。
Hibernate 捕获一般的 SQLException,然后检查对象以确定异常的真正原因,然后将其包装在 Hibernate 特定的异常中,该异常更能描述问题所在。
但是,如果您只是捕获一个异常并抛出相同的异常,那么您很可能会这样做,以便您可以首先记录异常(尽管这不一定是最好的使用方法)。
【解决方案3】:
正如其他人所说,在您的示例中没有区别,应避免使用第二种形式。
有时唯一有意义的地方是您需要捕获一些异常并让其他异常向上抛出。因为 Java 的异常处理语法是有限的,所以有时你可能会这样做:
try {
somePieceOfCode();
} catch( RuntimeException e ) {
throw e;
} catch( Exception e ) {
handleException(e);
}
如果somePieceOfCode 抛出许多不同的检查异常,这些异常没有共同的基类(Exception 除外)但需要以相同的方式处理,则有时会这样做。您可能不想只捕获Exception,因为这也会捕获NullPointerException 之类的东西,您可能更希望这些异常按原样冒泡。
此代码将让所有RuntimeExceptions 向上冒泡,但处理所有其他异常。
这个成语有点不寻常,并不是每个人都喜欢它,但你可能会在某些地方看到它。
【解决方案4】:
捕获和重新抛出异常的标准原因是:
- 记录事件和异常。
- 由于异常进行一些清理(但通常最好在 finally 块中完成)。
- 将异常包装在更合适的异常中(即,您的 processAccount() 方法可能更适合抛出 AccountException(或类似的)而不是 DbException)。
【解决方案5】:
通过这种方法,您可以修改异常。例如,您可以给它一个更具体的消息。
话虽如此,您通常不应该使用这种技术,因为
- 它将您的代码聚集起来,没有额外的好处(在正常情况下)
- 您应该只在实际需要时使用 try-catch 块,因为它会减慢执行速度
【解决方案6】:
我不认为有太大的区别,但就其价值而言,我更喜欢第一个。
您不应该捕获异常,除非您可以对此采取措施,或者如果您的类是异常不应传播的边界(例如,捕获所有异常并将用户路由到友好错误页面的 Web 控制器)。
简单地捕捉和重新抛出是对击键的浪费。它没有添加任何信息,也没有做任何建设性的事情。
一个例外是捕获一个已检查的异常并将其包装为一个未检查的异常。否则避免使用第二个成语。
【解决方案7】:
没有区别。除非你想在重新抛出它之前对它做点什么(例外)。