Zrozumienie sprawdzonych i niesprawdzonych wyjątków w Javie

703

Tak powiedział Joshua Bloch w „ Effective Java

Użyj sprawdzonych wyjątków dla warunków do odzyskania i wyjątków czasu wykonywania dla błędów programowania (pozycja 58 w 2. edycji)

Zobaczmy, czy dobrze to rozumiem.

Oto moje rozumienie sprawdzonego wyjątku:

try{
    String userInput = //read in user input
    Long id = Long.parseLong(userInput);
}catch(NumberFormatException e){
    id = 0; //recover the situation by setting the id to 0
}

1. Czy powyższy uważa się za sprawdzony wyjątek?

2. Czy RuntimeException jest niesprawdzonym wyjątkiem?

Oto moje rozumienie niesprawdzonego wyjątku:

try{
    File file = new File("my/file/path");
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){

//3. What should I do here?
    //Should I "throw new FileNotFoundException("File not found");"?
    //Should I log?
    //Or should I System.exit(0);?
}

4. Czy powyższy kod nie może być sprawdzonym wyjątkiem? Czy mogę spróbować przywrócić taką sytuację? Czy mogę? (Uwaga: moje trzecie pytanie znajduje się catchpowyżej)

try{
    String filePath = //read in from user input file path
    File file = new File(filePath);
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){
    //Kindly prompt the user an error message
    //Somehow ask the user to re-enter the file path.
}

5. Dlaczego ludzie to robią?

public void someMethod throws Exception{

}

Dlaczego pozwalają na wyjątek? Czy wcześniejsza obsługa błędu nie jest lepsza? Po co bańkować?

6. Czy mam wykreślić dokładny wyjątek, czy zamaskować go za pomocą wyjątku?

Poniżej moje odczyty

Kiedy w Javie powinienem utworzyć sprawdzony wyjątek, a kiedy powinien to być wyjątek czasu wykonywania?

Kiedy wybrać sprawdzone i niezaznaczone wyjątki

Thang Pham
źródło
6
Mam świetny przykład niesprawdzonego wyjątku. Mam DataSeriesklasę, która przechowuje dane, które zawsze muszą pozostawać w porządku czasowym. Istnieje metoda dodania nowego DataPointna końcu DataSeries. Jeśli cały mój kod działa poprawnie w całym projekcie, DataPointnigdy nie należy go dodawać na końcu, który ma wcześniejszą datę niż ta na końcu. Każdy moduł w całym projekcie jest zbudowany z tego truizmu. Sprawdzam jednak ten warunek i rzucam niezaznaczony wyjątek, jeśli tak się stanie. Dlaczego? Jeśli tak się stanie, chcę wiedzieć, kto to robi i to naprawić.
Erick Robertson
3
Aby dodać jeszcze więcej zamieszania. Wiele osób opowiadało się za sprawdzonymi wyjątkami ~ 10 lat temu, ale dzisiejszy pogląd coraz bardziej zmierza w kierunku „sprawdzone wyjątki są złe”. (Nie zgadzam się jednak z tym)
Kaj
10
Przydaje się to do obsługi wyjątku tylko wtedy, gdy masz z nim coś pożytecznego, w przeciwnym razie powinieneś pozwolić osobie wywołującej go obsłużyć. Rejestrowanie go i udawanie, że tak się nie stało, zwykle nie jest przydatne. Ponowne rzucenie go jest bezcelowe. Zawijanie w RuntimeException nie jest tak przydatne, jak niektórzy sądzą, po prostu sprawia, że ​​kompilator przestaje ci pomagać. (IMHO)
Peter Lawrey
40
Powinniśmy przestać używać kompleksowo wprowadzających w błąd warunków sprawdzanych / niesprawdzonych wyjątków. Powinny one być nazywane zameldowania upoważniona vs check-nie-upoważnionych wyjątków.
Błogosławiony Geek
3
Myślałem również, że twoja piąta punkt public void method_name zgłasza wyjątek {} dlaczego niektórzy to robią?
Maveň ツ

Odpowiedzi:

474

Wiele osób twierdzi, że sprawdzone wyjątki (tj. Te, które należy wyraźnie złapać lub powrócić) nie powinny być w ogóle używane. Zostały one wyeliminowane na przykład w języku C #, a większość języków ich nie ma. Możesz więc zawsze rzucić podklasę RuntimeException(niezaznaczony wyjątek)

Myślę jednak, że sprawdzone wyjątki są przydatne - są one używane, gdy chcesz zmusić użytkownika interfejsu API do zastanowienia się, jak poradzić sobie z wyjątkową sytuacją (jeśli można ją odzyskać). Po prostu sprawdzone wyjątki są nadużywane na platformie Java, co sprawia, że ​​ludzie ich nienawidzą.

Oto mój rozszerzony pogląd na ten temat .

Co do poszczególnych pytań:

  1. Czy NumberFormatExceptionrozważany jest sprawdzony wyjątek?
    Nie NumberFormatExceptionjest zaznaczone (= podklasa RuntimeException). Dlaczego? Nie wiem (ale powinna istnieć metoda isValidInteger(..))

  2. Czy RuntimeExceptionniesprawdzony wyjątek?
    Tak, dokładnie.

  3. Co mam tu zrobić?
    Zależy to od tego, gdzie jest ten kod i co chcesz zrobić. Jeśli jest w warstwie interfejsu użytkownika - złap go i pokaż ostrzeżenie; jeśli jest w warstwie serwisowej - nie łap go wcale - pozwól mu się bąbelkować. Tylko nie połykaj wyjątku. Jeśli w większości przypadków wystąpi wyjątek, wybierz jeden z tych:

    • zaloguj się i wróć
    • rzuć go ponownie (zadeklaruj, że zostanie wyrzucony metodą)
    • skonstruuj nowy wyjątek, przekazując bieżący do konstruktora
  4. Czy powyższy kod nie może być sprawdzonym wyjątkiem? Czy mogę spróbować przywrócić taką sytuację? Czy mogę?
    To mogło być. Ale nic nie stoi na przeszkodzie, aby złapać również niesprawdzony wyjątek

  5. Dlaczego ludzie dodają klasę Exceptionw klauzuli rzucania?
    Najczęściej dlatego, że ludzie są leniwi, zastanawiając się, co złapać, a co rzucić. Rzucanie Exceptionjest złą praktyką i należy jej unikać.

