美文网首页
ITEM 9: 用 TRY-WITH-RESOURCES 替代

ITEM 9: 用 TRY-WITH-RESOURCES 替代

作者: rabbittttt | 来源:发表于2019-04-15 16:26 被阅读0次

ITEM 9: PREFER TRY-WITH-RESOURCES TO TRY-FINALLY
  Java库包含许多必须通过调用close方法手动关闭的资源,如 InputStream, OutputStream, java.sql.Connection.。关闭资源常常被用户忽视,其后果可想而知。虽然这些库包中有许多使用 finalizer 作为安全网,但是 finalizer 并不能很好地工作(第8项)。
  通常,try-finally 语句是确保资源被正确关闭的最佳方法,即使在出现异常或返回时也是如此:

// try-finally - No longer the best way to close resources!
static String firstLineOfFile(String path) throws IOException { 
  BufferedReader br = new BufferedReader(newFileReader(path)); 
  try {
    return br.readLine(); 
  } finally {
    br.close(); 
  }
}

  这看起来不错,但是当你添加第二个资源时,情况就更糟了:

// try-finally is ugly when used with more than one resource!
static void copy(String src, String dst) throws IOException {
  InputStream in = new FileInputStream(src); 
  try {
    OutputStream out = new FileOutputStream(dst);
    try {
      byte[] buf = new byte[BUFFER_SIZE]; int n;
      while ((n = in.read(buf)) >= 0)
        out.write(buf, 0, n); 
    } finally {
      out.close();
    }
  } finally {
    in.close(); 
  }
}

  这可能很难相信,但即使是优秀的程序员在大多数情况下也会犯这样的错误。首先,我在Java Puzzlers [Bloch05] 的88页上做错了,多年来没有人注意到这一点。事实上,2007年Java库中使用close方法的三分之二是错误的。即使这样使用 try-finally 语句关闭资源的正确代码(如前两个代码示例所示)也有一个细微的缺陷:try 块和finally 块中的代码都能够抛出异常。例如,在firstLineOfFile 方法中,对 readLine 的调用可能会由于底层物理设备中的故障引发异常,而对 close 的调用也可能因为相同的原因而失败。在这种情况下,第二个异常完全覆盖了第一个异常。在异常堆栈跟踪中没有关于第一个异常的记录,这可能会使系统中的调试变得非常复杂 —— 通常,为了诊断问题,您希望看到的是第一个异常。虽然可以编写代码来抑制第二个异常同时支持第一个异常,但实际上没有人这样做,因为它太冗长了。
  当Java 7 引入 try-with-resources 语句时,所有这些问题都一下子解决了。要使用这个结构,资源必须实现 AutoCloseable 接口,该接口包含一个返回 void 的close方法。Java库和第三方库中的许多类和接口现在都实现或扩展了 AutoCloseable。如果您编写了一个表示必须关闭的资源的类,那么您的类也应该实现AutoCloseable。
  下面是第一个使用try-with-resources的示例:

// try-with-resources - the the best way to close resources!
static String firstLineOfFile(String path) throws IOException { 
  try (BufferedReader br = new BufferedReader(
    new FileReader(path))) { 
      return br.readLine();
    } 
}

  下面是第二个使用try-with-resources的示例:

// try-with-resources on multiple resources - short and sweet
static void copy(String src, String dst) throws IOException {
  try (InputStream in = new FileInputStream(src); 
        OutputStream out = new FileOutputStream(dst)) {
          byte[] buf = new byte[BUFFER_SIZE]; 
          int n;
          while ((n = in.read(buf)) >= 0)
            out.write(buf, 0, n); 
  }
}

  与原始版本相比,TRY-WITH-RESOURCES 版本不仅更短、更易于阅读,而且提供了更好的调式信息。考虑 firstLineOfFile 方法。如果 readLine 调用和(不可见的)关闭都抛出异常,则后一个异常将被抑制,以支持前一个异常。实际上,可能会抑制多个异常,以便保留您实际上想要看到的异常。这些被抑制的异常不是简单地被抛弃,它们还被打印在堆栈跟踪中,并带有表示它们被抑制的符号。您还可以使用getsuppress 方法以编程方式访问它们,该方法是在Java 7中添加到Throwable中的。
  您可以将 catch 子句放在 try-with-resources 语句上,就像您可以放在常规try-finally 语句上一样。这允许您处理异常,而不需要使用另一层嵌套破坏代码。作为一个稍微有点做作的例子,下面是我们的 firstLineOfFile 方法的一个版本,它不会抛出异常,但是如果它不能打开文件或从文件中读取,它会返回一个默认值:

// try-with-resources with a catch clause
static String firstLineOfFile(String path, String defaultVal) { 
  try (BufferedReader br = new BufferedReader(
     new FileReader(path))) { 
    return br.readLine();
  } catch (IOException e) { 
    return defaultVal;
  } 
}

  在处理必须关闭的资源时,始终使用资源 try-with-resources 而不是 try-finally。生成的代码更短、更清晰,并且生成的异常更有用。try-with-resources 语句使得使用必须关闭的资源编写正确的代码变得很容易,而使用 try-finally 实际上是不可能的。

相关文章

网友评论

      本文标题:ITEM 9: 用 TRY-WITH-RESOURCES 替代

      本文链接:https://www.haomeiwen.com/subject/evauwqtx.html