Kiedy wybrać sprawdzone i niezaznaczone wyjątki

213

W Javie (lub innym języku z zaznaczonymi wyjątkami), kiedy tworzysz własną klasę wyjątków, jak decydujesz, czy powinna być zaznaczona, czy niezaznaczona?

Instynktownie mówię, że sprawdzany byłby wyjątek w przypadkach, w których osoba dzwoniąca mogłaby odzyskać w jakiś produktywny sposób, gdzie jako niesprawdzony wyjątek byłby bardziej skuteczny w przypadkach niemożliwych do odzyskania, ale interesowałyby mnie inne myśli.

Matt Sheppard
źródło
11
Barry Ruzek napisał doskonały przewodnik na temat wybierania sprawdzonych lub niezaznaczonych wyjątków.
sigget

Odpowiedzi:

241

Sprawdzone wyjątki są świetne, o ile wiesz, kiedy należy z nich skorzystać. Podstawowy interfejs API języka Java nie przestrzega tych zasad dla SQLException (a czasem dla IOException), dlatego są tak straszne.

Sprawdzone wyjątki powinny być stosowane w przypadku przewidywalnych , ale niemożliwych do uniknięcia błędów, których odzyskanie jest uzasadnione .

Niezaznaczone wyjątki powinny być stosowane do wszystkiego innego.

Zrobię to dla ciebie, ponieważ większość ludzi nie rozumie, co to znaczy.

  1. Przewidywalne, ale niemożliwe do uniknięcia : osoba dzwoniąca zrobiła wszystko, co w jej mocy, aby sprawdzić parametry wejściowe, ale niektóre warunki poza ich kontrolą spowodowały niepowodzenie operacji. Na przykład próbujesz odczytać plik, ale ktoś go usuwa między momentem sprawdzenia, czy plik istnieje, a momentem rozpoczęcia operacji odczytu. Deklarując zaznaczony wyjątek, informujesz osobę dzwoniącą, aby przewidziała niepowodzenie.
  2. Rozsądne odzyskanie po : Nie ma sensu informować dzwoniących, aby przewidzieli wyjątki, od których nie mogą odzyskać. Jeśli użytkownik spróbuje odczytać z nieistniejącego pliku, osoba dzwoniąca może poprosić go o nową nazwę pliku. Z drugiej strony, jeśli metoda zawiedzie z powodu błędu programistycznego (nieprawidłowe argumenty metody lub implementacja błędnej metody), aplikacja nie może nic zrobić, aby rozwiązać problem w trakcie wykonywania. Najlepsze, co może zrobić, to zarejestrować problem i poczekać, aż programista go naprawi w późniejszym czasie.

O ile wyjątek, który rzucasz, nie spełnia wszystkich powyższych warunków, powinien użyć Niezaznaczonego wyjątku.

Ponownie oceniaj na każdym poziomie : czasami metoda wychwytywania sprawdzonego wyjątku nie jest właściwym miejscem do obsługi błędu. W takim przypadku zastanów się, co jest rozsądne dla Twoich rozmówców. Jeśli wyjątek jest przewidywalny, niemożliwy do uniknięcia i uzasadniony, aby mógł je odzyskać, powinieneś sam zgłosić sprawdzony wyjątek. Jeśli nie, powinieneś zawinąć wyjątek w niesprawdzony wyjątek. Jeśli zastosujesz się do tej reguły, przekonwertujesz sprawdzone wyjątki na niesprawdzone wyjątki i odwrotnie, w zależności od tego, w jakiej jesteś warstwie.

Zarówno dla zaznaczonych, jak i niezaznaczonych wyjątków, użyj odpowiedniego poziomu abstrakcji . Na przykład repozytorium kodu z dwiema różnymi implementacjami (bazą danych i systemem plików) powinno unikać ujawniania szczegółów specyficznych dla implementacji przez rzucanie SQLExceptionlub IOException. Zamiast tego powinien zawrzeć wyjątek w abstrakcji obejmującej wszystkie implementacje (np RepositoryException.).

Gili
źródło
2
„próbujesz odczytać plik, ale ktoś go usuwa między momentem sprawdzenia, czy plik istnieje, a momentem rozpoczęcia operacji odczytu”. => Jak to się w ogóle „oczekuje”? Dla mnie to brzmi bardziej: Nieoczekiwany i można temu zapobiec. Kto by oczekiwał, że plik zostanie usunięty tylko między 2 instrukcjami?
Koray Tugay
9
@KorayTugay Oczekiwany nie oznacza, że ​​scenariusz jest typowy. Oznacza to po prostu, że możemy przewidzieć ten błąd występujący z wyprzedzeniem (w porównaniu z błędami programowania, których nie można przewidzieć z wyprzedzeniem). Nieprzewidziane odnosi się do faktu, że programiści nie mogą nic zrobić, aby uniemożliwić użytkownikowi lub innym aplikacjom usunięcie pliku między czasem, w którym sprawdzamy, czy istnieje, a czasem rozpoczęcia operacji odczytu.
Gili
Czy jakieś problemy związane z bazą danych w ramach metody muszą zgłosić sprawdzony wyjątek?
ivanjermakov
59

