Почему Java не позволяет генерировать проверенное исключение из статического блока инициализации? Что послужило причиной этого дизайнерского решения?
java
exception
static-initializer
missingfaktor
источник
источник
Ответы:
Потому что невозможно обработать эти проверенные исключения в вашем источнике. У вас нет никакого контроля над процессом инициализации, и статические блоки {} не могут быть вызваны из вашего источника, чтобы вы могли окружить их try-catch.
Поскольку вы не можете обработать любую ошибку, указанную в проверенном исключении, было решено запретить выбрасывание статических блоков проверенных исключений.
Статический блок не должен выбрасывать проверенные исключения, но все же позволяет создавать непроверенные / исключительные ситуации времени выполнения. Но по вышеуказанным причинам вы также не сможете справиться с этим.
Подводя итог, можно сказать, что это ограничение не позволяет разработчику (или, по крайней мере, усложняет его) создавать что-то, что может привести к ошибкам, которые приложение не сможет восстановить.
источник
static { if(1 < 10) { throw new NullPointerException(); } }
Вы можете обойти проблему, перехватывая любое проверенное исключение и выбрасывая его как исключение без проверки. Это бесконтрольно класс исключения хорошо работает в качестве оболочки:
java.lang.ExceptionInInitializerError
.Образец кода:
источник
catch (Exception e) {
вместо.System.exit(...)
(или эквивалентно) - ваш единственный выбор,Это должно выглядеть следующим образом (это не допустимый код Java)
а как бы объявление где вы его ловили? Проверенные исключения требуют отлова. Представьте себе некоторые примеры, которые могут инициализировать класс (или нет, потому что он уже инициализирован), и просто для того, чтобы привлечь внимание к сложности, которую он представляет, я поместил примеры в другой статический инициализатор:
И еще одна неприятная вещь -
Вообразите, что у ClassA был статический инициализатор, выдававший проверенное исключение: в этом случае MyInterface (который является интерфейсом со «скрытым» статическим инициализатором) должен был бы выбросить исключение или обработать его - обработка исключений в интерфейсе? Лучше оставить все как есть.
источник
main
может бросить проверенные исключения. Очевидно, что с этим нельзя справиться.main()
который печатает исключение с трассировкой стекаSystem.err
, а затем вызываетSystem.exit()
. В конце концов, ответ на этот вопрос, вероятно, таков: «потому что Java-дизайнеры так сказали».Технически, вы можете сделать это. Однако проверенное исключение должно быть перехвачено в блоке. Проверенное исключение не может распространяться за пределы блока.
Технически, также возможно позволить неконтролируемому исключению распространяться из статического блока 1 инициализатора . Но это действительно плохая идея делать это сознательно! Проблема в том, что сама JVM перехватывает непроверенное исключение, оборачивает его и перебрасывает как
ExceptionInInitializerError
.NB: это
Error
не обычное исключение. Вы не должны пытаться оправиться от этого.В большинстве случаев исключение не может быть поймано:
Вы нигде не можете разместить
try ... catch
выше, чтобы пойматьExceptionInInitializerError
2 .В некоторых случаях вы можете поймать это. Например, если вы вызвали инициализацию класса с помощью вызова
Class.forName(...)
, вы можете заключить вызов в atry
и поймать либоExceptionInInitializerError
последующие, либоNoClassDefFoundError
.Однако, если вы попытаетесь оправиться от него,
ExceptionInInitializerError
вы можете столкнуться с контрольно-пропускным пунктом. Проблема заключается в том, что перед выдачей ошибки JVM помечает класс, вызвавший проблему, как «сбой». Вы просто не сможете его использовать. Кроме того, любые другие классы, которые зависят от отказавшего класса, также перейдут в сбойное состояние, если они попытаются инициализироваться. Единственный путь вперед - выгрузить все неудачные классы. Это может быть осуществимо для динамически загружаемого кода 3 , но в целом это не так.1 - Это ошибка компиляции, если статический блок безоговорочно генерирует непроверенное исключение.
2. Вы можете перехватить его, зарегистрировав обработчик необработанных исключений по умолчанию, но это не позволит вам восстановиться, потому что ваш «основной» поток не может запуститься.
3 - Если вы хотите восстановить сбойные классы, вам нужно избавиться от загрузчика классов, который их загрузил.
Он защищает программиста от написания кода, который генерирует исключения, которые не могут быть обработаны!
Как мы уже видели, исключение в статическом инициализаторе превращает типичное приложение в кирпич. Лучше всего подумать, что разработчики языка могут сделать, это рассматривать проверенный случай как ошибку компиляции. (К сожалению, это не практично делать для непроверенных исключений.)
Итак, что вы должны сделать, если ваш код «нуждается» в генерации исключений в статическом инициализаторе. В основном, есть две альтернативы:
Если (полное!) Восстановление из исключения внутри блока возможно, то сделайте это.
В противном случае реструктурируйте свой код так, чтобы инициализация не происходила в статическом блоке инициализации (или в инициализаторах статических переменных).
источник
Взгляните на спецификации языка Java : указано, что это ошибка времени компиляции, если статический
сбойинициализатораможетзавершиться внезапно с проверенным исключением.источник
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...
Поскольку ни один код, который вы пишете, не может вызывать статический блок инициализации, бесполезно бросать флажок
exceptions
. Если бы это было возможно, что бы сделал jvm, когда выбрасываются проверенные исключения?Runtimeexceptions
распространяются вверх.источник
Например: Spring DispatcherServlet (org.springframework.web.servlet.DispatcherServlet) обрабатывает сценарий, который перехватывает проверенное исключение и выдает другое непроверенное исключение.
источник
Я могу скомпилировать бросая проверенное исключение также ....
источник