Автор оригинала: 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 ()
, и доступ к ним осуществляется иначе, чем к “обычным” исключениям.
Важно понимать, что порядок исполнения:
- попробуйте-с-ресурсами заблокировать
- неявное, наконец
- блок catch (если в [1] и/или [2] было выдано исключение)
- (явно) наконец
Например, вот ресурс, который ничего не делает, кроме как создает исключения:
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 пытается закрыть ресурсы.
Вывод
попробуйте-с-ресурсами следует использовать вместо обычного попробуйте-поймайте-наконец , когда это возможно. Легко забыть закрыть один из ваших ресурсов после нескольких часов кодирования или забыть закрыть ресурс, который вы только что добавили в свою программу, после случайного порыва вдохновения.
Код более читабелен, его легче изменять и поддерживать, и обычно он короче.