【问题标题】:Why is the Catch(Exception) almost always a bad Idea?为什么 Catch(Exception) 几乎总是一个坏主意?
【发布时间】:2010-03-10 11:09:49
【问题描述】:

为什么catch(Exception) 几乎总是一个坏主意?

【问题讨论】:

标签: java exception exception-handling


【解决方案1】:

因为当您捕获异常时您应该正确处理它。而且你不能期望在你的代码中处理所有类型的异常。此外,当您捕获所有异常时,您可能会得到一个无法处理的异常,并阻止堆栈中的上层代码正确处理它。

一般原则是尽可能捕捉最具体的类型。

【讨论】:

  • 例如,当要求停止线程时会引发 java.lang.InterruptedException。如果被捕获并忽略,线程处理将无法正常停止,您的代码将变得不适合在工作线程中运行。这是一个例子,可能还有其他例子。
  • 我非常喜欢人们理解他们的框架。这个建议很糟糕的主要原因是,90% 的时间,人们编写异常处理的代码都在他们的 API 的边界上。我同意在 API 中,您应该始终捕获适当的异常,但是当您处于 API 边界时,请捕获不会杀死系统的所有内容。
  • @coding 通常不会编写框架代码。 你的框架会捕捉到Exception并采取适当的行动。
【解决方案2】:

简短的故事:它被称为错误掩蔽。如果你有一段代码运行不好并抛出异常(或者你向那段代码传递了格式错误的输入),而你只是通过捕获所有可能的异常来蒙蔽你的眼睛,你实际上永远不会发现错误并修复它。

【讨论】:

  • “捕获”异常并不意味着继续执行,就像没有任何问题一样。 “catch”块是您在其中执行某事的代码块。 某事,可能是记录有关异常的信息、用户正在做什么或 UI 的状态、关闭连接和文件以便应用程序可以正常退出,并通知用户发生了什么。这是如何“掩盖”任何东西的?
  • @user316117 Exception 是 Java 中所有异常的顶级类。抓住这意味着你抓住了所有可能的错误。你怎么知道在这样的街区做什么?如果您想对某个错误做出反应,请捕获其特定异常并处理它。捕捉所有可能的异常除了让你的眼睛失明之外没有实际用处(我见过很多同事这样做,因为他们想在工作日结束时回家)。
  • @dimitarvp Throwable 是所有异常中的顶级。它有两个子类,在两个分支中划分异常。其中一个是 EXCEPTION,另一个是 ERROR。
【解决方案3】:

这取决于你需要什么。如果您需要以不同的方式处理不同类型的异常,那么您应该使用多个 catch 块并尽可能多地捕获特定异常。

但有时您可能需要以相同的方式处理所有异常。在这种情况下,catch(Exception) 可能没问题。例如:

    try
    {
        DoSomething();
    }
    catch (Exception e)
    {
        LogError(e);
        ShowErrorMessage(e); // Show "unexpected error ocurred" error message for user.
    }

【讨论】:

  • 这不是一个好的模式;日志记录不应该这样完成,因为你会在 try-catch 的每个“级别”中得到一堆相同的条目。
  • 我同意,我仅将其用作示例。可能不是最好的例子:)
  • @Lucero:我认为最好有一个可以合并冗余条目的日志框架,而不是假设任何层处理(吞下)异常都会记录它。此外,除非异常太多以至于多次记录它们会造成性能瓶颈(在这种情况下,我会说这是一个需要修复的问题)我会认为日志中有冗余信息,这然后可以通过日志查看实用程序进行过滤,这比拥有更简洁的日志更可取,该日志缺少最终需要的一条信息。
  • 您应该始终列出捕获的异常。他们是被预测的。如果你想捕获不可预测的异常并以同样的方式处理它们,你应该捕获 RuntimeException。
【解决方案4】:

我发现catch(Exception) 有两种可接受的用法:

  • 在应用程序的顶层(就在返回给用户之前)。这样您就可以提供充分的信息。
  • 使用它将低级异常屏蔽为业务异常。

第一个案例是不言自明的,但让我开发第二个:

在做:

try {
    // xxxx
} catch(Exception e) {
    logger.error("Error XXX",e)
}

就像@dimitarvp 所说的那样屏蔽错误。

但下面不一样:

try {
    // xxxx
} catch(Exception e) {
    throw new BusinessException("Error doing operation XXX",e)
}

这样您就不会忽略错误并将它们隐藏在地毯下。您正在向更高的应用程序层提供一个带有更多解释性消息的高级异常。

在正确的层管理异常也很重要。如果将低级异常上报到高层业务层,高层几乎不可能很好地管理它。

在这种情况下,我更喜欢使用能够提供更好的上下文和消息并且还具有能够进入细节的原始异常的业务异常来掩盖低级异常。

