Dlaczego ludzie korzystający z języka Java często po cichu używają wyjątków?


81

Nigdy wcześniej nie zajmowałem się poważnym kodowaniem w Javie, ale nauczyłem się składni, bibliotek i koncepcji w oparciu o moje istniejące umiejętności (Delphi i C #). Jedyną rzeczą, której prawie nie rozumiem, jest to, że widziałem tyle kodu, który po cichu pochłania wyjątki po printStackTracetak:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

Jest podobny kod jak ten w prawie każdym artykule i projekcie Java, z którym się spotkałem. W oparciu o moją wiedzę jest to bardzo złe. Wyjątek powinien prawie zawsze być przekazywany do kontekstu zewnętrznego w następujący sposób:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

W większości przypadków wyjątek powinien być obsługiwany w najbardziej zewnętrznej pętli, która należy do podstawowej struktury (na przykład Java Swing). Dlaczego w świecie Javy wygląda to na normę? Jestem zdziwiony.

W oparciu o moje tło wolałbym całkowicie usunąć printStackTrace . Po prostu wrzuciłbym ponownie jako nieobsługiwany aka RuntimeException(lub nawet lepiej AssertionError), a następnie złapałbym go i zarejestrował w najbardziej odpowiednim miejscu: najbardziej zewnętrznej pętli struktury.

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            throw new AssertionError(e);
        }
    }

56
Powiedziałbym, że nie jest cicho, gdy drukowany jest ślad stosu :)
willcodejavaforfood

13
Czy mogę zacytować Isaaca Wallera "Ale twój program nie kończy się i kontynuuje, najprawdopodobniej w nieokreślonym stanie"
Sake

15
@willcodejavaforfood, Twój komentarz otrzymał tak wiele głosów, a to jeszcze bardziej zastanawia mnie, jeśli chodzi o typowy sposób myślenia w Javie.
Sake

13
@willcodejavaforfood - z perspektywy rozmówcy jest cichy. Dzwoniący nie ma pojęcia, że ​​coś się wydarzyło. Jeśli tak jest, to w porządku, ale ogólnie to zła wiadomość. Pomyśl także o tym, co dzieje się w aplecie - użytkownik nigdy nie zobaczy danych wyjściowych (chyba że zdarzy się, że otworzy konsolę java, co nigdy nie jest). Ok, więc już nikt nie używa apletów;) Podobnie jest z serwletem - użytkownik końcowy nie wie, że coś się wydarzyło - po prostu zostaje schowane w logu.
Scott Stanchfield

4
Rzucanie wyjątków w Javie jest tak powszechne, że większość programistów Javy je ignoruje, ponieważ jeśli wykonasz poprawne wyłapywanie błędów, będziesz musiał napisać znacznie więcej kodu ... więc w zasadzie wszyscy ci programiści Java robią to, ponieważ są leniwi.
Trevor Boyd Smith

Odpowiedzi:


203

Zawsze myślałem, że jest to podobne do następującego scenariusza:

„Mężczyzna zostaje postrzelony.

Wstrzymuje oddech i ma dość siły, żeby wsiąść do autobusu.

10 mil później mężczyzna wysiada z autobusu, przechodzi kilka przecznic i umiera. "

Kiedy policja dociera do ciała, nie ma pojęcia, co się właśnie stało. W końcu mogą, ale jest znacznie trudniej.

Lepiej jest:

„Mężczyzna zostaje postrzelony i natychmiast umiera, a ciało leży dokładnie w miejscu, w którym nastąpiło morderstwo”.

Kiedy przyjeżdża policja, wszystkie dowody są na miejscu.

Jeśli system ma zawieść, lepiej jest szybko zawieść

Odpowiadając na pytanie:

  1. Ignorancja.
      +
  2. Leniwiec

EDYTOWAĆ:

Oczywiście sekcja haczyków jest przydatna.

Jeśli można coś zrobić z wyjątkiem, to właśnie tam należy to zrobić.

Prawdopodobnie NIE jest to wyjątek dla podanego kodu, prawdopodobnie jest to coś, czego się spodziewamy (aw mojej analogii jest to jak kurtka kuloodporna, a na strzał czekał człowiek na pierwszym miejscu).

I tak, haczyk można wykorzystać do rzucania wyjątków odpowiednich do abstrakcji


2
Przełykanie wyjątków nie zawodzi, nie wspominając o szybkiej awarii. Z drugiej strony, jeśli kontrakt metody ma parametr, który jest ciągiem cyfr i analizujesz go na liczbę całkowitą, możesz połknąć ParseException. Jeśli piszesz określoną funkcjonalność, zdefiniuj niestandardowy wyjątek dla tej funkcji, która się nie powiedzie, a następnie przechwyć wyjątki i wyrzuć niestandardowy wyjątek (z oryginalnym wyjątkiem jako argumentem). Może wtedy szybko zawieść do miejsca, w którym może wystąpić znaczące zachowanie (wyświetlenie użytkownikowi, wysłanie wiadomości e-mail, po prostu zalogowanie się, zamknięcie tego wątku serwera itp.).
JeeBee

21
Chwileczkę ... Najpierw muszę przestać się śmiać ... Dobra ... To była zdecydowanie najbardziej zabawna odpowiedź na pytanie, jakie do tej pory widziałem. To bardzo dobrze przenosi. Chociaż przekazanie wyjątku „w górę łańcucha” do obsługi może być bardzo pomocne, lepiej jest przynajmniej najpierw złapać wyjątek, a następnie przekazać go, aby można było dołączyć przydatne informacje.
Pan Will

