【问题标题】:Preferred way to clean-up resources in Java 1.6Java 1.6 中清理资源的首选方式
【发布时间】:2012-08-25 08:22:14
【问题描述】:

我经常看到这种资源清理方式:

InputStream in = null;
try {
    in = new FileInputStream(file);
    // ...
} finally {
    if (in != null) {
        in.close();
    }
}

我一直使用以下风格:

final InputStream in = new FileInputStream(file);
try {
    // ...
} finally {
    in.close();
}

但是我错过了什么吗?前者有我没有看到的优势吗?

【问题讨论】:

  • 你可能遗漏了一些东西...new FileInputStream() 可以抛出 FileNotFoundException。这样该异常就不会在该代码块中被捕获,但会传播给调用者。
  • @Augusto 两个示例都没有捕获 FileNotFoundException。但是不确定这是否是故意的。
  • 我认为前者只有在它也有一个 catch 块时才有意义。在其他情况下,您的表单更简洁。

标签: java idioms


【解决方案1】:

假设您需要打开的不是一个文件,而是两个文件。你会做的

final InputStream in = new FileInputStream(file1);
final OutputStream out = new FileOutputStream(file2);
try {
    // ...
} finally {
    out.close();
    in.close();
}

如果out 无法打开,你会得到一个异常,因为它在try 块之外,in 不会在finally 块中关闭。

另一种方法:

InputStream in = null;
OutputStream out = null;
try {
    in = new FileInputStream(file1);
    out = new FileOutputStream(file2);
    // ...
} finally {
    if (out != null) out.close();
    if (in != null) in.close();
}

如果out 无法打开,您将转到finally 块并关闭两个流。如果in打开失败,你会去finally块,只释放in——因为out==null

编辑

正如 aetheria 所提到的,该代码不起作用,因为 close() 在 Java 中引发异常。它可以通过将每个资源发布放在它自己的try-catch 块中来轻松修复:

InputStream  in  = null;
OutputStream out = null;
try {
    in = new FileInputStream(file1);
    out = new FileOutputStream(file2);
    // ...
} finally {
    try{ out.close(); }catch(Exception e){}
    try{ in.close();  }catch(Exception e){}
}

我放弃了 null 检查 - 如果 inout 为 null,它将抛出一个 NullPointerException 将被忽略。我忽略close 异常的原因是处理方法首先不应该抛出异常。如果需要处理关闭异常,您可以随时再次关闭流, finally 块之后。这样,可以关闭的任何流都将已经关闭(因此您不必担心),并且您可以更优雅地处理来自close 的任何异常。

现在,aetheria 还建议为每个资源放置一个单独的 try-finally 块,如下所示:

final InputStream in = new FileInputStream(file1);
try {
    final OutputStream out = new FileOutputStream(file2);
    try {
        // ...
    } finally {
        out.close();
    }
} finally {
    in.close();
}

这可行,但即使只有两个资源,它也不太优雅,因为它会拆分分配和释放代码,使得跟踪它变得更加困难(至少在我看来)。

【讨论】:

  • 此代码包含错误:如果out.close() 抛出IOException,您将永远不会关闭in
  • 添加到我的最后一条评论。我会做你建议我在你的问题顶部做的事情。我会为每个流设置单独的 try..finally 块。
  • 你是对的,但我宁愿把这归咎于 Java,也不愿承认自己的错误。无论如何,我已经更新了答案。
【解决方案2】:

我怀疑它是为了避免使用两个嵌套的 try/catch 块而不是一个。

InputStream in = null;
try {
    in = new FileInputStream(file);
    // ...
} catch(IOException ioe) {
    // handle exception.
} finally {
    IOUtils.closeQuietly(in);
}

第二种情况不完整。

try {
    final InputStream in = new FileInputStream(file);
    try {
        // ...
    } finally {
        in.close();
    }
} catch(IOException e) {
    // handle exception
}

如果您有多个文件,这可能会变得非常混乱。

【讨论】:

  • 我喜欢这个答案,尽管它很短且缺少解释。
  • 你能展示一下其他版本的异常处理是什么样的吗?真的更干净吗?
  • @aetheria 第一个例子已经完成,你不需要外接。
  • “完成”是什么意思?第一个版本没有任何 catch 块!
  • @aetheria 这取决于您的假设,但如果它们相同,第一个示例应该看起来像我添加的那样。
【解决方案3】:

null 在此处检查InputStream 是必要的,因为可能未分配变量。在这种情况下,在调用时尝试关闭它时会抛出 NullPointerException:

in.close();

在 try/catch 块的第二块 外面

final InputStream in = new FileInputStream(file);
try {
    // ...
} finally {
    in.close();
}

在进入区块之前很容易遇到其他异常,InputStream 永远不会关闭。

【讨论】:

  • in 在尝试之前直接分配。不能是null
  • @aetheria //... 可以使其为空
【解决方案4】:

第二个不会编译,因为FileInputStream 的构造函数可以抛出一个FileNotFoundException,因此你需要一个额外的try-catch 块,当然,除非方法本身抛出它。

另一个常见的习惯用法是编写一个closeQuietly() 方法,以避免在你的finally 块上写if (is != null) 检查。这就是 Apache Common 的 IOUtils 所做的:

public static void closeQuietly(InputStream input) {
  try {
    if (input != null) {
      input.close();
    }
  } catch (IOException ioe) {
    // ignore
  }
}

还要注意 Java 7,你可以使用以下:

try (InputStream is = new FileInputStream(file)) {
} catch (final FileNotFoundException | IOException ex) { 
}

【讨论】:

  • 当然,我没有展示它所在的代码,但是该方法被声明为抛出IOException,所以它编译得很好。
【解决方案5】:

这是一个非常简单的示例,并且可能不会产生问题,因为您在同一个人中创建 InputStream。但是如果InputStream 因为Exception or 其他故障而关闭,在这种情况下你的代码将会失败,所以最好检查InputStream 是否可用

【讨论】:

  • 我的代码不会失败。大多数流允许多次调用close(),但在最坏的情况下,对close() 的调用可能会抛出IOException,但这没关系。可以像任何其他IOException 一样在调用堆栈的上方进一步处理异常。
【解决方案6】:

假设在第一个示例中,您在定义 in 之前还有一些其他代码可以让您退出 try 块。如果您在未定义的情况下到达 finally,您将在尝试关闭它时收到 NullPointerException。因此,您必须进行检查以避免此类错误。

【讨论】:

  • 是的,但在我的版本中,in 不能是 null
猜你喜欢
  • 2021-01-25
  • 1970-01-01
  • 2016-12-24
  • 1970-01-01
  • 1970-01-01
  • 2019-05-23
  • 1970-01-01
相关资源
最近更新 更多