Рубрики
Без рубрики

Инструкция try-with-resources в Java

Автор оригинала: Olivera Popović.

Вступление

try-with-resources является одним из нескольких операторов try в Java, направленных на освобождение разработчиков от обязанности освобождать ресурсы, используемые в блоке try .

Изначально он был представлен в Java 7, и вся идея заключалась в том, что разработчику не нужно беспокоиться об управлении ресурсами для ресурсов, которые они используют только в одном try-catch-finally блоке. Это достигается за счет устранения необходимости в блоках finally , которые разработчики использовали только для закрытия ресурсов на практике.

Кроме того, код, использующий try-with-resources , часто является более чистым и читаемым, что облегчает управление кодом, особенно когда мы имеем дело со многими блоками try .

Синтаксис

Синтаксис try-with-resources почти идентичен обычному синтаксису try-catch-finally . Единственное различие-это скобки после try , в которых мы объявляем, какие ресурсы мы будем использовать:

BufferedWriter writer = null;
try {
    writer = new BufferedWriter(new FileWriter(fileName));
    writer.write(str);  // do something with the file we've opened
} catch (IOException e) {
   // handle the exception
} finally {
    try {
        if (writer != null)
            writer.close();
    } catch (IOException e) {
       // handle the exception
    }
}

Тот же код, написанный с использованием try-with-resources , будет выглядеть следующим образом:

try(BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))){
    writer.write(str); // do something with the file we've opened
}
catch(IOException e){
    // handle the exception
}

То, как Java понимает этот код:

Ресурсы, открытые в скобках после оператора try , понадобятся только здесь и сейчас. Я вызову их методы .close () , как только закончу работу в блоке try . Если в блоке try возникнет исключение, я все равно закрою эти ресурсы.

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

catch и наконец часть попробуйте с ресурсами работают, как ожидалось, с catch блоками, обрабатывающими соответствующие исключения, и наконец блок выполняется независимо от того, было ли исключение или нет. Единственное отличие-это подавленные исключения, которые объясняются в конце этой статьи.

Примечание : Начиная с Java 9, нет необходимости объявлять ресурсы в инструкции try-with-resources . Вместо этого мы можем сделать что-то подобное:

BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
try (writer) {
    writer.write(str); // do something with the file we've opened
}
catch(IOException e) {
    // handle the exception
}

Множество Ресурсов

Еще одним хорошим аспектом попробуйте с ресурсами является простота добавления/удаления ресурсов, которые мы используем, с гарантией того, что они будут закрыты после того, как мы закончим.

Если бы мы хотели работать с несколькими файлами, мы бы открыли файлы в инструкции try() и разделили их точкой с запятой:

try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
    Scanner scanner = new Scanner(System.in)) {
if (scanner.hasNextLine())
    writer.write(scanner.nextLine());
}
catch(IOException e) {
    // handle the exception
}

Затем Java позаботится о том, чтобы вызвать .close() на всех ресурсах, которые мы открыли в try() .

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

Поддерживаемые Классы

Все ресурсы, объявленные в try () , должны реализовывать Автоклавируемый интерфейс. Обычно это различные типы устройств записи, считывателей, сокетов, потоков вывода или ввода и т. Д. Все, что вам нужно будет написать resource.close() после того, как вы закончите с ним работать.

Это, конечно, включает в себя определенные пользователем объекты, реализующие Автоклавируемый интерфейс. Однако вы редко столкнетесь с ситуацией, когда захотите написать свои собственные ресурсы.

В случае, если это произойдет, вам необходимо реализовать Автоклавируемый или Закрываемый (только там, чтобы сохранить обратную совместимость, предпочитайте Автоклавируемый ) интерфейс и переопределить метод .close() :

public class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        // close your resource in the appropriate way
    }
}

Обработка исключений

Если исключение создается из блока Java try-with-resources , любой ресурс, открытый в круглых скобках блока try , все равно будет автоматически закрыт.

Как упоминалось ранее, try-with-resources работает так же , как try-catch-finally , за исключением одного небольшого дополнения. Добавление называется подавленные исключения . Не необходимо понимать подавленные исключения , чтобы использовать try-with-resources , но чтение о них может быть полезно для отладки, когда больше ничего не работает.

Представьте себе ситуацию:

  • По какой-то причине в блоке try-with-resources возникает исключение
  • Java останавливает выполнение в блоке try-with-resources и вызывает .close() для всех ресурсов, объявленных в try()
  • Один из методов .close() создает исключение
  • Какое исключение будет “ловить” блок catch ?

Эта ситуация знакомит нас с вышеупомянутыми подавленными исключениями. Подавленное исключение-это исключение, которое каким-то образом игнорируется, когда оно создается в неявном блоке finally блока try-with-resources , в случае, когда исключение также создается из блока try .

Эти исключения являются исключениями, которые встречаются в методах .close () , и доступ к ним осуществляется иначе, чем к “обычным” исключениям.

Важно понимать, что порядок исполнения:

  1. попробуйте-с-ресурсами заблокировать
  2. неявное, наконец
  3. блок catch (если в [1] и/или [2] было выдано исключение)
  4. (явно) наконец

Например, вот ресурс, который ничего не делает, кроме как создает исключения:

public static class MyResource implements AutoCloseable {
    // method throws RuntimeException
    public void doSomething() {
        throw new RuntimeException("From the doSomething method");
    }

    // we'll override close so that it throws an exception in the implicit finally block of try-with-resources (when it attempts to close our resource)
    @Override
    public void close() throws Exception {
        throw new ArithmeticException("I can throw whatever I want, you can't stop me.");
    }
}

public static void main(String[] arguments) throws Exception {
    // declare our resource in try
    try (MyResource resource = new MyResource()) {
        resource.doSomething();
    }
    catch (Exception e) {
        System.out.println("Regular exception: " + e.toString());

        // getting the array of suppressed exceptions, and its length
        Throwable[] suppressedExceptions = e.getSuppressed();
        int n = suppressedExceptions.length;

        if (n > 0) {
            System.out.println("We've found " + n + " suppressed exceptions:");
            for (Throwable exception : suppressedExceptions) {
                System.out.println(exception.toString());
            }
        }
    }
}

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Этот код доступен для выполнения. Вы можете использовать его для экспериментов с использованием нескольких объектов MyResource или просмотра того, что происходит, когда try-with-resources не создает исключения, но .close() создает.

Подсказка : Внезапно исключения, возникающие при закрытии ресурсов, становятся важными.

Важно отметить, что в случае, если ресурс создает исключение при попытке его закрыть, любые другие ресурсы, открытые в том же блоке try-with-resources , все равно будут закрыты.

Еще один факт, который следует отметить, заключается в том, что в ситуации, когда блок try не создает исключения и когда при попытке .закрыть() используемые ресурсы возникает несколько исключений, первое исключение будет распространяться по стеку вызовов, в то время как остальные будут подавлены.

Как вы можете видеть в коде, вы можете получить список всех подавленных исключений, обратившись к массиву Throwable , возвращаемому Throwable.getSuppressed() .

Помните, что внутри блока try может быть создано только одно исключение. Как только возникает исключение, код блока try завершается, и Java пытается закрыть ресурсы.

Вывод

попробуйте-с-ресурсами следует использовать вместо обычного попробуйте-поймайте-наконец , когда это возможно. Легко забыть закрыть один из ваших ресурсов после нескольких часов кодирования или забыть закрыть ресурс, который вы только что добавили в свою программу, после случайного порыва вдохновения.

Код более читабелен, его легче изменять и поддерживать, и обычно он короче.