【问题标题】:Why do Java people frequently consume exceptions silently?为什么 Java 人经常默默消费异常?
【发布时间】:2009-05-28 15:26:26
【问题描述】:

我以前从未做过任何认真的 Java 编码,但我根据我现有的技能(Delphi 和 C#)学习了语法、库和概念。我几乎不明白的一件事是,我见过太多在printStackTrace 之后默默消耗异常的代码,如下所示:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

在我遇到的几乎所有 Java 文章和项目中都有类似的代码。根据我的知识,这是非常糟糕的。异常几乎总是应该像这样转发到外部上下文:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

大多数情况下,异常最终应该在属于底层框架(例如 Java Swing)的最外层循环中处理。为什么在 Java 世界中这样的编码看起来像是常态?我很困惑。

根据我的背景,我更愿意完全删除 printStackTrace。我会简单地重新抛出一个未处理的又名RuntimeException(或者,甚至更好,AssertionError),然后在最合适的位置捕获并记录它:框架最外层循环。

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            throw new AssertionError(e);
        }
    }

【问题讨论】:

  • 我想说的是,当堆栈跟踪被打印出来时,它并不是真的很安静:)
  • 我可以引用 Isaac Waller 的话“但是你的程序没有退出并继续运行,很可能处于未定义状态”
  • @willcodejavaforfood,您的评论获得了如此多的投票,这让我对典型的 Java 思维方式更加困惑。
  • @willcodejavaforfood - 从调用者的角度来看它是沉默的。来电者不知道发生了什么事。现在,如果这是预期的,那很好,但总的来说,这是个坏消息。另外 - 想想小程序中发生的事情 - 用户将永远不会看到输出(除非他们碰巧打开了 Java 控制台,但永远不会)。好的,所以没有人再使用小程序了;)虽然 servletm 类似——最终用户不知道发生了什么——它只是被隐藏在日志中。
  • Java 中的异常抛出是如此普遍,以至于大多数 Java 编码人员都忽略了它们,因为如果您进行了正确的错误捕获,那么您将需要编写更多代码......所以基本上所有这些 Java 程序员都这样做因为他们很懒惰。

标签: java exception exception-handling


【解决方案1】:

当您无法从异常中恢复但并不严重时,您通常会吞下异常。一个很好的例子是关闭数据库连接时可以抛出的 IOExcetion。如果发生这种情况,你真的想崩溃吗?这就是 Jakarta 的 DBUtils 有 closeSilently 方法的原因。

现在对于您无法恢复但很关键的已检查异常(通常是由于编程错误),请不要吞下它们。我认为应该在最接近问题根源的地方记录异常,因此我不建议删除 printStackTrace() 调用。您将希望将它们转换为 RuntimeException,其唯一目的是不必在您的业务方法中声明这些异常。确实,使用高级业务方法(例如 createClientAccount() 抛出 ProgrammingErrorException(读取 SQLException))是没有意义的,如果您的 sql 中有拼写错误或访问错误的索引,您将无能为力。

