Статьи

Как указать несколько ресурсов в одной инструкции Try-With-Resources

Одной из наиболее полезных функций Java 7 было введение оператора try-with-resources , также известного как  автоматическое управление ресурсами ( ARM ). Привлекательность оператора try-with-resources заключается в его обещании «обеспечить закрытие каждого ресурса в конце оператора». «Ресурс» в этом контексте — это любой класс, который реализует AutoCloseable и его метод close () и создается в предложении «try» оператора try-with-resources .

Спецификация языка Java (JLS) описывает заявление примерочных с-ресурсами подробно в разделе 14.20.3  на Java SE 10 JLS в этом случае. JLS утверждает, что « tryоператор -with-resources параметризован локальными переменными (известными как ресурсы ), которые инициализируются до выполнения tryблока и закрываются автоматически, в обратном порядке, из которого они были инициализированы, после выполнения tryблока».

JLS четко указывает, что несколько ресурсов могут быть определены в отношении одного tryоператора -with-resources, и определяет, как указано несколько ресурсов. В частности, это указывает, что за ним tryможет следовать « ResourceSpecification », который состоит из « ResourceList », который состоит из одного или более « Resource ‘s». Когда существует более одного объявленного ресурса, несколько ресурсов разделяются точкой с запятой ( ;). Эта спецификация нескольких ресурсов в списке, разделенном точкой с запятой, важна, поскольку любые ресурсы-кандидаты, не объявленные таким образом, не будут поддерживаться (не будут закрываться автоматически) оператором try-with-resources.

Наиболее вероятным источником ошибок при указании нескольких ресурсов в операторе try-with-resources является «вложение» экземпляров «ресурсов» вместо явного создания экземпляров локальных переменных каждой из них по отдельности с точками с запятой между каждой реализацией. Приведенные ниже примеры иллюстрируют разницу.

Два смешных, но иллюстративных занятия показаны далее. Каждый класс реализует  AutoCloseable и может использоваться вместе с try-with-resources, и его close()метод будет вызываться автоматически при правильном использовании с tryоператором -with-resources. Они названы так, чтобы отражать, что OuterResourceэкземпляр может быть создан с помощью экземпляра 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 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 closed , поскольку InnerResourceэкземпляр был правильно назначен переменной в tryинструкции -with-resources. Следовательно, его close()метод вызывается правильно, даже если во время его создания возникает исключение.

Заявление примерочных с-ресурсов раздел из Java Обучение включает в себя примеры правильного определения ресурсов в try—with-ресурсов как определения индивидуального переменных разделенных точкой с запятой. Один пример демонстрирует этот правильный подход с java.util.zip.ZipFile и java.io.BufferedWriter . Другой пример демонстрирует этот правильный подход с экземплярами java.sql.Statement и java.sql.ResultSet .

Внедрение try-with-resources в JDK 7 стало долгожданным дополнением к языку, благодаря которому разработчики Java упростили создание ресурсо-безопасных приложений, которые с меньшей вероятностью могут утекать или тратить ресурсы. Однако, когда несколько ресурсов объявляются в одном tryвыражении -with-resources, важно обеспечить, чтобы каждый ресурс создавался индивидуально и назначался своей собственной переменной, объявленной в tryсписке спецификаторов ресурса, чтобы гарантировать, что каждый ресурс правильно закрыт. , Быстрый способ проверить это состоит в том, чтобы убедиться, что для AutoCloseable  реализуются ресурсы, указанные в разделе try.Должно быть  n-1 точка с запятой, разделяющих эти экземпляры ресурсов.