Автор оригинала: 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 пытается закрыть ресурсы.
Вывод
попробуйте-с-ресурсами следует использовать вместо обычного попробуйте-поймайте-наконец , когда это возможно. Легко забыть закрыть один из ваших ресурсов после нескольких часов кодирования или забыть закрыть ресурс, который вы только что добавили в свою программу, после случайного порыва вдохновения.
Код более читабелен, его легче изменять и поддерживать, и обычно он короче.