Od ucznia Java :

Kiedy wystąpi wyjątek, musisz albo złapać i obsłużyć wyjątek, albo powiedzieć kompilatorowi, że nie możesz go obsłużyć, deklarując, że twoja metoda zgłasza ten wyjątek, a następnie kod, który używa twojej metody, będzie musiał obsłużyć ten wyjątek (nawet on może również zadeklarować, że zgłasza wyjątek, jeśli nie może sobie z tym poradzić).

Kompilator sprawdzi, czy zrobiliśmy jedną z dwóch rzeczy (złap lub zadeklaruj). Są to tak zwane wyjątki sprawdzone. Ale błędy i wyjątki czasu wykonywania nie są sprawdzane przez kompilator (nawet jeśli możesz wybrać przechwytywanie lub deklarowanie, nie jest to wymagane). Te dwa są nazywane wyjątkami niesprawdzonymi.

Błędy reprezentują warunki występujące poza aplikacją, takie jak awaria systemu. Wyjątki czasu wykonywania zwykle występują z powodu błędu w logice aplikacji. W takich sytuacjach nic nie możesz zrobić. W przypadku wystąpienia wyjątku czasu wykonywania należy ponownie napisać kod programu. Nie są one więc sprawdzane przez kompilator. Te wyjątki czasu wykonywania zostaną odkryte w fazie rozwoju i testowania. Następnie musimy zmienić kod w celu usunięcia tych błędów.

Espo
źródło
13
To jest ortodoksyjny pogląd. Ale jest w tym wiele kontrowersji.
artbristol
49

Używam zasady: nigdy nie używaj niezaznaczonych wyjątków! (lub gdy nie widzisz żadnej możliwości obejścia tego)

Jest bardzo silny argument przeciwny: nigdy nie używaj sprawdzonych wyjątków. Nie chcę brać udziału w debacie, ale wydaje się, że panuje powszechna zgoda co do tego, że wprowadzenie sprawdzonych wyjątków było złą decyzją z perspektywy czasu. Proszę nie strzelać do posłańca i odwoływać się do tych argumentów .

Konrad Rudolph
źródło
3
IMHO, sprawdzone wyjątki mogłyby być dużym atutem, gdyby istniał łatwy sposób na deklarację, że metody nie oczekują, że metody wywoływane w określonym bloku kodu wyrzucą pewne wyjątki (lub w ogóle) i że wszystkie sprawdzone wyjątki, które są zgłaszane wbrew takim oczekiwaniom, powinny być zawinięte w inny typ wyjątku i ponownie wprowadzone. Zakładam, że w 90% przypadków, gdy kod nie jest przygotowany na poradzenie sobie z zaznaczonym wyjątkiem, takie zawijanie i ponowne rzucanie byłoby najlepszym sposobem na poradzenie sobie z tym, ale ponieważ obsługa języka jest rzadko wykonywana.
supercat,
@ superuper Zasadniczo to wszystko: jestem zagorzałym fanem ścisłego sprawdzania typów, a sprawdzone wyjątki są logicznym rozszerzeniem tego. Całkowicie porzuciłem sprawdzone wyjątki, chociaż koncepcyjnie bardzo je lubię.
Konrad Rudolph
1
Wulgaryzmy dotyczące wyjątków, które rozwiązałby opisany przeze mnie mechanizm, polegają na tym, że jeśli foojest udokumentowane jako rzucanie barExceptionpodczas czytania poza końcem pliku i foowywołuje metodę, która rzuca, barExceptionchoć foosię tego nie spodziewa, kod, który foowywoła informację, że koniec pliku został osiągnięty, i nie będzie miał pojęcia, że ​​stało się coś nieoczekiwanego. Uważam, że ta sytuacja jest tą, w której sprawdzone wyjątki powinny być najbardziej przydatne, ale jest to również jedyny przypadek, w którym kompilator pozwoli na nieobsługiwane sprawdzone wyjątki.
supercat
@ superupat: Istnieje już prosty sposób, aby kod zrobił to, co chcesz w pierwszym komentarzu: zawiń kod w bloku try, wyłap wyjątek i zawiń wyjątek w RuntimeException i powtórz.
Warren Dew
1
@KonradRudolph supercat odnosi się do „określonego bloku kodu”; biorąc pod uwagę, że blok musi zostać zdefiniowany, deklaratywna składnia nie zmniejszyłaby znacząco wzdęć. Jeśli myślisz, że byłoby to deklaratywne dla całej funkcji, zachęciłoby to do złego programowania, ponieważ ludzie po prostu wtrąciliby się do deklaracji, zamiast faktycznie patrzeć na sprawdzone wyjątki potencjalnie wychwycone i upewniać się, że nie ma innego lepszego sposobu poradzić sobie z nimi.
Warren Dew
46

