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 printStackTrace
tak:
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);
}
}
źródło
Odpowiedzi:
Zawsze myślałem, że jest to podobne do następującego scenariusza:
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:
Kiedy przyjeżdża policja, wszystkie dowody są na miejscu.
Jeśli system ma zawieść, lepiej jest szybko zawieść
Odpowiadając na pytanie:
+
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
źródło
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.
źródło
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”.źródło
źródło
Uważam, że często są dwa powody, dla których tak się dzieje
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.
źródło
ponieważ zaznaczone wyjątki to eksperyment zakończony niepowodzeniem
(może printStackTrace () jest prawdziwym problemem ? :)
źródło
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.
źródło
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 pisaniethrow wrap(e)
(bez nudnego definiowaniawrap
) 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ę).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.
źródło
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”.
źródło
Jak zauważyli inni, widzisz to z jednego z trzech powodów:
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 .
źródło
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.
źródło
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).
źródło
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 :)
źródło
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:
Nawiasem mówiąc, zdecydowanie zgadzam się, że głupotą jest przechwytywanie wpisanego wyjątku w celu ponownego jego osadzenia w RuntimeException:
źródło
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.
źródło
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.
źródło
Jego leniwa praktyka - naprawdę nic poza tym.
Zwykle dzieje się tak, gdy naprawdę nie obchodzi cię wyjątek - zamiast zwiększać pracę palców.
źródło
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ę.
źródło
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.
źródło
FooException
, obecna sytuacja nie będzie zgodna z tym, czego spodziewałby się wywołujący nadrzędny poFooException
wyrzuceniu a.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); } } }
źródło
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:
Jak napisać „we właściwy sposób” niż?
źródło
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.
źródło
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”.
źródło
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.
źródło
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.
źródło
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).
źródło
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.
źródło
Thread#stop()
został w międzyczasie wycofany i wyłączony (wyrzucaUnsupportedOperationException
).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.
źródło