在Java(或任何其他带有受控异常的语言)中,当创建您自己的异常类时,您如何决定它应该被检查还是未检查?
我的直觉是,在调用者可能能够以某种有效的方式恢复的情况下,将调用checked异常,而作为未检查的异常则更多地用于不可恢复的情况,但我对其他人的想法感兴趣。
在Java(或任何其他带有受控异常的语言)中,当创建您自己的异常类时,您如何决定它应该被检查还是未检查?
我的直觉是,在调用者可能能够以某种有效的方式恢复的情况下,将调用checked异常,而作为未检查的异常则更多地用于不可恢复的情况,但我对其他人的想法感兴趣。
当前回答
这里有一个非常简单的方法来解决你的“受控/未受控”困境。
规则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).
其他回答
来自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.
我同意将未检查异常作为规则的偏好,特别是在设计API时。调用方总是可以选择捕获记录在案的、未检查的异常。你只是没有必要强迫打电话的人。
I find checked exceptions useful at the lower-level, as implementation detail. It often seems like a better flow of control mechanism than having to manage a specified error "return code". It can sometimes help see the impact of an idea for a low level code change too... declare a checked exception downstream and see who would need to adjust. This last point doesn't apply if there are a lot of generic: catch(Exception e) or throws Exception which is usually not too well-thought out anyway.
我们必须根据是否是程序员错误来区分这两种类型的异常。
If an error is a programmer error, it must be an Unchecked Exception. For example: SQLException/IOException/NullPointerException. These exceptions are programming errors. They should be handled by programmer. While in JDBC API, SQLException is Checked Exception, In Spring JDBCTemplate it is an Unchecked Exception.Programmer doesn't worry about SqlException, when use Spring. If an error is not a programmer error and the reason is coming from external, it must be a Checked Exception. For example: if the file is deleted or file permission is changed by someone else, It should be recovered.
FileNotFoundException是理解细微差别的好例子。在找不到文件的情况下抛出FileNotFoundException。这种例外有两个原因。如果文件路径是由开发人员定义的或通过GUI从最终用户获取的,那么它应该是一个未检查的异常。如果文件被其他人删除,它应该是一个Checked Exception。
检查异常可以用两种方式处理。它们使用try-catch或传播异常。在异常传播的情况下,由于异常处理,调用堆栈中的所有方法都将紧密耦合。这就是为什么我们必须小心地使用已检查异常。
如果您开发了一个分层的企业系统,您必须选择大多数未检查的异常来抛出,但不要忘记在您什么都做不了的情况下使用已检查的异常。
受控异常非常好,只要你知道什么时候应该使用它们。对于SQLException(有时对于IOException), Java核心API无法遵循这些规则,这就是它们如此糟糕的原因。
受控异常应该用于可预测的、但无法预防的、可以合理恢复的错误。
未检查异常应该用于其他所有事情。
我来解释一下,因为大多数人都误解了这句话的意思。
Predictable but unpreventable: The caller did everything within their power to validate the input parameters, but some condition outside their control has caused the operation to fail. For example, you try reading a file but someone deletes it between the time you check if it exists and the time the read operation begins. By declaring a checked exception, you are telling the caller to anticipate this failure. Reasonable to recover from: There is no point telling callers to anticipate exceptions that they cannot recover from. If a user attempts to read from an non-existing file, the caller can prompt them for a new filename. On the other hand, if the method fails due to a programming bug (invalid method arguments or buggy method implementation) there is nothing the application can do to fix the problem in mid-execution. The best it can do is log the problem and wait for the developer to fix it at a later time.
除非您抛出的异常满足上述所有条件,否则它应该使用未检查异常。
Reevaluate at every level: Sometimes the method catching the checked exception isn't the right place to handle the error. In that case, consider what is reasonable for your own callers. If the exception is predictable, unpreventable and reasonable for them to recover from then you should throw a checked exception yourself. If not, you should wrap the exception in an unchecked exception. If you follow this rule you will find yourself converting checked exceptions to unchecked exceptions and vice versa depending on what layer you are in.
对于已检查和未检查的异常,使用正确的抽象级别。例如,具有两种不同实现(数据库和文件系统)的代码存储库应该通过抛出SQLException或IOException来避免暴露特定于实现的细节。相反,它应该将异常包装在一个跨越所有实现的抽象中(例如RepositoryException)。
我使用的规则是:永远不要使用未经检查的异常!(或者当你看不到任何方法的时候)
有一种情况正好相反:永远不要使用受控异常。我不愿意在辩论中偏袒任何一方(双方都有很好的论据!),但相当多的专家认为,事后看来,受控例外是一个错误的决定。
对于一些讨论,请查看维基百科网站的“已检查异常的价值可疑”。另一个早期广泛争论的例子是Rod Waldhoff的博客文章。