RAII在Java...就是资源的处置总是那么的丑陋?
-
10-07-2019 - |
题
我只是打了Java文件系统API,并来到了以下的功能,用来拷贝二进制文件。原来源来自于网上,但是我加了尝试/赶/最后条款可以肯定的是,如果东西是错误的发生,缓冲区流将被关闭(和因此,我的操作系统资源释放)之前退出的功能。
我修剪下来的功能,显示的模式:
public static void copyFile(FileOutputStream oDStream, FileInputStream oSStream) throw etc...
{
BufferedInputStream oSBuffer = new BufferedInputStream(oSStream, 4096);
BufferedOutputStream oDBuffer = new BufferedOutputStream(oDStream, 4096);
try
{
try
{
int c;
while((c = oSBuffer.read()) != -1) // could throw a IOException
{
oDBuffer.write(c); // could throw a IOException
}
}
finally
{
oDBuffer.close(); // could throw a IOException
}
}
finally
{
oSBuffer.close(); // could throw a IOException
}
}
据我了解,我不能把两个 close()
在最后条款,因为第一个 close()
可能扔掉,然后,第二次不会被执行。
我知道C#有 处置 模式,将处理这个的 using
关键词。
我甚至不知道更好C++码就已经喜欢的东西(使用Java-API):
void copyFile(FileOutputStream & oDStream, FileInputStream & oSStream)
{
BufferedInputStream oSBuffer(oSStream, 4096);
BufferedOutputStream oDBuffer(oDStream, 4096);
int c;
while((c = oSBuffer.read()) != -1) // could throw a IOException
{
oDBuffer.write(c); // could throw a IOException
}
// I don't care about resources, as RAII handle them for me
}
我失去了一些东西,或者我真的已经产生的丑陋和臃肿的代码在Java只是为了处理例外情况 close()
方法的一个缓冲流?
(请告诉我,我是错的地方...)
编辑:是我,或者在更新这一页面上,我看到两个问题和所有的答案下降一个点,在一个几分钟吗?是有人享受自己太多而建设和平委员会组织委匿名?
编辑2: 麦克道尔 提供了一个非常有趣的链接我觉得我必须在这里提到的:http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html
编辑3:以下麦克的链接,我跌提议Java7的一个模式类似的C#使用的模式: http://tech.puredanger.com/java7/#resourceblock .我的问题是明确地描述。显然,即使有Java7 do
, 问题依然存在。
解决方案
试用/最终模式是正确的方式来处理流在大多数情况下Java6和降低。
有些倡导默默地关闭流。小心这样做对于这些原因: Java:怎么不做一个混乱的流处理
Java7引入了 尝试与资源:
/** transcodes text file from one encoding to another */
public static void transcode(File source, Charset srcEncoding,
File target, Charset tgtEncoding)
throws IOException {
try (InputStream in = new FileInputStream(source);
Reader reader = new InputStreamReader(in, srcEncoding);
OutputStream out = new FileOutputStream(target);
Writer writer = new OutputStreamWriter(out, tgtEncoding)) {
char[] buffer = new char[1024];
int r;
while ((r = reader.read(buffer)) != -1) {
writer.write(buffer, 0, r);
}
}
}
AutoCloseable
类型将自动关闭:
public class Foo {
public static void main(String[] args) {
class CloseTest implements AutoCloseable {
public void close() {
System.out.println("Close");
}
}
try (CloseTest closeable = new CloseTest()) {}
}
}
其他提示
有问题,但你发现躺在约在网络上的代码是真正的穷人。
关闭缓冲流下方关闭该流。你真的不希望这样做。所有你想要做的就是刷新输出流。也有在指定的基本流是文件没有意义的。性能很烂,因为你是在同一时间(实际上,如果你使用的java.io使用可以使用的transferTo / transferFrom这是一个快一点还是)复制一个字节。虽然我们看好它,变量名吮吸到。所以:
public static void copy(
InputStream in, OutputStream out
) throw IOException {
byte[] buff = new byte[8192];
for (;;) {
int len = in.read(buff);
if (len == -1) {
break;
}
out.write(buff, 0, len);
}
}
如果你发现自己使用try-最后很多,那么你就可以用“执行围绕”成语因素吧。
在我看来:Java的应该有好歹在范围结束时关闭资源。我建议增加private
作为一元后缀运算收于封闭块的末尾。
是的,这就是java的是如何工作的。有控制反转 - 对象的用户必须知道如何清理的对象,而不是自己后清理对象本身。这不幸导致大量的清理代码散布在整个Java代码。
C#具有“使用”的关键字来自动调用Dispose当对象超出范围。 Java有没有这样的事情。
不幸的是,这种类型的代码往往会在Java中有点臃肿。
顺便说一句,如果调用oSBuffer.read或oDBuffer.write之一抛出一个异常,那么你可能需要让该异常渗透到调用层次。
具有无人看守的呼叫以关闭()的一个最后子句内将导致原始异常由一个由闭合产生被替换() - 调用。换句话说,一个失败的close() - 方法可以隐藏()由读产生的原来的异常或写()。所以,我觉得你要忽略close()方法,如果的且仅当的其他方法没有抛出。
抛出的异常我通常包括显式近距离呼叫时,内部的try内部解决这个问题:
try { while (...) { read... write... } oSBuffer.close(); // exception NOT ignored here oDBuffer.close(); // exception NOT ignored here } finally { silentClose(oSBuffer); // exception ignored here silentClose(oDBuffer); // exception ignored here }
static void silentClose(Closeable c) { try { c.close(); } catch (IOException ie) { // Ignored; caller must have this intention } }
最后,对于性能,代码应该可能与缓冲器(每个读/写多个字节)工作。不能备份,通过数字,但更少的调用应该是更有效的比顶部添加缓冲数据流。