Wyjątkiem nie jest błąd. Wyjątkiem jest postrzelenie mężczyzny, a następnie opisanie incydentu w notatce i dołączenie go do gołębia pocztowego, który jest wyszkolony do latania do najbliższego oddziału zabójstw.
Jherico

13
@Oscar, +100 gdybym mógł. Słaba obsługa wyjątków jest wynikiem IGNORANCJI i ZŁOŚLIWOŚCI, a nie lenistwa. Lenistwo = jak najmniejszy wysiłek. Głupota w obsłudze wyjątków powoduje więcej pracy, a nie ją zmniejsza… oczywiście, zwykle powoduje więcej pracy dla kogoś innego, przez co wydaje się „leniwa”.
James Schek

1
Widzę twój punkt widzenia oscar, ale cała obsługa wyjątku lub rejestrowanie to pokazuje, że wyjątek się wydarzył. Jeśli wrzucisz go do końca łańcucha, dowiesz się, która metoda ostatecznie wywołała wyjątek.
Mauro,

33

Zwykle jest to spowodowane tym, że IDE oferuje pomocną „szybką poprawkę”, która opakowuje nieprawidłowy kod w blok try-catch z obsługą wyjątków. Chodzi o to, że faktycznie coś ZROBISZ, ale leniwi programiści nie.

To bez wątpienia zła forma.


2
Zgoda, podany przykład wygląda jak domyślna szybka poprawka Eclipse, z usuniętym komentarzem // FIXME:.
JeeBee

2
Cóż, powiedziałeś: „To zła forma, bez wątpienia ”, a ja w 100% się z tym zgadzam. Jednak niektóre inne odpowiedzi i komentarze sprawiają, że jestem bardzo zaciekawiony filozofią stojącą za tą bardzo dużą (jak sądzę) częścią programistów Java.
Sake

4
Naprawdę chciałbym, żeby Eclipse zrobił tam coś drastycznego. Na przykład e.printStackTrace (); System.exit (1);
Adam Jaskiewicz

4
Zmieniłem ustawienia IDE, aby wygenerować treść przechwytywania, która ponownie zgłasza ją jako RuntimeException. To znacznie lepsze domyślne.
Esko Luontola

6
Jeśli jestem pewien, że wyjątek nigdy nie powinien się zdarzyć, zwykle rzucam AssertionError zamiast RuntimeException jako sposób na udokumentowanie moich zamiarów.
Esko Luontola

20

To klasyczny argument słomianego człowieka . printStackTrace()to pomoc w debugowaniu. Jeśli widziałeś to na blogu lub w czasopiśmie, to dlatego, że pisarz był bardziej zainteresowany zilustrowaniem punktu innego niż obsługa wyjątków. Jeśli widziałeś to w kodzie produkcyjnym, twórca tego kodu był ignorantem lub leniwym, nic więcej. Nie należy tego traktować jako przykładu powszechnej praktyki w „świecie java”.


1
więc pro-programista może pisać zły kod na blogu lub w magazynie, a nie w produkcji? id raczej pisać zły kod w produkcji. jeśli naprawdę sprytny facet używa printStackTrace, przekonuje ludzi, aby go używali. Rozumiem, że kod wyjątku jest o 8 znaków dłuższy, ale ...
IAdapter

8
@ABCDE: Całkowicie się nie zgadzam. Wolisz napisać zły kod w produkcji? To kod, który powinien działać! Artykuły z blogów i czasopism mają zilustrować pewien punkt. Jeśli nie chodzi o obsługę błędów, może to być po prostu bałagan. Wielu autorów używa w artykułach znacznie uproszczonej obsługi błędów lub wcale. To nie jest przykład do naśladowania.
Bill the Lizard

19
  1. Java wymusza jawną obsługę wszystkich wyjątków. Jeśli metoda, którą wywołuje twój kod, jest zadeklarowana do zgłaszania FooException i BarException, twój kod MUSI obsłużyć (lub zgłosić ) te wyjątki. Jedynym wyjątkiem od tej reguły jest RuntimeException , który jest cichy jak ninja.
  2. Wielu programistów jest leniwych (łącznie ze mną) i bardzo łatwo jest po prostu wydrukować ślad stosu.

7
Ale twój program nie kończy pracy i działa dalej, najprawdopodobniej w niezdefiniowanym stanie.
Isaac Waller

5
W jaki sposób stan jest niezdefiniowany, jeśli znasz typ wyjątku? To ładny, mały cytat, ale zazwyczaj program MOŻE i MUSI BYĆ kontynuowany. Nie chcesz, aby cała aplikacja uległa awarii z powodu wyjątku - powiedz użytkownikowi i poproś go o zrobienie tego, cokolwiek to było.
GreenieMeanie

2
Aplikacja nie ulegnie awarii, jeśli wyjątek zostanie poprawnie obsłużony na najwyższym poziomie.
Sake

2
Co najmniej połowa wyjątków to „RuntimeExceptions”, przez co wygląda na to, że jest tylko jeden. I nie jest cichy, a nieprzechwycony RutimeException wydrukuje ślad stosu i spowoduje awarię programu.
Bill K

