Почему Java не позволяет генерировать проверенное исключение из статического блока инициализации? Что послужило причиной такого дизайнерского решения?
Почему Java не позволяет генерировать проверенное исключение из статического блока инициализации?
Ответы (8)
Потому что невозможно обработать эти проверенные исключения в вашем источнике. У вас нет никакого контроля над процессом инициализации, и блоки static{} не могут быть вызваны из вашего источника, чтобы вы могли окружить их с помощью try-catch.
Поскольку вы не можете обработать любую ошибку, указанную проверенным исключением, было решено запретить создание статических блоков проверенных исключений.
Статический блок не должен генерировать исключения checked, но по-прежнему позволяет генерировать непроверенные исключения/исключения времени выполнения. Но в соответствии с вышеуказанными причинами вы также не сможете справиться с этим.
Подводя итог, можно сказать, что это ограничение не позволяет (или, по крайней мере, затрудняет) разработчику создавать что-то, что может привести к ошибкам, после которых приложение не сможет восстановиться.
static{ try { System.out.println("Test.enclosing_method()"); } catch (Exception e) { throw new NullPointerException(); } }
Я бросаю нулевой указатель в статический, это компилируется.
- person Ravisha; 09.04.2015
static { if(1 < 10) { throw new NullPointerException(); } }
- person Kosi2801; 15.04.2015
Вы можете обойти эту проблему, перехватив любое проверенное исключение и повторно сгенерировав его как непроверенное исключение. Этот непроверяемый класс исключений хорошо работает в качестве оболочки: 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);
}
}
}
catch (Exception e) {
вместо этого.
- person kevinarpe; 04.05.2015
System.exit(...)
(или эквивалент) — ваш единственный вариант,
- person Stephen C; 19.10.2015
Это должно выглядеть так (это не допустимый код 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 (который является интерфейсом со «скрытым» статическим инициализатором) должен был бы генерировать исключение или обрабатывать его — обработка исключений на интерфейсе? Лучше оставить как есть.
main
может генерировать проверенные исключения. Понятно, что с ними не справиться.
- person Mechanical snail; 15.01.2013
main()
, который печатает исключение с трассировкой стека в System.err
, а затем вызывает System.exit()
. В конце концов, ответ на этот вопрос, вероятно, таков: потому что так сказали разработчики Java.
- person kevinarpe; 29.01.2017
Почему Java не позволяет генерировать проверенное исключение из статического блока инициализации?
Технически это можно сделать. Однако проверяемое исключение должно быть перехвачено внутри блока.
Фактическое ограничение Java заключается в том, что проверенному исключению не разрешено распространяться за пределы блока.
Технически также возможно разрешить распространение исключения unchecked из блока статического инициализатора1. Но это действительно плохая идея делать это преднамеренно! Проблема в том, что сама JVM перехватывает непроверенное исключение, упаковывает его и повторно выдает как ExceptionInInitializerError
.
NB: это ExceptionInInitializerError
является 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 – вы можете перехватить его, зарегистрировав значение по умолчанию. uncaught обработчик исключений, но это не позволит вам восстановиться, потому что ваш основной поток не может запуститься.
3 - Если вы хотите восстановить неудавшиеся классы, вам нужно избавиться от загрузчика классов, который их загрузил.
Что послужило причиной такого дизайнерского решения?
Это сделано для того, чтобы защитить программиста от написания кода, который генерирует исключения, которые невозможно обработать... потому что у программиста нет возможности написать обработчик.
Как мы видели, исключение в статическом инициализаторе превращает типичное приложение в кирпич. Лучшее, что разработчики языка могут сделать, чтобы помочь программисту, это указать, что проверенный случай 1 является ошибкой компиляции. К сожалению, это нецелесообразно делать и для непроверенных исключений.
Итак, что вам делать, если ваш код должен генерировать исключения в статическом инициализаторе. В принципе, есть две альтернативы:
Если (полное!) восстановление из исключения внутри блока возможно, то сделайте это.
В противном случае реструктурируйте свой код, чтобы инициализация не происходила в статическом блоке инициализации (или в инициализаторах статических переменных). Поместите его в метод или конструктор, который можно вызывать из обычного потока.
Ознакомьтесь с спецификациями языка Java. : указано, что это ошибка времени компиляции, если статический инициализатор fail может завершиться внезапно с проверенным исключением.
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
Вывод: Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: whathappenswhenastaticblockthrowsanexception at Main.<clinit>(Main.java:6) Caused by: java.lang.ClassNotFoundException: whathappen...
- person Konrad Höffner; 08.06.2012
Поскольку никакой код, который вы пишете, не может вызвать статический блок инициализации, нет смысла выбрасывать проверенный exceptions
. Если бы это было возможно, что бы делал jvm, когда выбрасываются проверенные исключения? Runtimeexceptions
распространяются вверх.
Например: DispatcherServlet Spring (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());
}
Я также могу скомпилировать исключение проверенного исключения ....
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}