【问题标题】:Need authoritative source for why you shouldn't throw or catch java.lang.Exception为什么不应该抛出或捕获 java.lang.Exception 需要权威来源
【发布时间】:2009-10-16 14:52:36
【问题描述】:

我有一个多小时后的编码标准会议,我需要一个快速的答案。

有经验的 Java 程序员的共同智慧是不要抛出或捕获 java.lang.Exception(很少有异常 - 没有双关语的意思)。你不这样做的原因是声明

catch (java.lang.Exception ex) {...}

还会捕获未经检查的异常,在大多数情况下,这不是预期的。

我们已经有很多由现有团队成员编写的遗留代码,他们在其中捕获 java.lang.Exception 的子类、记录错误并将子类重新抛出为 java.lang.Exception。

我需要说服他们

  1. 他们需要停止编写这样的代码。
  2. 需要修复使用此反模式的现有代码

数字 2 意味着大量的重构。

如果我可以展示 Java 社区重量级人物之一(即 Joshua Bloch、James Gosling)的文章或博客条目,这将缩短会议上的争论。到目前为止,我的 google-fu 还没有出现任何内容。

有没有人知道一位受人尊敬的 Java 大师的文章或博客说您不应该抛出或捕获 java.lang.Exception?

非常感谢快速回答。

院长

【问题讨论】:

  • 只是想告诉你这个问题:stackoverflow.com/questions/237585/… 里面有一些不错的答案
  • 我在发这个之前读过那个。几个答案和 cmets 说同样的话,但我正在寻找权威。这将导致大量的重构,因此有专家的意见来支持我会有所帮助。

标签: java exception-handling


【解决方案1】:

这里有一些东西:Java Tip 134: When catching exceptions, don't cast your net too wide (JavaWorld)

Effective Java (Second Edition) by Joshua Bloch 可能在第 9 章(例外)中对此有所了解,尽管我无法很快找到任何关于不关注 Exception 的信息。

这里是 a Q&A from JavaWorld 关于这个问题(也指向 Java Tip 134)——它也解释了为什么有时你必须打破不捕捉 Exception 甚至 Throwable 的规则。