Niestety, nie ma jednej reguły, która pozwala określić, kiedy złapać, kiedy ponownie rzucić, kiedy użyć zaznaczone, a kiedy użyć niesprawdzonych wyjątków. Zgadzam się, że powoduje to wiele zamieszania i dużo złego kodu. Ogólną zasadę określa Bloch (zacytowałeś jej część). A ogólna zasada polega na ponownym odrzuceniu wyjątku od warstwy, w której można go obsłużyć.

Bozho
źródło
36
Jeśli chodzi o zgłaszanie wyjątków, nie zawsze jest to spowodowane tym, że ludzie są leniwi. Często zdarza się, że implementując frameworki, pozwalasz użytkownikom na rzucanie dowolnych wyjątków. Możesz np. Sprawdzić podpis interfejsu Callable w JSE
Kaj
10
@Kaj - tak, takie ogólne rzeczy jak Callable, przechwytywacze i polubienia to szczególne przypadki. Ale w większości przypadków
dzieje się
7
odp: 3.1 „zaloguj się i zwróć” Zrób to rozsądnie. Jest to bardzo zbliżone do jedzenia lub ukrywania się i wyjątku. Zrobiłbym to dla czegoś, co nie wskazuje na problem, który nie jest naprawdę wyjątkowy. Dzienniki są zalewane i zbyt łatwo ignorowane.
Chris
4
„gdy chcesz zmusić użytkownika interfejsu API do myślenia, jak poradzić sobie z wyjątkową sytuacją” - nie możesz zmusić nikogo do myślenia, jeśli tego nie chce. Jeśli nie chcą myśleć, napiszą słaby blok wyjątków, który w ogóle nic nie robi lub, co gorsza, usuwa lub zakłóca krytyczne informacje o błędach. Dlatego sprawdzone wyjątki są błędem.
adrianos
3
@adrianos „… nie możesz zmusić nikogo do myślenia, jeśli nie chce…” Przy takim myśleniu moglibyśmy również usunąć błędy kompilacji… Nie jestem tak naprawdę celem, słyszałem o tym argumentami za każdym razem i wciąż uważam, że jest to najbiedniejsze możliwe wytłumaczenie oznaczenia Sprawdzonych wyjątków jako niepowodzenia. Na marginesie, widziałem wcześniej taki język, w którym kompilacja (a także błędy w czasie wykonywania) były skutecznie uniemożliwiane przez desing. Ta droga prowadziła do bardzo ciemnych miejsc.
Newtopian
233

To, czy coś jest „sprawdzonym wyjątkiem”, nie ma nic wspólnego z tym, czy go złapiesz, czy co robisz w bloku catch. Jest to właściwość klas wyjątków. Wszystko, co jest podklasą Exception z wyjątkiem dla RuntimeExceptionjej podklasy jest sprawdzana wyjątkiem.

Kompilator Java zmusza Cię do przechwycenia sprawdzonych wyjątków lub zadeklarowania ich w podpisie metody. Miało to poprawić bezpieczeństwo programu, ale wydaje się, że większość uważa, że ​​nie warto tworzyć problemów projektowych.

Dlaczego pozwalają na wyjątek? Czy błąd obsługi nie jest krótszy, tym lepiej? Po co bańkować?

Ponieważ to jest cały punkt wyjątków. Bez tej możliwości nie potrzebujesz wyjątków. Umożliwiają one obsługę błędów na wybranym przez Ciebie poziomie, zamiast zmuszać Cię do radzenia sobie z nimi przy użyciu metod niskiego poziomu, w których pierwotnie występują.

Michael Borgwardt
źródło
3
Dziękuję Ci! Czasami odrzucam wyjątki od moich metod ze względu na zasadę badziewia. Jeden z programistów w moim zespole chce wprowadzić niepoprawne wyrażenie xpath, aby zająć się wyjątkiem. W mało prawdopodobnym przypadku wychwytują wyjątek i nie robią nic, co usłyszą o tym podczas przeglądu kodu.
jeremyjjbrown
12
„Wszystko, co jest podklasą Throwable, z wyjątkiem RuntimeException i jego podklas, jest sprawdzonym wyjątkiem”. - Twoje oświadczenie jest nieprawidłowe. Błąd dziedziczy także Throwable i nie jest zaznaczony.
Bartzilla,
8
@JonasEicher: w zasadzie główną zaletą wyjątków jest to, że pozwalają one wybrać, gdzie w stosie wywołań chcesz obsługiwać błędy, które często są dość wysokie, zachowując między nimi warstwy całkowicie wolne od artefaktów obsługi błędów. Sprawdzone wyjątki niszczą dokładnie tę przewagę. Innym problemem jest to, że rozróżnienie zaznaczone / niezaznaczone jest powiązane z klasą wyjątków, która również reprezentuje koncepcyjną kategoryzację wyjątków - mieszając dwa aspekty, które niekoniecznie są ze sobą powiązane.
Michael Borgwardt
2
„ale wydaje się, że większość uważa, że ​​nie warto tworzyć problemów projektowych”. - Proszę o cytowanie?
kervin
3
@Bartzilla Tak. Dla kompletności, jak Throwablenapisano w javadoc : „Throwable i każda podklasa Throwable, która nie jest również podklasą RuntimeException lub Error, są uważane za sprawdzone wyjątki”
Bart van Heukelom
74
  1. Czy powyższe jest uważane za sprawdzony wyjątek? No Fakt, że są obsługi wyjątku nie sprawiają, że Checked Exceptionjeśli jest to RuntimeException.

  2. Czy ? takRuntimeExceptionunchecked exception

