W3docs

Java try...catch

Gestisci gli errori in Java con i blocchi try...catch per mantenere il programma in esecuzione quando qualcosa va storto.

try/catch è l'unità minima di gestione delle eccezioni in Java. Si racchiude il codice rischioso in try e lo si segue con uno o più blocchi catch che specificano cosa fare se viene lanciata una determinata eccezione. Insieme formano un'unica istruzione; non è possibile avere l'uno senza l'altro (o senza un finally, che vedremo in un capitolo successivo).

Struttura

try {
  // code that might throw
} catch (ExceptionType e) {
  // code that runs only if a matching exception was thrown
}

Alcune cose che all'inizio è facile trascurare:

  • La variabile nel catch (e per convenzione) è circoscritta al blocco catch. Puoi chiamarla come vuoi; e ed ex sono le scelte più comuni.
  • Il parametro del catch è effettivamente final — puoi leggerlo ma non dovresti riassegnarlo. Trattarlo come immutabile rende il codice più facile da seguire.
  • Il blocco try ha un proprio scope. Le variabili dichiarate al suo interno non sono visibili nel catch né dopo l'istruzione. Se hai bisogno di un valore calcolato nel try in seguito, dichiaralo fuori.
String content;
try {
  content = Files.readString(path);
} catch (IOException e) {
  content = "";   // works because `content` was declared outside the try
}
System.out.println(content);

Quali catch corrispondono

Un blocco catch (T e) viene eseguito quando l'eccezione lanciata è un'istanza di T — incluso qualsiasi sottotipo di T. Quindi:

  • catch (Exception e) cattura quasi tutto: IOException, NullPointerException, le tue eccezioni personalizzate.
  • catch (RuntimeException e) cattura i bug a runtime ma non le eccezioni verificate come IOException.
  • catch (NullPointerException e) cattura solo le NPE (e le sottoclassi, che in genere non esistono).

All'interno del try, viene eseguito solo il primo catch corrispondente. Se elenchi un tipo più generico prima di uno più specifico, il catch più specifico è irraggiungibile e il compilatore rifiuta il codice:

try { ... }
catch (Exception e)        { ... }   // matches everything
catch (IOException e)      { ... }   // ERROR: unreachable

Elenca sempre i catch dal più specifico al più generale.

Cosa fare in un catch

Un blocco catch non è un posto dove far sparire le eccezioni. È un posto dove decidere cosa fare riguardo a un fallimento noto. Le opzioni realistiche sono:

  • Recuperare — riprovare, ricorrere a un valore predefinito, passare a una risorsa diversa. Questo è il caso migliore, ed è più raro di quanto si pensi.
  • Registrare e rilanciare — registrare i dettagli e lasciare che l'eccezione continui a propagarsi verso un gestore di livello superiore che sa cosa fare.
  • Avvolgere e rilanciare — tradurre un'eccezione di basso livello in una che si adatta al vocabolario di questo livello (IOExceptionConfigLoadException).
  • Tradurre in un valore di ritorno — quando il fallimento è genuinamente atteso (ad esempio durante il parsing dell'input utente), restituire Optional.empty() o un valore sentinella.

La cosa da non fare è catturare un'eccezione e continuare silenziosamente senza registrarla né agire su di essa. È così che i bug scompaiono nel nulla. Torneremo su questo nel capitolo sulle best practice.

Leggere le informazioni sull'eccezione

Il parametro del catch è un oggetto. Tre metodi che userai costantemente:

  • e.getMessage() — la descrizione leggibile dall'utente. Può essere null.
  • e.toString() — nome della classe più messaggio, ad es. java.io.IOException: file not found.
  • e.printStackTrace() — scrive la traccia su System.err. Comodo durante il debug, ma usa un vero logger nel codice di produzione (instrada la traccia attraverso lo stesso canale di tutto il resto).

Un quarto metodo, e.getCause(), restituisce l'eccezione sottostante quando una è stata avvolta — utile quando occorre comprendere il fallimento originale all'interno di un livello di traduzione.

Catturare Exception vs. catturare Throwable

Puoi scrivere catch (Throwable t) e catturerà tutto — inclusi gli Error come OutOfMemoryError. Non farlo. Gli Error significano che la JVM è in difficoltà e il tuo codice non è in grado di reagire sensatamente a essi. Catturare Throwable maschera bug che dovrebbero far terminare il processo.

Attieniti a Exception o a uno dei suoi sottotipi. Se hai davvero bisogno di registrare l'imprevisto prima di lasciarlo propagare, la forma corretta è catch (RuntimeException e) { log; throw; }.

Un esempio pratico

Una piccola utility che analizza un intero da ogni riga di input. Alcune righe contengono numeri validi, altre no, una è null. Usiamo try/catch per proseguire oltre le righe errate e contare quanto è successo. Chiamare line.trim() lancia una NullPointerException sull'elemento null, mentre Integer.parseInt lancia una NumberFormatException su tutto ciò che non è un intero — due tipi di eccezione distinti, ciascuno con il proprio catch.

java— editable, runs on the server

L'output è:

not an integer: "hello"
null line — skipped
not an integer: "3.14"
---
parsed: 3, failed: 3, sum: 118

Due cose da notare. Prima, il programma termina. Senza i catch, la prima riga errata avrebbe interrotto l'esecuzione e nulla dopo di essa sarebbe stato contato. Seconda, i due fallimenti sono distinti: la riga null prende il ramo NullPointerException e stampa il proprio messaggio, mentre "hello" e "3.14" prendono il ramo NumberFormatException. Selezionare i catch per tipo è il modo per evitare che fallimenti diversi si confondano tra loro.

Cosa c'è dopo

Un solo catch è il caso più semplice. Il codice reale di solito deve gestire più fallimenti diversi contemporaneamente. Continua con i blocchi catch multipli e il multi-catch in Java. Dopo di ciò, il blocco finally e try-with-resources completano la sintassi, mentre throw e throws coprono l'altro lato: segnalare i fallimenti dal proprio codice.

Esercitazione

Pratica
Un blocco try esegue `a[5] = 1;` su un array di 2 elementi, con `catch (RuntimeException e)` scritto per primo e `catch (ArrayIndexOutOfBoundsException e)` scritto per secondo. Cosa succede?
Un blocco try esegue `a[5] = 1;` su un array di 2 elementi, con `catch (RuntimeException e)` scritto per primo e `catch (ArrayIndexOutOfBoundsException e)` scritto per secondo. Cosa succede?
Was this page helpful?