W każdym wystarczająco dużym systemie z wieloma warstwami sprawdzony wyjątek jest bezużyteczny, ponieważ w każdym razie potrzebujesz strategii na poziomie architektury, aby obsłużyć wyjątek (użyj bariery błędów)

Z zaznaczonymi wyjątkami strategia zarządzania błędami jest zarządzana przez mikro i jest nie do zniesienia w każdym dużym systemie.

Przez większość czasu nie wiesz, czy błąd można „naprawić”, ponieważ nie wiesz, w której warstwie znajduje się obiekt wywołujący interfejs API.

Powiedzmy, że tworzę API StringToInt, które konwertuje reprezentację ciągu liczb całkowitych na Int. Czy muszę zgłosić sprawdzony wyjątek, jeśli interfejs API jest wywoływany za pomocą ciągu „foo”? Czy można to odzyskać? Nie wiem, ponieważ w jego warstwie obiekt wywołujący mój interfejs API StringToInt mógł już zweryfikować dane wejściowe, a jeśli zgłoszony zostanie wyjątek, jest to błąd lub uszkodzenie danych i nie można go odzyskać dla tej warstwy.

W takim przypadku obiekt wywołujący interfejs API nie chce wychwycić wyjątku. Chce tylko, aby wyjątek „wybuchł”. Jeśli wybiorę sprawdzony wyjątek, ten rozmówca będzie miał wiele bezużytecznych bloków przechwytywania tylko po to, aby sztucznie ponownie go wyłączyć.

To, co można odzyskać, zależy w większości przypadków od osoby wywołującej API, a nie od autora API. Interfejs API nie powinien używać sprawdzonych wyjątków, ponieważ tylko niesprawdzone wyjątki pozwalają wybrać przechwycenie lub zignorowanie wyjątku.

Stephane
źródło
3
Jest to bardzo zbliżone do userstories.blogspot.com/2008/12/…
alexsmail
16
@alexsmail Internet nigdy mnie nie zaskakuje, tak naprawdę to mój blog :)
Stephane
30

Masz rację.

Niezaznaczone wyjątki są używane, aby system mógł szybko zawieść, co jest dobrą rzeczą. Powinieneś jasno określić, czego oczekuje Twoja metoda, aby działać poprawnie. W ten sposób możesz zweryfikować dane wejściowe tylko raz.

Na przykład:

/**
 * @params operation - The operation to execute.
 * @throws IllegalArgumentException if the operation is "exit"
 */
 public final void execute( String operation ) {
     if( "exit".equals(operation)){
          throw new IllegalArgumentException("I told you not to...");
     }
     this.operation = operation; 
     .....  
 }
 private void secretCode(){
      // we perform the operation.
      // at this point the opreation was validated already.
      // so we don't worry that operation is "exit"
      .....  
 }

Żeby dać przykład. Chodzi o to, że jeśli system szybko zawiedzie, będziesz wiedział, gdzie i dlaczego zawiódł. Dostaniesz ślad stosu, taki jak:

 IllegalArgumentException: I told you not to use "exit" 
 at some.package.AClass.execute(Aclass.java:5)
 at otherPackage.Otherlass.delegateTheWork(OtherClass.java:4569)
 ar ......

I będziesz wiedział, co się stało. Klasa OtherClass w metodzie „delegateTheWork” (w linii 4569) wywołała twoją klasę z wartością „exit”, nawet jeśli nie powinna itp.

W przeciwnym razie będziesz musiał posypać sprawdzanie poprawności całym kodem, co jest podatne na błędy. Ponadto czasami trudno jest wyśledzić, co poszło nie tak i możesz spodziewać się godzin frustrującego debugowania

To samo dzieje się z NullPointerExceptions. Jeśli masz klasę 700 linii z 15 metodami, która używa 30 atrybutów i żadna z nich nie może mieć wartości null, zamiast sprawdzania poprawności w każdej z tych metod pod kątem dopuszczalności zerowania, możesz ustawić wszystkie atrybuty tylko do odczytu i zweryfikować je w konstruktorze metoda fabryczna.

 public static MyClass createInstane( Object data1, Object data2 /* etc */ ){ 
      if( data1 == null ){ throw NullPointerException( "data1 cannot be null"); }

  }


  // the rest of the methods don't validate data1 anymore.
  public void method1(){ // don't worry, nothing is null 
      ....
  }
  public void method2(){ // don't worry, nothing is null 
      ....
  }
  public void method3(){ // don't worry, nothing is null 
      ....
  }

Sprawdzone wyjątki Są przydatne, gdy programista (ty lub twoi współpracownicy) zrobił wszystko dobrze, sprawdził dane wejściowe, przeprowadził testy i cały kod jest doskonały, ale kod łączy się z usługą internetową innej firmy, która może być wyłączona (lub plik używany był usunięty przez inny proces zewnętrzny itp.). Usługa internetowa może nawet sprawdzić poprawność przed próbą nawiązania połączenia, ale podczas przesyłania danych coś poszło nie tak.

