В try-with-resources один и тот же объект AutoCloseable указан в двух ресурсных позициях. Сколько раз Java вызовет его close()?
Метод close() будет вызван дважды — по одному разу для каждой ресурсной позиции. Java не сравнивает ресурсы по ссылке и не устраняет дублирование автоматически.
Вызовы выполняются в обратном порядке объявления ресурсов. Поэтому один и тот же объект может быть закрыт повторно, что безопасно только при корректной, обычно идемпотентной реализации close().
Try-with-resources появился в Java 7, чтобы автоматически освобождать ресурсы и не дублировать шаблонный код в блоках finally. Модель основана на ресурсных объявлениях: каждая позиция описывает отдельный этап управления временем жизни ресурса.
Язык не вводит специальное правило владения или дедупликации объектов. Если несколько ресурсных позиций содержат одну ссылку, они всё равно считаются отдельными ресурсами с независимыми операциями закрытия.
Повторное указание одной ссылки может появиться при работе с обёртками, адаптерами или сложной инициализацией ресурсов. Разработчик может ошибочно ожидать, что JVM заметит одинаковый объект и вызовет close() только один раз.
Это опасно для ресурса с неидемпотентным закрытием: второй вызов может привести к исключению, повреждению состояния или нежелательному повторному освобождению внешнего ресурса. Даже если второй вызов не приводит к ошибке, он может нарушить логику класса.
Каждая ресурсная позиция преобразуется компилятором в отдельную логику закрытия. При завершении блока Java проверяет ресурс на null, вызывает его close(), а затем переходит к следующему ресурсу в обратном порядке объявления.
Например:
Здесь сначала закрывается second, затем first, но обе переменные указывают на один объект. Поэтому будут выведены 1, затем 2: метод вызван дважды.
Если первый вызов close() выбросит исключение, Java всё равно пытается закрыть остальные ресурсные позиции. Исключение от последующего закрытия становится основным либо подавленным (suppressed) в соответствии с тем, какое исключение уже возникло при завершении конструкции.
Практическое правило: один объект не следует регистрировать в нескольких ресурсных позициях. Если это неизбежно, его close() должен быть безопасен при повторном вызове, а лучше явно разделить владение ресурсом между слоями.
Сервис получает соединение и передаёт его двум компонентам. Оба компонента добавляют полученную ссылку в собственные ресурсные области. После выполнения операции один компонент закрывает соединение, а затем второй пытается закрыть тот же объект.
Вариант с двумя ресурсными позициями прост и автоматически закрывает все позиции, но допускает двойной вызов close(). Вариант с ручным finally позволяет централизовать закрытие, однако увеличивает риск утечки при исключениях и усложняет анализ подавленных ошибок.
Предпочтительное решение — назначить одного владельца соединения и поместить ресурс только в его try-with-resources. Остальным компонентам передавать ресурс без права закрытия либо использовать отдельные объекты-обёртки. Это устраняет двойное закрытие и делает ответственность за освобождение явной.
null?Нет. Для каждой ресурсной позиции выполняется проверка на null, и вызов close() для такой позиции не производится. Отсутствие вызова связано именно со значением null, а не с устранением дублирующихся ссылок.
close() выбросит исключение, а второй вызов того же объекта завершится нормально?Оба вызова будут предприняты в обратном порядке ресурсных позиций. Исключение от неудачного закрытия будет передано дальше, если тело блока не выбросило более раннее исключение; нормальное завершение второго вызова это исключение не отменяет.
close() быть идемпотентным для любого AutoCloseable?Нет, интерфейс AutoCloseable не гарантирует идемпотентность закрытия. Конкретная реализация может считать повторный вызов ошибкой, поэтому код не должен без необходимости вызывать close() повторно и должен учитывать контракт используемого ресурса.