Ponowne przeglądanie wyjątków w Javie bez utraty śladu stosu

417

W języku C # mogę użyć throw;instrukcji, aby ponownie wygenerować wyjątek, zachowując ślad stosu:

try
{
   ...
}
catch (Exception e)
{
   if (e is FooException)
     throw;
}

Czy jest coś takiego w Javie ( która nie traci pierwotnego śladu stosu )?

ripper234
źródło
4
Jak myślisz, dlaczego traci pierwotny ślad stosu? Jedyny sposób, aby go zgubić, gdy rzucisz nowy SomeOtherException i zapomnisz przypisać główną przyczynę w konstruktorze lub w initCause ().
akarnokd
4
Wierzę, że tak zachowuje się kod w .Net, ale nie jestem już pozytywny. Warto gdzieś to sprawdzić lub przeprowadzić mały test.
ripper234
11
Throwablenie są modyfikowane przez ich rzucanie. Aby zaktualizować ślad stosu, musisz wywołać fillInStackTrace(). Dogodnie ta metoda jest wywoływana w konstruktorze Throwable.
Robert
50
Tak, w C # throw e;utraci ślad stosu. Ale nie w Javie.
Tim Goodman
Niektóre dokumenty firmy Oracle na temat wyjątków w Javie 7: Łapanie wielu typów wyjątków i przeglądanie wyjątków dzięki ulepszonemu sprawdzaniu typów
Guillaume Husta

Odpowiedzi:

560
catch (WhateverException e) {
    throw e;
}

po prostu zwróci wyjątek, który złapałeś (oczywiście otaczająca metoda musi na to pozwolić poprzez swój podpis itp.). Wyjątek zachowa oryginalny ślad stosu.

Brian Agnew
źródło
4
Cześć, InterruptedException e daje nieobsługiwany komunikat wyjątku, gdy dodam linię „throw e”. Nie tak, jeśli zastąpię go szerszym wyjątkiem e. Jak należy to zrobić poprawnie?
James P.
1
@James, właśnie zauważyłem, że komunikat znika, jeśli dodanie „zgłasza wyjątek XxxExx” w deklaracji funkcji.
shiouming
2
W Javie 7 kompilator do takiego powrotu jest bardziej inteligentny. Teraz działa dobrze z określonymi wyjątkami „rzuca” w metodzie zawierającej.
Waldemar Wosiński
193
@James Jeśli to nie catch(Exception e) { throw e; }będzie możliwe. Jeśli catch(InterruptedException ie) { throw ie; }to zostanie załatwione. Zasadniczo, nie rób catch(Exception e)- to nie jest pokemon i nie chcemy ich wszystkich złapać!
corsiKa
3
@corsiKa To niekoniecznie prawda, że ​​nie chcesz „złapać ich wszystkich”, to po prostu inny przypadek użycia. Jeśli masz pętlę najwyższego poziomu lub moduł obsługi zdarzeń (na przykład wewnątrz przebiegu wątku), jeśli nie złapiesz przynajmniej wyjątku RuntimeException i nie zarejestrujesz go, często często całkowicie pomijasz wyjątek ORAZ po cichu wyłamujesz się z ważnej pętli jest często jednorazową porażką. Jest również bardzo dobry dla funkcji wtyczek, w których nie wiesz, co dodatkowy kod może zrobić lub rzucić ... Do zastosowań odgórnych, takich jak wyłapywanie wyjątków Często wyjątek jest nie tylko dobrym pomysłem, ale najlepszą praktyką.
Bill K
82

Wolałbym:

