Java I / O类java.io.Reader
,java.io.Writer
,java.io.InputStream
,java.io.OutpuStream
及其各种子类都有一个close()
方法,可以抛出IOException
。
对于处理此类例外的正确方法是否有任何共识?
我经常看到建议只是默默地忽略它们,但这感觉不对,至少在开放写入资源的情况下,关闭文件时出现问题可能意味着无法写入/发送未刷新的数据。
另一方面,在阅读资源时,我完全不清楚close()
为什么会扔掉它以及如何应对它。
那么有任何标准建议吗?
一个相关的问题是Does close ever throw an IOException?,但更多的是关于哪些实现真正抛出,而不是如何处理异常。
记录下来。
你无法对它做任何事情(例如编写一些从错误中恢复的代码),但它通常值得让人知道。
编辑: 在进一步调查并阅读其他评论之后,我会说如果你确实想要处理它,那么你将不得不知道实施的细节。相反,您可能需要了解实现的详细信息,以决定是否需要处理它。
但实际上,我无法想到任何流的示例,其中读取或写入将正常工作而不会抛出异常,但结束会。
“忽略或只是记录通常是一个坏主意。”那不是回答这个问题。关于close()的IOException应该怎么办?只是重新抛出它只会进一步推动问题,在那里处理起来更加困难。
理论
直接回答您的问题:当此IO操作失败时,您可能需要根据具体情况
您经常可以看到显示给用户的最后3个对话框。实际上,将选择委托给用户是可能的。
我认为重点是不要让系统处于不一致的状态。只是吞下一个接近的异常可能会给你留下一个残缺的文件,以后会导致令人讨厌的错误。
实践
使用已检查的异常有点麻烦。选项出现:
RuntimeException
,抛出它们并使它们处于足够高的水平有关IO monad的更多信息
在IO monad上下文中返回结果时,实际上并不是在执行IO操作,而是返回该操作的描述。为什么?这些动作有一个API,它们可以很好地组合(与throws / try / catch大屠杀相比)。
您可以决定是否要检查异常样式处理(使用Option
或Validation˙ in the return type), or dynamic style handling, with bracketing only on the final
unsafePerformIO`调用(实际执行组合的IO操作)。
要获得一些想法,请参阅我的Scala gist http://gist.github.com/2709535,它主持executeAndClose
方法。它返回资源处理的结果(如果可用)和未关闭的资源(如果关闭失败)。然后,用户决定如何处理这种不可关闭的资源。
如果需要一个或多个实际例外,它可以用qazxsw poi / qazxsw poi而不是qazxsw poi来加强。
您调用Validation
或ValidationNEL
的代码是一些高级代码,它委托给较低级别的Option
或InputStream.close()
类来执行某些高级计算。
你只有3个选择。
OutputStream.close()
。最后两个选项类似,因为您的代码会抛出异常。所以这个问题实际上是InputStream
问题的一个特例。在这种情况下,OutputStream
是:当且仅当替代方案是无法满足代码的后置条件或维护代码的不变量时。帖子条件指定了您的方法的用途。不变量指定类的特征。
所以,你需要问问自己,use a chained exception抛出异常是否会阻止你的方法做应该做的事情。
close()
。close()
阻止你的方法完成它的工作,但该方法可能不会抛出IOException
,你必须捕获just letting the exception propagate upwards并将其重新抛出为另一类异常,记录close()
作为抛出异常的原因。我知道IOException
抛出异常的任何情况都不能阻止计算成功。在您阅读完您感兴趣的数据之后,对IOException
的调用自然会发生。
但是,输出流可以在内部(在Java代码内)或在较低级别(在操作系统内)缓冲要写入输出目的地的数据。因此,在IOException
成功返回之前,您无法确定对输出流的写操作是否实际产生了实际输出(没有抛出异常)。因此,您应该像处理写入失败一样处理InputStream.close()
抛出的异常。
您的方法负责维护其不变量。如果close()
抛出异常,有时这将需要清理或回滚操作。您应该将OutputStream.close()
或OutputStream.close()
子句中的代码放入close()
中,即使您希望异常向上传播也是如此。如果使用catch子句并且想要传播它,则必须重新抛出它。
有些人会catch
。这几乎总是糟糕的建议。日志消息是程序用户界面的一部分。从根本上说,你必须总是问自己是否要记录任何东西,因为无用的措辞会分散和混淆阅读日志文件的用户(“用户”包括系统管理员)。记录的每条消息都应该用于某些有用的目的。每个人都应提供有助于用户做出决定的信息。
特别报告finally
失败很少有用。它如何帮助用户做出决定?如果异常没有阻止您的方法执行其工作,则没有问题,并且不需要用户执行任何操作。如果您的程序无法对流进行IOException
,这是一个问题,用户可以做些什么来帮助?
低级代码通常不负责日志记录。它改为执行抽象操作,通过抛出异常向程序的更高级别部分报告失败。关闭流的代码通常是相当低的级别,因此检测到advise you to log the exception的代码级别太低而无法执行任何日志记录。
close()
失败的具体事实很少有用。可能有用的是知道您的方法要执行的抽象操作失败。这可以通过让更高级别的代码捕获所有预期的异常并报告操作失败,而不是让您的方法精确报告“关闭失败”来完成。
在关闭writer之前尝试使用flush。关闭异常的主要原因是某些其他资源可能一直在使用数据,或者编写器/读取器可能根本不打开。在关闭之前尝试找到资源已经打开的东西。
首先,不要忘记将close()放在try catch块的“finally”部分中。其次,您可以使用另一个try catch异常包围close方法并记录异常。
这取决于你要关闭的内容。例如,关闭close()
什么都不做。 javadocs很清楚。在这种情况下,您可以忽略IOException,因为您知道它永远不会生成。从技术上讲,你甚至不需要打电话给close()
。
close()
对于其他流,请根据需要记录并处理异常。
通常资源处理应如下所示:
StringWriter
在(不太可能)close
抛出异常的情况下,/**
* Closing a <tt>StringWriter</tt> has no effect. The methods in this
* class can be called after the stream has been closed without generating
* an <tt>IOException</tt>.
*/
public void close() throws IOException {
}
抛出的任何异常都将被删除。我最接近捕手获胜的例外没有问题。我相信JDK7中的ARM块(?)在这种情况下会做一些疯狂的事情,比如重新抛出附加final Resource resource = context.acquire();
try {
use(resource);
} finally {
resource.release();
}
异常的release
异常。
如果您使用Execute Around习语,您可以将这些决定和可能混乱的代码放在一个(或几个)位置。
忽略或只是记录通常是一个坏主意。尽管您无法从中恢复,但应始终以统一的方式处理I / O异常,以确保您的软件以统一的方式运行。
我至少建议处理如下:
use
上面的内容非常冗长,但效果很好,因为它会在资源的I / O期间更喜欢use
,而不是在关闭期间,因为它更可能包含开发人员感兴趣的信息。当出现问题时,代码也将抛出release
,因此您可以获得统一的行为。
可能有更好的方法,但你明白了。
理想情况下,您将创建一个允许存储兄弟异常的新异常类型,因此如果您获得两个//store the first exception caught
IOException ioe = null;
Closeable resource = null;
try{
resource = initializeResource();
//perform I/O on resource here
}catch(IOException e){
ioe = e;
}finally{
if (resource != null){
try{
resource.close();
}catch(IOException e){
if(null == ioe){
//this is the first exception
ioe = e;
}else{
//There was already another exception, just log this one.
log("There was another IOException during close. Details: ", e);
}
}
}
}
if(null != ioe){
//re-throw the exception to the caller
throw ioe;
}
s,您可以将它们存储起来。
好吧,在大多数情况下,close()实际上不会抛出IOException。这是IOException
的代码:
IOException
关闭网络资源的错误实际上应该是某种类型的IOException
,因为您可以在程序连接到网络资源后断开连接。
您可以使用Google Code Search查看InputStream.java / Writer和Streams的各种实现的一些示例。 BufferedReader和PipedReader都没有实际抛出IOException,因此我认为你大部分时间都不会担心它。如果你真的很担心,你可以检查你正在使用的库的实现,看看你是否需要担心异常。
与其他提到的一样,除了记录IOException之外,你无法做很多事情。
毕竟, public void close() throws IOException
{
// Do nothing
}
条款中的RuntimeException
非常难看。
编辑:
进一步的检查揭示了Reader,try/catch blocks
和finally
等IOException
的子类,以及从中继承的类。所以只是捕捉一个InterruptedIOException
太普遍了 - 你不会知道如何处理除了记录之外的信息,因为它可能来自一系列错误。
编辑2:
Urk,SyncFailedException
是一个不好的例子,因为它需要一个ObjectStreamException
作为输入。我把它改成了IOException
然而,BufferedReader
<= Reader
<= InputStream.java
<= InputStream
(通过继承和私有实例)的层次结构都在FilterInputStream
中涓流到BufferedInputStream
方法。