Czy łowienie jest złą praktyką Throwable
?
Na przykład coś takiego:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Czy to zła praktyka, czy też powinniśmy być jak najbardziej konkretni?
Czy łowienie jest złą praktyką Throwable
?
Na przykład coś takiego:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Czy to zła praktyka, czy też powinniśmy być jak najbardziej konkretni?
Odpowiedzi:
Musisz być jak najbardziej konkretny. W przeciwnym razie nieprzewidziane błędy mogą zniknąć w ten sposób.
Poza tym Throwable
okładki Error
też i to zwykle nie ma sensu . Nie chcesz tego złapać / obsłużyć, chcesz, aby twój program natychmiast umarł, abyś mógł go poprawnie naprawić.
To jest zły pomysł. W rzeczywistości nawet łowienie Exception
jest zwykle złym pomysłem. Rozważmy przykład:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
inputNumber = 10; //Default, user did not enter valid number
}
Teraz powiedzmy, że getUserInput () blokuje się na chwilę, a inny wątek zatrzymuje twój wątek w najgorszy możliwy sposób (wywołuje thread.stop ()). Twój blok catch wykryje ThreadDeath
błąd. To jest bardzo złe. Zachowanie kodu po przechwyceniu tego wyjątku jest w dużej mierze niezdefiniowane.
Podobny problem występuje przy przechwytywaniu wyjątku. Może getUserInput()
nie powiodło się z powodu wyjątku InterruptException lub wyjątku odmowy uprawnień podczas próby rejestrowania wyników lub wszelkiego rodzaju innych niepowodzeń. Nie masz pojęcia, co poszło nie tak, ponieważ z tego powodu nie masz również pojęcia, jak rozwiązać problem.
Masz trzy lepsze opcje:
1 - Wyłap dokładnie wyjątek (y), z którymi umiesz sobie poradzić:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
inputNumber = 10; //Default, user did not enter valid number
}
2 - Ponownie wyświetl każdy wyjątek, na który napotkasz i nie wiesz, jak sobie poradzić:
try {
doSomethingMysterious();
} catch(Exception e) {
log.error("Oh man, something bad and mysterious happened",e);
throw e;
}
3 - Użyj końcowego bloku, aby nie pamiętać o ponownym rzucie:
Resources r = null;
try {
r = allocateSomeResources();
doSomething(r);
} finally {
if(r!=null) cleanUpResources(r);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Zostanie to zarejestrowane w jednym ładnym wyjątku z 10 przyczynami.
Należy również pamiętać, że złapanie Throwable
można również złapać, InterruptedException
co wymaga specjalnego traktowania. Aby uzyskać więcej informacji, zobacz Radzenie sobie z InterruptedException .
Jeśli chcesz złapać tylko niezaznaczone wyjątki, możesz również rozważyć ten wzorzec
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
W ten sposób, gdy zmodyfikujesz kod i dodasz wywołanie metody, które może zgłosić zaznaczony wyjątek, kompilator przypomni Ci o tym, a następnie będziesz mógł zdecydować, co zrobić w tym przypadku.
bezpośrednio z javadoc klasy Error (która zaleca, aby ich nie łapać):
* An <code>Error</code> is a subclass of <code>Throwable</code>
* that indicates serious problems that a reasonable application
* should not try to catch. Most such errors are abnormal conditions.
* The <code>ThreadDeath</code> error, though a "normal" condition,
* is also a subclass of <code>Error</code> because most applications
* should not try to catch it.
* A method is not required to declare in its <code>throws</code>
* clause any subclasses of <code>Error</code> that might be thrown
* during the execution of the method but not caught, since these
* errors are abnormal conditions that should never occur.
*
* @author Frank Yellin
* @version %I%, %G%
* @see java.lang.ThreadDeath
* @since JDK1.0
Nie jest to zła praktyka, jeśli absolutnie nie można wyrzucić wyjątku z metody.
To zła praktyka, jeśli naprawdę nie możesz sobie poradzić z wyjątkiem. Lepiej jest dodać „throws” do sygnatury metody niż po prostu złapać i ponownie rzucić lub, co gorsza, zawinąć ją w RuntimeException i ponownie wyrzucić.
Throwable
instancji - np. W przypadku niestandardowego rejestrowania wyjątków.
Łapanie Throwable jest czasami konieczne, jeśli używasz bibliotek, które z nadmiernym entuzjazmem zgłaszają błędy, w przeciwnym razie twoja biblioteka może zabić twoją aplikację.
Jednak w takich okolicznościach najlepiej byłoby określić tylko konkretne błędy generowane przez bibliotekę, a nie wszystkie Throwables.
Throwable to podstawowa klasa dla wszystkich klas, którą można rzucić (nie tylko wyjątki). Niewiele możesz zrobić, jeśli złapiesz OutOfMemoryError lub KernelError (zobacz Kiedy przechwycić java.lang.Error? )
wyłapywanie wyjątków powinno wystarczyć.
zależy to od twojej logiki lub być bardziej szczegółowym od twoich opcji / możliwości. Jeśli jest jakiś wyjątek, na który możesz zareagować w znaczący sposób, możesz go najpierw złapać i zrobić.
Jeśli nie ma i jesteś pewien, że zrobisz to samo dla wszystkich wyjątków i błędów (na przykład wyjście z komunikatem o błędzie), to złapanie przedmiotu rzucanego nie jest problemem.
Zwykle pierwszy przypadek jest prawidłowy i nie złapiesz przedmiotu do rzucania. Ale wciąż jest wiele przypadków, w których złapanie działa dobrze.
Chociaż jest to opisywane jako bardzo zła praktyka, czasami możesz znaleźć rzadkie przypadki, że nie tylko użyteczne, ale także obowiązkowe. Oto dwa przykłady.
W aplikacji internetowej, w której musisz pokazać użytkownikowi pełną stronę błędu. Ten kod zapewnia, że tak się stanie, ponieważ jest on try/catch
obszerny wokół wszystkich uchwytów żądań (serwletów, akcji rozpórek lub dowolnego kontrolera ...)
try{
//run the code which handles user request.
}catch(Throwable ex){
LOG.error("Exception was thrown: {}", ex);
//redirect request to a error page.
}
}
Jako inny przykład rozważmy klasę usług, która obsługuje transfer środków. Ta metoda zwraca wartość, TransferReceipt
jeśli transfer został wykonany lub NULL
nie mógł.
String FoundtransferService.doTransfer( fundtransferVO);
Teraz obrazując, otrzymujesz List
przelewy środków od użytkownika i musisz skorzystać z powyższej usługi, aby wykonać je wszystkie.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}
Ale co się stanie, jeśli zdarzy się jakikolwiek wyjątek? Nie powinieneś przerywać, ponieważ jeden transfer mógł się udać, a drugi nie, powinieneś kontynuować przez wszystkich użytkowników List
i pokazywać wynik dla każdego transferu. Więc kończysz z tym kodem.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}catch(Throwable ex){
LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
}
}
Możesz przeglądać wiele projektów open source, aby zobaczyć, że throwable
jest on naprawdę buforowany i obsługiwany. Na przykład o to poszukiwanie tomcat
, struts2
oraz primefaces
:
https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch % 28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable
throwable
, o co chodzi w tym pytaniu
Pytanie jest trochę niejasne; pytasz „czy można złapać Throwable
”, czy „czy można złapać Throwable
i nic nie robić”? Wiele osób odpowiedziało na to drugie pytanie, ale to kwestia poboczna; W 99% przypadków nie należy „konsumować” ani odrzucać wyjątku, niezależnie od tego, czy jest on łapany, Throwable
czyIOException
czy cokolwiek innego.
Jeśli propagujesz wyjątek, odpowiedź (podobnie jak odpowiedź na wiele pytań) brzmi „to zależy”. To zależy od tego, co robisz z wyjątkiem - dlaczego to łapiesz.
Dobrym przykładem tego, dlaczego chcesz przechwycić, Throwable
jest zapewnienie pewnego rodzaju czyszczenia, jeśli wystąpi jakikolwiek błąd. Na przykład w JDBC, jeśli wystąpi błąd podczas transakcji, chcesz wycofać transakcję:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
Zauważ, że wyjątek nie jest odrzucany, ale propagowany.
Ale ogólnie rzecz biorąc, łapanie, Throwable
ponieważ nie masz powodu i jesteś zbyt leniwy, aby zobaczyć, które konkretne wyjątki są rzucane, jest kiepską formą i złym pomysłem.
Ogólnie rzecz biorąc, chcesz uniknąć łapania Error
s, ale przychodzą mi do głowy (przynajmniej) dwa konkretne przypadki, w których jest to właściwe:
AssertionError
co w innym przypadku jest nieszkodliwe.Jeśli używamy przedmiotu miotanego , obejmuje on również błąd i to wszystko.
Przykład.
public class ExceptionTest {
/**
* @param args
*/
public static void m1() {
int i = 10;
int j = 0;
try {
int k = i / j;
System.out.println(k);
} catch (Throwable th) {
th.printStackTrace();
}
}
public static void main(String[] args) {
m1();
}
}
Wynik:
java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)
Rzut jest superklasą wszystkich błędów i wyjątków. Jeśli użyjesz Throwable w klauzuli catch, nie tylko wyłapie wszystkie wyjątki, ale także wyłapie wszystkie błędy. JVM generuje błędy w celu wskazania poważnych problemów, które nie są przeznaczone do obsługi przez aplikację. Typowe przykłady to OutOfMemoryError lub StackOverflowError. Obie są spowodowane sytuacjami, które są poza kontrolą aplikacji i nie można ich obsłużyć. Więc nie powinieneś łapać Throwables, chyba że jesteś całkiem pewien, że będzie to tylko wyjątek znajdujący się w Throwable.
Chociaż ogólnie złą praktyką jest łapanie Throwable (jak wyjaśniono w licznych odpowiedziach na to pytanie), scenariusze, w których łapanie Throwable
jest przydatne, są dość powszechne. Pozwólcie, że wyjaśnię jeden taki przypadek, który wykorzystuję w pracy, na uproszczonym przykładzie.
Rozważ metodę, która polega na dodaniu dwóch liczb, a po pomyślnym dodaniu wysyła alert e-mail do określonych osób. Załóżmy, że zwracany numer jest ważny i używany przez metodę wywołującą.
public Integer addNumbers(Integer a, Integer b) {
Integer c = a + b; //This will throw a NullPointerException if either
//a or b are set to a null value by the
//calling method
successfulAdditionAlert(c);
return c;
}
private void successfulAdditionAlert(Integer c) {
try {
//Code here to read configurations and send email alerts.
} catch (Throwable e) {
//Code to log any exception that occurs during email dispatch
}
}
Kod służący do wysyłania alertów e-mailowych odczytuje wiele konfiguracji systemu, dlatego z tego bloku kodu może być wyrzucanych wiele wyjątków. Ale nie chcemy, aby jakikolwiek wyjątek napotkany podczas wysyłania alertu był propagowany do metody wywołującej, ponieważ ta metoda jest po prostu związana z sumą dwóch wartości całkowitych, które zapewnia. W związku z tym kod do wysyłania alertów e-mailowych jest umieszczony w try-catch
bloku, w którym Throwable
jest przechwytywany, a wszelkie wyjątki są jedynie rejestrowane, co pozwala na kontynuowanie pozostałej części przepływu.
Exception
s jak najbardziej, ale nie Throwable
.