Исключения при инициализации
Поведение зависит от того, инициализируется класс или экземпляр.
Instance initializer
Исключение из экземплярного поля/блока прерывает конструктор. Объект не возвращается вызывающему коду.
final class Dictionary {
private final Map<String, String> values;
{
// unchecked ошибка прервёт создание экземпляра
if (System.getProperty("dict.mode") == null) {
throw new IllegalStateException("dict.mode is required");
}
}
Dictionary(Path path) throws IOException {
values = load(path); // checked exception объявлена конструктором
}
}Checked exception из инициализатора должна быть перехвачена или совместима с throws всех применимых конструкторов. Частично построенный объект может утечь только если код сам опубликовал this, чего делать не следует.
Static initializer
Static-блок не имеет throws, поэтому checked exception надо обработать или завернуть. Если инициализация завершается ошибкой:
final class Config {
static final Properties VALUES;
static {
try (InputStream in = Config.class.getResourceAsStream("/app.properties")) {
if (in == null) {
throw new IOException("resource not found");
}
var properties = new Properties();
properties.load(in);
VALUES = properties;
} catch (IOException error) {
throw new ExceptionInInitializerError(error);
}
}
}Первый активный пользователь класса получает ExceptionInInitializerError (если исходная причина не была Error). Класс считается ошибочно инициализированным; дальнейшие обращения тем же загрузчиком обычно приводят к NoClassDefFoundError.
Практический вывод
Не выполнять в static-инициализации сеть, подключение к БД и восстановимую работу. Лучше явный объект конфигурации/фабрика: тогда ошибку можно обработать, повторить и протестировать.
На собеседовании
Можно ли объявить throws у static-блока? Нет.
Что происходит с объектом при ошибке instance initializer? Конструктор не завершается, ссылка вызывающему коду не возвращается.
Можно ли повторно инициализировать класс после ошибки? Не тем же определением класса/загрузчиком обычным обращением.
Почему после первой ошибки виден NoClassDefFoundError? JVM помнит, что инициализация этого класса завершилась неуспешно.
Связано: Блоки инициализации, Работа с исключениями в Java, Class loader.