正确的成语在试穿与资源块管理多个链接的资源呢?(Correct idiom for managing

2019-06-18 06:41发布

只使用一个当Java 7 的try-与资源语法(也称为ARM块( 自动资源管理 ))是很好的,简短易懂AutoCloseable资源。 但是,我不知道什么是正确的成语时,我需要声明的是依赖于彼此的多个资源,例如FileWriterBufferedWriter一个包装它。 当然,这个问题涉及到任何情况下,当一些AutoCloseable资源被包裹,不仅这两个特定的类。

我想出了以下三种选择:

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块。 它只会通过闭合close包装的方法BufferedWriter 。 问题是,如果一个异常从抛出bw的构造,其close不会被调用,因此潜在FileWriter 不会被关闭

2)

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
    }
}

在这里,无论是底层和包装资源在ARM管理变量的声明,所以他们都肯定会被关闭,但潜在的fw.close() 会被调用两次 :不仅直接,而且通过包装bw.close()

这不应该成为双方实现这两个特定的类问题Closeable (这是一个亚型AutoCloseable ),其合同规定,多次调用close允许:

关闭此流并释放与之关联的所有系统资源。 如果流已经关闭,则调用此方法无效。

然而,在一般情况下,我可以只实现资源AutoCloseable (而不是Closeable ),这并不能保证close可多次调用:

请注意,与java.io.Closeable的close方法,则不需要此close方法为幂等。 换句话说,调用此方法接近不止一次可能有一些明显的副作用,不像Closeable.close这是需要有没有,如果调用一次以上的效果。 然而,我们强烈建议此接口的实施者,使他们的close方法幂等。

3)

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
    }
}

这个版本应该是理论上是正确的,因为只有fw表示需要清理一个真正的资源。 该bw本身不拥有任何资源,它只是委托给fw ,所以它应该是足够的,只关闭底层fw

在另一方面,语法是有点不规则也,Eclipse中发出警告,我相信这是虚惊一场,但它仍然是一个必须面对的警告:

资源泄漏:“体重”永远不会关闭


所以,去哪些方法呢? 还是我错过了一些其他的成语是正确的

Answer 1:

下面是我采取的方案:

1)

try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
    bw.write(text);
}

对我来说,从传统的C ++ 15年前来到Java的最好的事情是,你可以相信你的计划。 即使事情是在渣土和脚麻,他们经常这样做,我想代码的其余部分是在最好的行为,玫瑰气味。 的确, BufferedWriter将在这里抛出异常。 运行内存应该并不罕见,例如。 对于其他的装饰,你知道其中的java.io包装类扔从它们的构造经过检查的异常? 我不。 如果依靠那种晦涩的知识没有做代码的可理解性多好。

也有“毁灭”。 如果出现错误情况,那么你可能不希望被冲洗垃圾到需要删除(代码为未显示)的文件。 虽然,当然,删除文件也是另一个有趣的操做错误处理。

通常你想finally块尽可能短和可靠的。 添加冲洗不利于这一目标。 对于许多版本的一些在JDK缓冲类的有来自哪里异常的错误flushclose导致close称不上装饰对象。 虽然已经固定了一段时间,指望它从其他实现。

2)

try (
    FileWriter fw = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(fw)
) {
    bw.write(text);
}

我们仍然在隐冲洗finally块(现反复close -这会变得更糟,你增加更多的装饰),但建筑是安全的,我们必须隐含finally块所以即使失败flush并不能防止资源释放。

3)

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