W tym scenariuszu nie ma nic, co Ty lub Twoi współpracownicy możecie zrobić, aby pomóc. Ale nadal musisz coś zrobić i nie pozwolić aplikacji po prostu umrzeć i zniknąć w oczach użytkownika. Używasz do tego sprawdzonego wyjątku i obsługujesz wyjątek. Co możesz zrobić, gdy tak się stanie ?, przez większość czasu, po prostu próbując zarejestrować błąd, prawdopodobnie zapisać swoją pracę (działanie aplikacji) i przekazać wiadomość użytkownikowi . (Strona blabla nie działa, spróbuj ponownie później itp.)

Jeśli zaznaczony wyjątek zostanie nadużywany (przez dodanie „wyjątku rzucania” we wszystkich sygnaturach metod), kod stanie się bardzo delikatny, ponieważ wszyscy zignorują ten wyjątek (ponieważ jest zbyt ogólny), a jakość kodu będzie poważnie poważna zagrożone.

Jeśli nadużyjesz niezaznaczonego wyjątku, wydarzy się coś podobnego. Użytkownicy tego kodu nie wiedzą, czy coś może pójść nie tak. Pojawi się dużo prób {...} catch (Throwable t).

OscarRyz
źródło
2
Dobrze powiedziane! +1. Zawsze zaskakuje mnie to, że dzwoniący rozróżnienie (niezaznaczone) / callee (zaznaczone) nie jest bardziej oczywisty ...
VonC,
19

Oto moja „ostateczna zasada”.
Używam:

  • niezaznaczony wyjątek w kodzie mojej metody dla niepowodzenia z powodu wywołującego (który obejmuje jawną i kompletną dokumentację )
  • sprawdzono wyjątek pod kątem niepowodzenia z powodu odbiorcy, który muszę jawnie wskazać każdemu, kto chce użyć mojego kodu

W porównaniu do poprzedniej odpowiedzi jest to jasne uzasadnienie (na podstawie którego można się zgodzić lub nie zgodzić) na stosowanie jednego lub drugiego (lub obu) wyjątków.


Dla obu tych wyjątków utworzę własny niesprawdzony i zaznaczony wyjątek dla mojej aplikacji (dobra praktyka, jak wspomniano tutaj ), z wyjątkiem bardzo częstego niesprawdzonego wyjątku (takiego jak NullPointerException)

Na przykład celem tej konkretnej funkcji poniżej jest stworzenie (lub uzyskanie, jeśli już istnieje) obiektu,
co oznacza:

  • kontener obiektu do utworzenia / pobrania MUSI istnieć (odpowiedzialność CALLER
    => odznaczony wyjątek ORAZ wyczyść komentarz javadoc dla tej wywoływanej funkcji)
  • pozostałe parametry nie mogą mieć wartości zerowej
    (wybór kodera do umieszczenia go w CALLER: koder nie będzie sprawdzał parametru zerowego, ale koder DOKUMENTUJE TO)
  • wynik NIE MOŻE BYĆ NULL
    (odpowiedzialność i wybór kodu odbiorcy, wybór, który będzie bardzo interesujący dla dzwoniącego
    => zaznaczony wyjątek, ponieważ każdy dzwoniący MUSI podjąć decyzję, jeśli obiekt nie może zostać utworzony / znaleziony, i że decyzja musi zostać wykonana w czasie kompilacji: nie mogą korzystać z tej funkcji bez konieczności radzenia sobie z tą możliwością, czyli z tym sprawdzonym wyjątkiem).

Przykład:


/**
 * Build a folder. <br />
 * Folder located under a Parent Folder (either RootFolder or an existing Folder)
 * @param aFolderName name of folder
 * @param aPVob project vob containing folder (MUST NOT BE NULL)
 * @param aParent parent folder containing folder 
 *        (MUST NOT BE NULL, MUST BE IN THE SAME PVOB than aPvob)
 * @param aComment comment for folder (MUST NOT BE NULL)
 * @return a new folder or an existing one
 * @throws CCException if any problems occurs during folder creation
 * @throws AssertionFailedException if aParent is not in the same PVob
 * @throws NullPointerException if aPVob or aParent or aComment is null
 */
