Pourquoi Java ne permet-il pas de lever une exception vérifiée à partir d'un bloc d'initialisation statique? Quelle était la raison de cette décision de conception?
java
exception
static-initializer
manquant
la source
la source
Réponses:
Parce qu'il n'est pas possible de gérer ces exceptions vérifiées dans votre source. Vous n'avez aucun contrôle sur le processus d'initialisation et les blocs static {} ne peuvent pas être appelés depuis votre source afin que vous puissiez les entourer de try-catch.
Étant donné que vous ne pouvez gérer aucune erreur indiquée par une exception vérifiée, il a été décidé d'interdire la levée de blocs statiques d'exceptions vérifiées.
Le bloc statique ne doit pas lancer d' exceptions vérifiées mais autorise toujours la levée d'exceptions non vérifiées / d'exécution. Mais selon les raisons ci-dessus, vous ne pourriez pas non plus les gérer.
Pour résumer, cette restriction empêche (ou du moins la rend plus difficile pour) le développeur de créer quelque chose qui peut entraîner des erreurs dont l'application ne pourrait pas récupérer.
la source
static { if(1 < 10) { throw new NullPointerException(); } }
Vous pouvez contourner le problème en interceptant toute exception vérifiée et en la renvoyant en tant qu'exception non vérifiée. Cette classe d'exception non contrôlée fonctionne bien comme un emballage:
java.lang.ExceptionInInitializerError
.Exemple de code:
la source
catch (Exception e) {
place.System.exit(...)
(ou équivalent) est votre seule option,Cela devrait ressembler à ceci (ce n'est pas du code Java valide)
mais comment annoncer où vous l'attrapez? Les exceptions vérifiées doivent être capturées. Imaginez quelques exemples qui peuvent initialiser la classe (ou non parce qu'elle est déjà initialisée), et juste pour attirer l'attention sur la complexité de ce qu'elle introduirait, je mets les exemples dans un autre initalizer statique:
Et une autre chose désagréable -
Imaginez que ClassA ait un initialiseur statique lançant une exception vérifiée: Dans ce cas, MyInterface (qui est une interface avec un initialiseur statique «caché») devrait lever l'exception ou la gérer - gestion des exceptions à une interface? Mieux vaut le laisser tel quel.
la source
main
peut lever des exceptions vérifiées. De toute évidence, ceux-ci ne peuvent pas être traités.main()
qui imprime l'exception avec la trace de la pile àSystem.err
, puis appelleSystem.exit()
. En fin de compte, la réponse à cette question est probablement: "parce que les concepteurs Java l'ont dit".Techniquement, vous pouvez le faire. Cependant, l'exception vérifiée doit être interceptée dans le bloc. Une exception vérifiée n'est pas autorisée à se propager hors du bloc.
Techniquement, il est également possible d'autoriser une exception non vérifiée à se propager à partir d'un bloc d'initialisation statique 1 . Mais c'est vraiment une mauvaise idée de le faire délibérément! Le problème est que la machine virtuelle Java elle-même intercepte l'exception non vérifiée, l'encapsule et la renvoie en tant que fichier
ExceptionInInitializerError
.NB: ce n'est
Error
pas une exception régulière. Vous ne devez pas tenter de récupérer.Dans la plupart des cas, l'exception ne peut pas être interceptée:
Il n'y a nulle part où vous pouvez placer un
try ... catch
dans ce qui précède pour attraper leExceptionInInitializerError
2 .Dans certains cas, vous pouvez l'attraper. Par exemple, si vous avez déclenché l'initialisation de la classe en appelant
Class.forName(...)
, vous pouvez placer l'appel dans untry
et attraper leExceptionInInitializerError
ou un suivantNoClassDefFoundError
.Cependant, si vous tentez de vous remettre d'un
ExceptionInInitializerError
problème, vous risquez de vous heurter à un barrage routier. Le problème est qu'avant de lancer l'erreur, la machine virtuelle Java marque la classe à l'origine du problème comme "échouée". Vous ne pourrez tout simplement pas l'utiliser. De plus, toutes les autres classes qui dépendent de la classe ayant échoué passeront également en état d'échec si elles tentent de s'initialiser. La seule façon d'avancer est de décharger toutes les classes ayant échoué. Cela pourrait être faisable pour le code 3 chargé dynamiquement , mais en général ce n'est pas le cas.1 - C'est une erreur de compilation si un bloc statique lève sans condition une exception non vérifiée.
2 - Vous pourrez peut- être l'intercepter en enregistrant un gestionnaire d'exceptions non interceptées par défaut, mais cela ne vous permettra pas de récupérer, car votre thread "principal" ne peut pas démarrer.
3 - Si vous vouliez récupérer les classes ayant échoué, vous devrez vous débarrasser du chargeur de classes qui les a chargées.
C'est pour protéger le programmeur de l'écriture de code qui lève des exceptions qui ne peuvent pas être gérées!
Comme nous l'avons vu, une exception dans un initialiseur statique transforme une application typique en brique. La meilleure chose que les concepteurs de langage pourraient faire est de traiter la casse cochée comme une erreur de compilation. (Malheureusement, il n'est pas pratique de le faire également pour les exceptions non vérifiées.)
OK, alors que devez-vous faire si votre code "a besoin" de lever des exceptions dans un initialiseur statique. Fondamentalement, il existe deux alternatives:
Si une récupération (complète!) De l'exception dans le bloc est possible, faites-le.
Sinon, restructurez votre code afin que l'initialisation ne se produise pas dans un bloc d'initialisation statique (ou dans les initialiseurs de variables statiques).
la source
Jetez un œil aux spécifications du langage Java : il est indiqué qu'il s'agit d'une erreur de compilation si l'initialiseur statique
échoue, ilest capable de se terminer brusquement avec une exception vérifiée.la source
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
Sortie: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...
Étant donné qu'aucun code que vous écrivez ne peut appeler un bloc d'initialisation statique, il n'est pas utile de lancer vérifié
exceptions
. Si c'était possible, que ferait le jvm lorsqu'une exception vérifiée est levée?Runtimeexceptions
se propagent.la source
Par exemple: le DispatcherServlet de Spring (org.springframework.web.servlet.DispatcherServlet) gère le scénario qui intercepte une exception vérifiée et lève une autre exception non vérifiée.
la source
Je suis capable de compiler en lançant une exception cochée également ....
la source