Quando scegliere le eccezioni selezionate e non selezionate


213

In Java (o in qualsiasi altra lingua con eccezioni verificate), quando si crea la propria classe di eccezioni, come si decide se deve essere selezionata o deselezionata?

Il mio istinto è quello di dire che un'eccezione controllata sarebbe richiesta nei casi in cui il chiamante potrebbe essere in grado di recuperare in qualche modo produttivo, dove un'eccezione non selezionata sarebbe più per i casi irrecuperabili, ma sarei interessato ai pensieri degli altri.


11
Barry Ruzek ha scritto un'eccellente guida sulla scelta delle eccezioni controllate o non controllate.
firma il

Risposte:


241

Le eccezioni controllate sono ottime, a condizione che tu capisca quando devono essere utilizzate. L'API core Java non segue queste regole per SQLException (e talvolta per IOException), motivo per cui sono così terribili.

Le eccezioni controllate devono essere utilizzate per errori prevedibili , ma non prevenibili, ragionevoli da cui recuperare .

Le eccezioni non selezionate dovrebbero essere utilizzate per tutto il resto.

Lo analizzerò per te, perché la maggior parte delle persone fraintende cosa significhi.

  1. Prevedibile ma imprevedibile : il chiamante ha fatto tutto il possibile per convalidare i parametri di input, ma alcune condizioni al di fuori del loro controllo hanno causato il fallimento dell'operazione. Ad esempio, si prova a leggere un file ma qualcuno lo elimina tra il momento in cui si controlla se esiste e il momento in cui inizia l'operazione di lettura. Dichiarando un'eccezione controllata, si sta dicendo al chiamante di anticipare questo errore.
  2. Ragionevole da cui recuperare : non ha senso dire ai chiamanti di anticipare eccezioni da cui non possono recuperare. Se un utente tenta di leggere da un file inesistente, il chiamante può richiedere loro un nuovo nome file. D'altra parte, se il metodo fallisce a causa di un bug di programmazione (argomenti del metodo non validi o implementazione del metodo errato) non c'è nulla che l'applicazione possa fare per risolvere il problema a metà esecuzione. Il meglio che può fare è registrare il problema e attendere che lo sviluppatore lo risolva in un secondo momento.

A meno che l'eccezione che stai generando soddisfi tutte le condizioni di cui sopra, dovrebbe usare un'eccezione non selezionata.

Rivalutare a tutti i livelli : a volte il metodo che rileva l'eccezione selezionata non è il posto giusto per gestire l'errore. In tal caso, considera ciò che è ragionevole per i tuoi chiamanti. Se l'eccezione è prevedibile, imprevedibile e ragionevole per il loro recupero, allora dovresti lanciare tu stesso un'eccezione controllata. In caso contrario, è necessario racchiudere l'eccezione in un'eccezione non selezionata. Se segui questa regola ti ritroverai a convertire le eccezioni controllate in eccezioni non controllate e viceversa a seconda del livello in cui ti trovi.

Per entrambe le eccezioni selezionate e non selezionate, utilizzare il livello di astrazione corretto . Ad esempio, un repository di codice con due diverse implementazioni (database e filesystem) dovrebbe evitare di esporre dettagli specifici dell'implementazione lanciando SQLExceptiono IOException. Invece, dovrebbe racchiudere l'eccezione in un'astrazione che abbraccia tutte le implementazioni (ad es RepositoryException.).


2
"si tenta di leggere un file ma qualcuno lo elimina tra il momento in cui si controlla se esiste e il momento in cui inizia l'operazione di lettura." => In che modo è persino "previsto"? Per me sembra più simile a: Inaspettato e prevenibile. Chi si aspetterebbe che un file venga eliminato solo tra 2 istruzioni?
Koray Tugay,