static public Folder makeOrGetFolder(final String aFoldername, final Folder aParent,
    final IPVob aPVob, final Comment aComment) throws CCException {
    Folder aFolderRes = null;
    if (aPVob.equals(aParent.getPVob() == false) { 
       // UNCHECKED EXCEPTION because the caller failed to live up
       // to the documented entry criteria for this function
       Assert.isLegal(false, "parent Folder must be in the same PVob than " + aPVob); }

    final String ctcmd = "mkfolder " + aComment.getCommentOption() + 
        " -in " + getPNameFromRepoObject(aParent) + " " + aPVob.getFullName(aFolderName);

    final Status st = getCleartool().executeCmd(ctcmd);

    if (st.status || StringUtils.strictContains(st.message,"already exists.")) {
        aFolderRes = Folder.getFolder(aFolderName, aPVob);
    }
    else {
        // CHECKED EXCEPTION because the callee failed to respect his contract
        throw new CCException.Error("Unable to make/get folder '" + aFolderName + "'");
    }
    return aFolderRes;
}
VonC
źródło
19

Nie chodzi tylko o zdolność do odzyskania od wyjątku. Moim zdaniem najważniejsze jest to, czy dzwoniący jest zainteresowany złapaniem wyjątku, czy nie.

Jeśli piszesz bibliotekę, która ma być używana w innym miejscu lub warstwę niższego poziomu w aplikacji, zapytaj siebie, czy osoba dzwoniąca jest zainteresowana przechwyceniem (wiedzy o) wyjątku. Jeśli nie jest, użyj niesprawdzonego wyjątku, abyś nie obciążył go niepotrzebnie.

Jest to filozofia stosowana przez wiele platform. Szczególnie przychodzą na myśl wiosna i hibernacja - przekształcają znany sprawdzony wyjątek w niesprawdzony wyjątek właśnie dlatego, że sprawdzone wyjątki są nadużywane w Javie. Jednym z przykładów, które mogę wymyślić, jest wyjątek JSONEx z json.org, który jest sprawdzonym wyjątkiem i jest w większości denerwujący - należy go odznaczyć, ale deweloper po prostu tego nie przemyślał.

Nawiasem mówiąc, przez większość czasu zainteresowanie osoby dzwoniącej wyjątkiem jest bezpośrednio skorelowane z możliwością odzyskania wyjątku, ale nie zawsze tak jest.

Yoni
źródło
13

Oto bardzo proste rozwiązanie twojego dylematu Checked / Unchecked.

Reguła 1: Pomyśl o niesprawdzonym wyjątku jako warunku sprawdzalnym przed wykonaniem kodu. na przykład…

x.doSomething(); // the code throws a NullPointerException

gdzie x ma wartość null ...… kod powinien mieć prawdopodobnie następujące ...

if (x==null)
{
    //do something below to make sure when x.doSomething() is executed, it won’t throw a NullPointerException.
    x = new X();
}
x.doSomething();

Reguła 2: Pomyśl o sprawdzonym wyjątku jako warunku niemożliwym do przetestowania, który może wystąpić podczas wykonywania kodu.

Socket s = new Socket(“google.com”, 80);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();

… W powyższym przykładzie adres URL (google.com) może być niedostępny z powodu awarii serwera DNS. Nawet w momencie, gdy serwer DNS działał i przekształcił nazwę „google.com” na adres IP, jeśli połączenie zostanie nawiązane z google.com, w dowolnym momencie posłowiem sieć może przestać działać. Po prostu nie można cały czas testować sieci przed odczytem i zapisaniem strumieni.

Są chwile, w których kod musi się po prostu wykonać, zanim będziemy mogli stwierdzić, czy jest problem. Zmuszając programistów do pisania kodu w taki sposób, aby zmusić ich do radzenia sobie z tymi sytuacjami za pośrednictwem Checked Exception, muszę dać upust swojemu twórcy Javy, który wynalazł tę koncepcję.

Zasadniczo prawie wszystkie interfejsy API w Javie są zgodne z 2 powyższymi zasadami. Jeśli spróbujesz zapisać do pliku, dysk może się zapełnić przed zakończeniem zapisu. Możliwe, że inne procesy spowodowały zapełnienie dysku. Po prostu nie ma sposobu na sprawdzenie tej sytuacji. Dla tych, którzy wchodzą w interakcję ze sprzętem, gdzie w dowolnym momencie jego użycie może się nie powieść, sprawdzone wyjątki wydają się być eleganckim rozwiązaniem tego problemu.

Jest w tym szara strefa. W przypadku, gdy potrzebnych jest wiele testów (oszałamiające, jeśli oświadczenie z dużą ilością && i ||), zgłoszony wyjątek będzie CheckedException po prostu dlatego, że jest to zbyt uciążliwe, aby rozwiązać problem - po prostu nie można powiedzieć o tym problemie jest błędem programowania. Jeśli jest dużo mniej niż 10 testów (np. „If (x == null)”), wówczas błąd programisty powinien stanowić wyjątek UncheckedException.

Sprawy stają się interesujące, gdy mamy do czynienia z tłumaczami. Czy zgodnie z powyższymi zasadami błąd składni należy traktować jako sprawdzony lub niesprawdzony wyjątek? Argumentowałbym, że jeśli można przetestować składnię języka przed jego wykonaniem, powinien to być wyjątek UncheckedException. Jeśli języka nie można przetestować - podobnie jak kod asemblera działa na komputerze osobistym, błąd składni powinien być sprawdzonym wyjątkiem.

Powyższe 2 reguły prawdopodobnie usuną 90% twojej obawy co do wyboru. Aby podsumować reguły, postępuj zgodnie z tym wzorem… 1) jeśli kod, który ma zostać wykonany, można przetestować przed jego uruchomieniem, aby działał poprawnie, a jeśli wystąpi wyjątek - inaczej błąd programisty, wyjątek powinien być wyjątkiem niezaznaczonym (podklasa RuntimeException ). 2) jeśli kod, który ma zostać wykonany, nie może zostać przetestowany przed jego wykonaniem, aby działał poprawnie, wyjątek powinien stanowić sprawdzony wyjątek (podklasa wyjątku).

