Java 在try-with-resources块中管理多个链接资源的正确习惯用法?
当只使用一个Java 在try-with-resources块中管理多个链接资源的正确习惯用法?,java,try-with-resources,Java,Try With Resources,当只使用一个AutoCloseable资源时,Java 7 try with resources语法(也称为ARM块(自动资源管理))非常好、简短和直接。但是,当我需要声明相互依赖的多个资源时,我不确定什么是正确的习惯用法,例如FileWriter和包装它的BufferedWriter。当然,这个问题涉及到包装某些可自动关闭的资源的任何情况,而不仅仅是这两个特定类 我提出了以下三个备选方案: 1) 我看到的一个简单习惯用法是只声明ARM托管变量中的顶级包装器: static void print
AutoCloseable
资源时,Java 7 try with resources语法(也称为ARM块(自动资源管理))非常好、简短和直接。但是,当我需要声明相互依赖的多个资源时,我不确定什么是正确的习惯用法,例如FileWriter
和包装它的BufferedWriter
。当然,这个问题涉及到包装某些可自动关闭的资源的任何情况,而不仅仅是这两个特定类
我提出了以下三个备选方案:
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
块中直接关闭。它将仅通过包装BufferedWriter
的close
方法关闭。问题是,如果从bw
的构造函数中抛出异常,将不会调用其close
,因此底层FileWriter
将不会关闭
2)
在这里,底层和包装资源都在ARM托管变量中声明,因此它们都肯定会被关闭,但是底层fw.close()
将被调用两次:不仅直接调用,而且还通过包装bw.close()
这对于这两个都实现了Closeable
(这是AutoCloseable
的子类型)的特定类来说应该不是问题,它们的合同规定允许对close
进行多次调用:
关闭此流并释放与之关联的所有系统资源。如果流已关闭,则调用此方法无效
但是,在一般情况下,我可以使用只实现自动关闭
(而不是可关闭
)的资源,这并不保证可以多次调用关闭
:
请注意,与java.io.Closeable的close方法不同,此close方法不要求是幂等的。换句话说,多次调用此close方法可能会产生一些明显的副作用,而Closeable.close则需要在多次调用时不起作用。但是,强烈鼓励此接口的实现者使其紧密方法幂等
3)
这个版本在理论上应该是正确的,因为只有fw
代表需要清理的真实资源。bw
本身不包含任何资源,它只委托给fw
,因此只关闭底层fw
另一方面,语法有点不规则,而且Eclipse发出了一个警告,我认为这是一个假警报,但它仍然是一个必须处理的警告:
资源泄漏:“bw”从未关闭
那么,选择哪种方法呢?还是我遗漏了其他正确的成语?选项4
如果可以,将资源更改为可关闭,而不是自动关闭。构造函数可以链接这一事实意味着关闭资源两次并非闻所未闻。(在ARM之前也是如此。)下面将详细介绍
选项5
不要非常小心地使用ARM和代码来确保close()不会被调用两次
选项6
不要使用ARM,让finally close()调用自己进行try/catch
为什么我不认为这个问题是ARM独有的
在所有这些示例中,finally close()调用应该位于catch块中。为便于阅读而遗漏
不好,因为fw可以关闭两次。(这对于FileWriter很好,但在您的假设示例中不适用):
不好,因为如果构造BufferedWriter时发生异常,则fw未关闭。(同样,不可能发生,但在您的假设示例中):
由于资源是嵌套的,因此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
}
我要说的是,不要用手臂,继续用Closeable。使用类似的方法
public void close(Closeable... closeables) {
for (Closeable closeable: closeables) {
try {
closeable.close();
} catch (IOException e) {
// you can't much for this
}
}
}
您也应该考虑调用 BuffeDeReDe> <代码>,因为它不只是委派接近文件写入器< /C> >,但它会像“代码>刷新FulsBuffs ”进行一些清理。
< P>这是我的备选方案:
1)
对我来说,15年前从传统C++中来到java的最好的事情就是你可以信任你的程序。即使事情变得一团糟,并且经常出错,我希望代码的其余部分都是关于最佳行为和玫瑰香的。实际上,BufferedWriter
可能会在这里抛出异常。例如,内存不足并不罕见。对于其他decorator,您知道哪个java.io
包装类会从其构造函数中抛出已检查异常吗?我不。如果依赖于这种模糊的知识,代码的可理解性就没有多大好处
还有“毁灭”。如果存在错误情况,那么您可能不希望将垃圾刷新到需要删除的文件中(未显示该文件的代码)。当然,删除文件也是另一个有趣的错误处理操作
通常,您希望最终块尽可能短且可靠。添加刷新无助于实现此目标。对于许多版本,JDK中的一些缓冲类有一个bug,其中close
中的flush
异常导致装饰对象上的close
无法调用。虽然这个问题已经解决了一段时间,但可以从其他实现中得到解决
2)
我们仍然在隐式finally块中刷新(否
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();
}
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if ( bw != null ) bw.close();
}
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
}
public void close(Closeable... closeables) {
for (Closeable closeable: closeables) {
try {
closeable.close();
} catch (IOException e) {
// you can't much for this
}
}
}
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
}
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);
}
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
bw.flush();
}
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();
}
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();
}
};
}
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
}
}
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.
}
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;
}
}