在不丢失堆栈跟踪的情况下重新抛出 Java 中的异常

Posted

技术标签:

【中文标题】在不丢失堆栈跟踪的情况下重新抛出 Java 中的异常【英文标题】:Rethrowing exceptions in Java without losing the stack trace 【发布时间】:2010-11-09 00:16:36 【问题描述】:

在 C# 中,我可以使用 throw; 语句重新引发异常,同时保留堆栈跟踪:

try

   ...

catch (Exception e)

   if (e is FooException)
     throw;

在 Java 中是否有类似的东西(不会丢失原始堆栈跟踪)?

【问题讨论】:

为什么你认为它丢失了原始的堆栈跟踪?当您抛出新的 SomeOtherException 并忘记在构造函数或 initCause() 中分配根本原因时,唯一的方法就是释放它。 我相信这就是代码在 .Net 中的行为方式,但我不再积极。在某个地方查找它或运行一个小测试可能是值得的。 Throwables 不会因为扔掉它们而被修改。要更新堆栈跟踪,您必须调用 fillInStackTrace()。在Throwable 的构造函数中调用此方法很方便。 在 C# 中,是的,throw e; 将丢失堆栈跟踪。但不是在 Java 中。 Oracle 的一些关于 Java 7 异常的文档:Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking 【参考方案1】:
catch (WhateverException e) 
    throw e;

将简单地重新抛出您捕获的异常(显然,周围的方法必须通过其签名等来允许这一点)。异常将保留原始堆栈跟踪。

【讨论】:

嗨,当我添加 throw e 行时,InterruptedException e 会给出未处理的异常消息。如果我用更广泛的异常 e 替换它,则不是这样。这应该如何正确完成? 在 Java 7 中,这种重新抛出的编译器更加智能。现在它可以很好地处理包含方法中的特定“抛出”异常。 @James 如果你 catch(Exception e) throw e; 将无法处理。如果您catch(InterruptedException ie) throw ie; ,它将被处理。根据经验,不要catch(Exception e) - 这不是口袋妖怪,我们不想全部抓到! @corsiKa 您不一定不想“全部抓住”,这只是一个不同的用例。如果您有一个***循环或事件处理程序(例如,在线程的运行中),如果您至少没有捕获 RuntimeException 并记录它,您通常会完全错过异常并默默地跳出一个重要的循环往往是一次性的失败。它对于插件功能也非常有用,您不知道附加代码可能会做什么或抛出什么......对于像这样的自上而下的使用,捕获异常通常不仅是一个好主意,而且是最佳实践。 @CorsiKa 我提到它是因为我开发的应用程序有一些线程随机吃异常。任何您不想静默中断的线程都应该在顶部有一个 catch 异常,并且您可能要考虑有很多开发人员并不处于您的确切情况 - 许多程序员使用线程,并非所有人都使用框架来抽象它们——有些人编写自己的框架。【参考方案2】:

我更喜欢:

try

    ...

catch (FooException fe)
   throw fe;

catch (Exception e)

    // Note: don't catch all exceptions like this unless you know what you
    // are doing.
    ...

【讨论】:

在 Java 中绝对适合捕获特定异常而不是泛型并检查实例。 +1 -1 因为除非您知道自己在做什么,否则您永远不应该捕获普通的“异常”。 @Stroboskop:是的,但要回答最好使用与问题中相同(相似)的代码! 有时捕获所有异常是可以的。比如当你写一个测试用例的时候。或用于记录目的。或者在主要情况下,不捕捉意味着崩溃。 @JohnHenckel 和其他人:指定的有效点。我更新了问题以明确在大多数(但不是全部)情况下,捕获Exception 通常不是正确的做法。【参考方案3】:

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

try

   ...

catch (Exception e)

     throw new YourOwnException(e);

【讨论】:

我还建议在旁边添加一条消息,使用 throw new YourOwnException("Error while trying to ....", e); 这是我一直在寻找的,尤其是第一条评论中的版本,您可以在其中传递自己的信息 这会正确显示错误消息,但堆栈跟踪将错误行显示为带有 'throw new.......(e)' 的行,而不是导致异常的原始行。【参考方案4】:

在Java中几乎是一样的:

try

   ...

catch (Exception e)

   if (e instanceof FooException)
     throw e;

【讨论】:

我会为 FooException 添加一个特定的捕获 在这种特定情况下,我同意,但添加特定的 catch 可能不是正确的选择 - 假设您有一些用于所有异常的通用代码,然后对于特定异常,重新抛出它。 @MarkusLausberg 但最终没有捕获异常。 是的,但这不是问题。 正如您在评论中提到的,只有在 if 之后有更多使用 e 的代码时,此代码才有意义。否则,使用instanceof 是不太正确的。您可以添加省略号或注释说明。【参考方案5】:

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

【讨论】:

【参考方案6】:

类似的东西

try 

  ...

catch (FooException e) 

  throw e;

catch (Exception e)

  ...

【讨论】:

【参考方案7】:
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;
    

这是一个具体示例,其中该方法抛出一个IOExceptionfinal 表示 t 只能保存从 try 块抛出的异常。可以在here 和here 找到其他阅读材料。

【讨论】:

不一定是最终的。见docs.oracle.com/javase/7/docs/technotes/guides/language/…和***.com/a/6889301/131160【参考方案8】:

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

try ... catch (FooException e) throw new BarException("Some usefull info", e);

【讨论】:

【参考方案9】:

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

try

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

为我工作....:)

【讨论】:

以上是关于在不丢失堆栈跟踪的情况下重新抛出 Java 中的异常的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在不抛出异常的情况下转储堆栈跟踪?

如何在不向用户显示堆栈跟踪的情况下处理 servlet 过滤器中的错误状态?

Android - 在没有堆栈跟踪的情况下调用 getJSONArray 抛出 JSONException

获取 PL/SQL 中重新抛出异常的完整堆栈跟踪(从点异常开始)

有没有办法在不删除视图并重新添加的情况下更新 StackView 中的视图?

如何避免记录来自 Java 异常的敏感信息?