użytkownik3598189
źródło
9

Możesz nazwać to sprawdzonym lub niezaznaczonym wyjątkiem; Jednak oba typy wyjątków mogą zostać przechwycone przez programistę, więc najlepszą odpowiedzią jest: zapisz wszystkie wyjątki jako niezaznaczone i udokumentuj je. W ten sposób programista korzystający z interfejsu API może zdecydować, czy chce złapać ten wyjątek i coś zrobić. Sprawdzone wyjątki są całkowitą stratą czasu każdego i sprawiają, że Twój kod jest szokującym koszmarem. Właściwe testowanie jednostek przyniesie wtedy wszelkie wyjątki, które możesz mieć do złapania i zrobienia czegoś.

Colin Saxton
źródło
1
+1 Dla wspomnienia, że ​​testy jednostkowe mogą być lepszym sposobem rozwiązania problemu, sprawdzone wyjątki mają być rozwiązane.
Keith Pinson
+1 za testowanie jednostkowe. Używanie sprawdzonych / niezaznaczonych wyjątków ma niewielki wpływ na jakość kodu. Argument, że jeśli użyje się sprawdzonych wyjątków, spowoduje to lepszą jakość kodu, to kompletny fałszywy argument!
user1697575,
7

Sprawdzony wyjątek: jeśli klient może odzyskać dane po wyjątku i chce kontynuować, użyj sprawdzonego wyjątku.

Niezaznaczony wyjątek: Jeśli klient nie może nic zrobić po wyjątku, wówczas zgłosz niezaznaczony wyjątek.

Przykład: Jeśli oczekuje się wykonania operacji arytmetycznej w metodzie A () i na podstawie danych wyjściowych z A (), należy przejść do innej operacji. Jeśli wynik jest zerowy w stosunku do metody A (), której nie oczekuje się w czasie wykonywania, oczekuje się, że zgłosi wyjątek wskaźnika zerowego Wyjątek, który jest wyjątkiem w czasie wykonywania.

Zobacz tutaj

Reachgoals
źródło
2

Zgadzam się z preferencją dla niesprawdzonych wyjątków z reguły, szczególnie przy projektowaniu API. Osoba dzwoniąca może zawsze wybrać przechwycenie udokumentowanego, niezaznaczonego wyjątku. Po prostu nie musisz niepotrzebnie zmuszać dzwoniącego do.

Sprawdzone wyjątki uważam za przydatne na niższym poziomie, jako szczegóły implementacji. Często wydaje się, że jest to lepszy przepływ mechanizmu kontroli niż konieczność zarządzania określonym błędem „kod powrotu”. Czasami może pomóc dostrzec wpływ pomysłu na zmianę kodu niskiego poziomu ... zadeklarować sprawdzony wyjątek poniżej i zobaczyć, kto musiałby się dostosować. Ten ostatni punkt nie ma zastosowania, jeśli istnieje wiele ogólnych: catch (wyjątek e) lub zgłasza wyjątek, który zwykle nie jest zbyt dobrze przemyślany.

Scott Kurz
źródło
2

Oto, czym chcę się podzielić po wielu latach doświadczeń programistycznych:

  1. Sprawdzony wyjątek. Jest to część biznesowego przypadku użycia lub przepływu połączeń, jest to część logiki aplikacji, której oczekujemy lub nie oczekujemy. Na przykład połączenie odrzucone, warunek nie jest spełniony itp. Musimy sobie z tym poradzić i wyświetlić użytkownikowi odpowiedni komunikat z instrukcjami, co się stało i co dalej (spróbuj ponownie później itp.). Zwykle nazywam to wyjątkiem przetwarzania końcowego lub „użytkownika”.

  2. Niezaznaczony wyjątek. Jest to część wyjątku programistycznego, jakiegoś błędu w programowaniu kodu oprogramowania (błąd, wada) i odzwierciedla sposób, w jaki programiści muszą używać API zgodnie z dokumentacją. Jeśli zewnętrzny dokument lib / framework mówi, że spodziewa się uzyskać dane w pewnym zakresie i niepustym, ponieważ NPE lub IllegalArgumentException zostaną zgłoszone, programista powinien się tego spodziewać i poprawnie używać API zgodnie z dokumentacją. W przeciwnym razie wyjątek zostanie zgłoszony. Zwykle nazywam to wyjątkiem przetwarzania wstępnego lub wyjątkiem „sprawdzania poprawności”.

