在开展学校项目时,我写了以下代码:
FileOutputStream fos;
ObjectOutputStream oos;
try {
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(shapes);
} catch (FileNotFoundException ex) {
// complain to user
} catch (IOException ex) {
// notify user
} finally {
if (oos != null) oos.close();
if (fos != null) fos.close();
}
问题是Netbeans告诉我resource.close()
行抛出IOException
因此必须被捕获或声明。它还抱怨oos
和fos
可能尚未初始化(尽管检查为空)。
这看起来有点奇怪,看到的是如何将IOException
停在那里。
我的下意识解决方法是这样做:
} finally {
try {
if (oos != null) oos.close();
if (fos != null) fos.close();
} catch (IOException ex) { }
}
但内心深处这让我感到困扰,感觉很脏。
我来自C#背景,我只是利用using
块,所以我不确定处理这个问题的“正确”方法。
是处理此问题的正确方法?
答案 0 :(得分:53)
如果您尝试从源头捕获并报告所有异常,则可以采用更好的解决方案:
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(shapes);
oos.flush();
} catch (FileNotFoundException ex) {
// complain to user
} catch (IOException ex) {
// notify user
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException ex) {
// ignore ... any significant errors should already have been
// reported via an IOException from the final flush.
}
}
}
注意:
close
和flush
传播到它们的包装流等。所以你只需要关闭或刷新最外面的包装器。IOException
的(实际)处理程序看到任何写入失败 1 。如果你经常需要“关闭一个忽略IOExceptions的可能为空的流”,那么你可以自己编写一个这样的辅助方法:
public void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ex) {
// ignore
}
}
}
然后你可以用:
替换前面的finally块} finally {
closeQuietly(oos);
}
(另一个答案指出,Apache Commons库中已经有closeQuietly
方法...如果您不介意为10行方法添加项目依赖项。 UPDATE < / strong>:请注意,在API的2.6版中不推荐使用这些方法。)
但请注意,您只在IO异常 无关的流上使用closeQuietly
。
1 - 使用try-with-resources时不需要这样做。
关于人们询问的flush()
与close()
的问题:
close()
导致刷新所有缓冲输出。您应该发现执行输出缓冲的所有其他(标准)输出类的行为方式相同。因此,对于标准类,在flush()
之前立即调用close()
是多余的。close()
方法可以说是已破坏。 最后,flush()
实际上有什么问题。 javadoc所说的是(OutputStream
...)
如果此流的预期目标是底层操作系统提供的抽象,例如文件,则刷新流只保证先前写入流的字节被传递给操作系统进行写入;它不能保证它们实际上写入物理设备,如磁盘驱动器。
所以...如果你希望/想象调用flush()
保证你的数据会持续存在,你错了!(如果你需要那样做,请看看FileChannel.force
方法......)
另一方面,如果您可以使用Java 7或更高版本,那么@Mike Clark的答案中描述的“新”试用资源是最佳解决方案。
如果您没有使用Java 7或更高版本来处理新代码,那么您可能陷入了深深的陷阱并深入挖掘。
答案 1 :(得分:25)
当前关于try / catch /最终涉及可关闭对象(例如Files)的最佳实践是使用Java 7的try-with-resource语句,例如:
try (FileReader reader = new FileReader("ex.txt")) {
System.out.println((char)reader.read());
} catch (IOException ioe) {
ioe.printStackTrace();
}
在这种情况下,FileReader会在try语句结束时自动关闭,而不需要在显式finally块中关闭它。这里有几个例子:
http://ppkwok.blogspot.com/2012/11/java-cafe-2-try-with-resources.html
官方Java描述位于:
http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html
答案 2 :(得分:12)
Java 7将添加Automatic Resource Management块。它们与C#的using
非常相似。
Josh Bloch写了the technical proposal,我强烈推荐阅读。这不仅仅是因为它将为您提供即将推出的Java 7语言功能,还因为该规范激发了对这种结构的需求,并且这样做,说明了即使在没有ARM的情况下如何编写正确的代码。
这是Asker代码的一个例子,翻译成ARM形式:
try (FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos))
{
oos.writeObject(shapes);
}
catch (FileNotFoundException ex)
{
// handle the file not being found
}
catch (IOException ex)
{
// handle some I/O problem
}
答案 3 :(得分:4)
我通常使用以下方法的小类IOUtil:
public static void close(Closeable c) {
if (c != null) {
try {
c.close();
}
catch (IOException e) {
// ignore or log
}
}
}
答案 4 :(得分:3)
这家伙怎么样?不是空检查,毫不奇怪。一切都在退出时清理干净。
try {
final FileOutputStream fos = new FileOutputStream(file);
try {
final ObjectOutputStream oos = new ObjectOutputStream(fos);
try {
oos.writeObject(shapes);
oos.flush();
}
catch(IOException ioe) {
// notify user of important exception
}
finally {
oos.close();
}
}
finally {
fos.close();
}
}
catch (FileNotFoundException ex) {
// complain to user
}
catch (IOException ex) {
// notify user
}
答案 5 :(得分:1)
不幸的是,没有语言级支持。但是有很多图书馆让这很简单。检查commons-io库。或现代google-guava @ http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html
答案 6 :(得分:1)
你做得对。它也困扰了我的废话。您应该明确地将这些流初始化为null - 这是常见的惯例。您所能做的就是加入俱乐部并希望获得using
。
答案 7 :(得分:0)
不能直接回答您的观点,但由于finally
和catch
都与try
相关联,因此人们认为它们属于一体,这是一个不幸的事实。 try
块的最佳设计是catch
或finally
,而不是两者。
在这种情况下,您的评论暗示出现了问题。为什么在处理文件IO的方法中,我们向用户抱怨任何事情。我们可能会在某个服务器上深入运行,并且看不见用户。
因此,当出现问题时,上面提到的代码应该finally
才能正常失败。它缺乏智能处理错误的能力,因此你的catch
属于调用链上的某个地方。