在开发中遇到了一个问题,关闭流的时候会出现某种莫名其妙的错误。后来一个巧合看到了这个解决方法。
先看问题(知道答案以后,才知道是这里出错了)
FileWriter writer = null;
String file="D:\\test.txt";
try{
writer = new FileWriter(file);
//某些处理后
writer.flush();
writer.close();
}catch(Exception e){
try {
if(writer != null){
writer.flush();
writer.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
碰巧看到的那段描述,如下:
方法可能因为checked exception导致清理流或资源失败
这种方法可能无法清理(关闭,处置)流,数据库对象,或者其他的资源,需要一个明确的清除操作。
一般来说,如果一个方法打开一个流或其他资源,方法应该使用try/ finally块来保证流或资源清理方法返回之前。
这种错误模式在本质上是一样的OS_OPEN_STREAM和ODR_OPEN_DATABASE_RESOURCE错误模式,而是基于一个不同的(希望更好)静态分析技术。见韦默和Necula,查找和防止运行时错误处理错误,对分析技术的描述。
刚开始看到这段话,也是一脸懵逼,我明明在catch中使用了 writer.close() 方法了,为什么还说我没清理流。。。
想了好久才把注意力转移到这个flush()方法上:
FileWriter的flush()方法是从OutputStreamWriter中继承来的,其作用就是清空缓冲区并完成文件写入操作的
跟踪flush方法,一直到内部sun.nio.cs.StreamEncoder,找到其实现:
public void flush() throws IOException {
synchronized (this.lock) {
ensureOpen();
implFlush();
}
}
再跟踪ensureOpen()方法:
private void ensureOpen() throws IOException {
if (!this.isOpen)
throw new IOException("Stream closed");
}
}
终于找到了,原来这里会抛出异常。
我的理解是,同时使用write.flush(); write.close();两个方法。如果flush发生了异常,就会中断程序,把异常抛出来,就不会执行close方法。那么这个流实际上还是没有清理的。但是如果只使用write.close(); 那么会首先执行flush();,即使执行过程中flush发生了异常,也不会抛出来,还是会close的。
所以我最终的解决方案是:在catch/finally中不使用write.flush();,只是用write.close();
FileWriter writer = null;
String file="D:\\test.txt";
try{
writer = new FileWriter(file);
//某些处理后
writer.flush();
writer.close();
}catch(Exception e){
//nothing
}finally{
try {
if(writer != null){
writer.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}