【讨论】:

    【解决方案2】:

    这是经典的straw man argumentprintStackTrace() 是一个调试辅助工具。如果您在博客或杂志上看到它,那是因为作者对说明其他点比异常处理更感兴趣。如果您在生产代码中看到它,那么该代码的开发人员是无知或懒惰的,仅此而已。不应将其作为“java 世界”中常见做法的示例。

    【讨论】:

    • 所以专业程序员可以在博客或杂志上而不是在生产环境中编写糟糕的代码吗?我宁愿在生产中编写糟糕的代码。如果真正聪明的人正在使用 printStackTrace,他会说服人们使用它。我知道异常代码长了 8 个字符,但是 ...
    • @ABCDE:我完全不同意。您宁愿在生产环境中编写糟糕的代码?那是应该工作的代码!博客和杂志文章应该说明一个观点。如果错误处理不是重点,那么它可能只是混乱。许多作者在文章中使用大大简化的错误处理或根本不使用。这不是一个可以效仿的例子。
    【解决方案3】:

    我一直认为,类似于下面的场景:

    “一个人被枪杀了。

    他屏住呼吸,有足够的力气坐公交车。

    10 英里后,这名男子下了公共汽车,走了几个街区就死了。”

    当警察找到尸体时,他们对刚刚发生的事情一无所知。他们最终可能有,但要困难得多。

    更好的是:

    “一个人被枪杀,当场死亡,尸体就在刚刚发生谋杀的地方。”

    当警察到达时,所有证据都已到位。

    如果系统出现故障,最好是fail fast

    解决问题:

    1. 无知。
        +
    2. 懒惰

    编辑:

    当然,catch 部分很有用。

    如果可以在异常情况下完成某些事情,那就应该这样做。

    对于给定的代码,这可能不是一个例外,这可能是预期的(在我的类比中,它就像一件防弹夹克,而那个人首先在等待射击)。

    是的,catch 可以用于Throw exceptions appropriate to the abstraction

    【讨论】:

    • 吞下异常不会失败,更别提快速失败了。另一方面,如果您的方法合同有一个参数是一串数字,并且您将其解析为整数,则可以吞下 ParseException。如果您正在编写特定功能,请为该功能失败定义自定义异常,然后捕获异常并抛出您的自定义异常(将原始异常作为参数)。然后它可能会快速失败到可能发生有意义行为的地方(向用户显示、发送电子邮件、仅记录、退出此服务器线程等)。
    • 等一下……我得先别笑了……好吧……这是迄今为止我看到的一个问题的最有趣的答案。它非常明白这一点。尽管将异常“向上”传递以进行处理可能非常有帮助,但最好至少先捕获异常然后传递它,以便包含一些有用的信息。
    • 异常不是错误。抛出一个例外是该男子被枪杀,然后在便条中详细说明事件并将其附在训练有素的信鸽飞到最近的凶杀组。
    • @Oscar,如果可以的话 +100。糟糕的异常处理是无知和恶意的结果,而不是懒惰。懒惰=尽可能少的努力。处理异常的愚蠢会导致更多的工作,而不是减少它......当然,它通常会给其他人带来更多的工作,这就是它看起来“懒惰”的原因。
    • 我明白你的观点 oscar,但所有处理异常或记录它所做的都是向你表明发生了异常。如果你把它一直扔到链上,它会让你知道哪个方法最终调用了异常。
    【解决方案4】:

    因为Checked Exceptions是一个失败的实验

    (也许 printStackTrace() 是真正的问题?:)

    【讨论】:

    • 它们并不是一个失败的实验。其实,我喜欢他们!正如其他答案所指出的那样,程序员往往很懒惰,只是忽略错误情况。由于检查的异常迫使您处理它们,因此您将拥有更好的代码。 (如果程序员没有默默地忽略它们)。此外,即使你不懒惰,你也不能忘记cath 一个重要的异常,因为编译器会告诉你这样做......
    • 检查异常失败有几个原因:检查其中一些mindview.net/Etc/Discussions/CheckedExceptions
    • +1 完全同意。已检查的异常通常会使代码 less 安全,这就是一个例子。
    • 检查异常肯定不是完美的,但它们确实迫使您面对现实,有时事情不会按计划进行。非检查异常只是试图假装我们生活在一个完美的世界中,没有任何问题
    • @Peter Lawrey:检查的异常是失败的,因为非常聪明的人喜欢那些带有惊人的 Swing 框架的人吐槽并拒绝使用它们。而且我非常怀疑像 Joshua Bloch 或 Spring 框架背后的人“不知道如何正确使用它们”。当然,您可以正确处理已检查的异常。真正的破碎是,某个地方的某个人认为扔一个是可以接受的做法。
    【解决方案5】:

    这通常是因为 IDE 提供了一个有用的“快速修复”,它将有问题的代码包装在一个带有异常处理的 try-catch 块中。这个想法是你实际上做了一些事情,但懒惰的开发人员没有。

    毫无疑问,这是一种糟糕的形式。

    【讨论】:

    • 同意,给出的示例看起来像 Eclipse 的默认快速修复,删除了 // FIXME: 注释。
    • 嗯,你说“这是一种糟糕的形式,毫无疑问”,我 100% 同意。但是,其他一些答案和 cmets 让我对很大一部分(我相信)Java 开发人员背后的哲学非常好奇。
    • 我真的希望 Eclipse 能在那里做一些激烈的事情。比如,e.printStackTrace(); System.exit(1);
    • 我已经更改了我的 IDE 设置以生成一个将其重新抛出为 RuntimeException 的 catch 主体。这是一个更好的默认设置。
    • 如果我确定该异常永远不会发生,我通常会抛出一个 AssertionError 而不是 RuntimeException 来记录我的意图。
    【解决方案6】:

    恐怕大多数 java 程序员不知道如何处理异常,并且总是认为它是一种烦恼,会减慢他们对“名义”情况的编码速度。 当然,他们完全错了,但很难让他们相信正确处理异常很重要。 每次遇到这样的程序员(经常发生)我都会给他两篇阅读文章:

    • 著名的 Java 思维
    • Barry Ruzek 的一篇简短而有趣的文章可在此处获得:www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html

    顺便说一句,我强烈同意捕获一个类型化的异常以重新抛出它嵌入到 RuntimeException 中是愚蠢的:

    • 如果你抓住它,就处理它。
    • 否则,请更改您的方法签名以添加您将/无法处理的可能异常,以便您的调用者有机会自己处理。

    【讨论】:

    • 在这个问题上我 100% 不会信任 Bruce Eckel :)
    【解决方案7】:
    1. Java 强制您显式处理所有异常。如果您的代码调用的方法被声明为抛出 FooException 和 BarException,那么您的代码必须处理(或抛出)这些异常。唯一的例外是 RuntimeException,它像忍者一样沉默。
    2. 很多程序员都很懒惰(包括我自己),打印堆栈跟踪很容易。

    【讨论】:

    • 但是您的程序并没有退出并继续运行,很可能处于未定义状态。
    • 如果知道异常的类型,状态未定义如何?这是一个可爱的小引语,但通常程序可以并且必须继续运行。您不希望整个应用程序因异常而崩溃 - 告诉用户并要求他们再次执行任何操作。
    • 如果异常在最外层处理得当,应用程序不会崩溃。
    • 至少有一半的异常是“RuntimeExceptions”,听起来好像只有一个。而且它不是静默的,未捕获的 RutimeException 会打印堆栈跟踪并使您的程序崩溃。
    • @greenieMeanie 通常在开发过程中,您希望它因最轻微的问题而严重崩溃——不要让开发人员因搞砸电话而逃脱惩罚——不要原谅任何事情。另一方面,当你运送它时,你想反其道而行之。以不可见的方式记录每个异常,并尽最大努力恢复并继续(Java 非常擅长)。
    【解决方案8】:

    我发现这样做通常有两个原因

    1. 程序员很懒
    2. 程序员想要保护组件的入口点(正确或错误)

    我不认为这是仅限于 Java 的现象。我也经常在 C# 和 VB.Net 中看到这样的编码。

    从表面上看,它非常令人震惊,看起来很糟糕。但实际上这并不是什么新鲜事。它一直发生在使用错误代码返回值与异常的 C++ 应用程序中。但不同之处在于,忽略可能致命的返回值与调用返回 void 的函数看起来并没有什么不同。

    Foo* pFoo = ...;
    pFoo->SomeMethod(); // Void or swallowing errors, who knows?
    

    这段代码看起来更好,但如果 SomeMethod() 说返回一个 HResult,它在语义上与吞下异常没有什么不同。

    【讨论】:

    • 对于这个问题,我认为原因 1 比原因 2 更常见
    • @matt b,同意。懒惰是我这些天修复的大部分错误的原因。
    • 忽略错误代码会更快,并且使用的样板代码要少得多!
    【解决方案9】:

    我不得不说我有点反感暗示这种松懈的错误处理行为是 Java 程序员的基本特征的语气。当然,Java 程序员可能很懒惰,就像其他程序员一样,而且 Java 是一种流行的语言,所以你可能会看到很多代码吞入异常。

    此外,正如在其他地方所指出的,Java 强制声明检查异常存在一些可以理解的挫败感,尽管我个人对此没有意见。

    我想我有一个问题,就是你在网上浏览了一堆文章和代码 sn-ps,而没有考虑上下文。事实是,当你写一篇技术文章试图解释某些特定 API 是如何工作的,或者如何开始使用某些东西时,你很可能会跳过代码的某些方面——不直接的错误处理与您所展示的内容相关的可能是处置的候选对象,尤其是在示例场景中不太可能发生异常的情况下。

    写这种性质的文章的人必须保持合理的信噪比,而且我认为相当公平,这意味着他们必须假设您了解您正在开发的语言的一些基础知识;如何正确处理错误以及许多其他事情。如果您看到一篇文章并注意到缺少适当的错误检查,那很好;只需确保当您将这些想法(当然,绝不是确切的代码本身,对吗?)合并到您的生产代码中时,您将以最重要的方式处理作者明智地遗漏的所有这些点点滴滴适合您正在开发的内容。

    我确实对非常高级的介绍性文章有疑问,这些文章轻而易举地解决了这些问题而不再返回它们,但请注意,Java 程序员在错误处理方面并没有某种特定的“心态”;我知道很多你心爱的 C# 程序员也懒得处理所有的问题。

    【讨论】:

    • 但是使用try {...} catch (IOException e) {} 写一篇高级文章是很愚蠢的(而且 TODO 没有帮助)。它肯定会误导许多初学者做同样的事情。此外,它throws IOException 要写得多,这在大多数情况下是正确的。也许每篇论文一次,这是不可能的,然后写throw wrap(e)(不用无聊地定义wrap)是一个很好的解决方案。 我学习 Java 的第一件事是,默默吞咽是一件可怕的事情,我非常小心,所以我什至暂时不会这样做(从长远来看,即使崩溃也会好得多)。
    【解决方案10】:

    System.out print 或 e.printStackTrace() - 这意味着使用 System.out 通常是一个危险信号,表示有人没有费心去做勤奋的工作。除了桌面 Java 应用程序之外,大多数 Java 应用程序最好使用日志记录。

    如果方法的失败模式是无操作,则吃异常是完全可以的,无论您是否记录原因(和存在)。然而,更典型的情况是,catch 子句应该采取某种异常行动。

    当您使用 catch 在必要信息仍然可用的级别清理部分工作时,或者当您需要将异常转换为更适合于来电者。

    【讨论】:

      【解决方案11】:

      只有在 catch 块真的为空时才会被静默消耗。

      就文章而言,除了如何处理异常之外,它们可能更有趣地证明了其他一些观点。他们只想直截了当并拥有尽可能短的代码。

      显然你是对的,如果异常要被“忽略”,至少应该记录它们。

      【讨论】:

        【解决方案12】:

        正如其他人指出的那样,您看到这个的原因是以下三个原因之一:

        1. IDE 生成了 try-catch 块
          • 代码已复制粘贴
          • 开发人员将堆栈跟踪放入调试,但从未回来正确处理异常

        最后一点是最不可能发生的。我这样说是因为我认为没有人真正以这种方式进行调试。使用调试器单步调试代码是一种更简单的调试方法。

        应该在 catch 块中做什么的最佳描述可以在Effective Java by Joshua Bloch 的第 9 章中找到。

        【讨论】:

          【解决方案13】:

          在 C# 中,所有异常都是运行时异常,但在 Java 中,您有运行时异常和检查异常,您必须在方法中捕获或声明它们。如果你调用任何最后有“抛出”的方法,你必须要么捕获那里提到的异常,要么你的方法也必须声明这些异常。

          Java 文章通常只打印堆栈跟踪或有注释,因为异常处理与文章的主题无关。但在项目中,应该根据异常的类型对其进行处理。

          【讨论】:

            【解决方案14】:

            如果程序员的工作做得好,你应该经常看到这一点。忽略异常是一种不好的做法!但是有些人可能会这样做以及更合适的解决方案有一些原因:

            • “这不会发生!” 当然,有时你“知道”这个异常不会发生,但它仍然更适合重新抛出运行时异常,并将发生的异常作为“原因”,而不是仅仅忽略它。我敢打赌它会在未来的某个时候发生。 ;-)

            • 原型代码 如果您只是输入您的内容以查看它是否有效,您可能希望忽略所有可能发生的异常。这是我做一些懒惰捕获(Throwable)的唯一情况。但如果代码变成有用的东西,我会包括适当的异常处理。

            • “我不知道该怎么办!” 我看到了很多代码,尤其是库代码,它们吞下了发生的异常,因为在应用程序的这一层无法进行适当的处​​理。不要这样做!只需重新抛出异常(通过在方法签名中添加 throws 子句或将异常包装到特定于库的异常中)。

            【讨论】:

            • 看到 catch(Throwable) 让我畏缩。
            • 绝对。如果我在玩,我只是在代码中使用 catch(Throwable) 或 throws Throwable。 “真实”代码不能包含它!期间。
            • 同意。我畏缩,因为我以前见过它。如果其他开发人员看到您编写了 catch(Throwable),那就太过分了! :)
            • 你有点想在最顶层捕获(Throwable),即使代码关闭了进程。尝试在缺少一根线的情况下蹒跚而行,可能不是最好的主意。
            【解决方案15】:

            您应该始终转发它或在现实环境中适当地处理它。许多文章和教程将简化他们的代码以了解更重要的点,最容易简化的事情之一是错误处理(除非你正在写一篇关于错误处理的文章 :))。由于 java 代码会检查异常处理,因此放置一个简单的静默(或日志记录语句)catch 块是提供工作示例的最简单方法。

            如果您在示例代码以外的任何地方发现此内容,请随时将代码转发到 TDWTF,尽管他们现在可能有太多示例了 :)

            【讨论】:

              【解决方案16】:

              检查异常和接口的组合导致代码必须处理永远不会被抛出的执行。 (这同样适用于普通继承,但它更常见,也更容易用接口解释)

              原因:接口的实现可能不会抛出(检查)接口规范中定义的异常以外的异常。出于这个原因,接口的创建者不知道实现接口的类的哪些方法实际上可能需要抛出异常,他们可能会指定所有方法可能至少抛出一种类型的异常。示例:JDBC,其中所有内容及其祖母都被声明为抛出 SQLException。

              但实际上,实际实现的许多方法根本不会失败,因此在任何情况下,它们都不会抛出异常。调用此方法的代码仍必须以某种方式“处理”异常,最简单的方法是吞下异常。没有人愿意用看似无用但永远不会执行的错误处理来弄乱他的代码。

              【讨论】:

              • 恕我直言,应该有一种语言速记来捕获和重新抛出诸如运行时错误之类的异常。用这种简写方式在代码和行为中都清楚地表明,这种异常应该被视为代表“意外”的条件。吞咽是不好的,因为如果确实抛出了异常,出现严重错误,调用代码应该会发现它。
              • supercat:我喜欢这个想法,但它可能会抵消检查异常的(IMO 有问题的)好处。
              • 如果检查性是捕获/抛出站点和异常实例而不是异常类的特征会更好,这样检查的异常可以作为同一类的未经检查的异常传播,但甚至是包装和- rethrow 将是对现状的重大改进,它可以很容易地作为新功能添加。当一个方法可能自己抛出一个检查异常的方法调用其他声明为抛出相同异常的方法时,它会特别有用但实际上并不期望这样做。如果后一种方法抛出...
              • ...期望处理从外部方法抛出的异常的调用者不应捕获内部方法抛出的异常,因为它不代表调用者所期望的条件。如果我从头开始设计一个运行时框架,我会为检查和未经检查的异常使用不同的机制,这样检查的异常会为成功的函数调用增加轻微的开销,但抛出时的开销要比未经检查的异常少得多(除其他外) ,一个被检查的异常不会有堆栈跟踪)。
              【解决方案17】:

              正如所指出的,调用 printStackTrace() 并不是真正的静默处理。

              这种“吞下”异常的原因是,如果你不断地将异常向上传递,你仍然必须在某处处理异常或让应用程序崩溃。因此,在发生信息转储的级别处理它并不比使用信息转储在顶层处理它差。

              【讨论】:

                【解决方案18】:

                这是一种懒惰的做法——真的是这样。

                它通常在您真的不关心异常时完成 - 而不是增加您的手指工作。

                【讨论】:

                  【解决方案19】:

                  我不同意重新抛出检查异常是一个更好的主意。捕捉意味着处理;如果你必须重新抛出,你不应该抓住。在这种情况下,我会将 throws 子句添加到方法签名中。

                  我会说,将已检查的异常包装在未检查的异常中(例如,Spring 将已检查的 SQLException 包装到其未检查层次结构的实例中的方式)是可以接受的。

                  记录可以被认为是处理。如果将示例更改为使用 log4j 记录堆栈跟踪而不是写入控制台,这是否可以接受?变化不大,IMO。

                  真正的问题是什么被认为是异常的和可接受的恢复程序。如果您无法从异常中恢复,您能做的最好的事情就是报告失败。

                  【讨论】:

                    【解决方案20】:

                    请永远不要将已检查的异常包装在未检查的异常中。

                    如果您发现自己在处理您认为不应该处理的异常,那么我的建议是您可能在错误的抽象级别上工作。

                    我会详细说明:已检查和未检查的异常是两种截然不同的野兽。检查异常类似于返回错误代码的旧方法......是的,它们比错误代码更难处理,但它们也有优势。未经检查的异常是编程错误和严重的系统故障……换句话说就是异常异常。当试图解释什么是异常时,很多人陷入了彻底的混乱,因为他们不承认这两个非常不同的案例的区别。

                    【讨论】:

                    • 包装在 AssertionError 中对我来说是合理的。 AssertionError 是适用于任何地方的抽象,IMO。
                    • 我会详细说明:已检查和未检查的异常是两种截然不同的野兽。检查异常类似于返回错误代码的旧方法......是的,它们比错误代码更难处理,但它们也有优势。未经检查的异常是编程错误和严重的系统故障……换句话说就是异常异常。当试图解释什么是异常时,很多人陷入了混乱,因为他们不承认这两个非常不同的案例的区别。
                    • 正如你所说的,我可能是陷入一团糟的人中的一员。但是,我真正的观点是,为什么在您可以选择中断执行时允许执行继续。 (使用 AssertionError 或者,如果您愿意,可以更改您的界面以包含额外的异常。
                    • 不能不同意这个答案的主要断言。阐述有其优点。
                    • @CurtainDog:检查的异常是针对“预期的”问题。如果在某些情况下方法 x 抛出(检查)FooException,并且调用 x 的方法 y 不希望出现这种情况,那么如果出现这种情况 确实,它们代表一个 unexpected 问题;即使调用堆栈上碰巧有一些期望FooException 的东西,目前的情况也与上游调用者在抛出FooException 时所期望的不匹配。
                    【解决方案21】:

                    因为他们还没有学会这个技巧:

                    class ExceptionUtils {
                        public static RuntimeException cloak(Throwable t) {
                            return ExceptionUtils.<RuntimeException>castAndRethrow(t);
                        }
                    
                        @SuppressWarnings("unchecked")
                        private static <X extends Throwable> X castAndRethrow(Throwable t) throws X {
                            throw (X) t;
                        }
                    }
                    
                    class Main {
                        public static void main(String[] args) { // Note no "throws" declaration
                            try {
                                // Do stuff that can throw IOException
                            } catch (IOException ex) {
                                // Pretend to throw RuntimeException, but really rethrowing the IOException
                                throw ExceptionUtils.cloak(ex);
                            }
                        }
                    }
                    

                    【讨论】:

                    【解决方案22】:

                    异常的真正意义在于简化错误处理并将其与错误检测分开。这与用错误代码表示错误相反,其中错误 处理代码分散在各处,检查每一个可能失败的调用 返回码。

                    如果异常代表错误(大多数情况下),通常最合理的处理方法是退出并将处理留给上层。 如果添加了一些有意义的语义,则应考虑重新抛出不同的异常,即此错误是不寻常的系统故障/临时(网络)问题/这是客户端或服务器端错误等。

                    在所有错误处理策略中,最无知的是隐藏或简单地打印错误消息并继续前进。


                    Sun 的人希望代码更明确,并迫使程序员编写哪些方法可以抛出哪些异常。这似乎是正确的举动——任何人都会 知道给定原型的任何方法调用会得到什么回报(它可能会返回 此类型的值或抛出指定类之一(或其子类)的实例)。

                    但事实证明,许多无知的 Java 程序员现在将异常处理视为一种语言错误/“功能”,需要一种变通方法,并以最糟糕或几乎最糟糕的方式编写代码:

                    • 在不适合决定如何处理的上下文中立即处理错误。
                    • 它会以静默方式显示或忽略,即使有更多代码,计算也会继续 没有机会正常运行。
                    • 方法的调用者无法区分是否成功完成。

                    “正确的方式”比怎么写?

                    • 指出可以在方法头中抛出的每个基类异常。 AFAICR Eclipse 可以自动完成。
                    • 使方法原型中的抛出列表有意义。 长列表毫无意义,“抛出异常”是懒惰的(但当你不打扰时很有用 很多关于异常的内容)。
                    • 在编写“错误方式”时,简单的“抛出异常”要好得多,并且需要 比 "try{ ... } catch(Exception e) { e.printStackTrace(); }" 少的字节数。
                    • 如果需要,重新抛出链式异常。

                    【讨论】:

                      【解决方案23】:

                      如果您希望在当前方法的范围之外处理您的异常,您实际上不需要捕获它,而是将“抛出”语句添加到方法签名中。

                      您看到的 try/catch 语句仅出现在程序员明确决定就地处理异常的代码中,因此不会进一步抛出它。

                      【讨论】:

                      • 不正确 - 如果您正在实现一个没有“抛出”声明的接口方法或覆盖超类方法,那么您不能只添加一个。在这些情况下,您要么必须将异常包装在 RuntimeException 中,要么将其吞下。
                      【解决方案24】:

                      我认为开发人员还尝试考虑在特定情况下“做正确的事”的重要性。很多时候扔掉代码或者向上传播异常不会买任何东西,因为异常是致命的,你不妨通过“做错事”来节省时间和精力。

                      【讨论】:

                        【解决方案25】:

                        根据经验,吞下异常主要是在未打印的情况下有害。如果您崩溃,它有助于引起注意,有时我会故意这样做,但只需打印异常并继续可以让您找到问题并修复它,但通常不会对使用相同代码库的其他人产生负面影响.

                        我实际上很喜欢 Fail-Fast/Fail-HARD,但至少要打印出来。如果您真的“吃掉”了一个例外(即真正什么都不做:{}),则需要花费 DAYS 天才能找到它。

                        问题在于,Java 迫使您捕获很多开发人员知道不会被抛出的东西,或者不关心它们是否会被抛出。最常见的是 Thread.sleep()。我意识到这里有可能导致线程问题的漏洞,但通常你知道你并没有打断它。期间。

                        【讨论】:

                          【解决方案26】:

                          使用 catch Exception 的原因可能有很多。在许多情况下,这是一个坏主意,因为您还捕获了 RuntimeExceptions - 而且您不知道发生这种情况后底层对象将处于什么状态?遇到意外情况,这总是很困难的事情:你能相信其余的代码在之后不会失败吗?

                          您的示例打印了堆栈跟踪,因此至少您会知道根本原因可能是什么。在更大的软件项目中,记录这些内容是一个更好的主意。并希望日志组件不会抛出异常,否则您可能会陷入无限循环(这可能会杀死您的 JVM)。

                          【讨论】:

                            【解决方案27】:

                            如果您有一个已检查的异常并且您不想在方法中处理它,您应该让该方法抛出异常。只有在你打算用它做一些有用的事情时才捕获并处理异常。仅仅记录它在我的书中并不是很有用,因为用户很少有时间阅读日志以寻找异常或知道如果抛出异常该怎么办。

                            虽然包装异常是一种选择,但我不建议您这样做,除非;您正在抛出一个不同的异常来匹配现有的接口,或者真的没有办法抛出这样的异常。

                            顺便说一句:如果你想重新抛出一个检查的异常,你可以这样做

                            try {
                               // do something
                            } catch (Throwable e) {
                               // do something with the exception
                               Thread.currentThread().stop(e); // doesn't actually stop the current thread, but throws the exception/error/throwable
                            }
                            

                            注意:如果您这样做,您应该确保该方法的 throws 声明是正确的,因为在这种情况下编译器无法为您执行此操作。

                            【讨论】:

                            • Just FTR: Thread#stop() 同时已被弃用和禁用(它会抛出 UnsupportedOperationException)。
                            【解决方案28】:

                            想象一下,您有 200 万行要处理。每行有 10 列。您知道只有 10 行在某些列中有一个字符,该字符不正确且不重要,并且适用于后续步骤的非处理项目。您是否检查每列的所有行以搜索此字符?包裹静默方法成本较低。当然,您有不同的方法来处理这种情况。但其中一个是静默方法。

                            【讨论】:

                              【解决方案29】:

                              因为这是最佳实践。我以为每个人都知道。
                              但冷酷的事实是,没有人真正了解如何处理异常。 C 错误处理风格更有意义。

                              【讨论】:

                              • 仅仅因为大量的开发者不去理解异常并不能使“错误返回”更好。 C 处理错误的方式过去和现在仍然是一种极易出错(双关语)的机制。吞下异常(这个例子就是这么做的,只是不是默默地)类似于在 C 中调用一个函数而不检查返回值。最终结果是一样的——程序“出错”了。此示例只是将 Java 错误处理作为坏 C 的一个案例。
                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2016-11-24
                              • 2012-07-17
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多