9
@KorayTugay Expected non significa che lo scenario sia tipico. Significa solo che possiamo prevedere che questo errore si verifichi in anticipo (rispetto agli errori di programmazione che non possono essere previsti in anticipo). Imprevedibile si riferisce al fatto che non c'è nulla che il programmatore possa fare per impedire all'utente o ad altre applicazioni di eliminare un file tra il momento in cui controlliamo se esiste e il momento in cui inizia l'operazione di lettura.
Gili,

Quindi eventuali problemi relativi al database all'interno del metodo devono generare un'eccezione controllata?
Ivanjermakov,

59

Da uno studente Java :

Quando si verifica un'eccezione, devi catturare e gestire l'eccezione oppure dire al compilatore che non puoi gestirla dichiarando che il tuo metodo genera quell'eccezione, quindi il codice che utilizza il tuo metodo dovrà gestire quell'eccezione (anche se può anche scegliere di dichiarare che genera l'eccezione se non può gestirla).

Il compilatore verificherà che abbiamo fatto una delle due cose (cattura o dichiara). Quindi queste sono chiamate eccezioni controllate. Ma gli errori e le eccezioni di runtime non sono controllati dal compilatore (anche se puoi scegliere di catturare o dichiarare, non è necessario). Quindi, questi due sono chiamati Eccezioni non selezionate.

Gli errori vengono utilizzati per rappresentare le condizioni che si verificano al di fuori dell'applicazione, come l'arresto anomalo del sistema. Le eccezioni di runtime si verificano in genere per errore nella logica dell'applicazione. Non puoi fare nulla in queste situazioni. Quando si verifica un'eccezione di runtime, è necessario riscrivere il codice del programma. Quindi, questi non sono controllati dal compilatore. Queste eccezioni di runtime sveleranno nello sviluppo e nel periodo di prova. Quindi dobbiamo riformattare il nostro codice per rimuovere questi errori.


13
Questa è la visione ortodossa. Ma ci sono molte controversie al riguardo.
artbristol,

49

La regola che uso è: non usare mai eccezioni non selezionate! (o quando non vedi alcun modo per aggirarlo)

C'è un caso molto forte per il contrario: non usare mai le eccezioni verificate. Sono riluttante a prendere posizione nel dibattito, ma sembra esserci un ampio consenso sul fatto che introdurre eccezioni verificate sia stata una decisione sbagliata col senno di poi. Per favore, non sparare al messaggero e fare riferimento a tali argomenti .


3
IMHO, le eccezioni controllate potrebbero essere state una risorsa importante se ci fosse stato un modo semplice per un metodo per dichiarare che non si aspetta che i metodi chiamati in un particolare blocco di codice generino determinate eccezioni (o nessuna) e che qualsiasi controllo le eccezioni che vengono lanciate in contrasto con tale aspettativa dovrebbero essere racchiuse in qualche altro tipo di eccezione e riproposte. Suppongo che il 90% delle volte in cui il codice non è pronto a far fronte a un'eccezione verificata, tale riavvolgimento sarebbe il modo migliore per gestirlo, ma poiché non c'è supporto linguistico raramente viene fatto.
supercat

@supercat In sostanza, è tutto: sono un fan sfegatato del controllo rigoroso dei tipi e le eccezioni verificate ne sono un'estensione logica. Ho completamente abbandonato le eccezioni verificate anche se concettualmente mi piacciono molto.
Konrad Rudolph

1
Una peeve che ho con la progettazione delle eccezioni, che il meccanismo che ho descritto avrebbe risolto, è che se fooè documentato come lanciato barExceptionquando legge oltre la fine di un file e foochiama un metodo che lancia barExceptionanche se foonon lo prevede, il codice che chiama foopenserà che sia stata raggiunta la fine del file e non avrà la minima idea che sia successo qualcosa di inaspettato. Considererei quella situazione come quella in cui le eccezioni controllate dovrebbero essere più utili, ma è anche l'unico caso in cui il compilatore consentirà eccezioni controllate non gestite.
supercat

@supercat: Esiste già un modo semplice per il codice di fare quello che vuoi nel primo commento: racchiudere il codice in un blocco try, catturare Eccezione e racchiudere l'eccezione in RuntimeException e riprovare.
Warren Dew,

1
Il supercat @KonradRudolph si riferiva a "un particolare blocco di codice"; dato che il blocco deve essere definito, la sintassi dichiarativa non ridurrebbe significativamente il gonfiamento. Se stai pensando che sarebbe dichiarativo per l'intera funzione, ciò incoraggerebbe una cattiva programmazione poiché le persone si limiterebbero a attenersi alla dichiarazione invece di guardare effettivamente le eccezioni verificate potenzialmente rilevate e assicurarsi che non ci sia un altro modo migliore per gestirli.
Warren Dew,

46

Su qualsiasi sistema abbastanza grande, con molti livelli, le eccezioni controllate sono inutili poiché, comunque, è necessaria una strategia a livello di architettura per gestire il modo in cui verrà gestita l'eccezione (utilizzare una barriera di errore)

Con le eccezioni verificate, la tua strategia di gestione degli errori è micro-gestita ed è insopportabile su qualsiasi sistema di grandi dimensioni.

Il più delle volte non sai se un errore è "recuperabile" perché non sai in quale livello si trova il chiamante della tua API.

Diciamo che creo un'API StringToInt che converte la rappresentazione di stringa di un numero intero in un Int. Devo lanciare un'eccezione controllata se l'API viene chiamata con la stringa "pippo"? È recuperabile? Non lo so perché nel suo livello il chiamante della mia API StringToInt potrebbe aver già convalidato l'input e se questa eccezione viene generata è un bug o un danneggiamento dei dati e non è recuperabile per questo livello.

In questo caso, il chiamante dell'API non vuole intercettare l'eccezione. Vuole solo che l'eccezione "si gonfi". Se ho scelto un'eccezione controllata, questo chiamante avrà un sacco di blocchi catch inutili solo per ridisegnare artificialmente l'eccezione.

Ciò che è recuperabile dipende la maggior parte delle volte dal chiamante dell'API, non dal writer dell'API. Un'API non dovrebbe utilizzare le eccezioni verificate poiché solo le eccezioni non selezionate consentono di scegliere di intercettare o ignorare un'eccezione.


3
Questo è molto vicino a userstories.blogspot.com/2008/12/…
alexsmail

16
@alexsmail Internet non manca mai di sorprendermi, in realtà è il mio blog :)
Stephane,