Checked Exceptionssubclassesz java.lang.Exception Unchecked Exceptionssubclasseszjava.lang.RuntimeException

Wywołania sprawdzające wyjątki muszą być ujęte w bloku try {} lub obsługiwane na wyższym poziomie w obiekcie wywołującym metodę. W takim przypadku bieżąca metoda musi zadeklarować, że zgłasza wspomniane wyjątki, aby osoby dzwoniące mogły dokonać odpowiednich ustaleń w celu obsługi wyjątku.

Mam nadzieję że to pomoże.

P: Czy mam wykreślić dokładny wyjątek, czy zamaskować go za pomocą wyjątku?

Odp .: Tak, to bardzo dobre pytanie i ważna uwaga dotycząca projektu. Wyjątek klasy jest bardzo ogólną klasą wyjątków i może być używany do zawijania wewnętrznych wyjątków niskiego poziomu. Lepiej utwórz niestandardowy wyjątek i zawiń w nim. Ale i duży - nigdy nie jest niejasny w pierwotnej pierwotnej przyczynie. Na przykład Don't everwykonaj następujące czynności -

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException("Cannot login!!"); //<-- Eat away original root cause, thus obscuring underlying problem.
}

Zamiast tego wykonaj następujące czynności:

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException(sqle); //<-- Wrap original exception to pass on root cause upstairs!.
}

Usunięcie pierwotnej przyczyny źródłowej powoduje, że faktyczna przyczyna nie do odzyskania jest koszmarem dla zespołów wsparcia produkcji, do których mają dostęp tylko do dzienników aplikacji i komunikatów o błędach. Chociaż ten ostatni jest lepszym projektem, ale wiele osób nie korzysta z niego często, ponieważ programiści po prostu nie przekazują podstawowej wiadomości dzwoniącemu. Zrób więc Always pass on the actual exceptionwyraźną notatkę: z powrotem, niezależnie od tego, czy jest zawarty w wyjątku specyficznym dla aplikacji.

Przy próbie łapania RuntimeExceptions

RuntimeExceptionZ zasady nie należy próbować łapać. Zazwyczaj sygnalizują błąd programowania i należy je pozostawić w spokoju. Zamiast tego programista powinien sprawdzić stan błędu przed wywołaniem kodu, który może spowodować błąd RuntimeException. Na przykład:

try {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welcome to my site!);
} catch (NullPointerException npe) {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

To zła praktyka programowania. Zamiast tego należy wykonać kontrolę zerową, jak -

if (userObject != null) {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!);
} else {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

Ale są chwile, kiedy takie sprawdzanie błędów jest kosztowne, takie jak formatowanie liczb, rozważ to -

try {
    String userAge = (String)request.getParameter("age");
    userObject.setAge(Integer.parseInt(strUserAge));
} catch (NumberFormatException npe) {
   sendError("Sorry, Age is supposed to be an Integer. Please try again.");
}

W tym przypadku sprawdzanie błędów przed wywołaniem nie jest warte wysiłku, ponieważ zasadniczo oznacza zduplikowanie całego kodu konwersji łańcucha na liczbę całkowitą w metodzie parseInt () - i jest podatne na błędy, jeśli zostanie zaimplementowane przez programistę. Lepiej więc po prostu zrezygnować z try-catch.

Tak więc NullPointerExceptioni NumberFormatExceptionoba RuntimeExceptions, łapanie a NullPointerExceptionpowinno zostać zastąpione wdzięcznym zerowaniem, podczas gdy ja polecam NumberFormatExceptionjawne łapanie, aby uniknąć możliwego wprowadzenia kodu podatnego na błędy.

d-live
źródło
Dziękuję Ci. Jeszcze jedno pytanie, kiedy wyrzucacie exceptionbąbelki, czy powinienem wykreślić dokładny wyjątek lub zamaskować go za pomocą Exception. Piszę kod na jakimś starszym kodzie i jestem Exceptionrozrzucony po całym świecie. Zastanawiam się, czy to jest właściwe zachowanie?
Thang Pham
1
To bardzo dobre i ważne pytanie, zredagowałem moją odpowiedź, aby dołączyć wyjaśnienie.
d-live
Dziękuję Ci bardzo. Czy byłbyś w stanie pokazać mi treść LoginFailureException(sqle)?
Thang Pham,
1
Nie mam żadnego kodu dla tych rzeczy, właśnie przygotowałem nazwy itp. Jeśli widzisz java.lang.Exception, ma 4 konstruktory, z których dwa akceptują java.lang.Throwable. W powyższych fragmentach założyłem, że LoginFailureExceptionrozszerza Exceptioni ogłasza konstruktorapublic LoginFailureException(Throwable cause) { super(cause) }
d-live
Najlepsza odpowiedź na ten temat. Myślę, że wyjątki czasu wykonywania nie powinny być wychwytywane, ponieważ wyjątki te występują z powodu braku dobrego programowania. Całkowicie zgadzam się z częścią „Usunięcie pierwotnej pierwotnej przyczyny ukrywa rzeczywistą przyczynę poza odzyskiwaniem to koszmar dla zespołów wsparcia produkcji, do których mają dostęp tylko do dzienników aplikacji i komunikatów o błędach”.
huseyin
19

1. Jeśli nie masz pewności co do wyjątku, sprawdź interfejs API:

 java.lang.Object
 extended by java.lang.Throwable
  extended by java.lang.Exception
   extended by java.lang.RuntimeException  //<-NumberFormatException is a RuntimeException  
    extended by java.lang.IllegalArgumentException
     extended by java.lang.NumberFormatException

2) Tak, i każdy wyjątek, który go rozszerza.