@greenieMeanie Generalnie podczas tworzenia aplikacji chcesz, aby program się zawiesił przy najmniejszym problemie - nie pozwól programistom uchylać się od zepsucia połączenia - nie wybaczaj niczego. Z drugiej strony, kiedy go wysyłasz, chcesz pójść na odwrót. Rejestruj każdy wyjątek w niewidoczny sposób i staraj się jak najlepiej odzyskać i kontynuować (w czym Java jest naprawdę dobra).
Bill K

12

Uważam, że często są dwa powody, dla których tak się dzieje

  1. Programista był leniwy
  2. Programista chciał strzec punktu wejścia do tego komponentu (poprawnie lub niepoprawnie)

Nie wierzę, że jest to zjawisko ograniczone do języka Java. Często widziałem takie kodowanie w C # i VB.Net.

Z pozoru jest to dość szokujące i wygląda okropnie. Ale tak naprawdę to nic nowego. Występuje cały czas w aplikacjach C ++, które używają wartości zwracanych przez kod błędu w porównaniu z wyjątkami. Różnica polega jednak na tym, że ignorowanie potencjalnie krytycznej wartości zwracanej nie różni się tak naprawdę od wywołania funkcji, która zwraca wartość void.

Foo* pFoo = ...;
pFoo->SomeMethod(); // Void or swallowing errors, who knows?

Ten kod wygląda lepiej, ale gdyby SomeMethod () powiedział, że zwraca HResult, semantycznie nie różni się to od połknięcia wyjątku.


3
Myślę, że powód 1 jest znacznie bardziej powszechny niż powód 2, szczególnie w przypadku tego pytania
Matt b

2
@matt b, zgodził się. Lenistwo jest odpowiedzialne za sporą część błędów, które obecnie naprawiam.
JaredPar

ignorowanie kodu błędu jest cholernie szybsze i zużywa znacznie mniej kodu standardowego!
gbjbaanb

11

ponieważ zaznaczone wyjątki to eksperyment zakończony niepowodzeniem

(może printStackTrace () jest prawdziwym problemem ? :)


17
To nie jest nieudany eksperyment. Właściwie to lubię! Jak wskazano w innych odpowiedziach, programiści są leniwi i po prostu ignorują przypadki błędów. Ponieważ zaznaczone wyjątki zmuszają Cię do radzenia sobie z nimi, będziesz mieć ogólnie lepszy kod. (Jeśli programista nie ignoruje ich po cichu). Poza tym, nawet jeśli nie jesteś leniwy, nie możesz zapomnieć o zrobieniu ważnego wyjątku, ponieważ kompilator powie ci, abyś to zrobił ...
Malax

5
sprawdzane wyjątki to porażka z kilku powodów: sprawdzić niektóre z nich mindview.net/Etc/Discussions/CheckedExceptions
DFA

2
+1 nie może się bardziej zgodzić. Sprawdzone wyjątki często sprawiają, że kod jest mniej bezpieczny i jest to jeden z przykładów.
cletus

2
Zaznaczone wyjątki na pewno nie są doskonałe, ale zmuszają do zmierzenia się z rzeczywistością, że czasami sprawy nie idą zgodnie z planem. Non-sprawdzone wyjątki są po prostu staramy się udawać, że żyjemy w idealnym świecie, gdzie nic nie idzie źle
mcjabberz

2
@Peter Lawrey: sprawdzone wyjątki są porażką, ponieważ bardzo inteligentni ludzie, tacy jak ci, którzy przyszli z niesamowitą platformą Swing, rzygają na nich i odmawiają ich użycia. I bardzo wątpię, że ludzie tacy jak Joshua Bloch lub ci, którzy stoją za frameworkiem Spring, „nie wiedzą, jak ich poprawnie używać”. Jasne, można prawidłowo postępować z sprawdzonej wyjątku. Prawdziwe załamanie polega na tym, że ktoś gdzieś uważał, że rzucanie jest dopuszczalną praktyką.
SyntaxT3rr0r

6

Muszę powiedzieć, że trochę urażam ton, który sugeruje, że tego rodzaju luźne zachowanie w obsłudze błędów jest czymś fundamentalnym dla programistów Java. Jasne, programiści Java mogą być leniwi, tak jak każdy inny programista, a Java jest popularnym językiem, więc prawdopodobnie zobaczysz wiele wyjątków połykania kodu.

Ponadto, jak wskazano w innym miejscu, istnieją zrozumiałe frustracje związane z wymuszaną deklaracją sprawdzonych wyjątków przez Javę, chociaż osobiście nie mam z tym problemu.

Wydaje mi się, że mam problem z tym, że przeglądasz kilka artykułów i fragmentów kodu w Internecie bez zastanawiania się nad kontekstem. Prawda jest taka, że ​​kiedy piszesz artykuł techniczny, próbując wyjaśnić, jak działa określony interfejs API lub jak zacząć z czymś, wtedy najprawdopodobniej pominiesz niektóre aspekty kodu - obsługę błędów, która nie jest bezpośrednio związany z tym, co demonstrujesz, jest prawdopodobnym kandydatem do usunięcia, zwłaszcza jeśli wyjątek prawdopodobnie nie wystąpi w przykładowym scenariuszu.

Ludzie, którzy piszą artykuły tego rodzaju, muszą utrzymywać rozsądny stosunek sygnału do szumu, i myślę, że raczej uczciwie, to oznacza, że ​​muszą założyć, że znasz podstawy języka, w którym się rozwijasz; jak prawidłowo radzić sobie z błędami i wieloma innymi rzeczami. Jeśli natkniesz się na artykuł i zauważysz brak odpowiedniego sprawdzania błędów, to w porządku; po prostu upewnij się, że kiedy włączysz te pomysły (ale oczywiście nigdy sam kod, prawda?) do swojego kodu produkcyjnego, poradzisz sobie z tymi wszystkimi drobiazgami, które autor rozsądnie pominął, w sposób, który jest najbardziej dopasowane do tego, co rozwijasz.