30

Hai ragione.

Le eccezioni non selezionate vengono utilizzate per consentire al sistema di fallire rapidamente, il che è positivo. Dovresti indicare chiaramente qual è il tuo metodo per funzionare correttamente. In questo modo è possibile convalidare l'input solo una volta.

Per esempio:

/**
 * @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"
      .....  
 }

Solo per fare un esempio. Il punto è che se il sistema si guasta rapidamente, allora saprai dove e perché ha fallito. Otterrai uno stacktrace come:

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

E saprai cosa è successo. La OtherClass nel metodo "delegateTheWork" (alla riga 4569) ha chiamato la tua classe con il valore "exit", anche quando non dovrebbe ecc.

Altrimenti dovresti cospargere le convalide su tutto il codice e questo è soggetto a errori. Inoltre, a volte è difficile rintracciare ciò che è andato storto e potresti aspettarti ore di frustrante debug

La stessa cosa accade con NullPointerExceptions. Se si dispone di una classe di 700 righe con circa 15 metodi, che utilizza 30 attributi e nessuno di essi può essere nullo, invece di convalidare in ciascuno di questi metodi per nullabilità è possibile rendere tutti quegli attributi di sola lettura e convalidarli nel costruttore o metodo di fabbrica.

 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 
      ....
  }

Eccezioni controllate Sono utili quando il programmatore (tu o i tuoi colleghi) avete fatto tutto bene, convalidato l'input, eseguito test e tutto il codice è perfetto, ma il codice si collega a un servizio Web di terze parti che potrebbe non essere attivo (o un file che stavi utilizzando è stato eliminato da un altro processo esterno ecc.). Il servizio web potrebbe anche essere convalidato prima di tentare la connessione, ma durante il trasferimento dei dati qualcosa è andato storto.

In quello scenario non c'è niente che tu o i tuoi colleghi potete fare per aiutarlo. Ma devi comunque fare qualcosa e non lasciare che l'applicazione muoia e scompaia agli occhi dell'utente. Usi un'eccezione controllata per questo e gestisci l'eccezione, cosa puoi fare quando ciò accade ?, il più delle volte, solo per tentare di registrare l'errore, probabilmente salvare il tuo lavoro (il lavoro dell'app) e presentare un messaggio all'utente . (Il sito blabla non è attivo, riprovare più tardi, ecc.)

Se l'eccezione selezionata viene utilizzata in modo eccessivo (aggiungendo la "eccezione Eccezione" in tutte le firme dei metodi), il codice diventerà molto fragile, perché tutti ignoreranno quell'eccezione (perché è troppo generale) e la qualità del codice sarà seriamente compromessa.

Se usi eccessivamente l'eccezione non selezionata, accadrà qualcosa di simile. Gli utenti di quel codice non sanno se qualcosa potrebbe andare storto e apparirà un sacco di tentativi {...} catch (Throwable t).


2
Ben detto! +1. Mi sorprende sempre che questa distinzione chiamante (non selezionata) / chiamata (selezionata) non sia più ovvia ...
VonC

19

Ecco la mia "regola empirica finale".
Io uso:

  • eccezione non selezionata nel codice del mio metodo per un errore dovuto al chiamante (che comporta una documentazione esplicita e completa )
  • verificato l'eccezione per un errore dovuto alla chiamata che devo rendere esplicito a chiunque voglia usare il mio codice

Rispetto alla risposta precedente, questa è una chiara logica (su cui si può concordare o non essere d'accordo) per l'uso dell'una o dell'altra (o di entrambi) tipi di eccezioni.


Per entrambe queste eccezioni, creerò la mia eccezione non selezionata e selezionata per la mia applicazione (una buona pratica, come menzionato qui ), ad eccezione dell'eccezione non controllata molto comune (come NullPointerException)

Quindi, ad esempio, l'obiettivo di questa particolare funzione di seguito è quello di rendere (o ottenere se già esiste) un oggetto, il che
significa:

  • il contenitore dell'oggetto da creare / ottenere DEVE esistere (responsabilità di CALLER
    => eccezione non selezionata E cancellare il commento javadoc per questa chiamata funzione)
  • gli altri parametri non possono essere nulli
    (scelta del programmatore per inserirlo nel CALLER: il programmatore non controllerà il parametro null ma il programmatore NON LO DOCUMENTA)
  • il risultato NON PU BE ESSERE NULL
    (responsabilità e scelta del codice del chiamante, scelta che sarà di grande interesse per il chiamante
    => eccezione verificata perché tutti i chiamanti DEVONO prendere una decisione se l'oggetto non può essere creato / trovato, e che la decisione deve essere eseguita al momento della compilazione: non possono utilizzare questa funzione senza avere a che fare con questa possibilità, vale a dire con questa eccezione verificata ).

Esempio:


/**
 * 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;
}

19

Non è solo una questione di capacità di recupero dall'eccezione. Ciò che conta di più, secondo me, è se il chiamante è interessato a cogliere l'eccezione o meno.

Se si scrive una libreria da utilizzare altrove o un livello di livello inferiore nell'applicazione, chiedersi se il chiamante è interessato a rilevare (conoscere) l'eccezione. Se non lo è, usa un'eccezione non selezionata, in modo da non caricarlo inutilmente.

Questa è la filosofia utilizzata da molti framework. La primavera e l'ibernazione, in particolare, vengono in mente: convertono l'eccezione controllata nota in eccezione non selezionata proprio perché le eccezioni controllate sono abusate in Java. Un esempio a cui riesco a pensare è JSONException di json.org, che è un'eccezione verificata e per lo più fastidiosa: dovrebbe essere deselezionata, ma lo sviluppatore semplicemente non ci ha pensato.

A proposito, la maggior parte delle volte l'interesse del chiamante per l'eccezione è direttamente correlato alla capacità di recuperare dall'eccezione, ma non è sempre così.


13

Ecco una soluzione molto semplice al tuo dilemma controllato / non controllato.

Regola 1: pensare a un'eccezione non selezionata come condizione verificabile prima dell'esecuzione del codice. per esempio…

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

dove x è null ... ... il codice dovrebbe possibilmente avere il seguente ...

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();

Regola 2: considerare un'eccezione controllata come una condizione non verificabile che può verificarsi durante l'esecuzione del codice.

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

... nell'esempio sopra, l'URL (google.com) potrebbe non essere disponibile a causa di un server DNS inattivo. Anche nel momento in cui il server DNS funzionava e risolveva il nome "google.com" in un indirizzo IP, se la connessione viene stabilita con google.com, in qualsiasi momento dopo la parola, la rete potrebbe andare in crash. Semplicemente non puoi testare continuamente la rete prima di leggere e scrivere su stream.

Ci sono momenti in cui il codice deve semplicemente essere eseguito prima che possiamo sapere se c'è un problema. Costringendo gli sviluppatori a scrivere il loro codice in modo tale da costringerli a gestire queste situazioni tramite Checked Exception, devo rivolgere il mio cappello al creatore di Java che ha inventato questo concetto.

In generale, quasi tutte le API in Java seguono le 2 regole precedenti. Se si tenta di scrivere su un file, il disco potrebbe riempirsi prima di completare la scrittura. È possibile che altri processi abbiano causato il riempimento del disco. Semplicemente non c'è modo di testare questa situazione. Per coloro che interagiscono con l'hardware dove, in qualsiasi momento, l'utilizzo dell'hardware può fallire, Checked Exceptions sembra essere una soluzione elegante a questo problema.

C'è un'area grigia in questo. Nel caso in cui siano necessari molti test (un'affermazione strabiliante con un sacco di && e ||), l'eccezione lanciata sarà una CheckedException semplicemente perché è troppo doloroso da risolvere - semplicemente non puoi dire questo problema è un errore di programmazione. Se sono presenti meno di 10 test (ad es. "If (x == null)"), l'errore del programmatore dovrebbe essere UncheckedException.

Le cose diventano interessanti quando si tratta di interpreti linguistici. Secondo le regole sopra, un errore di sintassi dovrebbe essere considerato un'eccezione controllata o non selezionata? Direi che se la sintassi del linguaggio può essere testata prima che venga eseguita, dovrebbe essere un UncheckedException. Se la lingua non può essere testata, in modo simile all'esecuzione del codice assembly su un personal computer, l'errore di sintassi dovrebbe essere un'eccezione verificata.

Le 2 regole sopra rimuoveranno probabilmente il 90% delle tue preoccupazioni tra cui scegliere. Per riepilogare le regole, seguire questo modello ... 1) se il codice da eseguire può essere testato prima che venga eseguito affinché funzioni correttamente e se si verifica un'eccezione, ovvero un errore del programmatore, l'eccezione dovrebbe essere UncheckedException (una sottoclasse di RuntimeException ). 2) se il codice da eseguire non può essere testato prima che venga eseguito affinché funzioni correttamente, l'eccezione dovrebbe essere un'eccezione controllata (una sottoclasse di eccezione).


9

Puoi chiamarlo un'eccezione selezionata o non selezionata; tuttavia, entrambi i tipi di eccezione possono essere rilevati dal programmatore, quindi la risposta migliore è: scrivere tutte le eccezioni come non selezionate e documentarle. In questo modo lo sviluppatore che utilizza la tua API può scegliere se desidera catturare tale eccezione e fare qualcosa. Le eccezioni verificate sono una perdita completa del tempo di tutti e rendono il tuo codice un incubo scioccante da guardare. Un corretto test unitario solleverà quindi tutte le eccezioni che potresti dover catturare e fare qualcosa.


1
+1 Per menzionare che i test unitari potrebbero essere un modo migliore per risolvere il problema che le eccezioni controllate devono risolvere.
Keith Pinson,

+1 per test unitari. L'uso delle eccezioni Controllato / Non fatto ha un impatto limitato sulla qualità del codice. Quindi l'argomento secondo cui se si usano le eccezioni verificate, si otterrebbe una migliore qualità del codice è un argomento fasullo completo!
user1697575

7

Eccezione controllata: se il client può recuperare da un'eccezione e desidera continuare, utilizzare l'eccezione selezionata.

Eccezione non selezionata: se un client non può fare nulla dopo l'eccezione, genera un'eccezione non selezionata.

Esempio: se si prevede di eseguire un'operazione aritmetica in un metodo A () e in base all'output di A (), è necessario eseguire un'altra operazione. Se l'output è nullo dal metodo A () che non si prevede durante il tempo di esecuzione, è necessario generare un'eccezione del puntatore Null che è un'eccezione del tempo di esecuzione.

Fare riferimento qui


2

Concordo con la preferenza per le eccezioni non selezionate come regola, specialmente quando si progetta un'API. Il chiamante può sempre scegliere di intercettare un'eccezione documentata e non selezionata. Non stai forzando inutilmente il chiamante.

Trovo le eccezioni verificate utili al livello inferiore, come dettagli di implementazione. Spesso sembra un flusso migliore del meccanismo di controllo rispetto alla necessità di gestire un "codice di ritorno" di errore specificato. A volte può aiutare a vedere l'impatto di un'idea per una modifica del codice di basso livello ... dichiarare un'eccezione verificata a valle e vedere chi avrebbe bisogno di aggiustare. Quest'ultimo punto non si applica se ci sono molti generici: catch (Exception e) o genera l'eccezione che di solito non è troppo ben congegnata.


2

Qui voglio condividere la mia opinione che ho dopo molti anni di esperienza nello sviluppo:

  1. Eccezione controllata. Questa è una parte del caso d'uso aziendale o del flusso di chiamate, questa è una parte della logica dell'applicazione che ci aspettiamo o meno. Ad esempio connessione rifiutata, condizione non soddisfatta ecc. Dobbiamo gestirla e mostrare all'utente il messaggio corrispondente con le istruzioni su cosa è successo e cosa fare dopo (riprovare più tardi, ecc.). Di solito la chiamo eccezione post-elaborazione o eccezione "utente".

  2. Eccezione non selezionata. Questa è una parte dell'eccezione di programmazione, un errore nella programmazione del codice software (bug, difetto) e riflette il modo in cui i programmatori devono usare l'API come da documentazione. Se un documento lib / framework esterno dice che prevede di ottenere dati in un certo intervallo e non nulli, poiché verrà generato NPE o IllegalArgumentException, il programmatore dovrebbe aspettarselo e utilizzare l'API correttamente come da documentazione. Altrimenti verrà generata l'eccezione. Di solito la chiamo eccezione di pre-elaborazione o "convalida".

Dal pubblico di destinazione. Ora parliamo di destinatari o gruppi di persone, le eccezioni sono state progettate (secondo la mia opinione):

  1. Eccezione controllata. I destinatari sono utenti / clienti.
  2. Eccezione non selezionata. I destinatari sono gli sviluppatori. In altre parole, le eccezioni non selezionate sono progettate solo per gli sviluppatori.

Per fase del ciclo di vita dello sviluppo di applicazioni.

  1. L'eccezione controllata è progettata per esistere durante l'intero ciclo di vita della produzione come meccanismo normale e previsto un'applicazione gestisce casi eccezionali.
  2. L'eccezione non selezionata è progettata per esistere solo durante il ciclo di vita dello sviluppo / test dell'applicazione, tutti dovrebbero essere risolti durante quel periodo e non dovrebbero essere generati quando un'applicazione è già in esecuzione in produzione.

Il motivo per cui i framework di solito utilizzano eccezioni non controllate (ad esempio Spring) è che il framework non è in grado di determinare la logica di business della propria applicazione, quindi spetta agli sviluppatori catturare e progettare la propria logica.


2

Dobbiamo distinguere questi due tipi di eccezione in base al fatto che si tratti di un errore del programmatore o meno.

  • Se un errore è un errore del programmatore, deve essere un'eccezione non selezionata . Ad esempio: SQLException / IOException / NullPointerException. Queste eccezioni sono errori di programmazione. Dovrebbero essere gestiti dal programmatore. Mentre nell'API JDBC, SQLException è un'eccezione controllata, in Spring JDBCTemplate è un'eccezione non controllata. Il programmatore non si preoccupa di SqlException, quando usa Spring.
  • Se un errore non è un errore del programmatore e il motivo proviene da esterno, deve essere un'eccezione controllata. Ad esempio: se il file viene eliminato o l'autorizzazione del file viene modificata da qualcun altro, dovrebbe essere ripristinata.

FileNotFoundException è un buon esempio per comprendere le sottili differenze. FileNotFoundException viene generata nel caso in cui il file non venga trovato. Ci sono due motivi per questa eccezione. Se il percorso del file è definito dallo sviluppatore o acquisito dall'utente finale tramite la GUI, dovrebbe essere un'eccezione non selezionata. Se il file viene eliminato da qualcun altro, dovrebbe essere un'eccezione controllata.

L'eccezione controllata può essere gestita in due modi. Questi utilizzano try-catch o propagano l'eccezione. In caso di propagazione di eccezioni, tutti i metodi nello stack di chiamate saranno strettamente accoppiati a causa della gestione delle eccezioni. Ecco perché, dobbiamo usare attentamente l'eccezione controllata.

Nel caso in cui sviluppi un sistema aziendale a più livelli, devi scegliere un'eccezione per lo più non selezionata da lanciare, ma non dimenticare di utilizzare l'eccezione selezionata per il caso in cui non puoi fare nulla.


1

Le eccezioni selezionate sono utili per i casi recuperabili in cui si desidera fornire informazioni al chiamante (ad esempio autorizzazioni insufficienti, file non trovato, ecc.).

Le eccezioni non selezionate vengono utilizzate raramente, se non del tutto, per informare l'utente o il programmatore di errori gravi o condizioni impreviste durante il runtime. Non lanciarli se stai scrivendo codice o librerie che verranno utilizzate da altri, in quanto potrebbero non aspettarsi che il tuo software generi eccezioni non controllate poiché il compilatore non impone che vengano catturati o dichiarati.


Non sono d'accordo con la tua affermazione "Le eccezioni non selezionate vengono utilizzate raramente, se non del tutto", in realtà dovrebbe essere opposto! Utilizzare le eccezioni non selezionate per impostazione predefinita quando si progetta la gerarchia delle eccezioni dell'applicazione. Consentire agli sviluppatori di decidere quando vogliono gestire l'eccezione (ad esempio, non sono obbligati a mettere blocchi catch o mettere la clausola dei tiri se non sanno come gestirlo).
user1697575

1

Qualunque sia un'eccezione meno probabile, e possiamo procedere anche dopo averlo rilevato e non possiamo fare nulla per evitare tale eccezione, quindi possiamo usare l'eccezione controllata.

Ogni volta che vogliamo fare qualcosa di significativo quando si verificano determinate eccezioni e quando tale eccezione è prevista ma non certa, possiamo usare l'eccezione controllata.

Ogni volta che un'eccezione naviga in diversi livelli, non è necessario rilevarla in tutti i livelli, in tal caso, possiamo usare l'eccezione di runtime o avvolgere l'eccezione come eccezione non selezionata.

L'eccezione di runtime viene utilizzata quando è più probabile che si verifichi un'eccezione, non c'è modo di andare oltre e nulla può essere recuperato. Quindi, in questo caso, possiamo prendere precauzioni rispetto a tale eccezione. EX: NUllPointerException, ArrayOutofBoundsException. È più probabile che ciò accada. In questo scenario, possiamo prendere precauzioni durante la codifica per evitare tale eccezione. Altrimenti dovremo scrivere provare a prendere blocchi ovunque.

Eccezioni più generali possono essere fatte Deselezionate, meno generali vengono verificate.


1

Penso che possiamo pensare alle eccezioni da diverse domande:

perché succede l'eccezione? Cosa possiamo fare quando succede

per errore, un bug. come viene chiamato un metodo di oggetto null.

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

Questo tipo di eccezione dovrebbe essere risolto durante il test. Altrimenti, interrompe la produzione e hai un bug molto alto che deve essere risolto immediatamente. Non è necessario verificare questo tipo di eccezioni.

per input da esterno, non è possibile controllare o fidarsi dell'output del servizio esterno.

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

Qui, potrebbe essere necessario verificare se il nome è null se si desidera continuare quando è null, altrimenti, è possibile lasciarlo da solo e si fermerà qui e darà al chiamante l'eccezione di runtime. Non è necessario verificare questo tipo di eccezioni.

per eccezione di runtime da esterno, non è possibile controllare o fidarsi del servizio esterno.

Qui, potrebbe essere necessario catturare tutte le eccezioni da ExternalService se si desidera continuare quando succede, altrimenti, è possibile lasciarlo da solo e si fermerà qui e darà al chiamante l'eccezione di runtime.

tramite un'eccezione controllata da esterno, non è possibile controllare o fidarsi del servizio esterno.

Qui, potrebbe essere necessario catturare tutte le eccezioni da ExternalService se si desidera continuare quando succede, altrimenti, è possibile lasciarlo da solo e si fermerà qui e darà al chiamante l'eccezione di runtime.

In questo caso, dobbiamo sapere che tipo di eccezione è avvenuta in ExternalService? Dipende:

  1. se riesci a gestire alcuni tipi di eccezioni, devi recuperarle ed elaborarle. Per gli altri, falli bolle.

  2. se hai bisogno di log o risposta per l'utente dell'esecuzione specifica, puoi prenderli. Per gli altri, falli bolle.


0

Penso che quando si dichiara Eccezione applicazione dovrebbe essere Eccezione non selezionata, cioè sottoclasse di RuntimeException. Il motivo è che non ingombrerà il codice dell'applicazione con try-catch e genera la dichiarazione sul metodo. Se l'applicazione utilizza Java Api che genera eccezioni verificate che devono comunque essere gestite. Per altri casi, l'applicazione può generare un'eccezione non selezionata. Se il chiamante dell'applicazione deve ancora gestire l'eccezione non selezionata, è possibile farlo.


-12

La regola che uso è: non usare mai eccezioni non selezionate! (o quando non vedi alcun modo per aggirarlo)

Dal punto di vista dello sviluppatore che utilizza la tua libreria o l'utente finale che utilizza la tua libreria / applicazione, fa davvero schifo confrontarsi con un'applicazione che si arresta in modo anomalo a causa di un'eccezione non rilevata. E contare anche su un tutto sommato non va bene.

In questo modo all'utente finale può comunque essere visualizzato un messaggio di errore, invece che l'applicazione scompaia completamente.


1
Non spieghi cosa pensi sia sbagliato in un catch-all per la maggior parte delle eccezioni non controllate.
Matthew Flaschen,

Completamente in disaccordo con la tua risposta non argomentata: "non usare mai eccezioni non controllate"
user1697575
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.