Odpowiedzi:
Generalnie nigdy.
Czasami jednak trzeba wyłapać określone błędy.
Jeśli piszesz kod framework-ish (ładowanie klas stron trzecich), rozsądnie może być przechwycenie LinkageError
(brak definicji klasy, niezadowalający link, niezgodna zmiana klasy).
Widziałem też kilka głupich podklas rzucających kod firm trzecich Error
, więc będziesz musiał sobie z nimi poradzić.
Nawiasem mówiąc, nie jestem pewien, czy nie da się wyzdrowieć OutOfMemoryError
.
Nigdy. Nigdy nie możesz być pewien, że aplikacja będzie w stanie wykonać następny wiersz kodu. Jeśli otrzymasz OutOfMemoryError
, nie masz gwarancji, że będziesz w stanie cokolwiek zrobić niezawodnie . Catch RuntimeException i zaznaczone Exceptions, ale nigdy Errors.
boolean assertionsEnabled = false; assert assertionsEnabled = true;
Generalnie zawsze powinieneś łapać java.lang.Error
i zapisać w dzienniku lub wyświetlić użytkownikowi. Pracuję jako pomoc techniczna i codziennie widzę, że programiści nie są w stanie powiedzieć, co się stało w programie.
Jeśli masz wątek demona, musisz uniemożliwić jego zakończenie. W innych przypadkach Twoja aplikacja będzie działać poprawnie.
Powinieneś łapać tylko java.lang.Error
na najwyższym poziomie.
Jeśli spojrzysz na listę błędów, zobaczysz, że większość można sobie poradzić. Na przykład ZipError
podczas odczytu uszkodzonych plików zip.
Najczęstszymi błędami są OutOfMemoryError
i NoClassDefFoundError
, które w większości przypadków są problemami w czasie wykonywania.
Na przykład:
int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];
może wygenerować błąd, OutOfMemoryError
ale jest to problem w czasie wykonywania i nie ma powodu do zakończenia programu.
NoClassDefFoundError
występują głównie wtedy, gdy nie ma biblioteki lub pracujesz z inną wersją Java. Jeśli jest to opcjonalna część programu, nie należy go przerywać.
Mogę podać o wiele więcej przykładów, dlaczego dobrze jest złapać Throwable
na najwyższym poziomie i wyświetlić pomocny komunikat o błędzie.
OutOfMemoryError
nie jest błędem w czasie wykonywania, nie ma gwarancji, że aplikacja będzie w stanie go naprawić. Jeśli masz szczęście, możesz dostać OOM, new byte[largeNumber]
ale jeśli ta alokacja nie wystarczy, aby spowodować OOM, może zostać wyzwolona w następnej linii lub następnym wątku. Jest to problem w czasie wykonywania, ponieważ jeśli length
dane wejściowe są niezaufane, należy je zweryfikować przed wywołaniem new byte[]
.
NoClassDefFoundError
może wystąpić wszędzie , ponieważ jest wywoływana, gdy skompilowany kod java nie może znaleźć klasy. Jeśli twój JDK jest źle skonfigurowany, może to spowodować próbę użycia java.util.*
klasy i praktycznie niemożliwe jest programowanie przeciwko niemu. Jeśli opcjonalnie dołączasz zależność, powinieneś użyć, ClassLoader
aby sprawdzić, czy istnieje, co zgłasza ClassNotFoundException
.
ZipError
wskazuje, że plik jar zawierający klasy jest uszkodzonym plikiem ZIP. Jest to dość poważny problem i na tym etapie nie można ufać żadnemu kodowi, który zostanie wykonany, a próba „odzyskania” go z tego powodu byłaby nieodpowiedzialna.
java.lang.Error
lub java.lang.Throwable
na najwyższym poziomie i próba zrobienia czegoś z tym - powiedzmy zarejestruj komunikat o błędzie. Ale w tym momencie nie ma gwarancji, że zostanie to wykonane. Jeśli Twoja maszyna JVM znajduje się w fazie OOM, próba zarejestrowania może przydzielić więcej adresów, String
co wyzwala inną OOM.
W środowisku wielowątkowym najczęściej chcesz to złapać! Kiedy go złapiesz, zaloguj się i zamknij całą aplikację! Jeśli tego nie zrobisz, wątek, który może wykonywać jakąś kluczową część, byłby martwy, a reszta aplikacji pomyśli, że wszystko jest w porządku. Z tego może zdarzyć się wiele niechcianych sytuacji. Najmniejszym problemem jest to, że nie byłoby łatwo znaleźć źródła problemu, gdyby inne wątki zaczęły zgłaszać wyjątki z powodu niedziałającego jednego wątku.
Na przykład zwykle pętla powinna wyglądać następująco:
try {
while (shouldRun()) {
doSomething();
}
}
catch (Throwable t) {
log(t);
stop();
System.exit(1);
}
Nawet w niektórych przypadkach chciałbyś inaczej obsługiwać różne błędy, na przykład w OutOfMemoryError byłbyś w stanie regularnie zamykać aplikację (może nawet zwolnić trochę pamięci i kontynuować), w innych niewiele można zrobić.
OutOfMemoryError
i kontynuowanie zamiast szybkiego istnienia jest nierozsądne, ponieważ program znajduje się wtedy w niezdefiniowanym stanie .
Error
Zazwyczaj nie powinien być złapany , gdyż wskazuje na nieprawidłowy stan, który nigdy nie powinien wystąpić .
Ze specyfikacji Java API dla Error
klasy:
Jest
Error
podklasą tego,Throwable
co wskazuje na poważne problemy, których rozsądna aplikacja nie powinna próbować wychwycić. Większość takich błędów to nieprawidłowe warunki. […]Metoda nie musi deklarować w swojej klauzuli throws żadnych podklas Error, które mogą zostać wyrzucone podczas wykonywania metody, ale nie zostaną przechwycone, ponieważ te błędy są nienormalnymi warunkami, które nigdy nie powinny wystąpić.
Jak wspomina specyfikacja, an Error
jest generowany tylko w okolicznościach, w których istnieje prawdopodobieństwo, że gdy Error
wystąpi, aplikacja może zrobić bardzo niewiele, aw niektórych okolicznościach sama wirtualna maszyna Javy może znajdować się w niestabilnym stanie (na przykład VirtualMachineError
)
Chociaż an Error
jest podklasą, Throwable
co oznacza, że może zostać przechwycony przez try-catch
klauzulę, ale prawdopodobnie nie jest naprawdę potrzebny, ponieważ aplikacja będzie w nienormalnym stanie, gdy Error
zostanie wyrzucona przez JVM.
Jest też krótki odcinek na ten temat w rozdziale 11.5 hierarchii Wyjątek od języka Java Specification, 2nd Edition .
I jest kilka innych przypadków, w których jeśli złapiesz błąd, musisz go ponownie zgłosić . Na przykład ThreadDeath nigdy nie powinien zostać przechwycony, może to spowodować duży problem, jeśli złapiesz go w zamkniętym środowisku (np. Serwerze aplikacji):
Aplikacja powinna przechwytywać wystąpienia tej klasy tylko wtedy, gdy musi zostać wyczyszczona po asynchronicznym zakończeniu. Jeśli ThreadDeath zostanie przechwycony przez metodę, ważne jest, aby został ponownie wyrzucony, aby wątek faktycznie umarł.
Error
s.
Bardzo, bardzo rzadko.
Zrobiłem to tylko dla jednego, bardzo konkretnego, znanego przypadku. Na przykład błąd java.lang.UnsatisfiedLinkError może zostać zgłoszony, jeśli dwie niezależne klasy ClassLoader ładują tę samą bibliotekę DLL. (Zgadzam się, że powinienem przenieść JAR do współdzielonego modułu ładującego)
Ale najczęstszym przypadkiem jest to, że trzeba było się zalogować, aby wiedzieć, co się stało, gdy użytkownik złożył skargę. Chcesz wiadomość lub wyskakujące okienko dla użytkownika, a nie cicho martwe.
Nawet programista w C / C ++, wyskakuje błąd i mówi coś, czego ludzie nie rozumieją, zanim się zakończy (np. Awaria pamięci).
W aplikacji na Androida łapię błąd java.lang.VerifyError . Biblioteka, której używam, nie będzie działać na urządzeniach ze starą wersją systemu operacyjnego i kod biblioteki będzie generował taki błąd. Mogłem oczywiście uniknąć błędu, sprawdzając wersję systemu operacyjnego w czasie wykonywania, ale:
W idealnym przypadku nie powinniśmy obsługiwać / wychwytywać błędów. Ale mogą być przypadki, w których musimy to zrobić, w oparciu o wymagania dotyczące frameworka lub aplikacji. Powiedzmy, że mam demona XML Parser, który implementuje Parser DOM, który zużywa więcej pamięci. Jeśli istnieje wymóg, taki jak Parser, wątek nie powinien być przerywany , gdy otrzyma OutOfMemoryError , zamiast tego powinien go obsłużyć i wysłać wiadomość / e-mail do administratora aplikacji / frameworka.
Występuje błąd, gdy maszyna JVM nie działa zgodnie z oczekiwaniami lub jest na skraju. Jeśli złapiesz błąd, nie ma gwarancji, że blok catch będzie działał, a tym bardziej, że będzie działał do końca.
Zależy to również od uruchomionego komputera, aktualnego stanu pamięci, więc nie ma możliwości testowania, próbowania i dawania z siebie wszystkiego. Będziesz miał tylko szybki wynik.
Zmniejszysz także czytelność swojego kodu.