#java #try-catch
#java #попробуйте-поймайте
Вопрос:
В документации Oracle Java для новой на тот момент попытки с ресурсами показаны все примеры, в AutoCloseable
которых рассматриваемый try(...)
конструктор создается в:
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
...
}
Это кажется безопасным в том смысле, что даже если конструктор (в отличие от некоторого последующего события) уже получает ресурсы, которые должны быть освобождены, они будут освобождены, если мы столкнемся с исключением.
Однако, как, например, https://www.baeldung.com/java-try-with-resources указывает, что, начиная с Java 9, мы можем эффективно использовать конечные ресурсы, инициализированные перед try(...)
:
FileOutputStream fos = new FileOutputStream("output.txt");
try (fos) {
// do stuff
}
В аналогичном, предшествующем Java-9 ключе, я видел:
CustomStreamLikeThing connection = new CustomStreamLikeThing("db_like_thing");
// ... do some stuff that may throw a RuntimeException
try (CustomStreamLikeThing connection2=connection) {
// do some more stuff
}
Что кажется законным, но в то же время несколько ослабляет концепцию: если CustomStreamLikeThing
приобретает закрываемые ресурсы, они могут не быть освобождены.
Существуют ли рекомендации о том, когда конструктор AutoCloseable
должен быть заключен в try(...)
, или это дело вкуса (как в «вложить все те вещи, которые, я думаю, могут вызвать исключение»)?
Комментарии:
1. Является ли переменная полезной для вас вне блока try-with-resources? Если нет, то вам, вероятно, следует объявить его внутри.
Ответ №1:
Вы должны всегда использовать попытку с ресурсами, если можете.
Когда вы выделяете ресурс, вы должны убедиться, что ресурс всегда освобождается, когда вы закончите с ним. Это «всегда» обычно означает, что вы должны использовать try-finally
, чтобы любая ошибка, возникающая после выделения ресурса, не препятствовала освобождению ресурса.
Это означает, что если вы не используете try-with-resources, вам нужно использовать try-finally
, так почему я сказал всегда использовать try-with-resources?
Потому что вызов close()
тоже может завершиться неудачей, и try-with-resources обрабатывает это за вас с помощью функции, добавленной при добавлении try-with-resources, называемой подавленными исключениями.
Вы поймете важность этого, если вы когда-либо проклинали stacktrace неудачного close()
вызова, который заменил исключение, вызванное кодом, который должен был находиться в блоке try-with-resources, так что теперь вы не знаете, что на самом деле вызвало проблему.
Подавленные исключения — это часто упускаемая из виду функция, но она значительно экономит время, когда close
метод генерирует каскадное исключение. Только для этого всегда используйте try-with-resources.
Побочное преимущество: Try-with-resources содержит меньше кода, чем using try-finally
, и вы должны определенно использовать один из этих двух при обработке ресурсов.
Ответ №2:
Существуют ли рекомендации о том, когда конструктор автоматически закрываемого должен быть заключен в
try(...)
, или это дело вкуса (как в «вложить все те вещи, которые, я думаю, могут вызвать исключение»)?
Основное правило в кодировании таково: вы делаете все специально. Для этого требуется, чтобы вы понимали, что вы делаете, и что делает каждая «вещь» в вашем коде.
Значение: если у вас есть что-то, что нужно «закрыть», то правило здравого смысла таково: используйте try с ресурсами.
Но вы спрашиваете конкретно о тонком аспекте «создания объекта». Должно ли это входить в try()
, или это можно сделать раньше?!
Для этого: никаких жестких правил, если ваш исходный код работает семантически правильно. Таким образом, это сводится к этим двум аспектам:
- Вы разговариваете со своей командой и соглашаетесь с вашим «предпочтительным» стилем, а затем вся ваша команда следует этому. Другими словами: выберите стиль, который вы предпочитаете, а затем придерживайтесь этого: будьте последовательны!
- Вы смотрите на свои требования. Ваш
try(fos)
вариант работает с java9. Для людей, которым не нужно исправлять старые продукты, это нормально. Но для людей, которым регулярно приходится вносить «одно и то же изменение» в разные версии одного и того же продукта… эти люди могут предпочесть исходный код, который можно просто объединить в среду java8.
Ответ №3:
При наличии конструктора непосредственно в инструкции try-with-resources, подобной этой:
try (MyCloseable c = new MyCloseable()) {
// do stuff with c
} catch (IOException e) {
// handle exception
}
тогда блок catch будет вызван, когда любое из следующих действий вызовет исключение:
- конструктор
- тело попытки
close()
-метод.
Таким образом, у вас не может быть блока catch для каждого особого случая, когда, например, все бросают IOException
.
В то время как с помощью кода, подобного этому, вы можете немного больше контролировать обработку исключений:
MyCloseable c;
try {
c = new MyCloseable();
} catch (IOException e) {
// handle constructor exception
}
try (c) {
// do stuff with c
} catch (IOException e) {
// handle exception from close() or try-body
}
Но опять же, у вас все еще есть один блок catch для try-body и метода close в приведенном выше примере, и делать что-то подобное не рекомендуется:
// initialize c like in the second example
try (c) {
try {
// do stuff with c
} catch (IOException e) {
// handle try-body exception
}
} catch (IOException e) {
// handle close() exception
}
Ответ №4:
Предварительная java 9 — это просто хак, позволяющий автоматически закрывать работу с базовым объектом и закрывать и освобождать ресурсы.
В Java 9 это больше встроено в сам язык, чтобы разрешить закрытие ресурсов, инициализированных вне блока try with.
С точки зрения предпочтений, это больше касается того, будет ли он элегантным к тому времени, когда вы закончите попытку с блоком, если он станет слишком большим, может также переместить его.