【讨论】:

    【解决方案2】:

    请参阅 Brian Goetz(并发向导)的 this article,他有自己的见解以及在 Effective Java 中引用 Josh Bloch 的话

    【讨论】:

      【解决方案3】:

      这里没有共同点。你会发现两组:

      1. 那些讨厌检查异常的人会将它们捕获并包装在某种RuntimeException 中。

      2. 讨厌RuntimeException的人

      第一组讨厌用try...catch 来混淆他们的代码,特别是因为在大多数情况下,当你第一次看到它时你无法处理它。想想IOException:你读取的每个字节都可以抛出它。对此有什么低级代码?它必须重新抛出它,以便更高层的人可以为错误添加一些有用的上下文(例如您正在阅读的文件)。

      另一组想看看可能出了什么问题。 RuntimeException 有效地隐藏了这一点。

      有一个简单的修复,顺便说一句:使异常扩展 RuntimException。疯狂的?并不真地。如果使用 JDK 7 执行此操作,则会出现两个编译错误。

      下一步是让所有 Java 编译器枚举所有运行时异常,并将它们列在类文件的 throws 条目中。您仍然不必抓住它们,但现在,您会知道哪些可能会发生。

      最后,扩展 IDE 以显示它。很快,两组人都会很高兴。不幸的是,这不会发生。

      【讨论】:

      • 有点啰嗦,但我喜欢!
      • 喜欢检查异常的人并不讨厌运行时异常,他们只是将它们的使用限制在适当的情况下。
      • @Brian:我的论点是没有办法就何时“合适”达成一致。在我看来,SQLExceptionIOException 显然是运行时异常,因为根据我的经验,它们更频繁地被抛出是因为编程错误而不是因为调用者可以实际处理的条件。
      • 是的,我特别认为 SQLException 是对检查异常有很多误解的主要原因。作为 API,他们认为您可能正在编写一个客户端应用程序,其中 SQL 代码是用户输入。然后真的应该检查一个 SQL 异常。还应检查数据库密码和连接问题。但在实践中,我们大多数人都在编写 Web 应用程序。我们认为 SQL 查询(和相关异常)是我们代码中的错误。我不乐意让 Exception 扩展 RuntimeException。如果开发人员能更好地理解何时使用什么,我会很高兴。
      • 在 IT 工作 27 年后,我已经放弃了“开发人员应该了解他们所做的事情”,而转向“让它真正易于正确使用”。
      【解决方案4】:

      这里是Bruce Eckel's viewpoint on checked exceptions in general,这在大多数情况下是个坏主意。也许那会有一些你可以使用的东西。

      【讨论】:

        【解决方案5】:

        它们捕获 java.lang.Exception 的子类,记录错误,然后将子类重新抛出为 java.lang.Exception。我需要说服他们不要再写这样的代码了。

        我同意他们应该使用另一种策略,但出于不同的原因。捕获异常只是为了记录并重新抛出它没有多大意义。

        另一种选择是:不捕获异常,让一些更高级别的代码(如 Java EE 过滤器,或 main() 方法中的 try/catch)捕获并记录所有未捕获的异常。然后确保每个异常只记录一次,并且您知道所有未捕获的异常都将被记录。

        如果您需要向异常添加额外信息,请捕获它,更改消息并重新抛出它。我通常为此使用 RuntimeException:

        } catch (Exception originalException) {
            throw new RuntimeException("user name was " + userName, originalException);
        }
        

        【讨论】:

        • 异常应该被记录在尽可能接近它们被抛出的地方。可能会在上游某个地方吞下异常,或者会发生另一个错误或运行时异常(例如 NullPointerException),这将阻止上游日志记录。您是说上面的 catch() 语句也要捕获 RuntimeException 吗?
        • @Dean Schulze 大概,如果一个异常被吞下,这意味着它不是一个意外的异常,但它是一个预期的异常,并且代码知道如何以编程方式处理它。另一种表述方式:处理异常的人应该记录它。如果它一直未处理,您应该有一些机制来处理和记录那些意外异常。这可确保堆栈跟踪仅在日志中出现一次。
        • @Dean Schulze 根据捕获异常的 catch 语句,是的,我试图表明您不需要在捕获异常时记录错误和记录变量值。相反,您可以捕获异常并将这些值添加到消息中,然后包装异常并重新抛出它。您的额外数据仍然会出现在日志中(并且堆栈跟踪仅输出一次,这样更容易在日志中搜索错误)。
        【解决方案6】:

        这里是一个很insightful article

        总结:

        1. 对与代码用途相关的罕见但有效的意外事件使用检查异常。客户端代码应该知道如何处理这些。

        2. 对不应该发生但确实发生的错误使用未经检查的异常。 (服务器宕机、类路径配置错误、SQL 语法错误等)管理服务器的 IT 人员,或者刚刚将错误的 SQL 传递给 prepareStatement() 的开发人员应该知道如何解决这些问题。故障应向上传播到日志记录层,以免信息丢失。

        【讨论】:

          【解决方案7】:

          这是一篇很长的文章,但this 建议您:

          1. 当客户端期望此错误定期发生并且必须处理它时使用检查异常(例如:用户输入的数据未通过验证)
          2. 对意外情况使用未经检查的异常(例如:数据库服务器关闭)

          理论上,#1(预期错误)应该有自己的异常类型,而唯一应该捕获 #2(RuntimeException)的地方是某种类型的顶级处理程序,它可以捕获异常、记录它并向用户显示错误消息,即使在这里,您也应该捕获 Throwable 以确保处理任何未捕获的异常。

          按照这些准则,您不应捕获 Exception,因为它不符合上述任何一个条件(也就是说,它不是 RuntimeException,也不是用于指示预期错误的专用 Exception 子类)。

          【讨论】:

          • 文章的 URL 引用了 has changed。 @TerrelShumway 在他的回答中发布了同一篇文章。
          • @hotshot309 已修复。谢谢!
          • 喜欢检查异常的人并不讨厌运行时异常,他们只是将它们的使用限制在适当的情况下。
          • @Brian 也许你打算在Aaron Digulla's answer上发布这个?
          • @bradCupit:哎呀!不知道这是怎么回事。
          【解决方案8】:

          我想重点是,如果您不知道如何处理特定异常,那么您不应该捕获它。它应该传播到更高的层次,希望它可能知道如何处理它。过早捕获异常会导致异常被无声地吞下,并且除了记录之外无法对它们做任何有用的事情。

          想象一下,如果FileInputStream 的构造函数在您尝试打开一个不存在的文件但没有向您的代码指示此失败时在内部记录异常会发生什么。错误被记录下来很好,但是您的代码想要捕获此异常并对其执行一些有用的操作(例如提示用户输入新文件名)。如果他们是catch (Exception),您将无法做到这一点。

          【讨论】:

            【解决方案9】:

            如果您手头有 Josh Bloch 的 Effective Java 的副本,我知道他在那里相当详细地介绍了异常处理。我目前无法访问它,所以我无法对其进行总结或给你页面参考,但我很确定他有一些很好的论据来说明没有捕获 java.lang.Exception。

            【讨论】:

            • 那将是第 9 章(17 页)。他不喜欢任何一个,但强调只有在调用者可以解决问题时才应该使用检查异常。所以 OOM 是一个 RuntimeExc.,而 NoMoreElements 是一个(坏的)检查过的 exc。
            猜你喜欢
            • 2015-02-13
            • 2013-10-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-05-25
            • 2010-10-27
            相关资源
            最近更新 更多