Według odbiorców docelowych. Porozmawiajmy teraz o grupie docelowej lub grupie osób, dla których zaprojektowano wyjątki (według mojej opinii):

  1. Sprawdzony wyjątek. Grupą docelową są użytkownicy / klienci.
  2. Niezaznaczony wyjątek. Grupą docelową są programiści. Innymi słowy, niezaznaczone wyjątki są przeznaczone tylko dla programistów.

Według fazy cyklu życia aplikacji.

  1. Sprawdzony wyjątek został zaprojektowany w taki sposób, aby istniał przez cały cykl produkcyjny jako normalny i oczekiwany mechanizm, w którym aplikacja obsługuje wyjątkowe przypadki.
  2. Niezaznaczony wyjątek został zaprojektowany tylko po to, aby istniał tylko podczas tworzenia / testowania aplikacji, wszystkie powinny zostać naprawione w tym czasie i nie powinny być zgłaszane, gdy aplikacja jest już uruchomiona na produkcji.

Powodem, dla którego frameworki zwykle używają niesprawdzonych wyjątków (na przykład Spring), jest to, że frameworki nie mogą określić logiki biznesowej aplikacji, to do deweloperów należy złapanie i zaprojektowanie własnej logiki.

Denys
źródło
2

Musimy rozróżnić te dwa typy wyjątków na podstawie tego, czy jest to błąd programisty, czy nie.

  • Jeśli błąd jest błędem programisty, musi to być niesprawdzony wyjątek . Na przykład: SQLException / IOException / NullPointerException. Te wyjątki to błędy programowania. Powinny być obsługiwane przez programistę. Podczas gdy w JDBC API SQLException jest sprawdzonym wyjątkiem, w Spring JDBCTemplate jest to niesprawdzony wyjątek. Programista nie martwi się o SqlException, gdy używasz Spring.
  • Jeśli błąd nie jest błędem programisty, a przyczyna pochodzi z zewnątrz, musi to być sprawdzony wyjątek. Na przykład: jeśli plik zostanie usunięty lub uprawnienia do pliku zostaną zmienione przez kogoś innego, należy je odzyskać.

FileNotFoundException jest dobrym przykładem do zrozumienia subtelnych różnic. FileNotFoundException jest zgłaszany w przypadku, gdy plik nie zostanie znaleziony. Są dwa powody tego wyjątku. Jeśli ścieżka do pliku jest definiowana przez programistę lub pobierana od użytkownika końcowego za pośrednictwem GUI, powinien to być niesprawdzony wyjątek. Jeśli plik zostanie usunięty przez kogoś innego, powinien to być wyjątek sprawdzony.

Sprawdzony wyjątek można rozwiązać na dwa sposoby. Używają one try-catch lub propagują wyjątek. W przypadku propagacji wyjątku wszystkie metody w stosie wywołań będą ściśle powiązane ze względu na obsługę wyjątków. Dlatego musimy ostrożnie korzystać z Checked Exception.

Jeśli tworzysz wielowarstwowy system korporacyjny, musisz wybrać głównie niezaznaczony wyjątek, ale nie zapomnij użyć sprawdzonego wyjątku w przypadku, gdy nic nie możesz zrobić.

ailhanli
źródło
1

Sprawdzone wyjątki są przydatne w przypadkach, w których można odzyskać dzwoniącego (np. Niewystarczające uprawnienia, nie znaleziono pliku itp.).

Niezaznaczone wyjątki są używane rzadko, jeśli w ogóle, do informowania użytkownika lub programisty o poważnych błędach lub nieoczekiwanych warunkach w czasie wykonywania. Nie wyrzucaj ich, jeśli piszesz kod lub biblioteki, które będą używane przez innych, ponieważ mogą nie oczekiwać, że twoje oprogramowanie wyrzuci niesprawdzone wyjątki, ponieważ kompilator nie zmusza ich do przechwycenia lub zadeklarowania.

David Crow
źródło
Nie zgadzam się z twoim stwierdzeniem „Niezaznaczone wyjątki są używane rzadko, jeśli w ogóle”, w rzeczywistości powinno być odwrotnie! Podczas projektowania hierarchii wyjątków aplikacji domyślnie używaj niesprawdzonych wyjątków. Pozwól programistom zdecydować, kiedy chcą obsłużyć wyjątek (np. Nie są zmuszeni do wstawiania bloków catch lub klauzuli throws, jeśli nie wiedzą, jak sobie z tym poradzić).
user1697575,
1

Ilekroć wyjątek jest mniej prawdopodobny i możemy kontynuować nawet po jego złapaniu, i nie możemy nic zrobić, aby uniknąć tego wyjątku, możemy użyć sprawdzonego wyjątku.

Ilekroć chcemy zrobić coś sensownego, gdy zdarzają się określone wyjątki i kiedy ten wyjątek jest oczekiwany, ale nie jest pewny, możemy użyć sprawdzonego wyjątku.

