leftso 729 0 2018-08-24 16:56:54

logo-cover-Java编程try-with-resources嵌套资实例化源无法关闭原因
Java编程try-with-resources嵌套资实例化源无法关闭

try-with-resources是Java 7 引入的一个新功能,自动资源管理。try-with-resources语句的吸引力在于其“确保在语句结束时关闭每个资源“的保证。此上下文中的“资源”是实现AutoCloseable及其close()方法的任何类。在try-with-resources语句的“try”子句中实例化。


在Java语言规范 [JLS]介绍了尝试,与资源声明中详细第14.20.3(的Java SE的10个JLS在这种情况下)。JLS声明“ try-with-resources语句使用局部变量(称为资源)参数化,这些局部变量在执行try块之前初始化,并在执行块之后自动关闭,并按照与它们初始化相反的顺序try。

JLS明确规定可以定义与单个try-with-resources语句相关的多个资源,并指定如何指定多个资源。具体而言,它表示try可以跟随“ ResourceSpecification ”,其由“ ResourceList ”组成,该“ ResourceList ”由一个或多个“ Resource ”组成。当存在多个声明的资源时,多个资源由semicolon(;)分隔。以分号分隔的列表中的多个资源的这种规范很重要,因为try-with-resources语句不支持以这种方式声明的任何候选资源(不会自动关闭)。

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

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

下面举个例子
 
$title(InnerResource.java)
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";
   }
}

$title(OuterResource.java)
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()方法方面不被视为“资源” 。

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

在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语句的资源列表中分配给它自己的单独变量。使用真实资源,其含义是资源未正确关闭。

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

在try资源声明中实现资源的正确方法
 
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 closed。”因为InnerResource实例被正确地分配给try-with-resources语句中的变量,因此close()即使异常也正确调用其方法在实例化期间发生。

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

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