一些执行不力的装饰其实都是资源,将需要可靠地关闭。 也有一些流可能需要以特定的方式(也许他们正在做的压缩和需要编写位玩完被关闭,而不能随便冲洗一切。

判决书

虽然3是一个技术出色的解决方案,软件开发原因使得2更好的选择。 然而,尝试与-资源仍然是不充分的修复,你应该与坚持进行以成语 ,它应该有在Java SE 8倒闭更清晰的语法。



Answer 2:

第一个样式是一个由Oracle建议 。 BufferedWriter不会抛出检查异常,所以如果有异常被抛出,预计不会在程序从中恢复,使资源回收大多是没有实际意义。

主要是因为它可以在一个线程中发生的,与螺纹dieing但该计划仍然在继续 - 比方说,有这还不够长,严重损害了程序的其余部分临时存储停运。 这是一个相当极端情况,不过,如果它发生的频率足以使资源泄漏的问题,在try-与资源是至少你的问题。



Answer 3:

选项4

改变你的资源是可关闭的,而不是AutoClosable如果你能。 该构造可以链接这个事实意味着它不是闻所未闻关闭资源的两倍。 (这是ARM之前确实太。)更多关于这下面。

选5

不要使用ARM和代码非常谨慎,以确保close()方法不叫两次!

选项6

不要使用ARM,有你终于接近()调用一个try / catch自己。

为什么我不认为这个问题是独一无二的ARM

在所有这些例子中,终于接近()调用应该是在一个catch块。 离开了以提高可读性。

没有好,因为FW可进行两次关闭。 (这是罚款的FileWriter但不是在你的hypothetial为例):

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

没有好,因为FW不是封闭的,如果上构建的BufferedWriter例外。 (再次,是不可能发生的,但在你的假设的例子):

FileWriter fw = null;
BufferedWriter bw = null;
try {
  fw = new FileWriter(file);
  bw = new BufferedWriter(fw);
  bw.write(text);
} finally {
  if ( bw != null ) bw.close();
}


Answer 4:

我只是想建立在不使用ARM但确保了FileWriter的总是关闭的只有一次的珍妮Boyarsky的建议。 不要以为这里有什么问题?

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

我猜,因为ARM只是语法糖,我们不能总是用它来代替最终块。 就像我们不能总是使用for-each循环做一些事情,是可能的迭代器。



Answer 5:

与早些时候的评论同意:最简单的是(2)使用Closeable在try-与资源子句中的资源,并宣布他们为了。 如果你只有AutoCloseable ,您可以在另一个(嵌套)类,仅仅检查包装他们close时才调用(外观模式),例如通过具有private bool isClosed; 。 在实践中,即使甲骨文刚刚(1)链的构造函数和不正确中途链处理异常。

另外,您也可以手动创建一个链接的资源,使用静态工厂方法; 这种封装的链条,如果失败部分的方式处理清理:

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.
}

复杂性来自处理多个异常; 否则它只是“您已至此采集密切资源”。 一种常见的做法似乎是首先初始化认为,持有资源的对象变量null (这里fileWriter ),然后包括在清理一空检查,但似乎没有必要:如果构造失败,没有什么清洁,所以我们可以只让该异常传播,简化了代码一点。

你也许可以做到这一点一般:

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;
  }
}

同样,你可以链三个地资源,等等。

作为数学之外,你甚至可以通过一次链接两个资源连锁三次,这将是关联的,这意味着你会得到成功的同一对象(因为构造函数是关联),以及同样的例外,如果有故障在任何构造函数。 假设你加入S到上述链(这样就开始与V和结束与一个S,通过依次施加U,TS),则得到相同的或者如果第一链条ST,U,对应于(ST)U,或如果第一链TU,S,对应于S(TU)。 然而,这将是更清晰只写了一个明确的三倍链在一个工厂的功能。



Answer 6:

因为你的资源是嵌套的,你试试,有条款也应该是:

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
}


Answer 7:

我要说不使用ARM和带关闭下去。 使用方法一样,

public void close(Closeable... closeables) {
    for (Closeable closeable: closeables) {
       try {
           closeable.close();
         } catch (IOException e) {
           // you can't much for this
          }
    }

}

你也应该考虑调用的密切BufferedWriter ,因为它不只是委派接近FileWriter ,但它确实有些像清理flushBuffer



Answer 8:

我的解决办法是做一个“提取方法”重构,如下:

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

    };
}

printToFile既可以写成

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
    }
}

对于IIb类设计师,我会建议他们延长AutoClosable与打压接近的另一种方法接口。 在这种情况下,我们可以手动控制关闭行为。

对于语言设计者的教训是,添加新功能,可能意味着加入了很多其他人。 在这种情况下的Java,显然ARM功能将更好地工作与资源的所有权转移机制。

UPDATE

原本上面的代码需要@SuppressWarning由于BufferedWriter在函数内部需要close()

正如评论所说,如果flush()之前关闭被称为作家,我们不需要任何前这样做return try块内(隐性或显性)语句。 目前还没有办法保证调用者这样做,我认为,因此必须被记录为writeFileWriter

再次更新

上述更新,使@SuppressWarning不必要的,因为它需要的功能将资源返回给调用者,所以本身并不需要被关闭。 不幸的是,这把我们拉回到情况开头:警告,现在搬回到发送方。

因此,要妥善解决这个问题,我们需要一个定制的AutoClosable ,每当它关闭,下划线BufferedWriterflush()版。 其实,这为我们展示了另一种方式来绕过警告,因为BufferWriter永远不会在任何一种方式关闭。



文章来源: Correct idiom for managing multiple chained resources in try-with-resources block?