Java中重新抛出异常而不丢失堆栈跟踪

505

在C#中,我可以使用throw;语句重新抛出异常并保留堆栈跟踪:

try
{
   ...
}
catch (Exception e)
{
   if (e is FooException)
     throw;
}

Java中有类似这样的东西吗(不会丢失原始堆栈跟踪)?


5
为什么您认为它会失去原始的堆栈跟踪?唯一可能会丢失它的方式是,当您抛出新的SomeOtherException并忘记在构造函数或initCause()中分配根本原因时。 - akarnokd
5
我认为这是.Net代码的行为,但我不确定了。可能值得在某个地方查找一下或运行一次小测试。 - ripper234
13
抛出Throwable时不会修改它们。要更新堆栈跟踪,必须调用fillInStackTrace()方法。方便的是,这个方法在Throwable的构造函数中被调用。 - Robert
62
在C#中,是的,throw e;会丢失堆栈跟踪信息。但在Java中不会。 - Tim Goodman
Oracle的一些关于Java 7异常处理的文档:捕获多个异常类型和通过改进类型检查重新抛出异常 - Guillaume Husta
9个回答

679
catch (WhateverException e) {
    throw e;
}

显然,周围的方法必须通过签名等方式允许此操作。否则,您捕获的异常将被简单地重新抛出,并保留原始的堆栈跟踪。


4
你好,当我加入 throw e 这行代码时,会出现未处理的异常信息 InterruptedException。但如果我把它替换成更广泛的 Exception e,则不会出现这个问题。请问应该如何正确处理这个情况? - James P.
2
在Java 7编译器中,对于这种重新抛出异常的处理更加智能。现在它可以很好地与包含方法中特定的“throws”异常配合使用。 - Waldemar Wosiński
240
@James 如果你写了 catch(Exception e) { throw e; } 这段代码,异常将无法被处理。如果你写了 catch(InterruptedException ie) { throw ie; } 则可以处理这个异常。通常情况下,不要使用 catch(Exception e) 这样的代码,因为我们不像玩 Pokémon 一样抓住所有的异常! - corsiKa
13
@corsiKa,并不一定意味着你不想“全抓住它们”,这只是一个不同的用例。如果你有顶层循环或事件处理程序(例如,在线程的运行内),如果你不至少捕获 RuntimeException 并记录它,你经常会完全错过异常,而且在一个重要的循环中悄然退出,而这通常是一次性失败。对于像这样的自上而下的用途,捕获 Exception 通常不仅是一个好主意,而且是最佳实践。对于插件功能而言,这也非常有用,因为你不知道可能会发生什么额外的代码或异常…… - Bill K
2
@CorsiKa 我提到这个是因为我正在开发的应用程序有一些线程会随机吃掉异常。任何你不想默默退出的线程都应该在顶部加上 catch Exception,而且你可能需要考虑到并不是所有的开发者都处于你的确切情况下——许多程序员使用线程,但并不都使用框架来抽象它们——有些人编写自己的框架。 - Bill K
显示剩余6条评论

90

你还可以将异常包装在另一个异常中,并通过将异常作为原因参数传递给Throwable来保留原始堆栈跟踪:

try
{
   ...
}
catch (Exception e)
{
     throw new YourOwnException(e);
}

13
我还建议同时添加一条消息,使用 throw new YourOwnException("在尝试...时发生错误", e); - Julien
这正是我所寻找的,特别是第一条评论中提供的版本,可以传递自己的消息。 - Csa77
4
这显示了错误信息,但堆栈跟踪将错误行显示为带有“throw new.......(e)”的行,而不是导致异常的原始行。 - Ashburn RK
只有在正确设置了YourOwnException的情况下,此代码才能正常工作。请参阅RuntimeException上的4个公共构造函数,这些构造函数调用了super - Captain Man

88

I would prefer:

try
{
    ...
}
catch (FooException fe){
   throw fe;
}
catch (Exception e)
{
    // Note: don't catch all exceptions like this unless you know what you
    // are doing.
    ...
}

6
在Java中,捕获特定异常并检查实例是否属于该异常类型比捕获通用异常并检查实例类型要更恰当。+1 - amischiefr
8
如果你不知道自己在做什么,就不应该捕获普通的“Exception”异常,因此会返回-1。请注意,这里的翻译并未改变原意。 - Stroboskop
24
@Stroboskop: 确实,但为了回答最好使用与问题中相同(或类似)的代码! - user85421
23
有时候捕获所有的异常是可以的。比如在编写测试用例时,或者为了记录日志。又或者在主程序中必须捕获,否则程序崩溃。 - John Henckel
2
@JohnHenckel和其他人:确实有些观点是正确的。我更新了问题,以明确捕获“Exception”通常不是正确的做法,在大多数情况下(但并非所有情况)。 - Per Lundberg
显示剩余2条评论

21

在Java中几乎是相同的:

try
{
   ...
}
catch (Exception e)
{
   if (e instanceof FooException)
     throw e;
}

36
我会为FooException添加一个特定的捕获。 - dfa
3
在这个具体的情况下,我同意,但添加一个具体的捕获可能不是正确的选择——想象一下,你有一些通用代码适用于所有异常,然后针对特定的异常重新抛出它。 - alves
1
@MarkusLausberg 但最终不会捕获异常。 - Robert
2
是的,但这不是问题所在。 - Markus Lausberg
正如您在评论中提到的那样,如果您在if之后有更多使用e的代码,那么这段代码才有意义。否则,使用instanceof就不太合适了。您可以添加省略号或注释来说明这一点。 - istepaniuk

15

在Java中,您只需要抛出捕获到的异常,因此使用throw e而不是仅使用throw。Java会维护堆栈跟踪。


7

如果您将捕获的异常包装到另一个异常中(以提供更多信息),或者仅重新抛出捕获的异常,则保留堆栈跟踪。

try{ ... }catch (FooException e){ throw new BarException("一些有用的信息", e); }


6

像这样的东西

try 
{
  ...
}
catch (FooException e) 
{
  throw e;
}
catch (Exception e)
{
  ...
}

5
public int read(byte[] a) throws IOException {
    try {
        return in.read(a);
    } catch (final Throwable t) {
        /* can do something here, like  in=null;  */
        throw t;
    }
}

这是一个具体的例子,其中方法会抛出IOException异常。 final表示t只能持有从try块中抛出的异常。可以在此处此处找到其他阅读材料。


2
它不必是最终版本。请参阅http://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html和https://dev59.com/iGw15IYBdhLWcg3wA28A#6889301。 - jcsahnwaldt Reinstate Monica

3

我之前遇到了类似的情况,我的代码可能会抛出多种不同的异常,而我只想重新抛出它们。上面描述的解决方案对我没有用,因为Eclipse告诉我throw e;会导致一个未处理的异常,所以我就这样做:

try
{
...
} catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {                    
    throw new RuntimeException(e.getClass().getName() + ": " + e.getMessage() + "\n" + e.getStackTrace().toString());
}

对我有用...:)

1
避免自己提取有关e的信息。最好使用throw new RuntimeException("一些有用的信息", e)将原始异常打包。在字符串中,提供一些有用的信息,这些信息在e中缺失,例如您的方法的一些重要参数、上下文信息或者任何有助于调试的内容。 - Florian H.
请不要这样做:1.您正在将已检查的异常打包到RuntimeExceptions中。2.您正在丢失信息。 - mgueydan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接