Java中的类IOException是否是未经检查的RuntimeException?

您是否同意Java类java.io.IOException的设计者应该使它成为从java.lang.RuntimeException派生的未经检查的运行时exception,而不是仅从java.lang.RuntimeException派生的已检查exception?

我认为类IOException应该是一个未经检查的exception,因为应用程序几乎无法解决文件系统错误等问题。 但是, 当你不能抛出exception时 , Elliotte Rusty Harold声称大多数I / O错误是暂时的,所以你可以在放弃之前多次重试I / O操作:

例如,IOComparator可能不会出现I / O错误,但是 – 因为许多I / O问题是暂时的 – 您可以重试几次,如清单7所示:

通常情况如此吗? Java应用程序可以纠正I / O错误还是等待系统恢复? 如果是这样,那么检查IOException是合理的,但如果不是这样,则应取消选中IOException,以便业务逻辑可以将此exception的处理委托给单独的系统error handling程序。

我完全不同意。 对我来说,模型是正确的。 RuntimeException是最常表示编程逻辑中的严重错误(例如ArrayIndexOutOfBounds,NullPointer或IllegalArgument)或运行时确定不应发生的事情(例如SecurityException)。

相反,IOException及其派生词是在程序的正常执行期间可能合理发生的exception,并且通用逻辑将指示应该处理这些问题,或者至少程序员应该意识到它们可以发生。 例如,如果您的应用程序记录器无法写入其数据,那么您宁愿被迫捕获潜在的IOException并恢复,或者对您的应用程序可能不重要的事情会导致整个JVM崩溃,因为没有人想要抓住它未经检查的exception(你可能已经猜到了,我会选择前者)。

我认为有很多情况下IOException是可恢复的,或者至少程序员应该明确意识到潜在的可能性,这样如果它不可恢复,系统可能会更“轻柔”地崩溃。

至于你对系统是否无法恢复的想法,总是有一个带有检查exception的替代方案。 你总是可以让你的方法在它们的throws中声明它,抛出它们自己的运行时exception或猛烈地崩溃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); } } 

我知道问题已经过去了4年,但Java 8中添加了一个UncheckedIOException 。

我认为将它作为一个经过检查的例外很聪明。 我认为运行时exception是错误 ,显然不是这种情况。 有时可以通过重试进行恢复,并且一些IOException消息可以为最终用户提供信息(例如,没有写入权限,没有足够的磁盘空间等)。

可能绝大多数IOexception都是可恢复的 – 权限错误,驱动器空间不足,连接关闭等等。我认为未经检查的exception应该用于“没有合理的方法从这种情况中恢复” 。

不,因为您可以从某些IOExceptions中恢复。 最值得注意的是低级索引读写。 如果它失败了,有时你可以只是重试而不会受到伤害。

在一个设计良好的例外框架中,应区分在某种程度上“预期”的exception和不在意外的exception。 Java尝试使用已检查和未检查的exception。 根据该标准,IOException应该是一个经过检查的exception。

然而,一个基本问题是,代码通常会以(可能是合理的)期望写入某个exception不会发生,并且如果有的话,没有什么可以有效地处理它。 如果一个方法被声明为抛出一个特定的已检查exception,那么Java允许它的调用者在它声明自己抛出该exception时忽略该exception,但是方法或代码块没有办法指定来自被调用方法的某些exception不是预计会发生 。 一个常见的反模式是:

 try { methodThatsDeclaredAsThrowingFooExceptionButWont() } catch FooException Ex { // Never going to happen } 

显而易见的假设是,由于该方法不会抛出FooException,并且catch的唯一原因是让编译器开心,因此catch没有理由做任何事情。 阴险,因为如果FooException被抛出,无论出于何种原因,它都将被检测不到。

另一种方法是调用throws FooException throws Exception其自身声明为throws FooExceptionthrows Exception ,但这也不合适。 如果一个不希望抛出FooException ,系统状态很容易与抛出FooException时的预期不同。

例如,假设有人试图加载文档,但文档加载例程需要读取一些应该存储在固定位置的非文档特定的转换表。 如果该尝试失败,让IOException渗透到调用者将意味着加载正在加载的文档时出现问题。 如果load-document例程没有准备好合理地处理load-translation-table例程可能失败的可能性,那么这种失败不应该像实际加载文档时发生的IOException那样渗透。

正确的补救措施不是IOException是一个未经检查的exception,而是有一个声明性的方法,通过该方法,代码可以指示不应允许一种或多种已检查的exception从被调用的方法渗透出某个块。因此,应该包含在其他一些exception类型中(例如RuntimeException)。