即便如此,如果你能捕捉到更具体的异常并为它们提供更好的处理,你就必须这样做。

如果在一个代码块中你可以得到一个SQLException 和一个NetworkException,你必须抓住它们并为它们中的每一个提供足够的消息和处理。 但是,如果在 try/catch 块的末尾有一个 Exception 映射到 BusinessException,这对我来说没问题。

事实上,我发现更高的服务层只抛出业务异常(里面有细节)就足够了。

【讨论】:

  • - 在应用程序的顶层足以捕获 RuntimeException。 - 屏蔽低级异常应该至少列出捕获的异常。如果一段时间后引入了新的捕获的异常,应该以不同的方式处理呢?您将如何在代码中找到更改代码的位置?
【解决方案5】:

只有在能够正确处理异常的情况下才应该捕获异常。由于您无法正确处理所有可能的异常,因此不应捕获它们:-)

【讨论】:

  • 这种想法的一个主要问题是,在很多情况下,对可能抛出的 99% 异常的正确处理是报告某些操作无法完成并继续生命,但确定每一种例外类型,如果不是不可能的话,这将是正确的行动方针。太糟糕了,没有区分“无法满足请求”异常和“CPU 着火”异常的机制。
  • CPU 着火可能会导致某种java.lang.Error
  • > Error 是 Throwable 的子类,表示合理的应用程序不应尝试捕获的严重问题。
【解决方案6】:

除了@anthares 尚未回答的内容:

因为当您捕获异常时您应该正确处理它。而且你不能期望在你的代码中处理所有类型的异常。此外,当您捕获所有异常时,您可能会得到一个无法处理的异常,并阻止堆栈中的上层代码正确处理它。

一般原则是尽可能捕捉最具体的类型。

catch(Exception) 是一种不好的做法,因为它也会捕获所有 RuntimeException(未经检查的异常)。

【讨论】:

    【解决方案7】:

    因为您并不真正知道为什么会发生异常,并且有几个异常需要非常特殊的汽车才能正确处理(如果可能的话),例如 OutOfMemoryException 和类似的低级系统异常。

    因此,您应该只捕获异常:

    • 您确切知道如何处理它(例如 FileNotFoundException 左右)
    • 之后您何时重新提升它们(例如执行故障后清理)
    • 当您需要将异常传输到另一个线程时

    【讨论】:

    • 好的,在这里挑剔一个 10 年前的答案:在 Java 中,OutOfMemoryErrorThrowable,但属于 java.lang.Error 子类型。所以它不是Exception 并且实际上不会被问题中的子句捕获。也就是说,还有 Exceptions 应该(几乎)永远不会被捕获,例如 NullPointerException,它表示编程错误并且您的积分都是有效的。
    【解决方案8】:

    这可能是特定于 java 的:

    有时您需要调用引发检查异常的方法。如果这是在您的 EJB / 业务逻辑层中,您有 2 个选择 - 捕获它们或重新抛出它们。

    捕获特定的异常类意味着当您查看此代码如何处理异常时,您需要重新分析可能引发异常的操作。您经常会陷入“如果……怎么办”的境地,如果异常处理正确,可能需要付出很多努力。

    重新抛出意味着调用您的 EJB 的代码将被捕获的代码弄得乱七八糟,而这些代码通常对调用类没有任何意义。注意从 EJB 方法抛出检查异常意味着您有责任手动回滚任何事务。

    【讨论】:

    • 不完全正确。在 EJB 中,捕获的异常默认被视为应用程序异常。但您也可以将它们重新定义为系统异常。
    【解决方案9】:

    但有时还可以!就像你有一段代码做一些“额外”的事情,你真的不关心,你不希望它炸毁你的应用程序。例如,我最近在一个大型应用程序中工作,我们的业务合作伙伴希望将某个日常事务汇总到一个新的日志文件中。他们解释说日志对他们来说并不是那么重要,而且它不符合要求。这只是一些额外的东西,可以帮助他们理解正在处理的数据。他们不需要它,因为他们可以在别处获得信息。所以这是一种罕见的情况,捕捉和吞下异常是完全没问题的。

    我还在一家公司工作,该公司捕获了所有 Throwable,然后在自定义 RuntimeException 中重新抛出。我不会推荐这种方法,只是指出它已经完成了。

    【讨论】:

      【解决方案10】:

      确保线程保持活动状态并捕获其中的异常不是另一种有效的方案吗?

      Thread shouldRunWhenApplicationRuns = new Thread() {
          @Override
          public void run() {
              try {
                  // do something that should never end
              } catch (Exception ex) {
                  // log it
              }
          };
          shouldRunWhenApplicationRuns.start();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-02
        • 1970-01-01
        • 2017-01-31
        相关资源
        最近更新 更多