Nat*_*tix 163 java try-with-resources
当仅使用一个资源时,Java 7 try-with-resources语法(也称为ARM块(自动资源管理))很好,简短而直接AutoCloseable
.但是,当我需要声明彼此依赖的多个资源时,我不确定什么是正确的习惯用法,例如a FileWriter
和a BufferedWriter
包装它.当然,这个问题涉及AutoCloseable
包装某些资源的任何情况,而不仅仅是这两个特定的类.
我提出了以下三种选择:
我见过的天真的习惯是只声明ARM管理变量中的顶级包装器:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Run Code Online (Sandbox Code Playgroud)
这很好而且简短,但它已被打破.由于底层FileWriter
未在变量中声明,因此永远不会在生成的finally
块中直接关闭它.它只能通过close
包装方法关闭BufferedWriter
.问题是,如果从bw
构造函数抛出异常,close
则不会调用它,因此底层FileWriter
将不会被关闭.
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
}
}
Run Code Online (Sandbox Code Playgroud)
这里,底层资源和包装资源都是在ARM管理的变量中声明的,因此它们肯定会被关闭,但底层fw.close()
将被调用两次:不仅直接调用,还通过包装调用bw.close()
.
这对于这两个实现Closeable
(它们是子类型AutoCloseable
)的特定类来说应该不是问题,其合同规定close
允许多次调用:
关闭此流并释放与其关联的所有系统资源.如果流已经关闭,则调用此方法无效.
但是,在一般情况下,我可以拥有仅实现AutoCloseable
(而不是Closeable
)的资源,但不能保证close
可以多次调用:
请注意,与java.io.Closeable的close方法不同,此close方法不需要是幂等的.换句话说,不止一次调用此close方法可能会产生一些可见的副作用,这与Closeable.close不同,如果多次调用则需要它无效.但是,强烈建议强制使用此接口的实现者使其close方法具有幂等性.
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
}
}
Run Code Online (Sandbox Code Playgroud)
这个版本在理论上应该是正确的,因为只有fw
代表需要清理的真实资源.在bw
本身不拥有任何资源,它只是委托给fw
,所以它应该是足够的,只关闭基础fw
.
另一方面,语法有点不规则,并且Eclipse发出警告,我认为这是一个误报,但它仍然是一个必须处理的警告:
资源泄漏:'bw'永远不会关闭
那么,采用哪种方法?或者我错过了其他一些正确的习语?
Tom*_*ine 74
这是我对替代方案的看法:
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
}
Run Code Online (Sandbox Code Playgroud)
对我来说,15年前从传统C++到Java的最好的事情就是你可以相信你的程序.即使事情发生在他们经常做的事情中,我也希望其余的代码能够达到最好的行为和玫瑰的味道.实际上,这BufferedWriter
可能会引发例外.例如,耗尽内存并不罕见.对于其他装饰器,您知道哪个java.io
包装类从其构造函数中抛出一个已检查的异常吗?我不.如果你依赖那种晦涩的知识,那么代码的可理解性就不会很好.
还有"破坏".如果存在错误情况,那么您可能不希望将垃圾清除到需要删除的文件(未显示的代码).当然,删除文件也是另一个有趣的错误处理操作.
通常,您希望finally
块尽可能短且可靠.添加刷新对此目标没有帮助.对于许多版本的一些在JDK缓冲类有一个地方从异常错误flush
中close
引起close
装饰对象不被调用.虽然已经修复了一段时间,但期望从其他实现中获得.
try (
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)
) {
bw.write(text);
}
Run Code Online (Sandbox Code Playgroud)
我们仍在冲洗隐含的finally块(现在重复close
- 当你添加更多装饰器时这会变得更糟),但构造是安全的,我们必须隐含finally块,所以即使失败flush
也不会阻止资源释放.
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
}
Run Code Online (Sandbox Code Playgroud)
这里有一个错误.应该:
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
bw.flush();
}
Run Code Online (Sandbox Code Playgroud)
一些执行不佳的装饰器实际上是资源,需要可靠地关闭.还有一些流可能需要以特定方式关闭(可能它们正在进行压缩并需要写入位来完成,并且不能只是刷新所有内容.
虽然3是技术上优越的解决方案,但软件开发的原因使2成为更好的选择.然而,try-with-resource仍然是一个不充分的修复,你应该坚持使用Execute Around成语,它应该有一个更清晰的语法与Java SE 8中的闭包.
选项4
如果可以,将资源更改为可关闭,而不是AutoClosable.构造函数可以链接的事实意味着关闭资源两次并不是闻所未闻.(在ARM之前也是如此.)更多内容如下.
选项5
不要非常小心地使用ARM和代码来确保close()不被调用两次!
选项6
不要使用ARM并在try/catch中自己进行finally close()调用.
为什么我认为这个问题不是ARM特有的
在所有这些示例中,finally close()调用应该在catch块中.遗漏以方便阅读.
不好,因为fw可以关闭两次.(这对于FileWriter来说很好,但在你的假设示例中没有):
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();
}
Run Code Online (Sandbox Code Playgroud)
没有好处,因为如果构造BufferedWriter时出现异常,则fw不会关闭.(再次,不可能发生,但在您的假设示例中):
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if ( bw != null ) bw.close();
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
31525 次 |
最近记录: |