Mam problem z bardzo wysokopoziomowymi artykułami wprowadzającymi, które omijają takie kwestie bez powrotu do nich, ale proszę mieć świadomość, że nie ma szczególnego „nastawienia” programistów Java dotyczących obsługi błędów; Znam wielu twoich ukochanych programistów C #, którzy też nie zawracają sobie głowy rozwiązywaniem wszystkich swoich problemów.


Ale pisanie wysokopoziomowego artykułu przy użyciu try {...} catch (IOException e) {}jest po prostu głupie (a TODO NIE pomaga). Z pewnością wprowadzi to w błąd wielu początkujących, którzy zrobią to samo. Co więcej, pisanie jest znacznie więcej niż throws IOException, co jest poprawne przez większość czasu. Może raz na artykuł nie jest to możliwe i wtedy pisanie throw wrap(e)(bez nudnego definiowania wrap) jest dobrym rozwiązaniem. Jedną z pierwszych rzeczy, na które oparłem się w Javie, jest to, że ciche połykanie jest okropne i bardzo się staram, więc nie robię tego nawet chwilowo (nawet awaria jest o wiele lepsza na dłuższą metę).
maaartinus

5

Wydruk System.out lub e.printStackTrace () - co sugeruje użycie System.out jest zwykle czerwoną flagą, co oznacza, że ​​ktoś nie zadał sobie trudu, aby wykonać sumienną pracę. Z wyjątkiem aplikacji Java na komputery stacjonarne, w większości aplikacji Java lepiej jest korzystać z rejestrowania.

Jeśli tryb niepowodzenia metody to brak operacji, zjadanie wyjątku jest całkowicie w porządku, niezależnie od tego, czy zanotujesz przyczynę (i istnienie), czy nie. Jednak częściej klauzula catch powinna obejmować jakieś wyjątkowe działanie.

Ponowne zgłoszenie wyjątku jest czymś, co najlepiej zrobić, gdy używasz catch do wyczyszczenia części pracy na poziomie, na którym niezbędne informacje są nadal dostępne, lub gdy musisz przekształcić wyjątek w typ wyjątku, który jest bardziej odpowiedni dla wywołującego.


3

Jest konsumowany tylko wtedy, gdy blok catch jest naprawdę pusty.

Jeśli chodzi o artykuły, to są one prawdopodobnie bardziej interesujące w udowadnianiu innych kwestii poza tym, jak radzić sobie z wyjątkami. Chcą tylko przejść od razu do rzeczy i mieć możliwie najkrótszy kod.

Oczywiście masz rację, jednak wyjątki powinny być rejestrowane przynajmniej, jeśli mają zostać „zignorowane”.


3

Jak zauważyli inni, widzisz to z jednego z trzech powodów:

  1. IDE wygenerowało blok try-catch
    • Kod został skopiowany i wklejony
    • Deweloper umieścił stacktrace w celu debugowania, ale nigdy nie wrócił, aby poprawnie obsłużyć wyjątek

Ostatni punkt jest najmniej prawdopodobny. Mówię to, ponieważ nie sądzę, aby ktokolwiek naprawdę debugował w ten sposób. Przechodzenie przez kod za pomocą debugera jest znacznie łatwiejszym sposobem debugowania.

Najlepszy opis tego, co należy zrobić w bloku catch można znaleźć w rozdziale 9 książki Effective Java autorstwa Joshua Blocha .


2

W C # wszystkie wyjątki są wyjątkami czasu wykonywania, ale w Javie masz wyjątki czasu wykonywania i sprawdzane wyjątki, które musisz złapać lub zadeklarować w swoich metodach. Jeśli wywołasz jakąkolwiek metodę, która ma na końcu „rzuca”, musisz albo złapać wymienione tam wyjątki, albo twoja metoda musi również je zadeklarować.

Artykuły w języku Java zwykle po prostu drukują ślad stosu lub mają komentarz, ponieważ obsługa wyjątków nie ma związku z tematem artykułu. Jednak w projektach należy coś z tym zrobić, w zależności od rodzaju wyjątku.


2

Powinieneś to zobaczyć bardzo często, jeśli programista dobrze wykonuje swoją pracę. Ignorowanie wyjątków to zła, zła praktyka! Ale jest kilka powodów, dla których niektórzy mogą to zrobić, i bardziej docenione rozwiązania:

  • "To się nie stanie!" Jasne, czasami „wiesz”, że ten Wyjątek się nie wydarzy, ale nadal bardziej stosowne jest ponowne zgłoszenie wyjątku czasu wykonania z zajętym Wyjątkiem jako „przyczyną” zamiast po prostu go zignorować. Założę się, że to nastąpi kiedyś w przyszłości. ;-)

  • Prototypowanie kodu Jeśli po prostu wpisujesz swoje rzeczy, aby sprawdzić, czy zadziała, możesz zignorować wszystkie wyjątki, które mogą wystąpić. To jedyny przypadek, w którym robię leniwy chwyt (rzucany). Ale jeśli kod okaże się przydatny, dołączam odpowiednią obsługę wyjątków.

  • „Nie wiem, co robić!” Widziałem dużo kodu, zwłaszcza kodu bibliotecznego, który połyka pojawiające się wyjątki, ponieważ w tej warstwie aplikacji nie można wykonać odpowiedniej obsługi. NIE ROBIĆ TEGO! Po prostu wyrzuć wyjątek ponownie (albo przez dodanie klauzuli throws w sygnaturze metody, albo zawinięcie wyjątku w specyficzny dla biblioteki).


