Статьи

Java 7: объяснение с использованием ресурсов

В этой статье рассматривается использование оператора try-with-resources . Это оператор try, который объявляет один или несколько ресурсов. Ресурс — это объект, который должен быть закрыт после завершения работы программы.

Оператор try-with-resources обеспечивает закрытие каждого ресурса в конце оператора. Любой объект, который реализует интерфейс java.lang.AutoCloseable или java.io.Closeable, может использоваться в качестве ресурса.

Перед попыткой использования ресурсов (до Java 7) при работе с объектами SQL Statement, ResultSet, Connection или другими объектами ввода-вывода необходимо было явно закрыть ресурс. Так что можно написать что-то вроде:

01
02
03
04
05
06
07
08
09
10
11
12
try{
  //Create a resource- R
} catch(SomeException e){
  //Handle the exception
} finally{
  //if resource R is not null then
  try{
    //close the resource
  }
  catch(SomeOtherException ex){
  }
}

Мы должны явно закрыть ресурс и тем самым добавить еще несколько строк кода. Есть несколько случаев, когда разработчик забудет закрыть ресурс. Таким образом, чтобы преодолеть эти и другие проблемы, в Java 7 введена попытка использования ресурсов .

Давайте посмотрим на пример того, как мы будем использовать try..catch… наконец, в предварительной версии Java 7. Позвольте мне создать 2 пользовательских исключения — ExceptionA и ExceptionB. Они будут использованы в примерах.

1
2
3
4
5
public class ExceptionA extends Exception{
    public ExceptionA(String message){
        super(message);
    }
}
1
2
3
4
5
public class ExceptionB extends Exception{
    public ExceptionB(String message){
        super(message);
    }
}

Давайте создадим некоторый ресурс, скажем OldResource, который имеет два метода — doSomeWork (): который выполняет некоторую работу, и close (): который выполняет закрытие. Обратите внимание, что это описывает использование общего ресурса — сделайте некоторую работу, а затем закройте ресурс. Теперь каждая из этих операций, doSomeWork и close, выдает исключение.

01
02
03
04
05
06
07
08
09
10
public class OldResource{
    public void doSomeWork(String work) throws ExceptionA{
        System.out.println("Doing: "+work);
        throw new ExceptionA("Exception occured while doing work");
    }
    public void close() throws ExceptionB{
        System.out.println("Closing the resource");
        throw new ExceptionB("Exception occured while closing");
    }
}

Давайте использовать этот ресурс в примере программы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class OldTry {
    public static void main(String[] args) {
        OldResource res = null;
        try {
            res = new OldResource();
            res.doSomeWork("Writing an article");
        } catch (Exception e) {
            System.out.println("Exception Message: "+
                      e.getMessage()+" Exception Type: "+e.getClass().getName());
        } finally{
            try {
                res.close();
            } catch (Exception e) {
                System.out.println("Exception Message: "+
                         e.getMessage()+" Exception Type: "+e.getClass().getName());
            }
        }
    }
}

Выход:

1
2
3
4
Doing: Writing an article
Exception Message: Exception occured while doing work Exception Type: javaapplication4.ExceptionA
Closing the resource
Exception Message: Exception occured while closing Exception Type: javaapplication4.ExceptionB

Программа проста: создайте новый ресурс, используйте его, а затем попытайтесь закрыть его. Можно посмотреть на количество дополнительных строк кода там.

Теперь давайте реализуем ту же самую программу, используя Java-конструкцию try-with-resource. Для этого нам понадобится новый ресурс — NewResource. В Java 7 появился новый интерфейс, java.lang.AutoCloseable . Те ресурсы, которые должны быть закрыты, реализуют этот интерфейс. Все более старые API ввода-вывода, API-интерфейсы сокетов и т. Д. Реализуют интерфейс Closeable — это означает, что эти ресурсы можно закрыть. В Java 7 java.io.Closeable реализует AutoCloseable . Так что все работает без нарушения существующего кода.

Код NewResource ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class NewResource implements AutoCloseable{
    String closingMessage;
  
    public NewResource(String closingMessage) {
        this.closingMessage = closingMessage;
    }
  
    public void doSomeWork(String work) throws ExceptionA{
        System.out.println(work);
        throw new ExceptionA("Exception thrown while doing some work");
    }
    public void close() throws ExceptionB{
        System.out.println(closingMessage);
        throw new ExceptionB("Exception thrown while closing");
    }
  
    public void doSomeWork(NewResource res) throws ExceptionA{
        res.doSomeWork("Wow res getting res to do work");
    }
}

