Warum erlaubt Java nicht, eine aktivierte Ausnahme aus einem statischen Initialisierungsblock auszulösen? Was war der Grund für diese Designentscheidung?
Warum erlaubt Java nicht, eine aktivierte Ausnahme aus einem statischen Initialisierungsblock auszulösen? Was war der Grund für diese Designentscheidung?
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.
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:
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) {
stattdessen.
System.exit(...)
(oder gleichwertig) ist Ihre einzige Option
Es müsste so aussehen (dies ist kein gültiger Java-Code)
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
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:
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?
}
}
Und noch eine böse Sache -
interface MyInterface {
final static ClassA a = new ClassA();
}
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.
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 druckt System.err
und dann aufruft System.exit()
. Am Ende lautet die Antwort auf diese Frage wahrscheinlich: "Weil Java-Designer dies gesagt haben".
Warum erlaubt Java nicht, eine aktivierte Ausnahme aus einem statischen Initialisierungsblock auszulösen?
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:
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)
Es gibt keinen Ort, try ... catch
an dem Sie ein oben platzieren können, um die ExceptionInInitializerError
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ßen try
und entweder den ExceptionInInitializerError
oder einen nachfolgenden abfangen NoClassDefFoundError
.
Wenn Sie jedoch versuchen, sich von einem zu erholen, könnenExceptionInInitializerError
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.
Was war der Grund für diese Designentscheidung?
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.
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 und mit einer aktivierten Ausnahme abrupt abgeschlossen werden kann .
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.
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.
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());
}
Ich kann das Kompilieren einer aktivierten Ausnahme kompilieren. Auch ....
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}