3
Widząc złapanie (rzucany) wzdrygnęłam się.
Bill the Lizard

2
Absolutnie. Po prostu używam catch (Throwable) lub rzucam Throwable w kodzie, jeśli się bawię. „Prawdziwy” kod nie może go zawierać! Kropka.
Malax

Zgoda. Wzdrygam się, bo już to widziałem. Jeśli inny programista zobaczy, że napisałeś catch (Throwable), to zaszło za daleko! :)
Bill the Lizard

1
Chcesz złapać (Throwable) na najwyższym poziomie, nawet jeśli kod zamyka proces. Próba utykania z brakiem jednego wątku może nie być najlepszym pomysłem.
Tom Hawtin - tackline

2

Zawsze należy przekazywać je dalej lub odpowiednio obchodzić się z nimi w kontekście rzeczywistym. Wiele artykułów i samouczków uprości swój kod, aby uzyskać ważniejsze punkty, a jedną z najłatwiejszych do uproszczenia rzeczy jest obsługa błędów (chyba że to, co robisz, to pisanie artykułu o obsłudze błędów :)). Ponieważ kod Java sprawdzi obsługę wyjątków, najprostszą metodą dostarczenia działającego przykładu jest umieszczenie prostego cichego (lub logującego) bloku catch.

Jeśli znajdziesz to w jakimkolwiek innym kodzie niż przykładowy, możesz przesłać go dalej do TDWTF, chociaż mogą mieć już zbyt wiele przykładów :)


2

Obawiam się, że większość programistów Java nie wie, co zrobić z Wyjątkami i dość zawsze uważa to za irytację, która spowalnia kodowanie „nominalnego” przypadku. Oczywiście całkowicie się mylą, ale trudno ich przekonać, że ważne jest prawidłowe postępowanie z wyjątkami. Za każdym razem, gdy spotykam takiego programistę (zdarza się to często), podaję mu dwa wpisy do czytania:

  • słynny Thinking In java
  • Krótki i ciekawy artykuł Barry'ego Ruzka dostępny tutaj: www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html

Nawiasem mówiąc, zdecydowanie zgadzam się, że głupotą jest przechwytywanie wpisanego wyjątku w celu ponownego jego osadzenia w RuntimeException:

  • jeśli to złapiesz, OBSERWUJ.
  • w przeciwnym razie zmień sygnaturę metody, aby dodać możliwe wyjątki, z którymi mógłbyś / nie mógłbyś sobie poradzić, aby wywołujący miał szansę zrobić to samodzielnie.


2

Połączenie sprawdzonych wyjątków i interfejsów prowadzi do sytuacji, w której kod musi obsługiwać wyjątki, które nigdy nie są zgłaszane. (To samo dotyczy normalnego dziedziczenia, ale jest bardziej powszechne i łatwiejsze do wyjaśnienia za pomocą interfejsów)

Przyczyna: implementacja interfejsu nie może generować (sprawdzanych) wyjątków innych niż te zdefiniowane w specyfikacji interfejsu. Z tego powodu twórcy interfejsu, nie wiedząc, które metody klasy implementującej interfejs mogą w rzeczywistości wymagać rzucenia wyjątku, mogą określić, że wszystkie metody mogą zgłaszać co najmniej jeden typ wyjątku. Przykład: JDBC, gdzie zadeklarowano, że wszystko i jego babcia zgłaszają wyjątek SQLException.

Ale w rzeczywistości wiele metod rzeczywistych implementacji po prostu nie może zawieść, więc pod żadnym pozorem nie zgłaszają wyjątku. Kod wywołujący te metody musi nadal w jakiś sposób „obsługiwać” wyjątek, a najłatwiejszym sposobem jest połknięcie wyjątku. Nikt nie chce zaśmiecać swojego kodu pozornie bezużyteczną obsługą błędów, która nigdy nie zostanie wykonana.


1
IMHO, powinien istnieć skrót językowy do wychwytywania i ponownego zgłaszania takich wyjątków, jak błędy czasu wykonania. Zapisanie takiego skrótu wyjaśniłoby zarówno w kodzie, jak i zachowaniu, że takie wyjątki powinny być traktowane jako reprezentujące „nieoczekiwane” warunki. Połykanie jest złe, ponieważ jeśli zostanie wyrzucony wyjątek, coś jest naprawdę nie tak i wywołanie kodu powinno się o tym dowiedzieć.
supercat

supercat: Podoba mi się ten pomysł, ale może zniwelować (wątpliwą dla IMO) korzyść ze sprawdzonych wyjątków.
Erich Kitzmueller

Byłoby lepiej, gdyby sprawdzanie było charakterystyczne dla witryn typu catch / throw i wystąpień wyjątków, a nie klas wyjątków, tak aby sprawdzone wyjątki mogły być propagowane jako niezaznaczone wyjątki tej samej klasy, ale nawet zawijanie i ponowne wysyłanie byłoby znacznym ulepszeniem status quo i można go łatwo dodać jako nową funkcję. Byłoby to szczególnie przydatne w przypadkach, gdy metoda, która może sama zgłosić sprawdzony wyjątek, wywołuje inne metody, które są zadeklarowane jako zgłaszające ten sam wyjątek, ale nie oczekuje się, że faktycznie to zrobią . Jeśli jedna z tych ostatnich metod rzuca ...
superkat

