java - 在try-with-resources块中管理多个链式资源的?

  显示原文与译文双语对照的内容
0 0

Java 7 try-with-resources 语法( 也称为ARM块( 自动资源管理 ) ) 很好,只在使用一个 AutoCloseable 资源时简单明了。 但是,我不确定什么是正确的习惯用法,当我需要声明多个依赖于彼此的资源时,例如一个 FileWriter 和一个包装它的BufferedWriter 。 当然,这个问题涉及任何 AutoCloseable 资源被包装的情况,而不仅仅是这两个特定的类。

我提出了以下三种备选方案:

1 )

我所见过的天真的成语是声明只有 ARM-managed top-level包装器的变量:

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 块中直接关闭。 它将关闭只有通过包装 BufferedWriterclose 方法。 问题是,如果从 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-managed中声明变量,所以他们两人肯定会关闭,所以当然底层 fw.close() 将调用两次,第一次直接或通过包装 bw.close() 第二次。

这对于两个实现 Closeable ( 它是 AutoCloseable的子类型)的特定类来说不应该是问题,它们的协定表明允许多次调用 close:

关闭此输入流并释放与该流关联的所有系统资源. 如果流已经关闭,则调用这里方法没有任何效果。

但是,在一般情况下,我可以拥有仅实现 AutoCloseable ( 而不是 Closeable )的资源,这不能保证 close 可以多次调用:

注意,与 java.io. Closeable的闭合方法不同,这里闭合方法不需要为幂等。 换句话说,多次调用这个close方法可能会有一些明显的副作用,这不同于 Closeable.close 所要求的,如果调用不止一次。 然而,强烈鼓励这个接口的实现者将它们的方法设置为 idempotent 。

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 发出警告,我认为这是一个错误警报,但它仍然是一个警告,它必须处理:

资源泄漏:'bw'从不关闭


那么,选择哪种方法? 还是我错过了一些成语,正确?

时间:原作者:12个回答

0 0

下面是我的替代方案:

1 )

try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
 bw.write(text);
}

对于我来说,从传统 C++ 15年前到Java的最好的事情是你可以信任你的程序。 即使在淤泥和出错,他们经常做,我想让其余的代码是最好的行为和闻到玫瑰。 BufferedWriter 可能会在这里抛出异常。 内存不足就不寻常了,例如。 对于其他的decorator,你知道哪个 java.io 包装类从它们的构造函数抛出一个checked异常? 我没有。如果你依靠这种晦涩的知识,代码的理解就会很好。

还有"销毁"。如果有一个错误条件,那么你可能不想将垃圾刷新到需要删除( 没有显示的代码)的文件。 当然,删除文件也是另一个有趣的操作,作为错误处理。

通常你希望 finally 块尽可能短和可靠。 添加刷新并不帮助这里目标。 对于许多版本的一些缓冲类在jdk Bug 异常从 flushclose 内引起 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);
}

这里有一个 Bug 。应该是:

try (FileWriter fw = new FileWriter(file)) {
 BufferedWriter bw = new BufferedWriter(fw);
 bw.write(text);
 bw.flush();
}

一些实现很差的decorator实际上是资源,需要可靠地关闭。 此外有些流可能需要以特定方式关闭(也许他们正在压缩,需要编写位完成关闭,并可不只是冲洗一切。

判定

尽管 3是一个技术上优越的解决方案,软件开发原因使得 2更好的选择。 但是,try-with-resource仍然是一个不足的解决方法,你应该坚持执行周围的习惯用法,它应该有一个在 Java SE 8中具有闭包的更清晰的语法。

原作者:
0 0

第一种风格是建议的一个。BufferedWriter 不会抛出检查过的异常,所以如果抛出异常,程序就不会从它恢复,使资源恢复起来几乎没有实际意义。

主要是因为它可以发生在一个线程,线程dieing但是程序还在继续--说,有一个临时的内存故障没有足够长的时间严重损害其他的项目。 这是一个相当大的案例,如果经常发生资源泄露,try-with-resources是你的问题。

原作者:
0 0

我只是想建立在不使用ARM的boyarsky建议的基础上,但确保FileWriter总是完全关闭一次。 不要认为这里有任何问题。

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();
}

我猜既然手臂只是句法糖,我们不能总是用它来替代finally块。 就像我们不能总是使用for-each循环来做迭代器可以做的事情。

原作者:
0 0

由于资源是嵌套的,所以try-with子句也应该是:

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
}
原作者:
0 0

我要说不要用手臂,也不要用 Closeable 。 使用方法,例如

public void close(Closeable... closeables) {
 for (Closeable closeable: closeables) {
 try {
 closeable.close();
 } catch (IOException e) {
//you can't much for this
 }
 }
}

另外,你应该考虑关闭 BufferedWriter,因为它不只是委托到 FileWriter,而是做一些清理像 flushBuffer

原作者:
0 0

我的解决方案是执行一个"提取方法"重构,如下所示:

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
 }
}

对于类库设计者,我建议他们用一个额外的方法扩展 AutoClosable 接口来抑制关闭。 在这种情况下,我们可以手动控制关闭行为。

对于语言设计者来说,添加一个新特性意味着添加很多其他特性。 在这个Java案例中,显然ARM特性可以更好地使用资源所有权转移机制。

更新

上面的代码需要 @SuppressWarning,因为函数内的BufferedWriter 需要 close()

如注释所建议的,如果要在关闭写入器之前调用 flush(),则需要在try尝试块inside内的任何 return ( 隐式或者显式) 语句之前进行。 目前没有办法确保调用方这么做,所以这必须为 writeFileWriter 记录。

再次更新

上面的更新使 @SuppressWarning 不需要,因为它要求函数将资源返回给调用者,所以它不需要关闭。 不幸的是,这让我们回到了最初的情形: 警告现在已经被移回到调用者端。

所以为了解决这个问题,我们需要一个定制的AutoClosable,当它关闭时,下划线 BufferedWriter 应该是 flush() 。 实际上,这向我们展示了绕过警告的另一种方法,因为 BufferWriter 从未以任何方式关闭过。

原作者:
...