为什么Java人经常默默地使用异常?

Sak*_*ake 76 java exception-handling exception

我之前从未做过任何严肃的Java编码,但我根据现有技能(Delphi和C#)学习了语法,库和概念.我很难理解的一件事是,我已经看到了这么多代码,在printStackTrace这样的情况下默默地消耗异常:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

Osc*_*Ryz 198

我一直认为,这与以下场景类似:

"一个人被枪杀了.

他屏住呼吸,有足够的力量乘坐公共汽车.

10英里后,这名男子下车,走了几个街区,然后死了."

当警察到达尸体时,他们不知道刚刚发生的事情.他们最终可能会有,但更难.

更好的是:

"一名男子被枪杀,他立即死亡,身体正好位于谋杀刚刚发生的地方."

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

如果系统失败,最好是快速失败

解决问题:

  1. 无知.
      +
  2. 懒惰

编辑:

当然,捕获部分很有用.

如果可以通过异常完成某些事情,那就应该在那里完成.

可能这不是给定代码的例外,可能它是预期的东西(在我的比喻中就像一个防弹夹克,而男人正在等待拍摄的第一个位置).

是的,捕获可用于抛出适合抽象的异常

  • 等一下......我需要先停止笑......好吧......到目前为止,这是迄今为止我所见过的最有趣的答案.它非常重要.尽管传递"向上链"处理异常可能非常有帮助,但最好至少先捕获异常,然后再传递它,这样就可以包含一些有用的信息. (21认同)
  • @Oscar,如果可以的话,+ 100.恶劣的异常处理是IGNORANCE和MALICE的结果,而不是懒惰.懒惰=尽可能少的努力.处理异常的愚蠢导致更多的工作,而不是减少它...当然,它通常会导致更多的工作给其他人,这就是为什么它似乎"懒惰". (13认同)
  • 我喜欢这个评论. (6认同)
  • 为"如果系统失败,最好是快速失败":) (3认同)
  • 吞咽异常不会失败,更不用说快速失败了.另一方面,如果您的方法契约具有一个数字字符串的参数,并且您将其解析为整数,则可以吞下ParseException.如果您正在编写特定功能,请为该功能定义失败的自定义异常,然后捕获异常并抛出自定义异常(将原始异常作为参数).然后它可以快速失败到可能发生有意义行为的地方(显示给用户,发送电子邮件,只记录,退出此服务器线程等). (2认同)
  • 我会upvote这个答案,但它有100,我不想破坏这个不错的数字:) (2认同)

Kri*_*ris 33

通常这是由于IDE提供了一个有用的"快速修复",它将使用该异常处理的try-catch块中的违规代码包装起来.这个想法是你实际上做了什么,但懒惰的开发人员没有.

毫无疑问,这是糟糕的形式.

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

Agi*_*Jon 19

  1. Java强制您明确处理所有异常.如果您的代码调用的方法被声明为抛出FooException和BarException,则您的代码必须处理(或抛出)这些异常.唯一的例外是RuntimeException,它像忍者一样沉默.
  2. 很多程序员都很懒(包括我自己),只需打印堆栈跟踪就很容易了.

  • 但是你的程序不会退出并继续运行,很可能处于未定义的状态. (7认同)
  • 哈哈@沉默像一个忍者 (5认同)
  • 如果你知道异常的类型,状态是如何定义的?这是一个可爱的小报价,但通常程序CAN和HAS继续前进.您不希望整个应用程序因异常而崩溃 - 告诉用户并要求他们再做一次. (5认同)
  • 如果在outest级别正确处理异常,应用程序将不会崩溃. (2认同)
  • 至少有一半的例外是"RuntimeExceptions",你说它听起来就像只有一个.并且它不是静默的,并且未被捕获的RutimeException将打印堆栈跟踪并使程序崩溃. (2认同)

Bil*_*ard 19

这是一个经典的稻草人论点. printStackTrace()是一个调试辅助工具.如果你看到它在博客或在一本杂志,这是因为笔者更感兴趣的是说明一点其他比异常处理.如果你在生产代码中看到它,那段代码的开发者就是无知或懒惰,仅此而已.它不应该被视为"java世界"中常见做法的一个例子.

  • @ABCDE:我完全不同意.你宁愿在生产中写错代码吗?这是应该工作的代码!博客和杂志文章应该说明一点.如果错误处理不是重点,那么它可能只是杂乱无章.许多作者使用大大简化的错误处理或者根本没有使用文章.这不是一个可以遵循的例子. (8认同)

Jar*_*Par 12

我发现这有两个原因

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

我不相信这是一种仅限于Java的现象.我也经常在C#和VB.Net中看到这样的编码.

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

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

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

  • 我认为理性1特别是对于这个问题而言比理性2更常见 (3认同)
  • @matt b,同意了.懒惰是我现在解决的很多错误的原因. (2认同)

dfa*_*dfa 11

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

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

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

Rob*_*Rob 6

我不得不说我略微反感暗示这种松散的错误处理行为是Java程序员的基础.当然,Java程序员可能很懒,就像其他程序员一样,而Java是一种流行的语言,所以你可能会看到许多代码吞噬了异常.

此外,正如其他地方所指出的那样,Java强制声明已检查的异常是可以理解的,尽管我个人对此没有任何问题.

我想,我遇到的问题是,你在网上轻松浏览一堆文章和代码片段而不必费心去考虑上下文.事实是,当你写一篇技术文章试图解释一些特定的API如何工作,或者如何开始使用某些东西时,那么你很可能会跳过代码的某些方面 - 错误处理不直接与您所展示的内容相关的可能是处置的候选者,尤其是在示例场景中不太可能发生异常的情况下.

写这种性质的文章的人必须保持合理的信噪比,而且相当公平,我认为,这意味着他们必须假设你已经掌握了一些关于你正在开发的语言的基础知识; 如何妥善处理错误,以及其他一些事情.如果您遇到一篇文章,并注意到缺少正确的错误检查,那就没关系; 只是确保当你将这些想法(但当然,从来没有确切的代码本身,对吗?)纳入你的生产代码时,你将处理作者明智地遗漏的所有那些碎片和爆炸,以一种最大的方式适合你正在开发的东西.

我确实遇到了一些非常高级的介绍性文章的问题,这些文章在没有返回它们的情况下轻易解决这些问题,但请注意,Java程序员没有一些关于错误处理的特定"思维模式"; 我知道很多心爱的C#程序员,他们也不打算处理他们遇到的所有问题.


小智 5

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

如果方法的失败模式是无操作,那么无论是否记录原因(和存在),都可以完全没问题.然而,更典型的是,catch子句应采取某种特殊行动.

重新抛出异常是最好的做法,当你使用catch来清理部分工作时,必要的信息仍然可用,或者你需要将异常转换为更适合调用者的异常类型.