¿Por qué Java no permite lanzar una excepción marcada desde un bloque de inicialización estática? ¿Cuál fue la razón detrás de esta decisión de diseño?
java
exception
static-initializer
missingfaktor
fuente
fuente
Respuestas:
Porque no es posible manejar estas excepciones marcadas en su fuente. No tiene ningún control sobre el proceso de inicialización y los bloques estáticos {} no se pueden invocar desde su fuente para que pueda rodearlos con try-catch.
Como no puede manejar ningún error indicado por una excepción marcada, se decidió no permitir el lanzamiento de bloques estáticos de excepciones marcadas.
El bloque estático no debe arrojar excepciones comprobadas, pero aún permite que se arrojen excepciones no verificadas / en tiempo de ejecución. Pero de acuerdo con las razones anteriores, tampoco podrá manejarlos.
Para resumir, esta restricción impide (o al menos dificulta) que el desarrollador construya algo que pueda generar errores de los que la aplicación no podrá recuperarse.
fuente
static { if(1 < 10) { throw new NullPointerException(); } }
Puede solucionar el problema detectando cualquier excepción marcada y volviéndola a lanzar como una excepción no verificada. Esta clase de excepción sin control funciona bien como un envoltorio:
java.lang.ExceptionInInitializerError
.Código de muestra:
fuente
catch (Exception e) {
lugar.System.exit(...)
(o equivalente) es su única opción,Tendría que tener este aspecto (este no es un código Java válido)
pero ¿cómo sería el anuncio donde lo atrapas? Las excepciones marcadas requieren captura. Imagine algunos ejemplos que pueden inicializar la clase (o no porque ya está inicializada), y solo para llamar la atención sobre la complejidad que introduciría, puse los ejemplos en otro inicializador estático:
Y otra cosa desagradable ...
Imagine ClassA tenía un inicializador estático que arrojaba una excepción marcada: en este caso, MyInterface (que es una interfaz con un inicializador estático 'oculto') tendría que lanzar la excepción o manejarla: ¿manejo de excepción en una interfaz? Mejor déjalo como está.
fuente
main
puede lanzar excepciones marcadas. Obviamente, esos no pueden ser manejados.main()
que imprime la excepción con el seguimiento de la pila ySystem.err
luego llamaSystem.exit()
. Al final, la respuesta a esta pregunta es probablemente: "porque los diseñadores de Java lo dijeron".Técnicamente, puedes hacer esto. Sin embargo, la excepción marcada debe quedar atrapada dentro del bloque. Una excepción marcada no puede propagarse fuera del bloque.
Técnicamente, también es posible permitir que una excepción no verificada se propague desde un bloque inicializador estático 1 . ¡Pero es una muy mala idea hacer esto deliberadamente! El problema es que la propia JVM detecta la excepción no verificada, la envuelve y la vuelve a lanzar como a
ExceptionInInitializerError
.NB: esa no es una
Error
excepción regular. No debe intentar recuperarse de él.En la mayoría de los casos, no se puede detectar la excepción:
No hay ningún lugar donde pueda colocar un
try ... catch
en el anterior para atrapar elExceptionInInitializerError
2 .En algunos casos puedes atraparlo. Por ejemplo, si activó la inicialización de la clase llamando
Class.forName(...)
, puede encerrar la llamada en untry
y capturar elExceptionInInitializerError
o el siguienteNoClassDefFoundError
.Sin embargo, si intenta recuperarse de una
ExceptionInInitializerError
, es probable que se encuentre con un obstáculo. El problema es que antes de arrojar el error, la JVM marca la clase que causó el problema como "fallida". Simplemente no podrás usarlo. Además, cualquier otra clase que dependa de la clase fallida también entrará en estado fallido si intentan inicializarse. El único camino a seguir es descargar todas las clases fallidas. Eso podría ser factible para el código 3 cargado dinámicamente , pero en general no lo es.1 - Es un error de compilación si un bloque estático genera incondicionalmente una excepción no verificada.
2 - Es posible que pueda interceptarlo registrando un controlador de excepción no capturado predeterminado, pero eso no le permitirá recuperarse, porque su hilo "principal" no puede iniciarse.
3 - Si quisieras recuperar las clases fallidas, deberías deshacerte del cargador de clases que las cargó.
¡Es para proteger al programador de escribir código que arroja excepciones que no se pueden manejar!
Como hemos visto, una excepción en un inicializador estático convierte una aplicación típica en un ladrillo. Lo mejor que los diseñadores de lenguaje podrían hacer es tratar el caso marcado como un error de compilación. (Desafortunadamente, tampoco es práctico hacer esto para excepciones no verificadas).
Bien, entonces, ¿qué debe hacer si su código "necesita" lanzar excepciones en un inicializador estático? Básicamente, hay dos alternativas:
Si es posible (¡completo!) La recuperación de la excepción dentro del bloque, entonces hazlo.
De lo contrario, reestructura tu código para que la inicialización no ocurra en un bloque de inicialización estática (o en los inicializadores de variables estáticas).
fuente
Echar un vistazo a las especificaciones Lenguaje Java : se afirma que se trata de un error de tiempo de compilación si inicializador estático
noes capaz de completar bruscamente con una excepción comprobada.fuente
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
Salida: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...
Como ningún código que escriba puede llamar al bloque de inicialización estático, no es útil lanzarlo marcado
exceptions
. Si fuera posible, ¿qué haría el jvm cuando se lanzan excepciones marcadas?Runtimeexceptions
se propagan hacia arriba.fuente
Por ejemplo: Spring's DispatcherServlet (org.springframework.web.servlet.DispatcherServlet) maneja el escenario que captura una excepción marcada y arroja otra excepción no marcada.
fuente
Puedo compilar lanzando una excepción marcada también ...
fuente