... obiekt wywołujący, który oczekuje obsługi wyjątku wyrzuconego z metody zewnętrznej, nie powinien przechwytywać wyjątku wyrzuconego przez metody wewnętrzne, ponieważ nie reprezentowałby warunku, którego oczekiwał wywołanie. Gdybym projektował środowisko uruchomieniowe od podstaw, użyłbym innego mechanizmu dla sprawdzonych i niezaznaczonych wyjątków, takich, że sprawdzone wyjątki dodałyby niewielki narzut do pomyślnego wywołania funkcji, ale miałyby znacznie mniej narzutu po rzuceniu niż niezaznaczone wyjątki (między innymi , wyjątek przechwycony jako sprawdzony nie miałby śladu stosu).
supercat

1

Jak już wspomniano, wywołanie printStackTrace () nie jest tak naprawdę cichą obsługą.

Powodem tego rodzaju „połykania” wyjątków jest to, że jeśli nadal przekazujesz wyjątek w górę łańcucha, nadal musisz gdzieś obsłużyć wyjątek lub pozwolić aplikacji się zawiesić. Tak więc obsługa go na poziomie, na którym występuje ze zrzutem informacji, nie jest gorsza niż obsługa na najwyższym poziomie ze zrzutem informacji.


1

Jego leniwa praktyka - naprawdę nic poza tym.

Zwykle dzieje się tak, gdy naprawdę nie obchodzi cię wyjątek - zamiast zwiększać pracę palców.


1

Nie zgadzam się, że ponowne zgłoszenie zaznaczonego wyjątku jest lepszym pomysłem. Łowienie oznacza obsługę; jeśli musisz rzucić ponownie, nie powinieneś łapać. W takim przypadku dodałbym klauzulę throws do podpisu metody.

Powiedziałbym, że zawijanie zaznaczonego wyjątku w niezaznaczony wyjątek (np. Sposób, w jaki Spring opakowuje sprawdzony SQLException w instancję jego niesprawdzonej hierarchii) jest dopuszczalne.

Rejestrowanie można uznać za obsługę. Gdyby przykład został zmieniony tak, aby rejestrować ślad stosu przy użyciu log4j zamiast zapisywać w konsoli, czy to byłoby akceptowalne? Niewielka zmiana, IMO.

Prawdziwym problemem jest to, co jest uważane za wyjątkową i akceptowalną procedurę odzyskiwania. Jeśli nie możesz odzyskać sprawności po wyjątku, najlepiej zgłoś awarię.


1

Proszę, nigdy, przenigdy nie zawijaj zaznaczonego wyjątku w niezaznaczony wyjątek.

Jeśli zauważysz, że masz do czynienia z wyjątkami, których Twoim zdaniem nie powinno być, radzę, że prawdopodobnie pracujesz na niewłaściwym poziomie abstrakcji.

Opowiem dalej: zaznaczone i niezaznaczone wyjątki to dwie bardzo różne bestie. Zaznaczone wyjątki są podobne do starej metody zwracania kodów błędów ... tak, są nieco bardziej bolesne w obsłudze niż kody błędów, ale mają też zalety. Niezaznaczone wyjątki to błędy programowania i krytyczne awarie systemu ... innymi słowy wyjątkowe wyjątki. Próbując wyjaśnić, czym jest wyjątek, wiele osób wpada w kompletny bałagan, ponieważ nie dostrzegają różnicy w tych dwóch, bardzo różnych przypadkach.


Zawijanie w AssertionError jest dla mnie rozsądne. AssertionError to abstrakcja, która ma zastosowanie wszędzie, IMO.
Sake

Opowiem dalej: zaznaczone i niezaznaczone wyjątki to dwie bardzo różne bestie. Zaznaczone wyjątki są podobne do starej metody zwracania kodów błędów ... tak, są one nieco bardziej bolesne w obsłudze niż kody błędów, ale mają też zalety. Niezaznaczone wyjątki to błędy programistyczne i krytyczne awarie systemu ... innymi słowy wyjątkowe wyjątki. Próbując wyjaśnić, czym jest wyjątek, wiele osób wpada w kompletny bałagan, ponieważ nie dostrzegają różnicy w tych dwóch, bardzo różnych przypadkach.
CurtainDog

