Java io丑陋的try-finally块
Posted
技术标签:
【中文标题】Java io丑陋的try-finally块【英文标题】:Java io ugly try-finally block 【发布时间】:2011-02-11 13:19:34 【问题描述】:是否有一种不那么丑陋的方式来处理close()
异常以关闭两个流:
InputStream in = new FileInputStream(inputFileName);
OutputStream out = new FileOutputStream(outputFileName);
try
copy(in, out);
finally
try
in.close();
catch (Exception e)
try
// event if in.close fails, need to close the out
out.close();
catch (Exception e2)
throw e; // and throw the 'in' exception
out.close();
更新:以上所有代码都在一个 try-catch 中,感谢您的警告。
终于(在答案之后):
使用Execute Around idiom 可以完成一个很好的实用方法(感谢 Tom Hawtin)。
【问题讨论】:
考虑在适当的抽象级别捕获异常,例如IOException等,而不是过于模糊的Exception。 我认为对于这种情况,抛出哪个异常无关紧要。或者为什么会? 请注意,您的代码不一定关闭in
,见***.com/questions/2441853/…
另见:***.com/questions/12552863/…
【参考方案1】:
这是正确的惯用语(而且效果很好):
InputStream in = null;
OutputStream out = null;
try
in = new FileInputStream(inputFileName);
out = new FileOutputStream(outputFileName);
copy(in, out);
finally
close(in);
close(out);
public static void close(Closeable c)
if (c == null) return;
try
c.close();
catch (IOException e)
//log the exception
之所以能正常工作,是因为在 finally 代码完成后会抛出在 finally 之前抛出的异常,前提是您的 finally 代码本身不会抛出异常或以其他方式异常终止。
编辑:从 Java 7(和 android SDK 19 - KitKat)开始,现在有一个 Try with resources 语法以使这个更干净。 this question 中解决了如何处理。
【讨论】:
注意:这不是所有流类型的正确习惯用法。如果OutputStream
缓冲数据 (BufferedOutputStream
) 或写入关闭块 (ZipOutputStream
),您可能会丢失数据并且应用程序不会处理它,因为它会吞下异常。日志记录不能替代正确的错误处理。
@McDowell,关于输出流,这是一个很好的观点(请参阅我对 Adamski 的回答的评论,其中说了类似的话),但我仍然会说这是标准的成语。
因为这是公认的答案,人们会看它——既然你同意@McDowell,请在你的答案中说明他的评论——在catch块中
@Mr_and_Mrs_D,我不知道,随着 Java 6 生命周期的结束,真正的答案是使用 Java 7 选项,它肯定会很快。我不愿意在不同的前提下重新制作得到赞成和接受的答案。
是的 - 但大部分将继续是 Java 6 - Android 的一件事......【参考方案2】:
你可以实现一个实用方法:
public final class IOUtil
private IOUtil()
public static void closeQuietly(Closeable... closeables)
for (Closeable c : closeables)
if (c != null) try
c.close();
catch(Exception ex)
那么你的代码将被简化为:
try
copy(in, out);
finally
IOUtil.closeQuietly(in, out);
附加
我想在第 3 方开源库中会有这样的方法。但是,我的偏好是避免不必要的库依赖,除非我使用它的大部分功能。因此,我倾向于自己实现这样的简单实用方法。
【讨论】:
这有点不同,因为 in 异常不会被重新抛出 好主意,一个 var-arg 的 closeables。 我会添加一个空检查。可能尚未分配可关闭资源。致 OP:Commons IO 库有类似的方法:commons.apache.org/io/api-1.4/org/apache/commons/io/… 你想“安静地”关闭输出流?! 如果只是被忽略,Java 开发人员为什么会引发异常?【参考方案3】:try
final InputStream in = new FileInputStream(inputFileName);
try
final OutputStream out = new FileOutputStream(outputFileName);
try
copy(in, out);
out.flush(); // Doesn't actually do anything in this specific case.
finally
out.close();
finally
in.close();
catch (IOException exc)
throw new SomeRelevantException(exc);
请记住,打开流可能会引发异常,因此您确实需要在流开口之间使用try
(请不要做一些涉及null
s 的hack。任何东西都可能引发Error
(这不是Exception
) 的实例。
事实证明catch
和finally
应该很少共享相同的try
。
从 Java SE 7 开始,您可以编写使用 try-with-resource 来避免太多缩进。尽管隐藏了被抑制的异常,但它或多或少做同样的事情。
try (
final InputStream in = new FileInputStream(inputFileName);
final OutputStream out = new FileOutputStream(outputFileName);
)
copy(in, out);
out.flush(); // Doesn't actually do anything in this specific case.
catch (IOException exc)
throw new SomeRelevantException(exc);
您可能想使用Execute Around idiom。
我相信标准的良好复制方法是使用 NIO 的transferTo
/transferFrom
。
【讨论】:
+1,这与我输入的答案相同(逐个字符)! :) 实际上非常好 - 与接受的答案相比,您能否评论一下它的好处?另外,如果说copy(in, out);
和out.close();
都抛出,我们不会丢失copy(in, out);
抛出的异常吗?
@Mr_and_Mrs_D 有点像。实际上,无缓冲 I/O 流不会从 close
抛出 IOException
。即使他们确实抛出了IOException
,控制流也是一样的。 /Java SE 7 添加了抑制异常。我不希望任何人搜索被抑制的树来找到正确的异常,这在本地甚至理论上都不可能做到,因为没有定义异常优先级的顺序。
谢谢 - 如果您发现 close() 异常并记录它,答案是否会更完整?
最佳答案在这里。您记得提到它在 Java 7 中要干净得多,您不必记住 close()
也可以抛出 RuntimeException
(或任何其他 Throwable
),并且该模式也适用于锁。
【参考方案4】:
我坚信在 Java 7.0 中,您不再需要自己显式关闭流。 Language Features in Java 7
try (BufferedReader br = new BufferedReader(new FileReader(path))
return br.readLine();
【讨论】:
当它最终发布时,这将是了不起的。您可以在 try(...) 参数中指定多个资源,这些资源由 JVM 为您完全关闭。 虽然如果BufferedReader
构造函数失败(不太可能但确实发生)你会泄漏。此外,您正在选择随机配置的字符编码。【参考方案5】:
Guava 有非常好的 IO API,可以消除这种需求。例如,您的示例是:
Files.copy(new File(inputFileName), new File(outputFileName));
更一般地说,它使用InputSupplier
s 和OutputSupplier
s 的概念来允许在其实用程序方法中创建InputStream
s 和OutputStream
s,允许它完全控制它们以便它可以处理正确关闭。
此外,它还有Closeables.closeQuietly(Closeable)
,这基本上是大多数答案所建议的方法类型。
其中的 IO 内容仍处于测试阶段,可能会发生变化,但值得一试甚至使用,具体取决于您正在处理的内容。
【讨论】:
【参考方案6】:从 Java 7 开始,对于 Closeable
资源,有一种更好的方式来编写 try-finally 块。
现在您可以在 try
关键字后面的括号中创建资源,如下所示:
try (initialize resources here)
...
并且它们将在代码块完成后自动关闭。不需要finally
部分。
一个例子:
try (
ZipFile zf = new ZipFile(zipFileName);
BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset);
)
// Enumerate each entry
for (Enumeration entries = zf.entries(); entries.hasMoreElements();)
// Get the entry name and write it to the output file
String newLine = System.getProperty("line.separator");
String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
并且在for
循环完成后,资源将被关闭!
【讨论】:
【参考方案7】:在 commons io 中,IOUtils 有一些 closeQuietly 方法。
【讨论】:
【参考方案8】:我有时使用的一个技巧是定义一个名为closeQuietly(Closeable)
的方法,该方法测试它的参数是否为null
,然后关闭它,忽略任何异常。但是你需要小心地关闭 OutputStreams 和 Writers,因为它们实际上可能会抛出一个重要的异常;例如如果最终刷新失败。
Java 7 可能会有所改进。据报道,它将有一个新的结构,提供一种更简洁的方式来处理托管资源;例如完成后需要关闭的流。
最后,您应该知道您的示例存在错误。如果方法调用打开了第二个流,第一个流将不会被关闭。第二次打开需要在try
块内完成。
【讨论】:
【参考方案9】:在大多数情况下,'in' close() 异常是无关紧要的,所以:
try
copy(in, out);
finally
try in.close() catch (Exception e) /* perhaps log it */
try out.close() catch (Exception e) /* perhaps log it */
吞下异常通常是不好的做法,但在这种情况下,我认为没关系。
【讨论】:
为什么忽略写出文件末尾的失败是可以的? 因为你知道它是一个ByteArrayOutputStream?【参考方案10】:使用
IOUtils.closeNoThrow(myInputStream);
简洁优雅。
【讨论】:
可能你的意思是IOUtils.closeQuietly?【参考方案11】:这是我的答案,希望好多了
https://***.com/a/35623998/2585433
try
fos = new FileOutputStream(new File("..."));
bos = new BufferedOutputStream(fos);
oos = new ObjectOutputStream(bos);
catch (Exception e)
finally
Stream.close(oos,bos,fos);
class Stream
public static void close(AutoCloseable... array)
for (AutoCloseable c : array)
try c.close();
catch (IOException e)
catch (Exception e)
【讨论】:
【参考方案12】:在 C# 中,using
构造会在我们离开作用域时自动关闭可关闭对象:
using(Stream s = new Stream(filename))
s.read();
我认为这是 java 的 try-finally 块的简写形式。 Java 6 引入了 Closable 接口。所以,using
就快到了。当最后一步在 Java 7 中完成时,它确实很棒。
【讨论】:
以上是关于Java io丑陋的try-finally块的主要内容,如果未能解决你的问题,请参考以下文章
第9项:尽量使用try-with-resources而不是try-finally(Prefer try-with-resources to try-finally)