【问题标题】:Why Would you catch Throwable and do nothing but print a stack trace?为什么你会捕捉 Throwable 并且除了打印堆栈跟踪之外什么都不做?
【发布时间】:2012-02-29 00:56:29
【问题描述】:

我曾尝试在其他线程上寻找答案,但到目前为止,我只看到线程表明捕获 Throwable 是不好的。我的问题是,你有没有理由想要这样做,然后在 catch 块中什么都不做,除了打印堆栈跟踪?

我最近被带入一个项目,任务是清理 RESTful 服务的现有类集的错误处理。一些辅助服务类具有仅捕获 Throwable 并打印出堆栈跟踪的 try/catch 块,如下所示:

class MainService {

  SubService1 s1;
  SubService2 s2;

  public doMainService() {
  }

}


class SubService1 {

  public int findSomething() {

    try {
        // Do Something
    } catch (Throwable t) {
        t.printStackTrace();
    }
  }
}

class SubService2 {

  public int findSomethingElse() {

    try {
        // Do Something
    } catch (Throwable t) {
        t.printStackTrace();
    }  
  }

}

有没有可以接受的情况?抛出 Exception 而没有 try/catch 块的方法会更好吗?

【问题讨论】:

  • 没有 ESP 和写它的人的直接链接......这个问题没有一个答案。最可能的答案?糟糕或懒惰的程序员。其他可能性?从未退出调试阶段的未完成/匆忙的代码。
  • 这是一个有效的问题,尤其是对于刚接触 Java 的人。他/她在问我们这些 Java 专家,这样做是否有充分的理由。
  • 对不起,我想我应该问这个问题更好一点,因为不可能知道以前的程序员的想法。代码运行良好,代码中有 ExceptionMappers 将异常映射到 HTTP 状态代码,这让我更加困惑。
  • "throws Exception" 通常也是个坏主意,顺便说一句。
  • 我提到泛型 Exception 类的原因是由于 try/catch throw Exception 中的一些代码(这些是对我无法控制的代码的调用)。其他是更具体的异常,例如 ParseException 和 IOException。

标签: java exception-handling


【解决方案1】:

由于各种众所周知的原因,这几乎不是一个好的做法。

特别是,它不区分checkedunchecked 异常和errors。更重要的是,这段代码的效果之一是允许应用程序在异常处理程序之外执行,这可能由于违反不变量而导致各种奇怪的行为。换句话说,由于捕获的异常实际上可能是任何东西,包括违反断言、编程错误、线程中断、缺少类、I/O 错误、OOM 条件甚至库和 VM 错误,因此程序状态实际上是不可预测的,超出了异常处理程序。

在一些罕见的情况下,广泛的异常处理可能是有意义的。想象一个服务器处理多个独立的请求。您可能不想因为在处理其中一个请求时遇到问题而崩溃。由于您不依赖异常处理程序之后留下的状态,因此您可以简单地打印堆栈跟踪并让其他人在服务器继续处理其他请求时进行调查。

即使在这些情况下,也应仔细考虑是否有错误,例如VirtualMachineError 真的应该被抓到。

【讨论】:

  • 我同意这不是一个好习惯。这是我永远不会想到做的事情,这就是我现在在这里的原因。删除它们并让方法抛出异常并让这些问题冒泡到主服务不是更好吗?
  • 是的,我也认为让异常正确传播会更好。作为额外的好处,这提供了一个机会来记录应用程序中使用的异常并减少异常处理程序的数量(当它们在未来发生变化时很有用)。请注意,根据应用程序的大小,这可能是一项重大工作。
【解决方案2】:

我认为人们这样做的一个原因仅仅是因为 Java 迫使您要么用 try/catch 块包围抛出的调用,要么在方法声明中添加 throws。

如果您“知道”您不会发生异常,那么这是一种防止异常上链的方法(因为如果您抛出异常,那么调用您的代码的人需要用try/catch 等),但如果确实发生了某些事情,它会转储它而不会崩溃。

【讨论】:

  • 这是处理这种情况的糟糕方法。如果您知道它永远不会抛出,那么用 UnsupportedOperationException 包装它并重新抛出它。这样,如果它确实抛出,你就不会丢失它。 e.printStackTrace() 转到 stderr,据您所知,它可能转到 /dev/null。
  • 哦,这是一种可怕的做法,但有些人就是这样做的。
  • 您的回答几乎听起来像是您认为这是一个合理的解决方案,这就是我评论的原因。您应该将您的评论添加到您的答案中。
【解决方案3】:

如果您不知道任何其他获取堆栈跟踪的方法并且想知道如何到达当前位置,则可以这样做。不是一个很好的理由,但可能是一个。不过,这似乎不适合您正在查看的内容;您似乎获得了不会崩溃但在错误处理方面做得不好的代码。

【讨论】:

    【解决方案4】:

    我使用它来查看我的程序崩溃的确切位置。所以基本上只是为了调试。也只是看看它是否以预期的方式流动。

    【讨论】:

    • 为此,您要么在顶层拥有它,要么重新抛出。
    【解决方案5】:

    他们可能希望在不使程序崩溃的情况下查看堆栈跟踪。

    例如,由线程执行的代码记录异常可能是合适的,但如果线程崩溃是不可接受的,则什么也不做,因为下一次迭代(例如线程从队列中获取项目的位置)可能会按预期工作.这取决于用例,但对于服务器,您通常希望线程是防弹的并记录任何错误,而不是停止任何进一步的处理(但用例可能会有所不同)。

    【讨论】:

      【解决方案6】:

      我们在您的问题中遇到的最简单的示例是关闭输入流。Follwoing 是 InputStream 类中的方法声明。

      public void close() 抛出 IOException

      尽管当我们调用这个方法时有可能抛出异常,但这对程序执行没有任何危害。(因为我们不会进一步使用 InputStream)在这种情况下,我们应该只记录错误并继续程序.但是如果你发现一个改变对象状态的异常,那么你必须考虑恢复代码或停止执行。

      【讨论】:

        【解决方案7】:

        永远不要这样做 (e.printStackTrace())。许多 IDE 默认使用它,但它很糟糕(许多应用程序使用 stderr 以无法访问的方式重定向运行,因此从未见过)。经验法则:

        • 如果您真的不关心异常(从不、永远、永远、永远会关心),抓住它,然后什么都不做,用一个大评论说“我们真的,真的不”不关心这个异常是否被抛出"
        • 如果您从不期望实际抛出异常(接口定义了已检查的异常,但您知道 impl 实际上并没有抛出它),请重新抛出异常,例如 new UnsupportedOperationException("this should never happen", t) (这样,如果/当底层 impl 发生更改时,您会尽早发现它)
        • 如果您预计该异常不太可能发生,并且您通常不会关心它,请使用适当的日志记录基础架构(log4j、commons logging 或 java.util.logging)对其进行记录。这样,如果您决定确实确实关心异常,它很可能会出现在您以后可以看到的地方。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-27
          • 2018-06-07
          • 1970-01-01
          相关资源
          最近更新 更多