针对检查异常的情况

百般思念 提交于 2020-02-26 05:20:48

多年以来,我一直无法获得以下问题的正确答案:为什么某些开发人员如此反对检查异常? 我进行了无数次对话,阅读了博客上的东西,阅读了布鲁斯·埃克尔(Bruce Eckel)所说的话(我看到的第一个反对他们的人)。

我目前正在编写一些新代码,并非常注意我如何处理异常。 我正在尝试查看“我们不喜欢检查的异常”人群的观点,但我仍然看不到它。

我的每一次对话都以相同的问题为结尾而结束...让我进行设置:

总体而言(根据Java的设计方式),

  • Error是针对不应该被抓到的东西(VM有花生过敏症,有人在上面撒了一罐花生)
  • RuntimeException适用于程序员做错的事情(程序员走出了数组的结尾)
  • ExceptionRuntimeException除外)适用于程序员无法控制的事情(写入文件系统时磁盘已满,已达到该进程的文件句柄限制,并且您无法打开其他文件)
  • Throwable只是所有异常类型的父级。

我听到的一个常见论点是,如果发生异常,那么开发人员要做的就是退出程序。

我听到的另一个常见论点是,检查异常会使重构代码更加困难。

对于“我要做的就是退出”参数,我说即使您要退出,也需要显示一条合理的错误消息。 如果您只是在处理错误,那么当程序退出时如果没有明确说明原因的话,您的用户将不会过分高兴。

对于“它很难重构”人群,这表明未选择适当的抽象级别。 与其声明一个方法抛出IOException而不是将IOException转换为更适合发生的异常。