try
{
    ...
}
catch (FooException fe){
   throw fe;
}
catch (Exception e)
{
    // Note: don't catch all exceptions like this unless you know what you
    // are doing.
    ...
}
Markus Lausberg
źródło
6
Zdecydowanie właściwe w Javie do wychwytywania określonych wyjątków niż ogólne i sprawdzanie na przykład. +1
amischiefr
8
-1, ponieważ nigdy nie powinieneś wychwytywać „wyjątku”, chyba że wiesz, co robisz.
Stroboskop
19
@Stroboskop: prawda, ale aby odpowiedzieć, najlepiej użyć tego samego (podobnego) kodu jak w pytaniu!
user85421,
14
Czasami łapanie wszystkich wyjątków jest w porządku. Na przykład, gdy piszesz przypadek testowy. Lub do celów logowania. Lub w zasadzie tam, gdzie nie złapanie oznacza rozbicie.
John Henckel,
1
@JohnHenckel i inni: Prawidłowe punkty inded. Zaktualizowałem pytanie, aby wyjaśnić, że łapanie Exceptionzwykle nie jest właściwym rozwiązaniem w większości (ale nie wszystkich) przypadkach.
Per Lundberg,
74

Możesz także owinąć wyjątek w inny ORAZ zachować ślad oryginalnego stosu, przekazując wyjątek jako Throwable jako parametr przyczyny:

try
{
   ...
}
catch (Exception e)
{
     throw new YourOwnException(e);
}
Olvagor
źródło
8
Radziłbym również dodać wiadomość obok, używającthrow new YourOwnException("Error while trying to ....", e);
Julien
tego właśnie szukałem, zwłaszcza wersja z pierwszego komentarza, w której możesz przekazać własną wiadomość
Csaba
To pokazuje komunikat o błędzie poprawnie, ale ślad stosu pokazuje linię błędu jako linię z „wyrzuć nowy ....... (e)”, a nie oryginalną linię, która spowodowała wyjątek.
Ashburn RK
22

W Javie jest prawie tak samo:

try
{
   ...
}
catch (Exception e)
{
   if (e instanceof FooException)
     throw e;
}
alves
źródło
5
Nie, dopóki nie utworzysz nowego obiektu wyjątku, stacktrace pozostaje taki sam.
Mnementh
28
Dodam zaczep specyficzny dla FooException
dfa
3
W tym konkretnym przypadku zgadzam się, ale dodanie konkretnego haczyka może nie być właściwym wyborem - wyobraź sobie, że masz jakiś wspólny kod dla wszystkich wyjątków, a po, dla konkretnego wyjątku, ponownie go zwróć.
alves
1
@MarkusLausberg Ale w końcu nie łapie wyjątków.
Robert
Tak, ale to nie było pytanie.
Markus Lausberg
14

W Javie po prostu rzucasz wyjątek, który złapałeś, throw ea nie tylko throw. Java utrzymuje śledzenie stosu.

David M.
źródło
6

coś takiego

try 
{
  ...
}
catch (FooException e) 
{
  throw e;
}
catch (Exception e)
{
  ...
}
cdeszaq
źródło
5
public int read(byte[] a) throws IOException {
    try {
        return in.read(a);
    } catch (final Throwable t) {
        /* can do something here, like  in=null;  */
        throw t;
    }
}

Jest to konkretny przykład, w którym metoda rzuca an IOException. Te finalśrodki tmogą posiadać tylko wyjątek wyrzucony z bloku try. Dodatkowe materiały do ​​czytania można znaleźć tutaj i tutaj .

Daniel
źródło
1
To nie musi być ostateczne. Zobacz docs.oracle.com/javase/7/docs/technotes/guides/language/… i stackoverflow.com/a/6889301/131160
jcsahnwaldt mówi GoFundMonica
3

Śledzenie stosu jest zachowywane, jeśli złapiesz wychwycony ekscytację w inny wyjątek (aby uzyskać więcej informacji) lub jeśli po prostu ponownie rzucisz złapany wykop.

try{ ... }catch (FooException e){ throw new BarException("Some usefull info", e); }

Sindre
źródło
2

Właśnie miałem podobną sytuację, w której mój kod potencjalnie generuje wiele różnych wyjątków, które chciałem po prostu ponownie wprowadzić. Rozwiązanie opisane powyżej nie działało dla mnie, ponieważ Eclipse powiedział mi, że throw e;prowadzi to do niewymienionego wyjątku, więc właśnie to zrobiłem:

try
{
...
} catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {                    
    throw new RuntimeException(e.getClass().getName() + ": " + e.getMessage() + "\n" + e.getStackTrace().toString());
}

Pracował dla mnie .... :)

Matthias
źródło