虽然我同意未经检查的异常使API更方便的观点,但在我看来,这并不是最重要的好处。相反,它是这样的:
抛出未经检查的异常有助于避免严重的错误。
原因如下。鉴于十个开发人员被迫处理检查的异常,您将获得二十种不同的策略来处理它们,其中许多是完全不合适的。以下是一些更常见的不良方法:
-
吞。捕获异常并完全忽略它。继续前进,就好像什么都没发生一样,即使应用程序现在处于不稳定状态。
-
记录并吞咽。捕获异常并记录它,认为现在我们要负责任。然后继续前进,好像什么都没发生过。
-
神秘默认值。捕获异常,并将某些字段设置为某个默认值,通常无需告知用户。例如,如果无法加载某些用户的角色,只需选择一个低权限角色并进行分配即可。用户想知道发生了什么。
-
愚蠢/危险的神秘违约。捕获异常,并将某些字段设置为某个非常糟糕的默认值。我在现实生活中看到的一个例子:无法加载用户的角色,所以继续假设最好的(即给他们一个高特权的角色,以免给任何人带来不便)。
-
误报。开发人员不知道异常意味着什么,所以只是想出了自己的想法。 变为“无法连接到服务器”,即使建立连接与问题无关。
IOException
-
通过广泛捕获和误报来掩盖。尝试通过捕获 或 (ugh) 而不是该方法实际引发的两个已检查异常来清理代码。异常处理代码不会尝试区分(例如)资源可用性问题(例如某些问题)和直接代码错误(例如)。事实上,它经常任意选择其中一个异常,并将每个异常都误报为该类型。
Exception
Throwable
IOException
NullPointerException
-
通过巨大的尝试和误报来掩盖。上述策略的一个变体是将一大堆异常声明调用放在单个大型 try 块的范围内,然后捕获其中任何一个,或者因为没有其他任何东西可以处理所有抛出的异常。
Exception
Throwable
-
抽象-不恰当的重新抛出。即使异常不适合抽象,也要重新调用异常(例如,从应该隐藏资源的服务接口中重新抛出与资源相关的异常)。
-
不换行的重新抛出。重新抛出一个异常(要么未选中,要么进行适合抽象的检查),但只需删除嵌套的异常,这将使任何人都有机会真正弄清楚发生了什么。
-
戏剧性的回应。通过退出 JVM 来响应非致命异常。(感谢这篇博客文章。
根据我的经验,看到上述方法比看到正确的反应更常见。许多开发人员(甚至是“高级”开发人员)都有这样的想法,即必须不惜一切代价抑制异常,即使这意味着在不稳定状态下运行应用程序。这是危险的错误。
未选中的异常有助于避免此问题。不知道如何处理异常的开发人员倾向于将异常视为需要克服的不便,并且他们不会不厌其烦地捕获它们。因此,异常只是冒泡到顶部,在那里它们提供了堆栈跟踪,并且可以以一致的方式处理它们。在极少数情况下,实际上有比让异常冒泡更好的事情要做,没有什么能阻止你。