Java 7更有用的新功能之一是引入了try-with-resources语句 [AKA 自动资源管理 ( ARM )]。 try-with-resources语句的吸引力在于其承诺 “确保在语句末尾关闭每个资源”。 在这种情况下,“资源”是实现AutoCloseable及其close()方法并在try-with-resources语句的“ try”子句中实例化的任何类。

Java语言规范 [JLS]在14.20.3节 (在这种情况下为Java SE 10 JLS )中详细描述了try-with-resource语句 。 JLS声明“ try -with-resources语句是使用局部变量(称为resources )进行参数化的,这些局部变量在try块执行之前被初始化,并在执行try之后以与初始化时相反的顺序自动关闭。块。”

JLS明确指定可以相对于单个try -with-resources语句定义多个资源,并且它指定如何指定多个资源。 具体地说,它表明try可以跟随一个“ ResourceSpecification ,其由一个”的“ ResourceList ,其由一个或多个“的” 资源 “秒。 当声明的资源多于一个时,多个资源用分号( ; )分隔。 用分号分隔的列表指定多个资源很重要,因为try -with-resources语句将不支持(不会自动关闭)未以此方式声明的任何候选资源。

在try -with-resources语句中指定多个资源时,最有可能的错误源是“嵌套”“资源”的实例化,而不是显式地实例化每个变量的局部变量,而在每个实例化之间不使用分号。 以下示例将说明差异。

接下来显示两个荒谬但说明性的类。 每个类都实现AutoCloseable ,因此可以与try -with-resources结合使用,并且在与try -with-resources语句正确使用时将自动调用其close()方法。 命名它们以反映可以使用InnerResource的实例实例化OuterResource 。

InnerResource.java

package dustin.examples.exceptions;

import static java.lang.System.out;

public class InnerResource implements AutoCloseable
{
   public InnerResource()
   {
      out.println("InnerResource created.");
   }

   public InnerResource(
      final RuntimeException exceptionToThrow)
   {
      throw  exceptionToThrow != null
         ? exceptionToThrow
         : new RuntimeException("InnerResource: No exception provided.");
   }

   @Override
   public void close() throws Exception
   {
      out.println("InnerResource closed.");
   }

   @Override
   public String toString()
   {
      return "InnerResource";
   }
}

OuterResource.java

package dustin.examples.exceptions;

import static java.lang.System.out;

public class OuterResource implements AutoCloseable
{
   private final InnerResource wrappedInnerResource;

   public OuterResource(final InnerResource newInnerResource)
   {
      out.println("OuterResource created.");
      wrappedInnerResource = newInnerResource;
   }

   public OuterResource(
      final InnerResource newInnerResource,
      final RuntimeException exceptionToThrow)
   {
      wrappedInnerResource = newInnerResource;
      throw  exceptionToThrow != null
           ? exceptionToThrow
           : new RuntimeException("OuterResource: No exception provided.");
   }

   @Override
   public void close() throws Exception
   {
      out.println("OuterResource closed.");
   }

   @Override
   public String toString()
   {
      return "OuterResource";
   }
}

现在,可以使用刚刚定义的两个类来演示在用分号分隔的列表中的同一try -with-resources语句中正确声明每个实例的实例与在外部资源的构造函数中错误地嵌套内部资源的实例之间的区别。 后一种方法效果不理想,因为就调用其AutoCloseable.close()方法而言,没有本地定义变量的内部资源不会被视为“资源”。

下一个代码清单演示了在try -with-resources语句中实例化“资源”的错误方法。

try -with-resources语句中实例化资源的错误方法

try (OuterResource outer = new OuterResource(
        new InnerResource(), new RuntimeException("OUTER")))
{
   out.println(outer);
}
catch (Exception exception)
{
   out.println("ERROR: " + exception);
}

执行上面的代码后,输出“ InnerResource created”。 可以看到,但是从未显示与资源关闭相关的输出。 这是因为InnerResource的实例是在对OuterResource类的构造函数的调用中实例化的,并且从未在try -with-resource语句的资源列表中分配给自己的单独变量。 对于真正的资源,这意味着资源没有正确关闭。

下一个代码清单演示了在try -with-resources语句中实例化“资源”的正确方法。

try -with-resources语句中实例化资源的正确方法

try(InnerResource inner = new InnerResource();
    OuterResource outer = new OuterResource(inner, new RuntimeException("OUTER")))
{
   out.println(outer);
}
catch (Exception exception)
{
   out.println("ERROR: " + exception);
}

当执行上面紧接的代码时,输出将同时包含“ InnerResource created”。 并且“ InnerResource已关闭。” 因为InnerResource实例已在try -with-resources语句中正确分配给变量,所以即使实例化期间发生异常,也将正确调用其close()方法。

Java教程的try-with-resources语句部分包括几个示例,这些示例正确地将try -with-resources中的资源指定为以分号分隔的单个变量定义。 一个示例通过java.util.zip.ZipFile和java.io.BufferedWriter展示了这种正确的方法,而另一个示例通过java.sql.Statement和java.sql.ResultSet的实例展示了这种正确的方法。

在JDK 7中引入try -with-resources是对该语言的一种受欢迎的补充,它使Java开发人员可以更轻松地编写不太可能泄漏或浪费资源的资源安全的应用程序。 但是,当在单个try -with-resources语句中声明多个资源时,重要的是要确保每个资源都单独实例化并分配给在try的资源说明符列表中声明的自己的变量,以确保每个资源都是正确关闭。 一种快速的检查方法是确保对于try指定的n个 AutoCloseable实现资源,应该有n-1个分号来分隔这些实例化的资源。