Обязателен ли блок catch в конструкции try-with-resources в Java?

Ответ

Нет, блок catch не является обязательным.

Главное требование try-with-resources — ресурсы (объявленные в круглых скобках) должны реализовывать интерфейс AutoCloseable или Closeable. Блок catch можно опустить, если вы не планируете обрабатывать исключения на месте.

Пример без catch: Ресурс закроется автоматически, даже если возникнет исключение.

try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
    System.out.println(br.readLine());
} // fis и br будут закрыты здесь автоматически
// Исключение, если возникло, пробросится дальше

Пример с catch: Для локальной обработки исключений.

try (FileInputStream fis = new FileInputStream("file.txt")) {
    // работа с ресурсом
} catch (FileNotFoundException e) {
    System.err.println("Файл не найден: " + e.getMessage());
} catch (IOException e) {
    System.err.println("Ошибка ввода-вывода: " + e.getMessage());
}

Важно: Если исключение возникает как при закрытии ресурса (в неявном finally), так и в самом блоке try, исключение из блока try подавляет исключение от закрытия. Подавленное исключение можно получить через Throwable.getSuppressed().

Ответ 18+ 🔞

А, ну слушай, вот это реально частый затык, я тебе щас всё разжую. Многие думают, что try-with-resources — это как обычный try, и там обязательно нужен catch, а то всё развалится. Хуй там! Главная фишка этой штуки — ресурсы сами закрываются, а блок catch — это вообще опциональная хуйня, на твоё усмотрение.

Представь, ты взял в аренду бензопилу (это твой ресурс). try-with-resources — это как договор, где пункт жирным шрифтом: «Мужик, как ты оттуда ни выйди — с переломом или с дровами — мы у тебя эту пилу заберём и в сарай положим». За тобой приберутся в любом случае, епта. А будешь ли ты орать на месте, если дерево на ногу упадёт (catch), или просто побежишь в травмпункт, выкидывая исключение дальше — это уже твои личные проблемы.

Вот, смотри, как без catch — чисто, минималистично, по-пацански:

try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
    System.out.println(br.readLine());
} // Тут магия! fis и br закроются нахуй сами, даже если строчка выше всё ебнула.
// А если ебнула — исключение полетит выше, пусть кто-то другой голову ломает.

А вот если ты параноик и хочешь всё порешать на месте:

try (FileInputStream fis = new FileInputStream("file.txt")) {
    // делаем что-то рискованное
} catch (FileNotFoundException e) {
    System.err.println("Бля, файл-то сгинул! Куда делся, а? " + e.getMessage());
} catch (IOException e) {
    System.err.println("Ой, всё! Читать не могу, ёпта: " + e.getMessage());
}

Но вот тут, блядь, важный нюанс, про который все забывают, а потом чешут репу! Представь ситуацию: в блоке try у тебя случилась одна херня (например, данные кривые), а в момент автоматического закрытия ресурса — другая херня (например, поток хуёво закрылся). Так вот, ебать-колотить, система не завалится двумя ошибками сразу. Исключение из блока try будет главным, его ты и увидишь. А то, что случилось при закрытии, будет тихонько припрятано в карман (suppressed). Достать его можно, если очень надо, через Throwable.getSuppressed(). Вот такая, блядь, хитрая жопа у этой конструкции!

Короче, суть в чём: catch — не царь, а товарищ. Хочешь — лови, не хочешь — пусть летит. Главное, чтобы ресурс был AutoCloseable, а остальное — дело техники и твоего желания всё контролировать.