3) Nie ma potrzeby łapania i rzucania tego samego wyjątku. W takim przypadku możesz wyświetlić nowe okno dialogowe pliku.

4 FileNotFoundException to już sprawdzonym wyjątkiem.

5 Jeśli oczekuje się, że wywołanie metodysomeMethod złapie wyjątek, ten drugi może zostać wyrzucony. Po prostu „podaje piłkę”. Przykładem tego może być użycie własnych metod prywatnych i obsłużenie wyjątku w metodzie publicznej.

Dobrym czytaniem jest sam dokument Oracle: http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html

Dlaczego projektanci zdecydowali się wymusić metodę określania wszystkich nieprzechwyconych sprawdzonych wyjątków, które mogą być zgłaszane w jej zakresie? Wszelkie wyjątki, które mogą zostać zgłoszone przez metodę, są częścią publicznego interfejsu programowania metody. Ci, którzy wywołują metodę, muszą wiedzieć o wyjątkach, które metoda może zgłosić, aby mogli zdecydować, co z nimi zrobić. Te wyjątki są w równym stopniu częścią interfejsu programowania tej metody, jak jej parametry i zwracana wartość.

Następne pytanie może brzmieć: „Jeśli tak dobrze jest udokumentować interfejs API metody, w tym wyjątki, które może zgłaszać, dlaczego nie określić też wyjątków czasu wykonywania?” Wyjątki w czasie wykonywania reprezentują problemy wynikające z problemów programistycznych i dlatego nie można oczekiwać, że kod klienta API odzyska od nich lub w jakikolwiek sposób sobie z nimi poradzi. Takie problemy obejmują wyjątki arytmetyczne, takie jak dzielenie przez zero; wyjątki wskaźnika, takie jak próba dostępu do obiektu przez odwołanie zerowe; oraz indeksowanie wyjątków, takich jak próba dostępu do elementu tablicy za pośrednictwem indeksu, który jest zbyt duży lub zbyt mały.

W specyfikacji języka Java znajduje się również ważna informacja :

Sprawdzone klasy wyjątków wymienione w klauzuli throws są częścią umowy między implementatorem a użytkownikiem metody lub konstruktora .

Najważniejsze jest to, że możesz złapać dowolne RuntimeException, ale nie jest to wymagane i, w rzeczywistości, wdrożenie nie jest wymagane do zachowania tych samych niezgłoszonych wyjątków, ponieważ nie są one częścią umowy.

Aleadam
źródło
Dziękuję Ci. Jeszcze jedno pytanie, kiedy wyrzucacie exceptionbąbelki, czy powinienem wykreślić dokładny wyjątek lub zamaskować go za pomocą Exception. Piszę kod na jakimś starszym kodzie i jestem Exceptionrozrzucony po całym świecie. Zastanawiam się, czy to jest właściwe zachowanie?
Thang Pham
1
@Harry Pozwolę, aby ludzie z większą wiedzą ode mnie odpowiedzieli: stackoverflow.com/questions/409563/...
Aleadam
10

1) Nie, wyjątek NumberFormatException jest niezaznaczonym wyjątkiem. Nawet jeśli go złapałeś (nie jest to wymagane), ponieważ nie jest zaznaczony. Jest tak, ponieważ jest to podklasa, IllegalArgumentExceptionktórej jest podklasą RuntimeException.

2) RuntimeExceptionjest źródłem wszystkich niezaznaczonych wyjątków. Każda podklasa RuntimeExceptionnie jest zaznaczona. Wszystkie inne wyjątki i Throwablesą sprawdzane z wyjątkiem błędów (które wchodzą w zakres Throwable).

3/4) Możesz ostrzec użytkownika, że ​​wybrał nieistniejący plik i poprosić o nowy. Lub po prostu przestań informować użytkownika, że ​​wprowadził coś nieprawidłowego.

5) Rzucanie i łapanie 'Exception'jest złą praktyką. Ale bardziej ogólnie, możesz wprowadzić inne wyjątki, aby osoba dzwoniąca mogła zdecydować, jak sobie z tym poradzić. Na przykład, jeśli napisałeś bibliotekę do obsługi odczytu danych wejściowych pliku, a do Twojej metody przekazano nieistniejący plik, nie masz pojęcia, jak sobie z tym poradzić. Czy dzwoniący chce zapytać ponownie lub wyjść? Rzucasz wyjątek w górę łańcucha z powrotem do dzwoniącego.

W wielu przypadkach unchecked Exceptionwystępuje, ponieważ programista nie zweryfikował danych wejściowych (w przypadku NumberFormatExceptionpierwszego pytania). Właśnie dlatego ich złapanie jest opcjonalne, ponieważ istnieją bardziej eleganckie sposoby uniknięcia generowania tych wyjątków.

dontocsata
źródło
Dziękuję Ci. Jeszcze jedno pytanie, kiedy wyrzucacie exceptionbąbelki, czy powinienem wykreślić dokładny wyjątek lub zamaskować go za pomocą Exception. Piszę kod na jakimś starszym kodzie i jestem Exceptionrozrzucony po całym świecie. Zastanawiam się, czy to jest właściwe zachowanie?
Thang Pham
Możesz po prostu poprosić swoją metodę o zgłoszenie wyjątku (co nie jest idealne). Lub złap wyjątek i rzuć lepszy wyjątek (jak IOException lub coś takiego). Wszystkie wyjątki mogą przyjmować wyjątki w swoim konstruktorze jako „przyczynę”, dlatego należy tego użyć.
dontocsata
8

