Как обрабатывать исключения с помощью оператора try-with-resource
Конструкция Java try with resources, также известная как Java try-with-resources, представляет собой механизм обработки исключений, который может автоматически закрывать ресурсы, такие как Java InputStream или JDBC Connection, когда вы сделано с ними.
Oracle добавила конструкцию try с ресурсами в язык Java в 2011 году, чтобы гарантировать, что такие объекты, как сетевые сокеты, подключения к базе данных и ссылки на файлы и папки, будут корректно завершены после их использования. Если разработчик не закроет эти ресурсы после того, как разработчик откроет для них дескриптор, это может привести к утечкам памяти, запуску предотвратимых процедур сборки мусора и нагрузке процессора сервера.
· До Java 7
· Как использовать оператор try-with-resource?
· Почему я должен использовать оператор try-with-resource?
· Пример
· Подавленные исключения
· Преимущества использования try-with-resources
· try-with-resources With Multiple Resources
· Java 9 — эффективно Окончательные переменные
До Java 7
В Java, если вы используете такой ресурс, как потоки ввода или вывода, вам всегда нужно закрывать его после использования. Он также может генерировать исключения, поэтому он должен находиться в блоке try-catch. Закрытие должно быть в блоке finally. По крайней мере, так было до Java 7. У этого есть несколько недостатков:
- Вам нужно будет проверить, является ли ваш ресурс null перед его закрытием.
- Само закрытие может вызывать исключения, поэтому ваш finally должен был содержать еще один try-catch.
- Программисты склонны забывать закрывать свои ресурсы
визит попробовать-поймать-наконец-то
Как использовать оператор try-with-resource?
Первоначально он был представлен в Java 7, и вся его идея заключалась в том, что разработчику не нужно беспокоиться об управлении ресурсами, которые они используют только в одном блоке try-catch-finally. Это достигается за счет устранения необходимости в блоках finally, которые разработчики использовали на практике только для закрытия ресурсов.
В Java оператор try-with-resources — это оператор try, который объявляет один или несколько ресурсов. Ресурс представляет собой объект, который необходимо закрыть после завершения программы. Когда выполнение покидает блок try-with-resources, любой ресурс, открытый в блоке try-with-resources, автоматически закрывается, независимо от того, генерируются ли какие-либо исключения из внутри блока try-with-resources или при попытке закрыть ресурсы.
Чтобы использовать языковую функцию «попробуй с ресурсами» в Java, применяются следующие правила:
- Все объекты, управляемые оператором try with resources, должны реализовывать интерфейс AutoCloseable.
- Несколько объектов AutoCloseable могут быть созданы в блоке try with resources в Java.
- Объекты, объявленные в операторе try with resources, имеют область действия в блоке try, но не в блоках catch и finally.
- Метод close() объектов, объявленных в блоке try with resources, вызывается независимо от того, выдается ли исключение во время выполнения.
- Если в методе close() генерируется исключение, оно может быть классифицировано как подавленное исключение.
Блоки catch и finally по-прежнему можно использовать в блоке try-with-resource, и они будут работать так же, как и в обычном блоке try.
Ресурс, на который ссылается объект AutoCloseable, всегда будет закрыт, если используется инструкция try with resources, и устраняются потенциальные утечки памяти, обычно вызываемые неправильным распределением ресурсов.
Синтаксис
try(declare resources here) { // use resources } catch(FileNotFoundException e) { // exception handling }
Использование попытки с ресурсами
Проще говоря, для автоматического закрытия ресурс должен быть объявлен и инициализирован внутри try:
try (PrintWriter writer = new PrintWriter(new File("test.txt"))) { writer.println("Hello World"); }
Замена try-catch-finally на try-with-resources
Простой и очевидный способ использования новой функциональности try-with-resources — заменить традиционный и многословный блок try-catch-finally.
Давайте сравним следующие примеры кода.
Первый типичный блок try-catch-finally :
Scanner scanner = null; try { scanner = new Scanner(new File("test.txt")); while (scanner.hasNext()) { System.out.println(scanner.nextLine()); } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (scanner != null) { scanner.close(); } }"); background-repeat: no-repeat; background-position: center center; background-color: rgb(99, 177, 117);">Copy
А вот новое очень лаконичное решение с использованием try-with-resources:
try (Scanner scanner = new Scanner(new File("test.txt"))) { while (scanner.hasNext()) { System.out.println(scanner.nextLine()); } } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); }
Разница между попыткой и попыткой с ресурсом
Когда дело доходит до исключений, есть разница между блоком try-catch-finally и блоком try-with-resources. Если исключение выдается как в блоке try, так и в блоке finally, метод возвращает исключение, сгенерированное в блоке finally.
Для попытки с ресурсами, если в блоке попытки и в инструкции попытки с ресурсами возникает исключение, метод возвращает исключение, созданное в блоке попытки. Исключения, выдаваемые блоком try-with-resources, подавляются, т. е. мы можем сказать, что блок try-with-resources генерирует подавленные исключения.
Почему я должен использовать оператор try-with-resource?
Оператор try-with-resources обеспечивает закрытие каждого ресурса в конце выполнения оператора. Если мы не закроем ресурсы, это может привести к утечке ресурсов, а также программа может исчерпать доступные ей ресурсы. это то, что происходит в основном, когда вы используете блок try-catch-finally
До Java SE 7 можно было использовать блок finally, чтобы убедиться, что ресурс закрыт независимо от того, завершается ли инструкция try нормально или внезапно. В следующем примере вместо оператора try-with-resources используется блок finally:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException { FileReader fr = new FileReader(path); BufferedReader br = new BufferedReader(fr); try { return br.readLine(); } finally { br.close(); fr.close(); } }
Однако в этом примере может быть утечка ресурсов. Программа должна делать больше, чем полагаться на сборщик мусора (GC), чтобы освободить память ресурса, когда она закончила с ним работать. Программа также должна вернуть ресурс обратно в операционную систему, обычно вызывая метод ресурса close
. Однако если программа не сделает этого до того, как GC вернет ресурс, информация, необходимая для освобождения ресурса, будет потеряна. Произошла утечка ресурса, который операционная система до сих пор считает используемым.
В этом примере, если метод readLine выдает исключение, а оператор br.close() в блоке finally выдает исключение, тогда произошла утечка FileReader. Поэтому используйте оператор try-with-resources вместо блока finally, чтобы закрыть ресурсы вашей программы.
Пример
В следующем примере считывается первая строка из файла. Он использует экземпляры FileReader и BufferedReader для чтения данных из файла. FileReader и BufferedReader — это ресурсы, которые необходимо закрыть после завершения работы программы:
import java.io.*; class Main { public static void main(String[] args) { String line; try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) { while ((line = br.readLine()) != null) { System.out.println("Line =>"+line); } } catch (IOException e) { System.out.println("IOException in try block =>" + e.getMessage()); } } }
В этом примере ресурсы, объявленные в операторе try-with-resources, являются BufferedReader. Операторы объявления этого ресурса появляются в круглых скобках сразу после ключевого слова try. Классы BufferedReader в Java SE 7 и более поздних версиях реализуют интерфейс java.lang.AutoCloseable. Поскольку экземпляры BufferedReader объявлены в инструкции try-with-resource, они будут закрыты независимо от того, завершится ли инструкция try нормально или внезапно. (в результате того, что метод BufferedReader.readLine() выдает IOException).
Подавленные исключения
Если блок try выдает исключение, а одно или несколько исключений выбрасываются блоком try-with-resources, исключения, создаваемые блоком try-with-resources, подавляются. Другими словами, мы можем сказать, что исключения, генерируемые попыткой использования ресурсов, являются подавленными исключениями.
Вы можете получить эти исключения, используя метод getSuppress() класса Throwable.
Пример:
В приведенном выше примере исключения могут быть вызваны оператором try-with-resources, когда:
- Файл test.txt не найден.
- Закрытие объекта BufferedReader.
Исключение также может быть вызвано блоком try, так как чтение файла может завершиться ошибкой по многим причинам в любое время.
Если исключения создаются как из блока try, так и из инструкции try-with-resources, создается исключение из блока try и исключение из оператор try-with-resources подавляется.
Получение подавленных исключений
В Java 7 и более поздних версиях подавленные исключения можно получить, вызвав метод Throwable.getSuppressed() из исключения, созданного блоком try.
getSuppress() возвращает массив, содержащий все исключения, которые были подавлены оператором try-with-resources. Если никакие исключения не были подавлены или подавление отключено, возвращается пустой массив.
Мы получаем подавленные исключения в блоке catch.
catch(IOException e) { System.out.println("Thrown exception=>" + e.getMessage()); Throwable[] suppressedExceptions = e.getSuppressed(); for (int i=0; i<suppressedExceptions.length; i++) { System.out.println("Suppressed exception=>" + suppressedExceptions[i]); } }
Преимущества использования try-with-resources
Вот преимущества использования try-with-resources:
- Более читаемый код и простой в написании.
- Автоматическое управление ресурсами.
- Количество строк кода уменьшено.
- Когда несколько ресурсов открываются в попытке с ресурсами, они закрываются в обратном порядке, чтобы избежать проблем с зависимостями.
finally block не требуется для закрытия ресурса
До того, как Java 7 представила эту функцию, нам приходилось использовать блок finally, чтобы убедиться, что ресурс закрыт, чтобы избежать утечек ресурсов.
Вот программа, похожая на Пример 1. Однако в этой программе мы использовали блок finally для закрытия ресурсов.
Пример. Закрытие ресурса с помощью блока finally
import java.io.*; class Main { public static void main(String[] args) { BufferedReader br = null; String line; try { System.out.println("Entering try block"); br = new BufferedReader(new FileReader("test.txt")); while ((line = br.readLine()) != null) { System.out.println("Line =>"+line); } } catch (IOException e) { System.out.println("IOException in try block =>" + e.getMessage()); } finally { System.out.println("Entering finally block"); try { if (br != null) { br.close(); } } catch (IOException e) { System.out.println("IOException in finally block =>"+e.getMessage()); } } } }
Вывод
Entering try block Line =>line from test.txt file Entering finally block
Как видно из приведенного выше примера, использование блока finally для очистки ресурсов усложняет код.
Обратите внимание на блок try…catch в блоке finally? Это связано с тем, что IOException также может возникнуть при закрытии экземпляра BufferedReader внутри этого блока finally, поэтому оно также перехватывается и обрабатывается.
Оператор try-with-resources осуществляет автоматическое управление ресурсами. Нам не нужно явно закрывать ресурсы, поскольку JVM автоматически закрывает их. Это делает код более читабельным и легким для написания.
попытка с ресурсами с несколькими ресурсами
Мы можем просто объявить несколько ресурсов в блоке try-with-resources, разделив их точкой с запятой:
try (Scanner scanner = new Scanner(new File("testRead.txt")); PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) { while (scanner.hasNext()) { writer.print(scanner.nextLine()); } }
Java 9 — эффективные окончательные переменные
До Java 9 мы могли использовать свежие переменные только внутри блока try-with-resources :
try (Scanner scanner = new Scanner(new File("testRead.txt")); PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) { // omitted }"); background-repeat: no-repeat; background-position: center center; background-color: rgb(99, 177, 117);">Copy
Как показано выше, это было особенно многословно при объявлении нескольких ресурсов. Начиная с Java 9 и как часть JEP 213, теперь мы можем использовать final или даже эффективно final переменные внутри блока try-with-resources :
final Scanner scanner = new Scanner(new File("testRead.txt")); PrintWriter writer = new PrintWriter(new File("testWrite.txt")) try (scanner;writer) { // omitted }"); background-repeat: no-repeat; background-position: center center; background-color: rgb(99, 177, 117);">Copy
Проще говоря, переменная является окончательной, если она не изменяется после первого присваивания, даже если она явно не помечена как final.
Как показано выше, переменная scanner объявляется final явным образом, поэтому мы можем использовать ее с блоком try-with-resources . Хотя переменная writer не является явным образом final, она не изменяется после первого присваивания. Таким образом, мы также можем использовать переменную writer.
Приятного обучения 😃
Присоединяйтесь к группе Mouad Oumous Java WhatsApp JOIN
Присоединяйтесь к Telegram-каналу Mouad Oumous JOIN
Поддержите нашу публикацию, подписавшись на нее