Prawdopodobnie jestem wśród ludzi, którzy wpadają w kompletny bałagan, jak to określiłeś. Jednak moim prawdziwym celem jest to, dlaczego pozwalasz na kontynuowanie wykonywania, podczas gdy możesz je przerwać. (za pomocą AssertionError lub, jeśli wolisz, zmień interfejs, aby uwzględnić dodatkowy wyjątek.
Powitaj

1
Nie mogę bardziej nie zgodzić się z podstawowym stwierdzeniem tej odpowiedzi. Opracowanie ma zalety.
Lawrence Dol

@CurtainDog: zaznaczone wyjątki dotyczą „oczekiwanych” problemów. Jeśli metoda x zgłasza (zaznaczone) wyjątek FooException w niektórych przypadkach, a metoda y, która wywołuje x, nie spodziewa się wystąpienia takich przypadków, to jeśli takie przypadki się pojawią, reprezentują nieoczekiwany problem; nawet jeśli zdarzy się, że dalej na stosie wywołań znajduje się coś, co oczekuje a FooException, obecna sytuacja nie będzie zgodna z tym, czego spodziewałby się wywołujący nadrzędny po FooExceptionwyrzuceniu a.
supercat

1

Ponieważ jeszcze nie nauczyli się tej sztuczki:

class ExceptionUtils {
    public static RuntimeException cloak(Throwable t) {
        return ExceptionUtils.<RuntimeException>castAndRethrow(t);
    }

    @SuppressWarnings("unchecked")
    private static <X extends Throwable> X castAndRethrow(Throwable t) throws X {
        throw (X) t;
    }
}

class Main {
    public static void main(String[] args) { // Note no "throws" declaration
        try {
            // Do stuff that can throw IOException
        } catch (IOException ex) {
            // Pretend to throw RuntimeException, but really rethrowing the IOException
            throw ExceptionUtils.cloak(ex);
        }
    }
}

to fajna sztuczka, ale osobiście wolałbym rzucić bardziej szczegółowy wyjątek z przyczyną coś w rodzaju `` wrzuć nowy ServiceUnavailableException (ex) ''
Greg

metoda rethrow ExceptionUtils Apache robi dokładnie to. To dobra sztuczka. github.com/apache/commons-lang/blob/master/src/main/java/org/…
Lluis Martinez

1

Prawdziwym punktem wyjątków jest uproszczenie obsługi błędów i oddzielenie go od wykrywania błędów. Jest to w przeciwieństwie do przedstawiania błędów za pomocą kodów błędów, gdzie kod obsługi błędów jest rozproszony wszędzie, a każde połączenie, które może się nie powieść, jest sprawdzane pod kątem kodu zwrotnego.

Jeśli wyjątek reprezentuje błąd (co jest w większości przypadków), zwykle najrozsądniejszym sposobem jego rozwiązania jest wyskoczenie i pozostawienie obsługi jakiejś wyższej warstwie. Należy rozważyć ponowne zgłoszenie innego wyjątku, jeśli zostanie do niego dodana znacząca semantyka, tj. Ten błąd jest nietypową awarią systemu / tymczasowym problemem (sieciowym) / jest to błąd po stronie klienta lub serwera itp.

Ze wszystkich strategii obsługi błędów, najbardziej ignoranci ukrywają lub po prostu wyświetlają komunikat o błędzie i idą dalej, ponieważ nic się nie wydarzyło.


Ludzie Sun chcieli, aby kod był bardziej przejrzysty i zmuszali programistów do pisania, które wyjątki mogą być generowane za pomocą jakiej metody. Wydawało się, że to właściwy ruch - każdy będzie wiedział, czego oczekiwać w zamian po wywołaniu metody, biorąc pod uwagę jej prototyp (może zwrócić wartość tego typu lub wyrzucić instancję jednej z określonych klas (lub jej podklasy)).

Ale jak się okazało, wielu ignorantów programistów Java traktują teraz obsługę wyjątków tak, jakby była to błąd języka / „funkcja”, która wymagała obejścia i pisania kodu w najgorszy lub prawie najgorszy możliwy sposób:

  • Błąd jest obsługiwany od razu w kontekście nieodpowiednim do podjęcia decyzji, co z nim zrobić.
  • Jest wyświetlany lub ignorowany po cichu, a przetwarzanie jest kontynuowane nawet wtedy, gdy dalszy kod nie ma szans na poprawne działanie.
  • Obiekt wywołujący metodę nie może odróżnić, czy zakończyła się pomyślnie, czy nie.

Jak napisać „we właściwy sposób” niż?

  • Wskaż każdą klasę bazową wyjątków, które mogą być zgłaszane w nagłówku metody. AFAICR Eclipse może to zrobić automatycznie.
  • Spraw, aby lista rzutów w prototypie metody miała znaczenie. Długie listy są bezcelowe, a „rzucanie wyjątków” jest leniwe (ale przydatne, gdy nie przejmujesz się zbytnio wyjątkami).
  • Pisząc w "zły sposób", prosty "rzut Exception" jest znacznie lepszy i zajmuje mniej bajtów niż "try {...} catch (Exception e) {e.printStackTrace ();}".
  • W razie potrzeby wyślij ponownie łańcuch wyjątku.

0

Jeśli chcesz, aby wyjątek był obsługiwany poza zakresem bieżącej metody, nie musisz go w rzeczywistości przechwytywać, zamiast tego dodajesz instrukcję „throws” do podpisu metody.

Instrukcje try / catch, które widzieliście, znajdują się tylko w kodzie, w którym programista jawnie zdecydował się obsłużyć wyjątek w miejscu, a zatem nie rzucać go dalej.


1
Nieprawda - jeśli implementujesz metodę interfejsu lub zastępujesz metodę nadklasy, która nie ma deklaracji „rzuca”, nie możesz jej po prostu dodać. W takich przypadkach musisz albo opakować wyjątek w RuntimeException, albo go połknąć.
Martin McNulty

0

Myślę, że programiści również starają się wziąć pod uwagę znaczenie „robienia właściwych rzeczy” w określonym kontekście. Często zdarza się, że wyrzucenie kodu lub propagowanie wyjątku w górę niczego nie kupi, ponieważ wyjątek jest fatalny, równie dobrze można zaoszczędzić czas i wysiłek, „robiąc niewłaściwą rzecz”.


0

Zwykle połknąłbyś wyjątek, gdy nie możesz go odzyskać, ale nie jest to krytyczne. Dobrym przykładem jest IOExcetion, które może zostać wyrzucone podczas zamykania połączenia z bazą danych. Czy naprawdę chcesz się zawiesić, jeśli tak się stanie? Dlatego DBUtils w Dżakarcie mają metody closeSilently.

Teraz dla sprawdzonego wyjątku, którego nie możesz odzyskać, ale jest krytyczny (zwykle z powodu błędów programowania), nie połykaj ich. Myślę, że wyjątki powinny być rejestrowane jak najbliżej źródła problemu, więc nie polecałbym usuwania wywołania printStackTrace (). Będziesz chciał przekształcić je w RuntimeException wyłącznie po to, aby nie musieć deklarować tych wyjątków w swojej metodzie biznesowej. Naprawdę nie ma sensu mieć wysokopoziomowych metod biznesowych, takich jak createClientAccount () generuje ProgrammingErrorException (czytaj SQLException), nic nie możesz zrobić, jeśli masz literówki w swoim sql lub uzyskujesz dostęp do złych indeksów.


0

Z doświadczenia wynika, że ​​połknięcie wyjątku jest szkodliwe głównie wtedy, gdy nie jest drukowane. Pomaga zwrócić uwagę w przypadku awarii i czasami robię to celowo, ale po prostu wydrukowanie wyjątku i kontynuowanie pozwala znaleźć problem i go naprawić, ale zwykle nie wpływa negatywnie na innych pracujących na tej samej bazie kodu .

Właściwie to Fail-Fast / Fail-HARD, ale przynajmniej wydrukuj to. Jeśli faktycznie „zjadasz” wyjątek (czyli naprawdę nic nie robić: {}) Znalezienie go będzie kosztować kogoś DNI.

Problem polega na tym, że Java zmusza cię do złapania wielu rzeczy, o których programista wie, że nie zostaną wyrzucone lub nie dba o to, czy tak jest. Najpopularniejszym jest Thread.sleep (). Zdaję sobie sprawę, że są tu dziury, które mogą powodować problemy z gwintowaniem, ale ogólnie wiesz, że nie przerywasz tego. Kropka.


0

Może być wiele powodów, dla których można by użyć catch Exception. W wielu przypadkach jest to zły pomysł, ponieważ przechwytujesz również RuntimeExceptions - a cóż, nie wiesz, w jakim stanie będą się znajdować obiekty bazowe po tym, jak to się stanie? To zawsze jest trudne w nieoczekiwanych warunkach: czy możesz ufać, że reszta kodu nie zawiedzie później.

Twój przykład drukuje ślad stosu, więc przynajmniej będziesz wiedział, jaka mogła być główna przyczyna. W większych projektach oprogramowania lepiej jest rejestrować te rzeczy. I miejmy nadzieję, że składnik dziennika nie wyrzuci wyjątków, co może skończyć się nieskończoną pętlą (która prawdopodobnie zabije Twoją maszynę JVM).


0

Jeśli masz zaznaczony wyjątek i nie chcesz obsługiwać go w metodzie, powinieneś po prostu spowodować, że metoda wyrzuci wyjątek. Wyłapuj i obsługuj wyjątki tylko wtedy, gdy masz zamiar zrobić z nim coś pożytecznego. Samo rejestrowanie nie jest zbyt przydatne w mojej książce, ponieważ użytkownicy rzadko mają czas na czytanie dzienników w poszukiwaniu wyjątków lub wiedzą, co zrobić, gdy zostanie zgłoszony wyjątek.

Chociaż zawijanie wyjątku jest opcją, nie sugerowałbym tego robić, chyba że; rzucasz inny wyjątek, aby dopasować istniejący interfejs lub naprawdę nie ma możliwości, aby taki wyjątek został zgłoszony.

BTW: Jeśli chcesz ponownie zgłosić zaznaczony wyjątek, możesz to zrobić za pomocą

try {
   // do something
} catch (Throwable e) {
   // do something with the exception
   Thread.currentThread().stop(e); // doesn't actually stop the current thread, but throws the exception/error/throwable
}

Uwaga: jeśli to zrobisz, powinieneś upewnić się, że deklaracja throws metody jest poprawna, ponieważ kompilator nie może tego zrobić za Ciebie w tej sytuacji.


Tylko FTR: Thread#stop()został w międzyczasie wycofany i wyłączony (wyrzuca UnsupportedOperationException).
maaartinus

-2

ponieważ jest to najlepsza praktyka. Myślałem, że wszyscy wiedzą.
ale zimna prawda jest taka, że ​​nikt tak naprawdę nie rozumie, jak pracować z wyjątkami. Styl obsługi błędów w języku C miał o wiele więcej sensu.


1
Tylko dlatego, że duża liczba programistów nie rozumie wyjątków, nie poprawia „powrotu błędu”. Sposób obsługi błędów w języku C był i nadal jest mechanizmem horrendalnie podatnym na błędy (zamierzonym). Połykanie wyjątków (co robi ten przykład, ale nie po cichu) jest podobne do wywoływania funkcji w C bez sprawdzania zwracanej wartości. Efekt końcowy jest taki sam - program „popełnia błędy”. Ten przykład jest tylko przykładem traktowania obsługi błędów w Javie jako złej C.
Lawrence Dol
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.