Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中的RAII。。。资源处置总是那么丑陋吗?_Java_Design Patterns_Raii_Resource Management - Fatal编程技术网

Java中的RAII。。。资源处置总是那么丑陋吗?

Java中的RAII。。。资源处置总是那么丑陋吗?,java,design-patterns,raii,resource-management,Java,Design Patterns,Raii,Resource Management,我刚刚使用了Java文件系统API,并实现了以下用于复制二进制文件的函数。最初的源代码来自Web,但我添加了try/catch/finally子句,以确保在退出函数之前,如果发生错误,缓冲流将被关闭(从而释放操作系统资源) 我缩减了函数以显示模式: public static void copyFile(FileOutputStream oDStream, FileInputStream oSStream) throw etc... { BufferedInputStream oSBuff

我刚刚使用了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()
放在finally子句中,因为第一个
close()
很可能抛出,然后第二个就不会执行

我知道C#具有Dispose模式,该模式可以使用
关键字
处理此问题

我甚至更清楚C++代码会是什么样子(使用java类API):

我遗漏了一些东西,或者我真的必须用Java生成丑陋和臃肿的代码,只是为了处理缓冲流的
close()
方法中的异常

(请告诉我我哪里错了…)

编辑:是我,还是在更新此页面时,我看到问题和所有答案在几分钟内减少了一个百分点?有人在还押anonymous时玩得太开心了吗?

编辑2:McDowell提供了一个非常有趣的链接,我觉得我必须在这里提到:


编辑3:根据McDowell的链接,我无意中发现了一个Java7的方案,其模式类似于C#using模式:。我的问题被明确地描述了。显然,即使使用Java7
do
,问题仍然存在。

不幸的是,这种类型的代码在Java中往往有点臃肿

顺便说一下,如果对oSBuffer.read或oDBuffer.write的某个调用引发异常,那么您可能希望让该异常渗透到调用层次结构中

在finally子句中对close()进行无保护调用将导致原始异常被close()调用生成的异常替换。换句话说,Faily close()-方法可能会隐藏由read()或write()生成的原始异常。因此,我认为您希望忽略close()引发的异常,前提是其他方法没有引发异常

我通常通过在内部try中包含一个显式的close调用来解决这个问题:

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 } 试一试{ 而(…){ 阅读 写 } oSBuffer.close();//此处不忽略异常 oDBuffer.close();//此处不忽略异常 }最后{ silentClose(oSBuffer);//此处忽略异常 silentClose(oDBuffer);//此处忽略异常 } 静态空隙silentClose(可关闭c){ 试一试{ c、 close(); }捕获(IOIE){ //忽略;调用方必须有此意图 } }
最后,为了提高性能,代码可能应该使用缓冲区(每次读/写多个字节)。不能通过数字来证明这一点,但是更少的调用应该比在上面添加缓冲流更有效。

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

有些人主张默默地关闭溪流。出于以下原因,请小心操作:


Java 7引入了try with resources:

类型将自动关闭:

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,use可以使用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 finally,那么您可以使用“executearound”成语来解决这个问题


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

是的,java就是这样工作的。还有控制反转-对象的用户必须知道如何清理对象,而不是在对象自身清理之后清理对象本身。不幸的是,这会导致大量清理代码分散在java代码中


C#具有“using”关键字,当对象超出范围时自动调用Dispose。Java没有这样的功能。

对于复制文件等常见IO任务,上面所示的代码正在重新发明轮子。不幸的是,JDK没有提供任何更高级别的实用程序,但apache commons io提供


例如,包含用于处理文件和目录(包括复制)的各种实用程序方法。另一方面,如果您确实需要在JDK中使用IO支持,则包含一组closequity()方法,这些方法可以关闭读卡器、写入器、流等,而不会引发异常。

如果您以这种方式静默关闭,那么如果close引发异常,您的代码不会进行错误处理。许多流(如BufferedOutputStream)在close上写入数据。McDowell:是的,如果close()抛出异常,它应该捕获异常。请注意,close()-调用首先在try块中进行!最后一个块是确保在任何方法引发异常时进行清理。对吗?(注意,我忘记了在第一个post版本中关闭一个缓冲区。)BufferedOutputStream有点问题。我赞成显式同花顺(在非例外情况下),但你必须记住它。IIRC,close在JavaSE1.6之前已经中断了异常处理。我知道close()中的异常非常重要。但是,如果close()-调用是在最后一次调用write()之后进行的,那么这不应该确保正确跟踪异常吗?麦克道尔,请确认这里是否有瑕疵;然后我会自己撤销代码,但我真的很想知道。:)有些加密/压缩有点令人讨厌。不仅
/** 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()) {}
  }
}
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);
    }
}