我只是想知道是否有更好的方法来处理这个问题。我对流的理解是,只要你关闭一个流,封装在其中的任何流都将被关闭,这就是我最终只关闭TarArchiveOutputStream的原因。如果我在rawDir或archiveDir上获取FileNotFound,我想记录它,否则我想抛出任何其他内容。
public static void createTarGzOfDirectory(File rawDir, File archiveFile) throws IOException {
FileOutputStream fOut = null;
BufferedOutputStream bOut = null;
GzipCompressorOutputStream gzOut = null;
TarArchiveOutputStream tOut = null;
try {
fOut = new FileOutputStream(archiveFile);
bOut = new BufferedOutputStream(fOut);
gzOut = new GzipCompressorOutputStream(bOut);
tOut = new TarArchiveOutputStream(gzOut);
addFileToTarGz(tOut, rawDir, "");
} catch (FileNotFoundException e) {
log.error("File not found: " + e);
} finally {
if(tOut != null) {
tOut.finish();
tOut.close();
}
}
关于改进事物的任何其他考虑或想法?
答案 0 :(得分:3)
我对流的理解是,只要你关闭一个流,封装在其中的任何流都将被关闭......
这是正确的。
但是,您的代码(有效地)假设如果tOut
为null
,则链中没有其他流已创建。这是一个有点狡猾的假设。考虑这个顺序:
FileOutputStream
已创建并已分配到fOut
。BufferedOutputStream
已创建并已分配到bOut
。GzipCompressorOutputStream
构造函数抛出异常或错误。 (也许堆已经满了......)。catch
被跳过...错误的例外。finally
检查tOut
,发现它是null
,并且什么也没做。净结果:我们泄露了FileOUtputStream
所持有的文件描述符/频道。
获得此示例(绝对)正确的关键是了解哪些流对象拥有关键资源,并确保关闭此流。其他不保存资源的流不必关闭。
} finally {
if (fOut != null) {
fOut.close();
}
}
另一点是,您需要在tOut.finish()
电话后将try
电话移至addFileToTarGz
阻止。
如果addFileToTarGz
来电引发异常,或者你没有那么远,那么finish
来电就是浪费时间。
finish
调用将尝试将索引写入存档,并且可能会抛出IOException。如果在finally
块中发生这种情况,则finally块中用于关闭流链的任何后续代码将不会被执行...并且文件描述符将被泄露。
答案 1 :(得分:1)
虽然它看起来很丑陋,而且可能不太可能出现这种情况,但你应该将它们全部关闭。是的,如果你关闭TarArchiveOutputStream,它应该关闭underlyning流。但是,根据实施情况,情况可能并非总是如此。而且,可能主要是,如果其中一个中间构造函数抛出异常,则tOut将为null,但其他可能不是。这意味着您的流已打开,但您没有关闭任何流。
答案 2 :(得分:0)
您可以将所有构造函数链接在一起:
tOut = new TarArchiveOutputStream(new GzipCompressorOutputStream(new BufferedOutputStream(new FileOutputStream(archiveFile))));
并为自己保存6行初始化和3个局部变量以进行调试。不是每个人都喜欢用这种方式链接东西 - 我个人觉得它更具可读性,但你团队的其他成员可能更喜欢它。
关于关闭流,看起来对我来说是正确的。