Ilekroć wyjątek porusza się na różnych warstwach, nie musimy łapać go na każdej warstwie, w takim przypadku możemy użyć wyjątku wykonawczego lub zawinąć wyjątek jako wyjątek niezaznaczony.

Wyjątek środowiska wykonawczego jest używany, gdy wyjątek najprawdopodobniej wystąpi, nie ma możliwości pójścia dalej i nic nie da się odzyskać. W takim przypadku możemy podjąć środki ostrożności w odniesieniu do tego wyjątku. EX: NUllPointerException, ArrayOutofBoundsException. Te są bardziej prawdopodobne. W tym scenariuszu możemy zachować ostrożność podczas kodowania, aby uniknąć takiego wyjątku. W przeciwnym razie będziemy musieli pisać try catch catch wszędzie.

Można uczynić bardziej ogólne wyjątki Niezaznaczone, sprawdzane są mniej ogólne.

ramu p
źródło
1

Myślę, że możemy pomyśleć o wyjątkach od kilku pytań:

dlaczego zdarza się wyjątek? Co możemy zrobić, kiedy to się stanie

przez pomyłkę błąd. na przykład wywoływana jest metoda zerowego obiektu.

String name = null;
... // some logics
System.out.print(name.length()); // name is still null here

Ten rodzaj wyjątku powinien zostać naprawiony podczas testu. W przeciwnym razie spowoduje to przerwanie produkcji i otrzymasz bardzo wysoki błąd, który należy natychmiast naprawić. Tego rodzaju wyjątki nie muszą być sprawdzane.

za pomocą danych zewnętrznych nie można kontrolować ani ufać wynikom usług zewnętrznych.

String name = ExternalService.getName(); // return null
System.out.print(name.length());    // name is null here

W tym przypadku może być konieczne sprawdzenie, czy nazwa ma wartość NULL, jeśli chcesz kontynuować, gdy ma ona wartość NULL, w przeciwnym razie możesz zostawić ją w spokoju, a ona zatrzyma się tutaj i nada programowi wywołującemu wyjątek czasu wykonywania. Tego rodzaju wyjątki nie muszą być sprawdzane.

w drodze wyjątku wykonawczego z zewnątrz nie można kontrolować ani ufać usłudze zewnętrznej.

W tym przypadku może być konieczne wychwycenie wszystkich wyjątków od ExternalService, jeśli chcesz kontynuować, gdy tak się stanie, w przeciwnym razie możesz zostawić to w spokoju, a zatrzyma się tutaj i da dzwoniącemu wyjątek czasu wykonywania.

przez zaznaczony wyjątek od zewnętrznego, nie możesz kontrolować ani ufać usłudze zewnętrznej.

W tym przypadku może być konieczne wychwycenie wszystkich wyjątków od ExternalService, jeśli chcesz kontynuować, gdy tak się stanie, w przeciwnym razie możesz zostawić to w spokoju, a zatrzyma się tutaj i da dzwoniącemu wyjątek czasu wykonywania.

Czy w takim przypadku musimy wiedzieć, jaki wyjątek wydarzył się w ExternalService? To zależy:

  1. jeśli potrafisz poradzić sobie z pewnymi wyjątkami, musisz je złapać i przetworzyć. Dla innych zastaw ich.

  2. jeśli potrzebujesz dziennika lub odpowiedzi dla konkretnego wykonania, możesz je złapać. Dla innych zastaw ich.

Jacky
źródło
0

Wydaje mi się, że deklarując wyjątek aplikacji, powinien to być Niesprawdzony wyjątek, tj. Podklasa RuntimeException. Powodem jest to, że nie zaśmieci kodu aplikacji try-catch i zgłasza deklarację metody. Jeśli Twoja aplikacja korzysta z interfejsu Java API, który zgłasza sprawdzone wyjątki, które i tak należy obsłużyć. W innych przypadkach aplikacja może zgłosić niesprawdzony wyjątek. Jeśli program wywołujący aplikację nadal musi obsłużyć niezaznaczony wyjątek, można to zrobić.

Akash Aggarwal
źródło
-12

Używam zasady: nigdy nie używaj niezaznaczonych wyjątków! (lub gdy nie widzisz żadnej możliwości obejścia tego)

Z punktu widzenia programisty korzystającego z biblioteki lub użytkownika końcowego korzystającego z biblioteki / aplikacji naprawdę trudno jest się skonfrontować z aplikacją, która ulega awarii z powodu niewyszukanego wyjątku. A liczenie na wszystko też nie jest dobre.

W ten sposób użytkownik końcowy może nadal wyświetlać komunikat o błędzie, zamiast całkowicie znikać z aplikacji.


źródło
1
Nie wyjaśniasz, co uważasz za niewłaściwe za pomocą catch-all dla większości niesprawdzonych wyjątków.
Matthew Flaschen
Całkowicie nie zgadzam się z nieuzasadnioną odpowiedzią: „nigdy nie używaj niesprawdzonych wyjątków”
user1697575,