【问题标题】:Disposing streams in Java在 Java 中处理流
【发布时间】:2010-02-15 19:41:28
【问题描述】:

在 C# 中,我在处理流对象时几乎总是使用using 模式。例如:

using (Stream stream = new MemoryStream())
{
    // do stuff
}

通过使用using 块,我们确保在该代码块执行后立即在流上调用 dispose。

我知道 Java 没有 using 关键字的等价物,但我的问题是,当在 Java 中使用像 FileOutputStream 这样的对象时,我们是否需要做任何内务以确保它被处理掉? ?我正在查看 this 代码示例,我注意到他们没有做任何事情。

我只是想知道 Java 在处理释放流时的最佳做法是什么,或者是否足以让垃圾收集器处理它。

【问题讨论】:

    标签: java stream


    【解决方案1】:

    通常,您必须执行以下操作:

    InputStream stream = null;
    try {
       // IO stuff - create the stream and manipulate it
    } catch (IOException ex){
      // handle exception
    } finally {
      try {
         stream.close();
      } catch (IOException ex){}
    }
    

    但是 apache commons-io 提供了 IOUtils.closeQuietly(stream); 将其放在 finally 子句中以使其不那么难看。我认为在 Java 7 中会有一些改进。

    更新:Jon Skeet 做了一个非常有用的评论,异常的实际处理很少发生在类本身中(除非它只是简单地记录它,但实际上并没有处理它)。因此,您最好声明您的方法抛出该异常,或将其包装在自定义异常中(简单的原子操作除外)。

    【讨论】:

    • 你知道 IOUtils.closeQuietly(stream) 是否处理空流,否则它可能会抛出 NPE 并导致同样的问题(原始异常被吃掉)。
    • 是的,IOUtils 在关闭前检查 null,因此没有 NPE 风险
    • 根据官方Java教程,只有当流不为空时才关闭。
    • 当然,您不能对 null 引用做任何事情,除非检查它们。
    • 我发现在这样的低级方法中真正处理 IOException 是非常罕见的。它几乎总是涉及抛出 some 类型的异常。请注意,您还吞下了关闭期间抛出的 IOException ;如果代码已经抛出异常,这通常是正确的做法,否则可能会隐藏错误。例如,如果您将多个项目写入 BufferedOutputStream 但将其留给 close() 方法进行适当刷新,吞下异常会隐藏写入基本上失败的事实。
    【解决方案2】:

    Java 7 中确实引入了您想要的功能,名称为“try-with-resources statement”(也称为自动资源管理 (ARM))。代码如下:

    try (InputStream in = new FileInputStream("foo.txt")) {
        ...
    }  // This finally calls in.close()
    

    【讨论】:

    • 这是最好的语法,在出现错误的情况下,您可以同时保留代码抛出的异常和关闭的异常
    【解决方案3】:

    (遗憾的是)Java 中没有与 using 声明等效的语句,尽管有一些关于在 Java 7 中包含类似内容的想法。(我认为上次我认为它们已经“出局”了,但我觉得很难跟上 Java 7 中功能的状态。)

    基本上你需要一个 try/finally 块:

    InputStream stream = new FileInputStream(...);
    try { 
        ...
    } finally {
        stream.close();
    }
    

    然后是在 close() 失败的情况下如何处理 IOException 的问题,以及那里的异常问题“覆盖”代码主体抛出的任何异常 - 尽管后者在 .NET 中也是一个问题。

    Guava 使用 Closeables 类使这更容易一些,静态 closecloseQuietly 方法将处理 stream 为空(在您在块之前声明变量但分配的情况下try 块中的值)。

    【讨论】:

    • 创建流也会抛出IOException,所以它也应该在try/catch/finally中。
    • 但是使用上面Bozho的方式会导致原始异常被保留,对,因为他基本上是在吃可能被抛出的IOException。另外,我认为您可能应该检查流是否为空,否则可能会导致 NPE,不是吗?
    • @Bozho 和 dcp:通常当我处理流时,它在一个声明为抛出 IOException 的方法中,所以我很少在那个级别捕获它。这就是你遇到问题的地方。这里给出的代码不需要检查是否为空,因为我正在调用构造函数:如果该方法无异常返回,stream 肯定不会为空。
    【解决方案4】:

    如果有人好奇,Java 7 中的新语法可能是:

    do (BufferedInputStream bis = ...; BufferedOutputStream bos = ...) {
       ... // Perform action with bis and bos
    }
    

    (见proposal by Joshua Bloch

    这是 Stack Overflow 上的 related thread

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-29
      • 2021-04-24
      • 2016-01-16
      相关资源
      最近更新 更多