Dlaczego Java nie pozwala na zgłoszenie sprawdzonego wyjątku z bloku statycznej inicjalizacji? Jaki był powód tej decyzji projektowej?
java
exception
static-initializer
missingfaktor
źródło
źródło
Odpowiedzi:
Ponieważ nie jest możliwe obsłużenie tych sprawdzonych wyjątków w twoim źródle. Nie masz żadnej kontroli nad procesem inicjalizacji, a statyczne bloki {} nie mogą być wywoływane ze źródła, aby móc je otoczyć try-catch.
Ponieważ nie możesz obsłużyć żadnego błędu wskazywanego przez sprawdzony wyjątek, zdecydowano się zabronić wyrzucania statycznych bloków sprawdzonych wyjątków.
Blok statyczny nie może zgłaszać sprawdzonych wyjątków, ale nadal umożliwia zgłaszanie wyjątków niezaznaczonych / środowiska uruchomieniowego. Ale z powyższych powodów nie byłbyś w stanie sobie z nimi poradzić.
Podsumowując, ograniczenie to uniemożliwia (lub przynajmniej utrudnia) deweloperowi zbudowanie czegoś, co może skutkować błędami, z których aplikacja nie byłaby w stanie odzyskać.
źródło
static { if(1 < 10) { throw new NullPointerException(); } }
Możesz obejść ten problem, przechwytując każdy zaznaczony wyjątek i zgłaszając go ponownie jako niezaznaczony wyjątek. Ten niekontrolowany klasa wyjątek działa również owijki:
java.lang.ExceptionInInitializerError
.Przykładowy kod:
źródło
catch (Exception e) {
zamiast tego.System.exit(...)
(lub równoważne) jest jedyną opcją,Musiałoby to wyglądać tak (to nie jest poprawny kod Java)
ale jak by reklama, gdzie ją złapałeś? Zaznaczone wyjątki wymagają wyłapania. Wyobraź sobie kilka przykładów, które mogą zainicjować klasę (lub nie, ponieważ jest już zainicjowana) i aby zwrócić uwagę na złożoność, którą wprowadziłaby, umieściłem przykłady w innym statycznym inicjatorze:
I kolejna paskudna rzecz -
Wyobraź sobie, że klasa A miała statyczny inicjator rzucający sprawdzony wyjątek: w tym przypadku MyInterface (który jest interfejsem z „ukrytym” statycznym inicjatorem) musiałby zgłosić wyjątek lub obsłużyć go - obsługa wyjątków w interfejsie? Lepiej zostaw to tak, jak jest.
źródło
main
może zgłaszać zaznaczone wyjątki. Oczywiście nie można sobie z nimi poradzić.main()
który drukuje wyjątek ze śladem stosu doSystem.err
, a następnie wywołujeSystem.exit()
. Ostatecznie odpowiedź na to pytanie brzmi prawdopodobnie: „ponieważ tak powiedzieli projektanci Javy”.Technicznie możesz to zrobić. Jednak zaznaczony wyjątek musi zostać przechwycony w bloku. Zaznaczony wyjątek nie może być propagowany poza blok.
Z technicznego punktu widzenia możliwe jest również zezwolenie niezaznaczonemu wyjątkowi na propagację z bloku 1 statycznego inicjatora . Ale robienie tego celowo jest naprawdę złym pomysłem! Problem polega na tym, że sama maszyna JVM przechwytuje niezaznaczony wyjątek, opakowuje go i ponownie zgłasza jako plik
ExceptionInInitializerError
.Uwaga: to
Error
nie jest zwykły wyjątek. Nie powinieneś próbować go odzyskać.W większości przypadków wyjątku nie można złapać:
Nigdzie nie możesz umieścić
try ... catch
w powyższym miejscu, aby złapaćExceptionInInitializerError
2 .W niektórych przypadkach możesz to złapać. Na przykład, jeśli wyzwoliłeś inicjalizację klasy przez wywołanie
Class.forName(...)
, możesz zawrzeć wywołanie w atry
i złapać alboExceptionInInitializerError
następnyNoClassDefFoundError
.Jeśli jednak spróbujesz odzyskać siły ,
ExceptionInInitializerError
możesz napotkać blokadę na drogach. Problem polega na tym, że przed zgłoszeniem błędu maszyna JVM oznacza klasę, która spowodowała problem, jako „nie powiodła się”. Po prostu nie będziesz w stanie go używać. Ponadto wszelkie inne klasy, które są zależne od klasy, która zakończyła się niepowodzeniem, również przejdą w stan niepowodzenia, jeśli spróbują zainicjować. Jedynym wyjściem jest zwolnienie wszystkich klas, które zakończyły się niepowodzeniem. Że może być wykonalne dla kodu dynamicznie załadowanego 3 , ale w ogóle nie jest.1 - jest to błąd kompilacji, jeśli blok statyczny bezwarunkowo zgłasza niesprawdzony wyjątek.
2 - Ty może być w stanie przechwycić go rejestrując domyślnej obsługi przechwycony wyjątek, ale to nie będzie można odzyskać, ponieważ „głównym” nić nie można uruchomić.
3 - Jeśli chcesz odzyskać klasy, które się nie powiodły, musisz pozbyć się modułu ładującego klasy, który je ładował.
Ma to na celu ochronę programisty przed pisaniem kodu, który rzuca wyjątki, których nie można obsłużyć!
Jak widzieliśmy, wyjątek w inicjatorze statycznym zamienia typową aplikację w cegłę. Najlepszą rzeczą, jaką mogliby zrobić projektanci języka, jest potraktowanie zaznaczonego przypadku jako błędu kompilacji. (Niestety nie jest to praktyczne również w przypadku niezaznaczonych wyjątków).
OK, więc co należy zrobić, jeśli Twój kod „musi” zgłaszać wyjątki w statycznym inicjatorze. Zasadniczo istnieją dwie alternatywy:
Jeśli (pełne!) Odzyskanie z wyjątku w bloku jest możliwe, zrób to.
W przeciwnym razie zrestrukturyzuj swój kod, aby inicjalizacja nie odbywała się w statycznym bloku inicjalizacji (lub w inicjatorach zmiennych statycznych).
źródło
Spójrz na specyfikacje języka Java : stwierdzono, że jest to błąd czasu kompilacji, jeśli inicjator statyczny
nie powiedziesię i może zakończyć się nagle z zaznaczonym wyjątkiem.źródło
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
Wyjście: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...
Ponieważ żaden kod, który piszesz, nie może wywołać statycznego bloku inicjalizacji, nie jest użyteczne rzucanie check
exceptions
. Gdyby to było możliwe, co zrobiłaby jvm, gdy zostaną wyrzucone sprawdzone wyjątki?Runtimeexceptions
są rozmnażane.źródło
Na przykład: Spring's DispatcherServlet (org.springframework.web.servlet.DispatcherServlet) obsługuje scenariusz, który wyłapuje sprawdzony wyjątek i wyrzuca inny niezaznaczony wyjątek.
źródło
Jestem w stanie skompilować, zgłaszając również zaznaczony wyjątek ....
źródło