Что происходит при закрытии ресурсов в блоке finally в Java
Что проверяют
Знаете ли вы про потерю исходного исключения и понимаете ли, зачем появилась конструкция с автозакрытием.
Как отвечать
Блок гарантированного выполнения действительно выполнится, и ресурс закроется. Но у ручного закрытия там есть три проблемы.
1. Потеря исходного исключения, главная. Если тело бросило одно исключение, а закрытие бросило другое, наружу уйдёт второе, а первое исчезнет бесследно. То есть настоящая причина сбоя теряется, а вы видите ошибку закрытия потока.
2. Проверка на пустую ссылку. Если ресурс не успел создаться, переменная останется пустой, и обращение к ней в блоке закрытия даст ещё одно исключение поверх исходного.
3. Вложенность. Два ресурса требуют двух уровней конструкции, потому что закрытие первого может упасть и помешать закрыть второй.
Отсюда получается громоздкая и легко ломающаяся конструкция, которую надо писать в каждом месте одинаково.
Именно это и решает конструкция с автоматическим закрытием:
try (var in = new FileInputStream(src);
var out = new FileOutputStream(dst)) {
in.transferTo(out);
}Здесь закрытие происходит автоматически, в порядке, обратном объявлению, а самое важное: исключение из тела остаётся основным, а исключение из закрытия становится подавленным и доступно отдельным методом. Настоящая причина не теряется, и обе ошибки видны.
Дополнительно стоит упомянуть родственную ошибку в том же блоке: возврат значения из него. Он перекрывает и возврат из тела, и летящее исключение, из-за чего сбой пропадает молча. То же относится к прерыванию цикла.
Практический вывод: ручное закрытие в блоке гарантированного выполнения оправдано только там, где ресурс не поддерживает автоматическое закрытие.
Где спотыкаются
Не знают про потерю исходного исключения.
Забывают проверку на пустую ссылку.
Пишут возврат значения в блоке гарантированного выполнения.
Закрывают ресурс вручную внутри конструкции с автозакрытием и получают двойное закрытие.