Статьи

Тщательно укажите несколько ресурсов в одном отчете о попытках использования ресурсов

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

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

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

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

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

InnerResource.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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

1
2
3
4
5
6
7
8
9
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

1
2
3
4
5
6
7
8
9
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 был правильно назначен переменной в инструкции try -with-resources, и поэтому его метод close() вызывается правильно, даже когда во время его создания возникает исключение.

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

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

Опубликовано на Java Code Geeks с разрешения Дастина Маркса, партнера нашей программы JCG . См. Оригинальную статью здесь: Тщательно укажите несколько ресурсов в одном заявлении о попытках использования ресурсов.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.