您是否同意Java类java.io.IOException
的设计者应该使其成为从java.lang.RuntimeException
派生的未经检查的运行时异常,而不是仅从java.lang.Exception
派生的已检查异常?
我认为类IOException
应该是一个未经检查的异常,因为应用程序几乎无法解决文件系统错误等问题。然而,
在When You Can't Throw An Exception中,Elliotte Rusty Harold声称大多数I / O错误都是暂时的,因此您可以在放弃之前多次重试I / O操作:
例如,IOComparator可能会 不要处理I / O错误,但是 - 因为很多I / O问题 瞬态 - 您可以重试几次, 如清单7所示:
通常情况如此吗? Java应用程序可以纠正I / O错误还是等待系统恢复?如果是这样,那么检查IOException是合理的,但如果不是这样,则应取消选中IOException,以便业务逻辑可以将此异常的处理委托给单独的系统错误处理程序。
答案 0 :(得分:19)
我完全不同意。对我来说,模型是正确的。 RuntimeException是最常表示编程逻辑中的严重错误(例如ArrayIndexOutOfBounds,NullPointer或IllegalArgument)或运行时确定不应发生的事情(例如SecurityException)。
相反,IOException及其派生词是在程序的正常执行期间可能合理发生的异常,并且通用逻辑将指示应该处理这些问题,或者至少程序员应该知道它们可以发生。例如,如果您的应用程序记录器无法写入其数据,那么您宁愿被迫捕获潜在的IOException并进行恢复,或者对您的应用程序可能不重要的事情会导致整个JVM崩溃,因为没有人想要抓住它未经检查的异常(你可能已经猜到了,我会选择前者)。
我认为有很多情况下IOException是可恢复的,或者至少程序员应该明确意识到潜在的可能性,这样如果它不可恢复,系统可能会“轻轻地”崩溃。
到目前为止,您对系统无法恢复的想法总是存在检查异常的替代方案。你总是可以让你的方法在它们的throws中声明它,抛出它们自己的运行时异常或猛烈地崩溃JVM:
public void doit() throws IOException {
try{
}catch(IOException e){
// try to recover
...
// can't recover
throw e;
}
}
public void doit() {
try{
}catch(IOException e){
// try to recover
...
// can't recover
throw new RuntimeException(e);
}
}
public void doit() {
try{
}catch(IOException e){
// try to recover
...
// OH NO!!!!
System.exit(Constant.UNRECOVERABLE_IO_ERROR);
}
}
答案 1 :(得分:15)
我知道问题已经过去了4年,但Java 8中添加了UncheckedIOException。
答案 2 :(得分:5)
我认为将其作为检查异常是很聪明的。我将运行时异常视为错误,显然不是这种情况。有时可以通过重试进行恢复,并且一些IOException消息可以为最终用户提供信息(例如,没有写入权限,没有足够的磁盘空间等)。
答案 3 :(得分:3)
可能绝大多数IO异常都是可恢复的 - 权限错误,驱动器空间不足,连接关闭等等。我相信未经检查的异常应该用于“没有合理的方法从此恢复”种种情况。
答案 4 :(得分:2)
否,因为您可以从某些IOExceptions中恢复。最值得注意的是低级索引读写。如果失败了,有时你可以在没有伤害的情况下重试。
答案 5 :(得分:1)
在设计良好的例外框架中,应区分在某种程度上“预期”的异常和不在意外的异常。 Java尝试使用已检查和未检查的异常。根据该标准,IOException应该是一个经过检查的异常。
但是,一个基本问题是,代码通常会以(可能是合理的)期望写入某个异常不会发生,并且如果有的话,没有任何东西可以用来处理它。如果一个方法被声明为抛出一个特定的已检查异常,那么Java允许它的调用者在它声明自己抛出该异常时忽略该异常,但是方法或代码块无法通过它来指定来自被调用方法的某些异常预计不会发生。一个常见的反模式是:try
{
methodThatsDeclaredAsThrowingFooExceptionButWont()
}
catch FooException Ex
{
// Never going to happen
}
明显的假设是,由于该方法不会抛出FooException,catch
的唯一原因是使编译器满意,因此catch
没有理由做任何事情。阴险,因为如果FooException
被抛出,无论出于何种原因,它都将被检测不到。
另一种方法是调用methodThatsDeclaredAsThrowingFooExceptionButWont将自己声明为throws FooException
或throws Exception
,但这也不合适。如果一个预期不会抛出FooException
的方法,系统状态很容易与抛出FooException
时的预期不同。
例如,假设有人试图加载文档,但文档加载例程需要读取一些应该存储在固定位置的非文档特定的转换表。如果该尝试失败,让IOException
渗透到调用者将意味着加载正在加载的文档时出现问题。如果load-document例程没有准备好合理地处理load-translation-table例程可能失败的可能性,那么这种失败不应该像实际加载时发生的IOException
那样渗透。文档。
适当的补救措施不是IOException
是一个未经检查的例外,而是存在一种声明性的方法,通过这种方式,代码可以表明不应允许一种或多种已检查的例外渗透来自由此调用的方法的某个块,但应该包含在其他一些异常类型中(例如RuntimeException)。