只使用一个当Java 7 的try-与资源语法(也称为ARM块( 自动资源管理 ))是很好的,简短易懂AutoCloseable
资源。 但是,我不知道什么是正确的成语时,我需要声明的是依赖于彼此的多个资源,例如FileWriter
和BufferedWriter
一个包装它。 当然,这个问题涉及到任何情况下,当一些AutoCloseable
资源被包裹,不仅这两个特定的类。
我想出了以下三种选择:
1)
天真的成语是我见过的ARM管理变量声明只顶级包装:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
这是很好的和短暂的,但它是坏了。 因为底层FileWriter
不变量声明的,它永远不会被直接在所生成的关闭finally
块。 它只会通过闭合close
包装的方法BufferedWriter
。 问题是,如果一个异常从抛出bw
的构造,其close
不会被调用,因此潜在FileWriter
不会被关闭 。
2)
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
在这里,无论是底层和包装资源在ARM管理变量的声明,所以他们都肯定会被关闭,但潜在的fw.close()
会被调用两次 :不仅直接,而且通过包装bw.close()
这不应该成为双方实现这两个特定的类问题Closeable
(这是一个亚型AutoCloseable
),其合同规定,多次调用close
允许:
关闭此流并释放与之关联的所有系统资源。 如果流已经关闭,则调用此方法无效。
然而,在一般情况下,我可以只实现资源AutoCloseable
(而不是Closeable
),这并不能保证close
可多次调用:
请注意,与java.io.Closeable的close方法,则不需要此close方法为幂等。 换句话说,调用此方法接近不止一次可能有一些明显的副作用,不像Closeable.close这是需要有没有,如果调用一次以上的效果。 然而,我们强烈建议此接口的实施者,使他们的close方法幂等。
3)
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
这个版本应该是理论上是正确的,因为只有fw
表示需要清理一个真正的资源。 该bw
本身不拥有任何资源,它只是委托给fw
,所以它应该是足够的,只关闭底层fw
。
在另一方面,语法是有点不规则也,Eclipse中发出警告,我相信这是虚惊一场,但它仍然是一个必须面对的警告:
资源泄漏:“体重”永远不会关闭
所以,去哪些方法呢? 还是我错过了一些其他的成语是正确的 ?
Answer 1:
下面是我采取的方案:
1)
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
}
对我来说,从传统的C ++ 15年前来到Java的最好的事情是,你可以相信你的计划。 即使事情是在渣土和脚麻,他们经常这样做,我想代码的其余部分是在最好的行为,玫瑰气味。 的确, BufferedWriter
将在这里抛出异常。 运行内存应该并不罕见,例如。 对于其他的装饰,你知道其中的java.io
包装类扔从它们的构造经过检查的异常? 我不。 如果依靠那种晦涩的知识没有做代码的可理解性多好。
也有“毁灭”。 如果出现错误情况,那么你可能不希望被冲洗垃圾到需要删除(代码为未显示)的文件。 虽然,当然,删除文件也是另一个有趣的操做错误处理。
通常你想finally
块尽可能短和可靠的。 添加冲洗不利于这一目标。 对于许多版本的一些在JDK缓冲类的有来自哪里异常的错误flush
内close
导致close
称不上装饰对象。 虽然已经固定了一段时间,指望它从其他实现。
2)
try (
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)
) {
bw.write(text);
}
我们仍然在隐冲洗finally块(现反复close
-这会变得更糟,你增加更多的装饰),但建筑是安全的,我们必须隐含finally块所以即使失败flush
并不能防止资源释放。
3)
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
}
这里有一个错误。 应该:
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
bw.flush();
}
一些执行不力的装饰其实都是资源,将需要可靠地关闭。 也有一些流可能需要以特定的方式(也许他们正在做的压缩和需要编写位玩完被关闭,而不能随便冲洗一切。
判决书
虽然3是一个技术出色的解决方案,软件开发原因使得2更好的选择。 然而,尝试与-资源仍然是不充分的修复,你应该与坚持进行以成语 ,它应该有在Java SE 8倒闭更清晰的语法。
Answer 2:
第一个样式是一个由Oracle建议 。 BufferedWriter
不会抛出检查异常,所以如果有异常被抛出,预计不会在程序从中恢复,使资源回收大多是没有实际意义。
主要是因为它可以在一个线程中发生的,与螺纹dieing但该计划仍然在继续 - 比方说,有这还不够长,严重损害了程序的其余部分临时存储停运。 这是一个相当极端情况,不过,如果它发生的频率足以使资源泄漏的问题,在try-与资源是至少你的问题。
Answer 3:
选项4
改变你的资源是可关闭的,而不是AutoClosable如果你能。 该构造可以链接这个事实意味着它不是闻所未闻关闭资源的两倍。 (这是ARM之前确实太。)更多关于这下面。
选5
不要使用ARM和代码非常谨慎,以确保close()方法不叫两次!
选项6
不要使用ARM,有你终于接近()调用一个try / catch自己。
为什么我不认为这个问题是独一无二的ARM
在所有这些例子中,终于接近()调用应该是在一个catch块。 离开了以提高可读性。
没有好,因为FW可进行两次关闭。 (这是罚款的FileWriter但不是在你的hypothetial为例):
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if ( fw != null ) fw.close();
if ( bw != null ) bw.close();
}
没有好,因为FW不是封闭的,如果上构建的BufferedWriter例外。 (再次,是不可能发生的,但在你的假设的例子):
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if ( bw != null ) bw.close();
}
Answer 4:
我只是想建立在不使用ARM但确保了FileWriter的总是关闭的只有一次的珍妮Boyarsky的建议。 不要以为这里有什么问题?
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if (bw != null) bw.close();
else if (fw != null) fw.close();
}
我猜,因为ARM只是语法糖,我们不能总是用它来代替最终块。 就像我们不能总是使用for-each循环做一些事情,是可能的迭代器。
Answer 5:
与早些时候的评论同意:最简单的是(2)使用Closeable
在try-与资源子句中的资源,并宣布他们为了。 如果你只有AutoCloseable
,您可以在另一个(嵌套)类,仅仅检查包装他们close
时才调用(外观模式),例如通过具有private bool isClosed;
。 在实践中,即使甲骨文刚刚(1)链的构造函数和不正确中途链处理异常。
另外,您也可以手动创建一个链接的资源,使用静态工厂方法; 这种封装的链条,如果失败部分的方式处理清理:
static BufferedWriter createBufferedWriterFromFile(File file)
throws IOException {
// If constructor throws an exception, no resource acquired, so no release required.
FileWriter fileWriter = new FileWriter(file);
try {
return new BufferedWriter(fileWriter);
} catch (IOException newBufferedWriterException) {
try {
fileWriter.close();
} catch (IOException closeException) {
// Exceptions in cleanup code are secondary to exceptions in primary code (body of try),
// as in try-with-resources.
newBufferedWriterException.addSuppressed(closeException);
}
throw newBufferedWriterException;
}
}
然后,您可以使用它作为一个尝试,与资源第一个资源:
try (BufferedWriter writer = createBufferedWriterFromFile(file)) {
// Work with writer.
}
复杂性来自处理多个异常; 否则它只是“您已至此采集密切资源”。 一种常见的做法似乎是首先初始化认为,持有资源的对象变量null
(这里fileWriter
),然后包括在清理一空检查,但似乎没有必要:如果构造失败,没有什么清洁,所以我们可以只让该异常传播,简化了代码一点。
你也许可以做到这一点一般:
static <T extends AutoCloseable, U extends AutoCloseable, V>
T createChainedResource(V v) throws Exception {
// If constructor throws an exception, no resource acquired, so no release required.
U u = new U(v);
try {
return new T(u);
} catch (Exception newTException) {
try {
u.close();
} catch (Exception closeException) {
// Exceptions in cleanup code are secondary to exceptions in primary code (body of try),
// as in try-with-resources.
newTException.addSuppressed(closeException);
}
throw newTException;
}
}
同样,你可以链三个地资源,等等。
作为数学之外,你甚至可以通过一次链接两个资源连锁三次,这将是关联的,这意味着你会得到成功的同一对象(因为构造函数是关联),以及同样的例外,如果有故障在任何构造函数。 假设你加入S到上述链(这样就开始与V和结束与一个S,通过依次施加U,T和S),则得到相同的或者如果第一链条S和T,则U,对应于(ST)U,或如果第一链T和U,则S,对应于S(TU)。 然而,这将是更清晰只写了一个明确的三倍链在一个工厂的功能。
Answer 6:
因为你的资源是嵌套的,你试试,有条款也应该是:
try (FileWriter fw=new FileWriter(file)) {
try (BufferedWriter bw=new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
} catch (IOException ex) {
// handle ex
}
Answer 7:
我要说不使用ARM和带关闭下去。 使用方法一样,
public void close(Closeable... closeables) {
for (Closeable closeable: closeables) {
try {
closeable.close();
} catch (IOException e) {
// you can't much for this
}
}
}
你也应该考虑调用的密切BufferedWriter
,因为它不只是委派接近FileWriter
,但它确实有些像清理flushBuffer
。
Answer 8:
我的解决办法是做一个“提取方法”重构,如下:
static AutoCloseable writeFileWriter(FileWriter fw, String txt) throws IOException{
final BufferedWriter bw = new BufferedWriter(fw);
bw.write(txt);
return new AutoCloseable(){
@Override
public void close() throws IOException {
bw.flush();
}
};
}
printToFile
既可以写成
static void printToFile(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
AutoCloseable w = writeFileWriter(fw, text);
w.close();
} catch (Exception ex) {
// handle ex
}
}
要么
static void printToFile(String text, File file) {
try (FileWriter fw = new FileWriter(file);
AutoCloseable w = writeFileWriter(fw, text)){
} catch (Exception ex) {
// handle ex
}
}
对于IIb类设计师,我会建议他们延长AutoClosable
与打压接近的另一种方法接口。 在这种情况下,我们可以手动控制关闭行为。
对于语言设计者的教训是,添加新功能,可能意味着加入了很多其他人。 在这种情况下的Java,显然ARM功能将更好地工作与资源的所有权转移机制。
UPDATE
原本上面的代码需要@SuppressWarning
由于BufferedWriter
在函数内部需要close()
正如评论所说,如果flush()
之前关闭被称为作家,我们不需要任何前这样做return
try块内(隐性或显性)语句。 目前还没有办法保证调用者这样做,我认为,因此必须被记录为writeFileWriter
。
再次更新
上述更新,使@SuppressWarning
不必要的,因为它需要的功能将资源返回给调用者,所以本身并不需要被关闭。 不幸的是,这把我们拉回到情况开头:警告,现在搬回到发送方。
因此,要妥善解决这个问题,我们需要一个定制的AutoClosable
,每当它关闭,下划线BufferedWriter
应flush()
版。 其实,这为我们展示了另一种方式来绕过警告,因为BufferWriter
永远不会在任何一种方式关闭。
文章来源: Correct idiom for managing multiple chained resources in try-with-resources block?