Допустима ли для вызывающего кода ссылка на объект, если его конструктор завершился исключением?
Обычно нет: выражение создания объекта завершается исключением и не возвращает вызывающему коду ссылку на успешно созданный объект. Однако ссылка может стать доступной, если конструктор успел передать this во внешний объект, статическое поле, другой поток или callback.
Такой объект считается частично инициализированным. Java не откатывает автоматически изменения внешнего состояния, выполненные до исключения.
Модель конструкторов Java разделяет успешное завершение создания объекта и аварийное завершение. Это позволяет вызывающему коду либо получить ссылку на объект, либо обработать исключение, не получая обычным способом ссылку на неуспешный результат.
При этом создание объекта не является транзакцией: виртуальная машина не отменяет автоматически действия конструктора, например запись в статическое поле, регистрацию обработчика или запуск фоновой задачи. Ответственность за безопасную инициализацию и очистку внешних ресурсов остаётся на разработчике.
Если конструктор выбросил исключение, дальнейший код после операции создания не получает ссылку через результат этой операции. Но побочные эффекты конструктора уже могли произойти, а переданная наружу ссылка может указывать на объект с незаполненными полями и невыполненными инвариантами.
Особенно опасно передавать this из конструктора: другой код может вызвать методы объекта до завершения его инициализации. Это приводит к некорректному состоянию, гонкам и трудно воспроизводимым ошибкам.
При создании объекта JVM выделяет память, устанавливает значения полей по умолчанию, выполняет инициализацию полей и блоков, затем конструкторы классов. Если любой из этих этапов завершается исключением, операция создания завершается аварийно, а ссылка не возвращается как результат выражения.
Исключение из конструктора распространяется обычным образом: его можно перехватить на внешнем уровне, если тип подходит обработчику. Проверяемое исключение должно быть объявлено конструктором в throws или обработано внутри него.
Ключевое исключение из правила возникает при утечке this:
Переменная leaked получает ссылку до выброса исключения, поэтому после перехвата исключения она не равна null. Однако это не означает, что объект корректно создан: конструктор мог не инициализировать часть полей.
Безопаснее не передавать this наружу из конструктора. Регистрацию объекта, запуск потоков и публикацию в общие структуры обычно выполняют после успешного завершения конструктора, например в фабричном методе или отдельном этапе start.
Если конструктор успел захватить ресурс, он должен гарантировать его освобождение при ошибке инициализации. Для этого применяют локальный try-finally или try-with-resources; полагаться на автоматическое уничтожение объекта нельзя.
Класс подключается к внешнему сервису в конструкторе, регистрирует себя в глобальном реестре, а затем падает при загрузке конфигурации. В результате обычный вызывающий код получает исключение, но реестр уже содержит ссылку на неготовый объект, а сетевое соединение может остаться открытым.
Вариант с публикацией this в начале конструктора прост, но небезопасен: объект виден до завершения инициализации. Вариант с запуском и регистрацией в самом конструкторе удобен для пользователей класса, но усложняет откат при частичной ошибке.
Предпочтительно использовать фабричный метод: сначала создать и полностью проверить объект, закрыть временные ресурсы при ошибке, а затем зарегистрировать готовый экземпляр. Это уменьшает окно публикации и позволяет явно контролировать порядок инициализации; цена решения — дополнительный метод создания и более сложный жизненный цикл.
Гарантирует ли исключение из конструктора, что деструктор или другой метод очистки автоматически освободит ресурсы?
Нет. Java не вызывает пользовательский метод очистки только потому, что конструктор завершился исключением. Если ресурс был захвачен до точки сбоя, очистку нужно организовать явно внутри конструктора либо передать владение объекту, который управляет ресурсом через try-with-resources.
Может ли ссылка на частично созданный объект появиться без явного присваивания this в статическое поле?
Да. Конструктор может передать this в callback, коллекцию, другой объект или новый поток. Любой такой путь публикует ссылку наружу до завершения инициализации, даже если прямого присваивания статическому полю нет.
Что произойдёт с полями объекта, если конструктор завершился исключением?
Они могут остаться в состоянии, достигнутом к моменту сбоя: часть полей будет иметь значения по умолчанию, часть — значения после инициализации. С точки зрения обычного вызывающего кода объект не возвращается, но при утечке this другой код сможет наблюдать это промежуточное состояние.