Warum erlaubt Java nicht, eine aktivierte Ausnahme aus einem statischen Initialisierungsblock auszulösen? Was war der Grund für diese Designentscheidung?
java
exception
static-initializer
fehlender Faktor
quelle
quelle
Antworten:
Weil es nicht möglich ist, diese geprüften Ausnahmen in Ihrer Quelle zu behandeln. Sie haben keine Kontrolle über den Initialisierungsprozess und statische {} Blöcke können nicht von Ihrer Quelle aufgerufen werden, sodass Sie sie mit try-catch umgeben können.
Da Sie keinen Fehler behandeln können, der durch eine aktivierte Ausnahme angezeigt wird, wurde beschlossen, das Auslösen von statischen Blöcken für aktivierte Ausnahmen nicht zuzulassen.
Der statische Block darf keine aktivierten Ausnahmen auslösen, ermöglicht jedoch weiterhin das Auslösen von nicht aktivierten / Laufzeitausnahmen. Aber aus den oben genannten Gründen wären Sie auch nicht in der Lage, damit umzugehen.
Zusammenfassend lässt sich sagen, dass diese Einschränkung den Entwickler daran hindert (oder zumindest erschwert), etwas zu erstellen, das zu Fehlern führen kann, von denen die Anwendung keine Wiederherstellung durchführen kann.
quelle
static { if(1 < 10) { throw new NullPointerException(); } }
Sie können das Problem umgehen, indem Sie eine aktivierte Ausnahme abfangen und als nicht aktivierte Ausnahme erneut auslösen. Diese nicht aktivierte Ausnahmeklasse eignet sich gut als Wrapper :
java.lang.ExceptionInInitializerError
.Beispielcode:
quelle
catch (Exception e) {
stattdessen.System.exit(...)
(oder gleichwertig) ist Ihre einzige OptionEs müsste so aussehen (dies ist kein gültiger Java-Code)
aber wie würde Werbung, wo Sie es fangen? Überprüfte Ausnahmen erfordern das Fangen. Stellen Sie sich einige Beispiele vor, die die Klasse initialisieren können (oder nicht, weil sie bereits initialisiert ist), und um die Aufmerksamkeit auf die Komplexität zu lenken, die sie einführen würde, füge ich die Beispiele in einen anderen statischen Initialisierer ein:
Und noch eine böse Sache -
Stellen Sie sich vor, ClassA hätte einen statischen Initialisierer, der eine aktivierte Ausnahme auslöst: In diesem Fall müsste MyInterface (eine Schnittstelle mit einem 'versteckten' statischen Initialisierer) die Ausnahme auslösen oder behandeln - Ausnahmebehandlung an einer Schnittstelle? Lass es besser so wie es ist.
quelle
main
kann geprüfte Ausnahmen auslösen. Offensichtlich können diese nicht gehandhabt werden.main()
dass an den laufenden Thread ein "magischer" (Standard-) Thread.UncaughtExceptionHandler angehängt ist, der die Ausnahme mit Stack-Trace an drucktSystem.err
und dann aufruftSystem.exit()
. Am Ende lautet die Antwort auf diese Frage wahrscheinlich: "Weil Java-Designer dies gesagt haben".Technisch können Sie dies tun. Die aktivierte Ausnahme muss jedoch innerhalb des Blocks abgefangen werden. Eine aktivierte Ausnahme darf sich nicht aus dem Block heraus verbreiten.
Technisch ist es auch möglich, zuzulassen, dass sich eine ungeprüfte Ausnahme aus einem statischen Initialisierungsblock 1 ausbreitet . Aber es ist eine wirklich schlechte Idee, dies absichtlich zu tun! Das Problem ist, dass die JVM selbst die ungeprüfte Ausnahme abfängt, sie umschließt und als
ExceptionInInitializerError
.NB: Das ist
Error
keine reguläre Ausnahme. Sie sollten nicht versuchen, sich davon zu erholen.In den meisten Fällen kann die Ausnahme nicht abgefangen werden:
Es gibt keinen Ort,
try ... catch
an dem Sie ein oben platzieren können, um dieExceptionInInitializerError
2 zu fangen .In einigen Fällen können Sie es fangen. Wenn Sie beispielsweise die Klasseninitialisierung durch Aufrufen ausgelöst haben
Class.forName(...)
, können Sie den Aufruf in a einschließentry
und entweder denExceptionInInitializerError
oder einen nachfolgenden abfangenNoClassDefFoundError
.Wenn Sie jedoch versuchen, sich von einem zu erholen, können
ExceptionInInitializerError
Sie auf eine Straßensperre stoßen. Das Problem ist, dass die JVM vor dem Auslösen des Fehlers die Klasse, die das Problem verursacht hat, als "fehlgeschlagen" markiert. Sie werden es einfach nicht benutzen können. Darüber hinaus werden alle anderen Klassen, die von der fehlgeschlagenen Klasse abhängen, ebenfalls in den Status "Fehlgeschlagen" versetzt, wenn sie versuchen, eine Initialisierung durchzuführen. Die einzige Möglichkeit besteht darin, alle fehlgeschlagenen Klassen zu entladen. Dies ist möglicherweise für dynamisch geladenen Code 3 möglich , im Allgemeinen jedoch nicht.1 - Es ist ein Kompilierungsfehler, wenn ein statischer Block bedingungslos eine ungeprüfte Ausnahme auslöst.
2 - Möglicherweise können Sie es abfangen, indem Sie einen standardmäßigen nicht erfassten Ausnahmebehandler registrieren. Dadurch können Sie ihn jedoch nicht wiederherstellen, da Ihr "Haupt" -Thread nicht gestartet werden kann.
3 - Wenn Sie die fehlgeschlagenen Klassen wiederherstellen möchten, müssen Sie den Klassenlader entfernen, der sie geladen hat.
Es soll den Programmierer vor dem Schreiben von Code schützen, der Ausnahmen auslöst, die nicht behandelt werden können!
Wie wir gesehen haben, verwandelt eine Ausnahme in einem statischen Initialisierer eine typische Anwendung in einen Baustein. Das Beste, was die Sprachdesigner tun könnten, ist, den geprüften Fall als Kompilierungsfehler zu behandeln. (Leider ist es nicht praktikabel, dies auch für ungeprüfte Ausnahmen zu tun.)
OK, was sollten Sie also tun, wenn Ihr Code Ausnahmen in einem statischen Initialisierer "auslösen" muss? Grundsätzlich gibt es zwei Alternativen:
Wenn eine (vollständige!) Wiederherstellung von der Ausnahme innerhalb des Blocks möglich ist, tun Sie dies.
Andernfalls strukturieren Sie Ihren Code so um, dass die Initialisierung nicht in einem statischen Initialisierungsblock (oder in den Initialisierern statischer Variablen) erfolgt.
quelle
Schauen Sie sich die Java-Sprachspezifikationen an : Es wird angegeben, dass es sich um einen Fehler bei der Kompilierung handelt, wenn der statische Initialisierer
fehlschlägt undmit einer aktivierten Ausnahme abrupt abgeschlossen werden kann .quelle
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
Ausgabe: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...
Da kein Code, den Sie schreiben, einen statischen Initialisierungsblock aufrufen kann, ist es nicht sinnvoll, aktiviert zu werfen
exceptions
. Wenn es möglich wäre, was würde der JVM tun, wenn geprüfte Ausnahmen ausgelöst werden?Runtimeexceptions
verbreitet werden.quelle
Beispiel: Das DispatcherServlet von Spring (org.springframework.web.servlet.DispatcherServlet) behandelt das Szenario, das eine aktivierte Ausnahme abfängt und eine weitere nicht aktivierte Ausnahme auslöst.
quelle
Ich kann das Kompilieren einer aktivierten Ausnahme kompilieren. Auch ....
quelle