Sprawdzone - podatne na zdarzenie. Sprawdzono w czasie kompilacji.

Np. FileOperations

Niezaznaczone - z powodu złych danych. Sprawdzone w czasie wykonywania.

Na przykład..

String s = "abc";
Object o = s;
Integer i = (Integer) o;

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at Sample.main(Sample.java:9)

Tutaj wyjątek wynika z złych danych i nie można go w żaden sposób ustalić podczas kompilacji.

bharanitharan
źródło
6

Sprawdzone wyjątki są sprawdzane w czasie kompilacji przez JVM i jego powiązane z zasobami (pliki / db / stream / socket itp.). Motywem sprawdzanego wyjątku jest to, że w czasie kompilacji, jeśli zasoby nie są dostępne, aplikacja powinna zdefiniować alternatywne zachowanie, aby poradzić sobie z tym w bloku catch / wreszcie.

Niezaznaczone wyjątki to błędy czysto programowe, błędne obliczenia, zerowe dane, a nawet awarie logiki biznesowej mogą prowadzić do wyjątków w czasie wykonywania. Absolutnie dobrze jest obsługiwać / wychwytywać niesprawdzone wyjątki w kodzie.

Wyjaśnienie pochodzi z http://coder2design.com/java-interview-questions/

Jatinder Pal
źródło
5

Mój absolutnie ulubiony opis różnicy między niezaznaczonymi i sprawdzonymi wyjątkami znajduje się w artykule na temat samouczka Java, „ Niezaznaczone wyjątki - kontrowersje ” (przepraszam, że wszystkie podstawowe elementy tego postu - ale, hej, podstawy są czasami najlepsze):

Oto podstawowa wytyczna: jeśli można zasadnie oczekiwać, że klient wyzdrowieje z wyjątku, uczyń go sprawdzonym wyjątkiem. Jeśli klient nie może nic zrobić w celu odzyskania od wyjątku, uczyń z niego niesprawdzony wyjątek

