代码人生

如何在一个Try-With-Resources语句中指定多个资源

代码人生 http://www.she9.com 2018-08-28 12:23 出处:网络 编辑:@技术狂热粉
在一个try-with-resources语句中指定多个资源的能力是Java 7中引入的一个特性。但是,如果不小心使用,它可能充满危险。继续往下读,确保你每次都做对。

Java 7的一个更有用的特性是引入了try-with-resources语句,也称为自动资源管理(ARM)。try-with-resources语句的吸引力在于它承诺“确保在语句末尾关闭每个资源”。在此上下文中,“资源”是实现AutoCloseable及其close()方法的任何类,并在try-with-resources语句的“try”子句中实例化。


Java语言规范(JLS)详细描述了Java SE 10 JLS 14.20.3节中的try-with-resources语句。JLS声明:“try-with-resources语句是用本地变量(称为资源)参数化的,这些变量在执行try块之前被初始化,并在执行try块之后以相反的顺序自动关闭。”


JLS明确指定可以根据单个try-with-resources语句定义多个资源,并指定如何指定多个资源。具体地说,它指出,try之后可以是由“ResourceList”组成的“resourcspecification”,它由一个或多个“Resource's”组成。当有多个声明的资源时,多个资源用分号(;)分隔。此分号分隔列表中的多个资源的规范非常重要,因为以这种方式未声明的任何候选资源都不受try-with-resources语句的支持(不会自动关闭)。


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


下面将展示两个荒谬但具有说明性的类。每个类都实现了一个AutoCloseable,可以与try-with-resources一起使用,当正确使用try-with-resources语句时,它的close()方法将被自动调用。它们的命名是为了反映外部资源可以用InnerResource的实例实例化。继续往下读,确保你每次都做对。

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已创建”,但从未显示与资源闭包相关的输出。这是因为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和关闭的InnerResource,因为InnerResource实例被正确地分配给了try-with-resources语句中的一个变量。因此,它的close()方法被正确调用,即使在实例化过程中出现异常。


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


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


请关注公众号:程序你好
0

精彩评论

暂无评论...
验证码 换一张
取 消