Теперь давайте используем NewResource в примере программы, используя try-with-resource:

01
02
03
04
05
06
07
08
09
10
public class TryWithRes {
    public static void main(String[] args) {
        try(NewResource res = new NewResource("Res1 closing")){
            res.doSomeWork("Listening to podcast");
        } catch(Exception e){
            System.out.println("Exception: "+
       e.getMessage()+" Thrown by: "+e.getClass().getSimpleName());
        }
    }
}

Выход:

1
2
3
Listening to podcast
Res1 closing
Exception: Exception thrown while doing some work Thrown by: ExceptionA

Одна вещь, которую следует отметить выше, состоит в том, что Исключение, выбрасываемое при закрытии, подавляется Исключением, которое выдается рабочим методом.

Таким образом, вы можете сразу заметить разницу между двумя реализациями, одна из которых использует try… catch… finally, а другая — try-with-resource. В приведенном выше примере только один ресурс объявлен как используемый. В блоке try можно объявить и использовать несколько ресурсов, а также вложить эти блоки try-with-resources.

Наряду с этим, несколько новых методов и конструктор были добавлены в класс java.lang.Throwable , все из которых связаны с подавлением исключений, генерируемых вместе с другими исключениями. Лучший пример для этого — ExceptionA, генерируемый блоком try, будет подавлен ExceptionB, генерируемым оператором finally (при закрытии ресурса), и это было поведение до Java 7.

Однако в Java 7 выброшенное исключение отслеживает исключения, которые оно подавляло на пути к перехвату / обработке. Таким образом, приведенный выше пример можно переформулировать следующим образом. ExceptionB, генерируемый методом close, добавляется в список исключенных исключений ExceptionA, который генерируется блоком try.

Позвольте мне объяснить вам вложенные попытки с ресурсами и исключенные исключения на следующих примерах.

Вложенная попытка с ресурсами

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class TryWithRes {
    public static void main(String[] args) {
        try(NewResource res = new NewResource("Res1 closing");
            NewResource res2 = new NewResource("Res2 closing")){
            try(NewResource nestedRes = new NewResource("Nestedres closing")){
                nestedRes.doSomeWork(res2);
            }
        } catch(Exception e){
            System.out.println("Exception: "+
      e.getMessage()+" Thrown by: "+e.getClass().getSimpleName());
        }
  
    }
}

Выход для вышеупомянутого будет:

1
2
3
4
5
Wow res getting res to do work
Nestedres closing
Res2 closing
Res1 closing
Exception: Exception thrown while doing some work Thrown by: ExceptionA

Обратите внимание на порядок закрытия ресурсов, последний в первую очередь. Также обратите внимание, что исключение, генерируемое каждой из этих операций close (), подавляется.

Давайте посмотрим, как мы можем получить исключенные исключения:

Исключенные исключения

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class TryWithRes {
    public static void main(String[] args) {
        try(NewResource res = new NewResource("Res1 closing");
            NewResource res2 = new NewResource("Res2 closing")){
            try(NewResource nestedRes = new NewResource("Nestedres closing")){
                nestedRes.doSomeWork(res2);
            }
        } catch(Exception e){
            System.out.println("Exception: "+
      e.getMessage()+" Thrown by: "+e.getClass().getSimpleName());
            if (e.getSuppressed() != null){
                for (Throwable t : e.getSuppressed()){
                    System.out.println(t.getMessage()+
                               " Class: "+t.getClass().getSimpleName());
                }
            }
        }
  
    }
}

Выход для вышеуказанного кода:

1
2
3
4
5
6
7
8
Wow res getting res to do work
Nestedres closing
Res2 closing
Res1 closing
Exception: Exception thrown while doing some work Thrown by: ExceptionA
Exception thrown while closing Class: ExceptionB
Exception thrown while closing Class: ExceptionB
Exception thrown while closing Class: ExceptionB

Метод getSuppressed () используется для извлечения исключений, подавленных сгенерированным исключением. Также был добавлен новый конструктор класса Throwable, который можно использовать для включения или отключения подавления исключений. Если отключено, ни одно из подавленных исключений не отслеживается.

Ссылка: Java 7 Project Coin: try-with-resources объяснено на примерах от нашего партнера по JCG Мохамеда Санауллы в блоге Experiences Unlimited .

Статьи по Теме :