在Java(或任何其他带有受控异常的语言)中,当创建您自己的异常类时,您如何决定它应该被检查还是未检查?
我的直觉是,在调用者可能能够以某种有效的方式恢复的情况下,将调用checked异常,而作为未检查的异常则更多地用于不可恢复的情况,但我对其他人的想法感兴趣。
在Java(或任何其他带有受控异常的语言)中,当创建您自己的异常类时,您如何决定它应该被检查还是未检查?
我的直觉是,在调用者可能能够以某种有效的方式恢复的情况下,将调用checked异常,而作为未检查的异常则更多地用于不可恢复的情况,但我对其他人的想法感兴趣。
当前回答
在任何一个足够大的系统上,有很多层,检查异常是无用的,因为无论如何,您需要一个架构级策略来处理异常将如何处理(使用故障屏障)。
使用受控异常,您的错误处理策略是微管理的,在任何大型系统上都无法承受。
大多数情况下,您不知道错误是否“可恢复”,因为您不知道API的调用者位于哪一层。
假设我创建了一个StringToInt API,用于将整数的字符串表示形式转换为Int。如果API是用“foo”字符串调用的,我必须抛出检查异常吗?它可以恢复吗?我不知道,因为在他的层中,我的StringToInt API的调用者可能已经验证了输入,如果抛出这个异常,它要么是一个错误,要么是一个数据损坏,它是不可恢复的这一层。
在这种情况下,API的调用者不想捕获异常。他只想让异常“冒出来”。如果我选择了一个受控异常,这个调用者将有大量无用的catch块,只能人为地重新抛出异常。
大多数时候,什么是可恢复的取决于API的调用者,而不是API的编写者。API不应该使用受控异常,因为只有未受控异常才允许选择捕获或忽略异常。
其他回答
这里有一个非常简单的方法来解决你的“受控/未受控”困境。
规则1:将未检查异常视为代码执行前的可测试条件。 例如……
x.doSomething(); // the code throws a NullPointerException
其中x为空… 代码应该有以下内容
if (x==null)
{
//do something below to make sure when x.doSomething() is executed, it won’t throw a NullPointerException.
x = new X();
}
x.doSomething();
规则2:将检查异常视为代码执行时可能出现的不可测试条件。
Socket s = new Socket(“google.com”, 80);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
在上面的例子中,URL (google.com)可能由于DNS服务器宕机而不可用。即使在DNS服务器工作并将“google.com”名称解析为IP地址的瞬间,如果连接到google.com,在任何时候,网络都可能瘫痪。你不能在读写流之前一直测试网络。
有时候,在我们知道是否存在问题之前,代码必须执行。通过强迫开发人员以这种方式编写代码,迫使他们通过检查异常来处理这些情况,我不得不向发明了这个概念的Java创造者致敬。
一般来说,几乎所有的Java api都遵循上述2条规则。如果尝试写入文件,磁盘可能会在完成写入之前被填满。可能是其他进程导致磁盘已满。根本没有办法测试这种情况。对于那些随时与硬件交互的人来说,使用硬件可能会失败,受控异常似乎是解决这个问题的一个优雅的解决方案。
这是一个灰色地带。在需要许多测试的情况下(一个带有大量&&和||的令人震惊的if语句),抛出的异常将是一个CheckedException,因为要正确处理它太痛苦了——你不能简单地说这个问题是一个编程错误。如果测试少于10个(例如' If (x == null) '),那么程序员错误应该是UncheckedException。
与语言口译员打交道时,事情变得有趣起来。根据上面的规则,语法错误应该被认为是一个检查或未检查的异常?我认为,如果语言的语法可以在执行之前进行测试,那么它应该是UncheckedException。如果无法测试该语言——类似于程序集代码在个人计算机上的运行方式,那么语法错误应该是一个已检查异常。
The 2 rules above will probably remove 90% of your concern over which to choose from. To summarize the rules, follow this pattern… 1) if the code to be execute can be tested before it’s executed for it to run correctly and if an Exception occurs — a.k.a. a programmer error, the Exception should be an UncheckedException (a subclass of RuntimeException). 2) if the code to be executed can not be tested before it’s executed for it to run correctly, the Exception should be a Checked Exception (a subclass of Exception).
在任何一个足够大的系统上,有很多层,检查异常是无用的,因为无论如何,您需要一个架构级策略来处理异常将如何处理(使用故障屏障)。
使用受控异常,您的错误处理策略是微管理的,在任何大型系统上都无法承受。
大多数情况下,您不知道错误是否“可恢复”,因为您不知道API的调用者位于哪一层。
假设我创建了一个StringToInt API,用于将整数的字符串表示形式转换为Int。如果API是用“foo”字符串调用的,我必须抛出检查异常吗?它可以恢复吗?我不知道,因为在他的层中,我的StringToInt API的调用者可能已经验证了输入,如果抛出这个异常,它要么是一个错误,要么是一个数据损坏,它是不可恢复的这一层。
在这种情况下,API的调用者不想捕获异常。他只想让异常“冒出来”。如果我选择了一个受控异常,这个调用者将有大量无用的catch块,只能人为地重新抛出异常。
大多数时候,什么是可恢复的取决于API的调用者,而不是API的编写者。API不应该使用受控异常,因为只有未受控异常才允许选择捕获或忽略异常。
这不仅仅是从异常中恢复的能力问题。在我看来,最重要的是调用者是否对捕获异常感兴趣。
如果您编写的库用于其他地方,或应用程序中的较低级别层,请询问自己调用者是否有兴趣捕获(了解)您的异常。如果他不是,那么使用一个未检查的异常,这样就不会给他带来不必要的负担。
这是许多框架使用的哲学。尤其是Spring和hibernate——它们精确地将已知的受控异常转换为未检查异常,因为Java中过度使用了受控异常。我能想到的一个例子是来自json.org的JSONException,这是一个受检查的异常,最烦人的是——它应该是不受检查的,但开发人员根本没有考虑到这一点。
顺便说一下,大多数情况下,调用者对异常的兴趣与从异常中恢复的能力直接相关,但情况并非总是如此。
来自Java学习者:
When an exception occurs, you have to either catch and handle the exception, or tell compiler that you can't handle it by declaring that your method throws that exception, then the code that uses your method will have to handle that exception (even it also may choose to declare that it throws the exception if it can't handle it). Compiler will check that we have done one of the two things (catch, or declare). So these are called Checked exceptions. But Errors, and Runtime Exceptions are not checked for by compiler (even though you can choose to catch, or declare, it is not required). So, these two are called Unchecked exceptions. Errors are used to represent those conditions which occur outside the application, such as crash of the system. Runtime exceptions are usually occur by fault in the application logic. You can't do anything in these situations. When runtime exception occur, you have to re-write your program code. So, these are not checked by compiler. These runtime exceptions will uncover in development, and testing period. Then we have to refactor our code to remove these errors.
以下是我在多年开发经验后的一些看法:
Checked exception. This is a part of business use case or call flow, this is a part of application logic we expect or not expect. For example connection rejected, condition is not satisfied etc. We need to handle it and show corresponding message to user with instructions what happened and what to do next (try again later etc). I usually call it post-processing exception or "user" exception. Unchecked exception. This is a part of programming exception, some mistake in software code programming (bug, defect) and reflects a way how programmers must use API as per documentation. If an external lib/framework doc says it expects to get data in some range and non null, because NPE or IllegalArgumentException will be thrown, programmer should expect it and use API correctly as per documentation. Otherwise the exception will be thrown. I usually call it pre-processing exception or "validation" exception.
目标受众。现在让我们来谈谈目标受众或设计例外的人群(根据我的观点):
检查异常。目标受众是用户/客户。 未经检查的异常。目标受众是开发人员。换句话说,未检查异常仅是为开发人员设计的。
通过应用程序开发生命周期阶段。
受控异常被设计为在整个生产生命周期中存在,作为应用程序处理异常情况的正常和预期机制。 未检查异常被设计为只存在于应用程序开发/测试生命周期期间,所有这些异常都应该在这段时间内得到修复,并且当应用程序已经在生产环境中运行时不应该抛出。
框架通常使用未检查异常(例如Spring)的原因是框架不能确定应用程序的业务逻辑,这取决于开发人员捕捉并设计自己的逻辑。