对于将Main与catch(Exception) (或在某些情况下, catch(Throwable)进行包装以确保程序可以正常退出,我没有任何问题-但我总是捕获所需的特定异常。至少显示适当的错误消息。

人们从不回覆的问题是:

如果抛出RuntimeException子类而不是Exception子类,那么您如何知道应该捕获的内容?

如果答案是catch Exception那么您也将以与系统异常相同的方式处理程序员错误。 对我来说这似乎是错误的。

如果捕获Throwable则将以相同的方式处理系统异常和VM错误(等等)。 对我来说这似乎是错误的。

如果答案是仅捕获已知的异常,那么您如何知道抛出了哪些异常? 当程序员X抛出一个新异常而忘记捕捉它时,会发生什么? 对我来说这很危险。

我会说显示堆栈跟踪的程序是错误的。 不喜欢检查异常的人会不会有这种感觉?

因此,如果您不喜欢检查异常,是否可以解释为什么不这样做并回答未得到回答的问题?

编辑:我不是在寻找何时使用任何一种模型的建议,我正在寻找的是为什么人们从RuntimeException扩展,因为他们不喜欢从Exception扩展和/或为什么他们捕获异常然后重新抛出RuntimeException而不是向其方法添加抛出。 我想了解不喜欢检查异常的动机。


#1楼

尝试仅解决未解决的问题:

如果抛出RuntimeException子类而不是Exception子类,那么您如何知道应该捕获的内容?

该问题包含似是而非的推理恕我直言。 仅仅因为API告诉您它抛出了什么并不意味着您在所有情况下都以相同的方式处理它。 换句话说,您需要捕获的异常取决于使用组件引发异常的上下文。

例如:

如果我正在为数据库编写连接测试器,或者要检查用户输入的XPath的有效性的东西,那么我可能想捕获并报告该操作引发的所有已检查和未检查的异常。

但是,如果我正在编写处理引擎,则可能会以与NPE相同的方式对待XPathException(已检查):我会让它运行到工作线程的顶部,跳过该批处理的其余部分,登录问题(或将其发送给支持部门进行诊断),并留下反馈让用户与支持人员联系。


#2楼

我们已经看到了一些有关C#首席架构师的参考。

这是Java专家关于何时使用检查的异常的另一种观点。 他承认其他人提到的许多负面因素: 有效例外


#3楼

好的...受检查的异常不是理想的情况,但有一些警告,但确实可以达到目的。 创建API时,某些特定情况下的失败是该API的约定。 当在诸如Java之类的高度静态类型化语言的上下文中,如果一个人不使用检查的异常,则必须依靠即席文档和约定来传达错误的可能性。 这样做会消除编译器带来的处理错误的所有好处,而您完全会被程序员的善意所取代。

因此,一个删除Checked异常,例如在C#中进行的操作,那么一个人如何以编程和结构方式传达错误的可能性? 如何通知客户代码这种错误可能发生并且必须加以解决?

在处理受检查的异常时,我听到了各种各样的恐惧,这是可以肯定的,但未经检查的异常也是如此。 我说等几年,当API堆叠到很深的层次时,您会乞求某种结构化方法的回报来传达故障。

以这种情况为例,该异常被抛出到API层底部的某个地方,并且由于没有人知道甚至可能发生此错误而冒泡了,即使这种错误类型在调用代码时非常合理扔掉它(例如FileNotFoundException而不是VogonsTrashingEarthExcept ...在这种情况下,我们是否处理它都没有关系,因为没有东西可以处理它了)。

许多人争辩说,无法加载文件几乎总是该过程的末日,它必定会导致可怕而痛苦的死亡。 所以是的..确定...确定..您为某些内容构建了一个API,并在某个时刻加载了文件...作为该API的用户,我只能做出响应...“您到底该决定我的时间程序应该崩溃!” 当然可以,因为可以选择在其中吞噬异常并且不留任何痕迹,或者具有比Marianna沟槽更深的堆栈跟踪的EletroFlabbingChunkFluxManifoldChuggingException,我会毫不犹豫地选择后者,但这是否意味着这是处理异常的理想方法? 我们可以不在中间的地方,在异常遍历到新的抽象级别时会重铸并包装该异常,以便它实际上意味着什么吗?

最后,我看到的大多数论据是“我不想处理异常,许多人不想处理异常。检查异常迫使我去处理它们,因此我讨厌检查异常”。将其释放到goto的鸿沟地狱只是愚蠢的,缺乏判断力和远见。

如果我们消除了检查异常,我们也可以消除函数的返回类型,并且总是返回“ anytype”变量……那会使生活变得如此简单,不是吗?


#4楼

不需要检查异常的充分证据是:

  1. 许多框架为Java做一些工作。 像Spring将JDBC异常包装为未检查的异常一样,将消息抛出到日志中
  2. Java之后的许多语言,甚至是Java平台上的顶级语言-它们都不使用它们
  3. 检查异常,这是有关客户端如何使用引发异常的代码的一种预测。 但是编写此代码的开发人员永远不会知道代码客户端正在从事的系统和业务。例如,一个Interfcace方法强制抛出已检查的异常。 系统上有100种实现,其中50种甚至90种实现不会引发此异常,但是如果客户端用户引用该接口,则客户端仍必须捕获此异常。 那些50或90个实现往往会自己处理这些异常,从而将异常放入日志中(这对他们来说是好行为)。 我们应该怎么做? 我最好有一些可以完成所有工作的后台逻辑-将消息发送到日志。 而且,如果我作为代码的客户,会觉得我需要处理异常-我会做的。 我可能会忘记它,对-但是,如果我使用TDD,那么我的所有步骤都已涵盖,并且我知道我想要什么。
  4. 当我在Java中使用I / O时的另一个示例,如果文件不存在,它会强制我检查所有异常。 我该怎么办? 如果不存在,则系统将不会进行下一步。 此方法的客户端不会从该文件中获取预期的内容-他可以处理运行时异常,否则我应该首先检查Checked Exception,将消息记录下来,然后从该方法中抛出异常。 否...否-我最好使用RuntimeEception自动执行此操作,它会自动/自动点亮。 手动处理没有任何意义-我很高兴在日志中看到一条错误消息(AOP可以帮助解决此问题。 如果最终我认为系统应该向最终用户显示弹出消息-我将显示它,这不是问题。

如果Java在使用I / O之类的核心库时能为我提供选择使用什么的选项 ,我感到很高兴。 Like提供了两个相同类的副本-一个用RuntimeEception包装。 然后我们可以比较人们会用什么 。 但是,到目前为止,许多人最好还是在Java或其他语言之上寻求一些框架。 像Scala一样,JRuby也可以。 许多人只是认为SUN是正确的。


#5楼

例外类别

在谈论异常时,我总是参考Eric Lippert的Vexing异常博客文章。 他将异常归入以下类别:

  • 致命 -这些异常不是您的错 :您无法阻止这种情况,也无法明智地处理它们。 例如, OutOfMemoryErrorThreadAbortException
  • 笨拙 -这些异常是您的错 :您应该阻止它们,它们表示代码中的错误。 例如, ArrayIndexOutOfBoundsExceptionNullPointerException或任何IllegalArgumentException
  • 恼人的 -这些异常不是例外 ,不是您的错,您无法阻止它们,但必须处理它们。 它们通常是不幸的设计决定的结果,例如从Integer.parseInt引发NumberFormatException而不是提供Integer.tryParseInt方法,该方法在解析失败时返回布尔值false。
  • 外在的 -这些异常通常是例外 ,不是您的错,您不能(合理地)阻止它们,但必须处理它们 。 例如, FileNotFoundException

API用户:

  • 不得处理致命愚蠢的例外。
  • 应该处理令人烦恼的异常,但它们不应在理想的API中发生。
  • 必须处理外部异常。

检查异常

API用户必须处理特定异常的事实是调用方和被调用方之间方法约定的一部分。 合同规定了:被调用者期望的参数的数量和类型,调用者可以期望的返回值的类型以及调用者希望处理的异常

由于API中不存在令人烦恼的异常,因此只有这些外部异常必须经过检查 ,才能成为方法约定的一部分。 相对较少的异常是外生的 ,因此任何API都应具有相对较少的已检查异常。

受检查的异常是必须处理的异常。 处理异常就像吞下它一样简单。 那里! 处理异常。 期。 如果开发人员希望以这种方式处理它,那很好。 但是他不能忽视这个例外,并受到警告。

API问题

但是,任何检查过烦人致命异常的API(例如JCL)都会给API用户带来不必要的压力。 这种异常必须处理,但任何的异常是很常见的,它不应该已经摆在首位的异常,或没有任何东西可以在搬运时完成。 导致Java开发人员讨厌检查的异常。

同样,许多API没有适当的异常类层次结构,导致所有种类的非外部异常原因都由单个检查的异常类(例如IOException )表示。 这也使Java开发人员讨厌检查的异常。

结论

外在异常是那些不是您的错,无法避免且应该处理的异常。 它们构成了所有可能抛出的异常的一小部分。 API应该仅具有检查的外部异常 ,而所有其他异常均未选中。 这将使API更好,减轻API用户的负担,从而减少捕获所有,吞下或重新抛出未经检查的异常的需求。

因此,不要讨厌Java及其检查的异常。 取而代之的是,讨厌过度使用已检查异常的API。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!