Sedno „jakiego rodzaju wyjątku do rzucenia” ma charakter semantyczny (do pewnego stopnia), a powyższy cytat stanowi doskonałą wskazówkę (stąd wciąż jestem pod wrażeniem przekonania, że ​​C # pozbył się sprawdzonych wyjątków - szczególnie jak argumentuje Liskov ich przydatność).

Reszta staje się zatem logiczna: na które wyjątki kompilator oczekuje, że ja odpowiem wprost? Te, od których oczekujesz powrotu klienta.

Tomasz
źródło
5

Aby odpowiedzieć na ostatnie pytanie (inni wydają się dokładnie odpowiedzieć powyżej): „Czy mam wykreślić konkretny wyjątek, czy zamaskować go za pomocą wyjątku?”

Zakładam, że masz na myśli coś takiego:

public void myMethod() throws Exception {
    // ... something that throws FileNotFoundException ...
}

Nie, zawsze deklaruj najbardziej precyzyjny możliwy wyjątek lub listę takich. Wyjątki, które deklarujesz jako metodę do rzucania, są częścią umowy między twoją metodą a dzwoniącym. Rzucanie "FileNotFoundException"oznacza, że ​​możliwe jest, że nazwa pliku jest nieprawidłowa i plik nie zostanie znaleziony; dzwoniący będzie musiał poradzić sobie z tym inteligentnie. Rzucanie Exceptionoznacza „Hej, kurwa się zdarza. Rozdanie”. Co jest bardzo biedne API.

W komentarzach do pierwszego artykułu jest kilka przykładów, w których „rzuty Exception” są prawidłową i rozsądną deklaracją, ale nie dotyczy to większości normalkodu, który kiedykolwiek napiszesz.

Tom Dibble
źródło
Dokładnie uczyń deklarację wyjątku czekowego częścią dokumentacji kodu, a także pomagaj osobie używającej twojego oprogramowania.
Salvador Valencia,
5

Wyjątki środowiska wykonawczego : wyjątki środowiska wykonawczego są nazywane wyjątkami niezaznaczonymi. Wszystkie pozostałe wyjątki są sprawdzanymi wyjątkami i nie pochodzą one od java.lang.RuntimeException.

Sprawdzone wyjątki : Sprawdzony wyjątek musi zostać wykryty gdzieś w kodzie. Jeśli wywołasz metodę, która zgłasza sprawdzony wyjątek, ale nie złapiesz gdzieś sprawdzonego wyjątku, kod nie zostanie skompilowany. Dlatego nazywane są sprawdzonymi wyjątkami: kompilator sprawdza, czy są obsługiwane lub zadeklarowane.

Wiele metod w Java API rzuca sprawdzone wyjątki, więc często będziesz pisać procedury obsługi wyjątków, aby poradzić sobie z wyjątkami wygenerowanymi przez metody, których nie napisałeś.

Omer
źródło
3

Dlaczego pozwalają na wyjątek? Czy wcześniejsza obsługa błędu nie jest lepsza? Po co bańkować?

Załóżmy na przykład, że masz aplikację typu klient-serwer, a klient wysłał żądanie dotyczące jakiegoś zasobu, którego nie można znaleźć, lub błędu, który mógł wystąpić po stronie serwera podczas przetwarzania żądania użytkownika, więc jest to obowiązek serwera, aby powiedzieć klientowi, dlaczego nie mógł dostać rzeczy, o którą prosił, więc aby to osiągnąć po stronie serwera, kod jest zapisywany w celu wyrzucenia wyjątku za pomocą słowa kluczowego throw zamiast połykania lub obsługi. jeśli serwer obsługuje / połknie to nie będzie szansy na poinformowanie klienta, że ​​wystąpił błąd.

Uwaga: Aby jasno opisać, jaki wystąpił typ błędu, możemy utworzyć własny obiekt wyjątku i przekazać go klientowi.

JAWA
źródło
Słuszna uwaga. Oznacza to przeniesienie go do najwyższej, najbardziej odpowiedzialnej warstwy, która kontroluje przepływ logiki i nadzoruje logikę biznesową aplikacji. Na przykład warstwa bazy danych nie byłaby w stanie poinformować klienta, że ​​brakuje czegoś krytycznego lub nie odpowiada. Gdy pojawi się on na najwyższej warstwie serwera, od razu można odświeżyć widok klienta za pomocą krytycznego komunikatu o błędzie.
Salvador Valencia,
3

Myślę, że sprawdzone wyjątki są dobrym przypomnieniem dla programisty korzystającego z zewnętrznej biblioteki, że w wyjątkowych sytuacjach może dojść do awarii kodu z tej biblioteki.

Przeczytaj więcej na temat sprawdzonych vs niesprawdzonych wyjątków tutaj http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/

Dan Moldovan
źródło
3

Chcę tylko dodać powód, dla którego w ogóle nie używam sprawdzonych wyjątków. To nie jest pełna odpowiedź, ale czuję, że odpowiada ona na część twojego pytania i uzupełnia wiele innych odpowiedzi.

Ilekroć w grę wchodzą zaznaczone wyjątki, throws CheckedExceptiongdzieś w metodzie jest podpis ( CheckedExceptionmoże to być dowolny sprawdzony wyjątek). Podpis NIE zgłasza wyjątku, zgłaszanie wyjątków jest aspektem implementacji. Interfejsy, podpisy metod, klasy nadrzędne, wszystkie te rzeczy NIE powinny zależeć od ich implementacji. Wykorzystanie sprawdzonych wyjątków tutaj (w rzeczywistości fakt, że musisz zadeklarowaćthrows w podpisie metody) wiąże twoje interfejsy wyższego poziomu z twoimi implementacjami tych interfejsów.

Pokażę ci przykład.

Miejmy ładny i czysty interfejs taki jak ten

public interface IFoo {
    public void foo();
}

Teraz możemy napisać wiele implementacji metody foo(), takich jak te

public class Foo implements IFoo {
    @Override
    public void foo() {
        System.out.println("I don't throw and exception");
    }
}

Klasa Foo jest w porządku. Teraz zróbmy pierwszą próbę klasy Bar

public class Bar implements IFoo {
    @Override
    public void foo() {
        //I'm using InterruptedExcepton because you probably heard about it somewhere. It's a checked exception. Any checked exception will work the same.
        throw new InterruptedException();
    }
}

Bar tej klasy nie będzie się kompilował. Ponieważ wyjątek InterruptedException jest sprawdzonym wyjątkiem, musisz go przechwycić (metodą try-catch wewnątrz metody foo ()) lub zadeklarować, że go zgłaszasz (dodając throws InterruptedExceptiondo podpisu metody). Ponieważ nie chcę przechwytywać tego wyjątku tutaj (chcę, aby propagował się w górę, aby móc odpowiednio sobie z nim poradzić gdzie indziej), zmieńmy podpis.

public class Bar implements IFoo {
    @Override
    public void foo() throws InterruptedException {
        throw new InterruptedException();
    }
}

Bar tej klasy też się nie skompiluje! Metoda baru foo () NIE zastępuje metody IFoo foo (), ponieważ ich podpisy są różne. Mógłbym usunąć adnotację @Override, ale chcę programować przeciwko interfejsowi IFoo like, IFoo foo;a później zdecydować, jakiej implementacji chcę użyć foo = new Bar();. Jeśli metoda Bar foo () nie przesłania metody foo IFoo, kiedy to zrobięfoo.foo(); nie wywoła implementacji Bar foo ().

Aby public void foo() throws InterruptedExceptionnadpisać Bar, IFoo public void foo()MUSZĄ dodać throws InterruptedExceptiondo sygnatury metody IFoo. Spowoduje to jednak problemy z moją klasą Foo, ponieważ sygnatura metody foo () różni się od sygnatury metody IFoo. Ponadto, jeśli throws InterruptedExceptiondodam do metody Foo foo (), otrzymam kolejny błąd stwierdzający, że metoda Foo foo () deklaruje, że zgłasza InterruptedException, ale nigdy nie zgłasza InterruptedException.

Jak widać (gdybym wykonał przyzwoitą robotę tłumacząc te rzeczy), fakt, że rzucam sprawdzony wyjątek, taki jak InterruptedException, zmusza mnie do powiązania mojego interfejsu IFoo z jedną z jego implementacji, co z kolei powoduje spustoszenie w IFoo inne wdrożenia!

Jest to jeden z głównych powodów, dla których sprawdzone wyjątki są ZŁE. W czapki

Jednym z rozwiązań jest przechwycenie zaznaczonego wyjątku, zawinięcie go w niesprawdzony wyjątek i wyrzucenie niesprawdzonego wyjątku.

Blueriver
źródło
2
Tak, jest źle, ponieważ powiedziałeś, że nie chcesz tego złapać. Ale aby nie wpłynąć na podpis IFOO, musisz to zrobić. Wolę to zrobić i przejść dalej, zamiast ponownie balansować wszystkie sygnatury interfejsów, aby uniknąć wyłapania wyjątku (tylko dlatego, że wyjątki są ZŁE).
Salvador Valencia,
Tak! Zgadzam się. Byłem trochę niejasny. Chcę propagować wyjątek, aby zająć się nim gdzie indziej. Jednym z rozwiązań jest wychwycenie wyjątku InterruptedException i zgłoszenie niesprawdzonego wyjątku. tzn. unikamy zaznaczonych wyjątków i omijamy niezaznaczone wyjątki (nawet jeśli mają one sens tylko jako opakowanie)
Blueriver
„To jednak spowoduje problemy z moją klasą Foo, ponieważ jej sygnatura metody foo () różni się od sygnatury metody IFoo”. Właśnie przetestowałem twój pomysł i można go skompilować, nawet jeśli dodamy throws InterruptedExceptiondo sygnatury metody IFoo bez rzucania czegokolwiek w jakąkolwiek implementację. Więc tak naprawdę nie powoduje żadnego problemu. Jeśli w interfejsie ustawiasz każdą metodę rzucania sygnaturami Exception, daje to po prostu implementacji możliwość rzucenia wyjątku (lub wyjątku, ponieważ Exceptionzawiera wszystkie wyjątki).
Flyout91
Przyznaję jednak, że może to być mylące, ponieważ kiedy zaimplementujesz taki interfejs i klikniesz coś w rodzaju „Dodaj niezaimplementowane metody”, zostaną one automatycznie utworzone z throw Exceptionklauzulą ​​w podpisie, nawet jeśli twoja implementacja niczego nie wyrzuci lub może być bardziej szczegółowy wyjątek. Ale nadal czuję, że dobrą praktyką jest zawsze zgłaszanie wyjątku dla metody interfejsu, ponieważ znowu daje to użytkownikowi możliwość rzucania lub nie rzucania czymkolwiek.
Flyout91
To pomija cały punkt. Interfejs ma na celu zadeklarowanie umowy, którą klient musi spełnić. Może to obejmować scenariusze awarii, które jest w stanie obsłużyć. Gdy implementacja napotka błąd, powinna odwzorować ten błąd na odpowiednią abstrakcyjną awarię zadeklarowaną przez interfejs klienta.
erickson,
3
  • Java rozróżnia dwie kategorie wyjątków (zaznaczone i niezaznaczone).
  • Java wymusza przechwycenie lub zadeklarowane wymaganie dla sprawdzonych wyjątków.
  • Typ wyjątku określa, czy wyjątek jest zaznaczony, czy niezaznaczony.
  • Wszystkie typy wyjątków, które są bezpośrednie lub pośrednie subclassesdla klasy, RuntimeException są niezaznaczone.
  • Wszystkie klasy, które dziedziczą z klasy, Exceptionale nie RuntimeExceptionsą uważane za checked exceptions.
  • Klasy dziedziczące po klasie Error są uważane za niezaznaczone.
  • Kompilator sprawdza każde wywołanie metody i opóźnienie, aby ustalić, czy metoda wyrzuca checked exception.
    • Jeśli tak, kompilator zapewnia przechwycenie wyjątku lub zadeklarowanie go w klauzuli throws.
  • Aby spełnić część deklarującą część catch-or-deklarate, metoda generująca wyjątek musi dostarczyć throwsklauzulę zawierającą checked-exception.
  • Exception klasy są definiowane do sprawdzania, gdy są uważane za wystarczająco ważne, aby je złapać lub zadeklarować.
tokhi
źródło
2

Oto prosta zasada, która może pomóc w podjęciu decyzji. Jest to związane ze sposobem używania interfejsów w Javie.

Weź swoją klasę i wyobraź sobie, że projektujesz dla niej interfejs w taki sposób, aby opisywał on funkcjonalność klasy, ale żadnej implementacji bazowej (tak jak powinien interfejs). Udawaj, że możesz zaimplementować klasę w inny sposób.

Spójrz na metody interfejsu i rozważ wyjątki, które mogą generować:

Jeśli wyjątek może zostać zgłoszony za pomocą metody, niezależnie od podstawowej implementacji (innymi słowy, opisuje tylko funkcjonalność), prawdopodobnie powinien to być sprawdzony wyjątek w interfejsie.

Jeśli wyjątek jest spowodowany przez implementację bazową, nie powinien znajdować się w interfejsie. Dlatego musi to być albo niesprawdzony wyjątek w twojej klasie (ponieważ niesprawdzone wyjątki nie muszą pojawiać się w podpisie interfejsu), albo musisz go owinąć i ponownie zgłosić jako sprawdzony wyjątek, który jest częścią metody interfejsu.

Aby zdecydować, czy należy zawijać i ponownie rzucać, należy ponownie rozważyć, czy użytkownik interfejsu ma natychmiastowe zajęcie się warunkiem wyjątku, czy wyjątek jest tak ogólny, że nie można nic z tym zrobić i należy propaguj stos. Czy zawinięty wyjątek ma sens, gdy wyraża się go jako funkcjonalność nowego interfejsu, który definiujesz, czy jest to po prostu nośnik worek możliwych błędów, które mogą się przytrafić także w przypadku innych metod? Jeśli to pierwsze, może być nadal sprawdzonym wyjątkiem, w przeciwnym razie powinno zostać odznaczone.

Zwykle nie powinieneś planować wyjątków od baniek (łapania i rzucania). Albo wyjątek powinien być obsłużony przez osobę dzwoniącą (w którym to przypadku jest zaznaczone), albo powinien przejść aż do obsługi wysokiego poziomu (w takim przypadku najłatwiej jest, jeśli nie jest zaznaczony).

rghome
źródło
2

Aby zaznaczyć, że jeśli wrzucisz sprawdzony wyjątek do kodu, a połowu jest kilka poziomów powyżej, musisz zadeklarować wyjątek w podpisie każdej metody między tobą a połowem. Tak więc enkapsulacja jest zerwana, ponieważ wszystkie funkcje na ścieżce rzucania muszą wiedzieć o szczegółach tego wyjątku.

Zoran Pandovski
źródło
2

Krótko mówiąc, wyjątki, które moduł lub moduły powyżej mają obsługiwać w czasie wykonywania, nazywane są sprawdzonymi wyjątkami; inni są niesprawdzonych wyjątki, które albo RuntimeExceptionalbo Error.

W tym filmie wyjaśniono sprawdzone i niezaznaczone wyjątki w Javie:
https://www.youtube.com/watch?v=ue2pOqLaArw

Ozgen
źródło
1

Wszystkie są sprawdzonymi wyjątkami. Niezaznaczone wyjątki to podklasy RuntimeException. Decyzja nie polega na tym, jak sobie z nimi poradzić, należy je wyrzucić. Jeśli nie chcesz, aby kompilator informował Cię, że nie obsłużyłeś wyjątku, użyj niesprawdzonego wyjątku (podklasa RuntimeException). Powinny być zapisane na sytuacje, z których nie można odzyskać, na przykład błędy braku pamięci i tym podobne.

mamboking
źródło
um. jeśli NumberFormatException jest sprawdzonym wyjątkiem, jak mówisz, czy nie jest to sprzeczne z faktem, że jest dziedziczony z RuntimeException ?
eis
Przepraszam, nie byłem bardzo jasny. Miałem na myśli wyjątek FileNotFoundException, a nie NumberFormatException. W oparciu o jego # 2 i # 4 wydawało się, że myślał, że Checked vs. Unchecked był oparty na tym, jak poradziłeś sobie z wyjątkiem po jego złapaniu. Nie tak to zostało zdefiniowane.
mamboking
-1

Jeśli komuś zależy na kolejnym dowodzie, by nie lubić sprawdzonych wyjątków, zobacz kilka pierwszych akapitów popularnej biblioteki JSON:

„Chociaż jest to sprawdzony wyjątek, rzadko można go odzyskać. Większość osób dzwoniących powinna po prostu owinąć ten wyjątek w niesprawdzony wyjątek i ponownie:

Dlaczego więc na świecie ktoś miałby zmuszać deweloperów do sprawdzania wyjątku, skoro zamiast tego powinniśmy go „po prostu owinąć”? lol

http://developer.android.com/reference/org/json/JSONException.html

Sławomir
źródło
4
Ponieważ tylko większość dzwoniących, a nie wszyscy dzwoniący, powinna się łączyć i ponownie rzucać. Fakt, że wyjątek jest sprawdzany, oznacza, że ​​dzwoniący musi pomyśleć o tym, czy jest jednym z „najbardziej” dzwoniących, czy też mniejszością, która może i powinna obsłużyć wyjątek.
Warren Dew
1
Jeśli lubisz sprawdzać błędy przy każdym wykonywanym wywołaniu, „wróć” do C. Wyjątki są sposobem na oddzielenie normalnego wykonywania programu od nienormalnego, bez zanieczyszczania kodu. Wyjątki zapewniają, że nie można ignorować błędu po cichu na pewnym poziomie .
Sławomir
-1

Sprawdzone wyjątki :

  • Wyjątki sprawdzane przez kompilator pod kątem płynnego wykonywania programu w czasie wykonywania są nazywane wyjątkiem sprawdzanym.

  • Występują one w czasie kompilacji.

  • Jeśli nie są one odpowiednio obsługiwane, powodują błąd czasu kompilacji (nie wyjątek).
  • Wszystkie podklasy klasy wyjątków, z wyjątkiem RuntimeException, są sprawdzonymi wyjątkami.

    Przykład hipotetyczny - Załóżmy, że wychodzisz z domu na egzamin, ale jeśli sprawdzisz, czy wziąłeś Bilet do Hall w domu (czas kompilacji), nie będzie żadnego problemu w Egzaminie (środowisko wykonawcze).

Niezaznaczony wyjątek :

  • Wyjątki, które nie są sprawdzane przez kompilator, nazywane są Niezaznaczonymi wyjątkami.

  • Występują one w czasie wykonywania.

  • Jeśli te wyjątki nie są obsługiwane poprawnie, nie powodują błędu czasu kompilacji. Ale program zostanie przedwcześnie zakończony w czasie wykonywania.

  • Wszystkie podklasy RunTimeException i Error są niezaznaczonymi wyjątkami.

    Przykład hipotetyczny - Załóżmy, że jesteś w sali egzaminacyjnej, ale jakoś szkoła miała wypadek pożarowy (czyli w czasie wykonywania), w którym nie możesz nic zrobić w tym czasie, ale wcześniej można podjąć środki ostrożności (czas kompilacji).

Varun Vashista
źródło
-2

Wszystkie wyjątki muszą być sprawdzone.

  1. Niezaznaczone wyjątki to nieograniczone gotos. A nieograniczone gotos są uważane za coś złego.

  2. Niezaznaczone wyjątki powodują przerwanie enkapsulacji. Aby je poprawnie przetworzyć, wszystkie funkcje w drzewie połączeń między rzucającym a łapaczem muszą być znane, aby uniknąć błędów.

  3. Wyjątkiem są błędy w funkcji, która je wyrzuca, ale nie błędy w funkcji, która je przetwarza. Wyjątki mają na celu dać programowi drugą szansę poprzez odroczenie decyzji, czy jest to błąd, czy nie, w innym kontekście. Tylko w innym kontekście można podjąć właściwą decyzję.

shawnhcorey
źródło