Почему Java не позволяет бросить проверенное исключение из блока статической инициализации? В чем причина этого дизайнерского решения?
Почему Java не позволяет выбросить проверенное исключение из блока статической инициализации?
Ответ 1
Потому что невозможно обработать эти проверенные исключения в вашем источнике. У вас нет никакого контроля над процессом инициализации, и статические {} блоки не могут быть вызваны из вашего источника, чтобы вы могли окружить их try-catch.
Поскольку вы не можете обработать какую-либо ошибку, указанную проверенным исключением, было решено запретить металирование проверенных исключений статических блоков.
Статический блок не должен выделять отмеченные исключения, но все же позволяет исключать исключенные/исключенные исключения. Но, согласно вышеприведенным причинам, вы также не сможете справиться с этим.
Подводя итог, это ограничение предотвращает (или, по крайней мере, усложняет) разработчика создание чего-то, что может привести к ошибкам, из которых приложение не сможет восстановить.
Ответ 2
Вы можете обойти проблему, поймав любое проверенное исключение и перевернув его как исключенное исключение. Этот неконтролируемый класс исключений хорошо работает как оболочка: java.lang.ExceptionInInitializerError
.
Пример кода:
protected static class _YieldCurveConfigHelperSingleton {
public static YieldCurveConfigHelper _staticInstance;
static {
try {
_staticInstance = new YieldCurveConfigHelper();
}
catch (IOException | SAXException | JAXBException e) {
throw new ExceptionInInitializerError(e);
}
}
}
Ответ 3
Это должно было бы выглядеть так (это не действительный Java-код)
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
но как объявление, где вы его поймаете? Проверенные исключения требуют лова. Представьте себе некоторые примеры, которые могут инициализировать класс (или могут не потому, что он уже инициализирован), и просто обратить внимание на сложность, которую он представит, я помещаю примеры в другой статичный инициализатор:
static {
try {
ClassA a = new ClassA();
Class<ClassB> clazz = Class.forName(ClassB.class);
String something = ClassC.SOME_STATIC_FIELD;
} catch (Exception oops) {
// anybody knows which type might occur?
}
}
И еще одна неприятная вещь -
interface MyInterface {
final static ClassA a = new ClassA();
}
Представьте, что у ClassA был статический инициализатор, бросающий проверенное исключение: в этом случае MyInterface (который является интерфейсом со "скрытым" статическим инициализатором) должен был бы выбросить исключение или обработать его - обработку исключений на интерфейсе? Лучше оставить это как есть.
Ответ 4
Почему Java не позволяет генерировать проверенное исключение из статического блока инициализации?
Технически, вы можете сделать это. Однако проверенное исключение должно быть перехвачено в блоке. Проверенное исключение не может распространяться за пределы блока.
Технически, также возможно позволить неконтролируемому исключению распространяться из статического блока 1 инициализатора. Но это действительно плохая идея делать это сознательно! Проблема заключается в том, что сама JVM перехватывает непроверенное исключение, оборачивает его и сбрасывает как ExceptionInInitializerError
.
NB: это Error
не обычное исключение. Вы не должны пытаться оправиться от этого.
В большинстве случаев исключение не может быть поймано:
public class Test {
static {
int i = 1;
if (i == 1) {
throw new RuntimeException("Bang!");
}
}
public static void main(String[] args) {
try {
// stuff
} catch (Throwable ex) {
// This won't be executed.
System.out.println("Caught " + ex);
}
}
}
$ java Test
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Bang!
at Test.<clinit>(Test.java:5)
Выше нигде нельзя разместить try... catch
выше, чтобы поймать ExceptionInInitializerError
2.
В некоторых случаях вы можете поймать это. Например, если вы инициировали инициализацию класса, вызвав Class.forName(...)
, вы можете заключить этот вызов в try
и перехватить либо ExceptionInInitializerError
либо последующий NoClassDefFoundError
.
Однако, если вы попытаетесь восстановиться после ExceptionInInitializerError
вы можете столкнуться с препятствием. Проблема в том, что перед выдачей ошибки JVM помечает класс, который вызвал проблему, как "сбой". Вы просто не сможете его использовать. Кроме того, любые другие классы, которые зависят от отказавшего класса, также перейдут в сбойное состояние, если они попытаются инициализироваться. Единственный путь вперед - выгрузить все неудачные классы. Это может быть осуществимо для динамически загружаемого кода 3 но в целом это не так.
1 - Это ошибка компиляции, если статический блок безоговорочно генерирует непроверенное исключение.
2. Вы можете перехватить его, зарегистрировав обработчик необработанных исключений по умолчанию, но это не позволит вам восстановиться, потому что ваш "основной" поток не может запуститься.
3 - Если вы хотите восстановить сбойные классы, вам нужно избавиться от загрузчика классов, который их загрузил.
Что послужило причиной этого дизайнерского решения?
Он защищает программиста от написания кода, который генерирует исключения, которые не могут быть обработаны!
Как мы уже видели, исключение в статическом инициализаторе превращает типичное приложение в кирпич. Лучше всего подумать, что разработчики языка могут сделать, это рассматривать проверенный случай как ошибку компиляции. (К сожалению, это не практично делать для непроверенных исключений.)
Итак, что вы должны сделать, если ваш код "нуждается" в генерации исключений в статическом инициализаторе. В основном, есть две альтернативы:
-
Если (полное!) Восстановление из исключения внутри блока возможно, то сделайте это.
-
В противном случае реструктурируйте свой код так, чтобы инициализация не происходила в статическом блоке инициализации (или в инициализаторах статических переменных).
Ответ 5
Взгляните на Спецификации языка Java: указано, что это ошибка времени компиляции, если статический инициализатор failed может завершиться внезапно с проверенным исключением.
Ответ 6
Так как никакой код, который вы пишете, может вызвать статический блок инициализации, нецелесообразно выбрасывать отмеченный exceptions
. Если бы это было возможно, что бы сделал jvm, когда были выбраны проверенные исключения? Runtimeexceptions
распространяются вверх.
Ответ 7
Например: Spring DispatcherServlet (org.springframework.web.servlet.DispatcherServlet) обрабатывает сценарий, который перехватывает проверенное исключение и выдает другое непроверенное исключение.
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
Ответ 8
Я могу скомпилировать бросок проверенного исключения.......
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}