RAII在Java中...资源处置总是那么丑陋吗?

我刚刚玩了Java文件系统API,并推出了以下功能,用于复制二进制文件。原始源代码来自Web,但我添加了try/catch/finally子句,以确保如果出现问题,缓冲区流将在退出函数之前关闭(因此,我的操作系统资源被释放)。

我修剪了函数以显示模式:

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#具有Dispose模式,该模式可以使用关键字来处理这个问题。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:在McDowell的链接之后,我提出了一个Java 7的建议,该模式类似于C#使用模式:http://tech.puredanger.com/java7/#resourceblock。我的问题被明确描述。显然,即使使用Java 7,问题仍然存在。do


答案 1

对于大多数 Java 6 及更低版本,try/finally 模式是处理流的正确方法。

有些人主张默默地关闭溪流。出于以下原因,请小心这样做:Java:如何不使流处理变得混乱


Java 7 引入了 try-with-resources

/** 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);
        }
    }
}

自动关闭类型将自动关闭:

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

答案 2

存在问题,但是您在网络上发现的代码真的很差。

关闭缓冲流将关闭下面的流。你真的不想那样做。您要做的就是刷新输出流。此外,指定底层流用于文件也没有意义。性能很糟糕,因为你一次复制一个字节(实际上,如果你使用 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-final,那么你可以用“执行”成语来分解它。

在我看来:Java应该有某种方式在范围结束时关闭资源。我建议添加为一元后缀运算符,以在封闭块的末尾关闭。private


推荐