Menu

try-catch in Java: gestire le eccezioni senza crash

Come usare try-catch in Java per gestire le eccezioni: catturare tipi specifici, il blocco finally, try-with-resources e gli errori che nascondono i bug.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Catturare un'eccezione invece di andare in crash

Sai già che quando qualcosa va storto Java lancia un'eccezione, e un'eccezione non gestita ferma il programma con uno stack trace. Un blocco try-catch è il modo per prendere il controllo: metti il codice rischioso nel try e, se lancia un'eccezione, Java salta al blocco catch corrispondente invece di andare in crash.

Nel momento in cui 10 / 0 lancia l'eccezione, il resto del blocco try viene saltato e il controllo passa al catch. Quando il catch finisce, l'esecuzione continua normalmente: il programma non muore.

La variabile del catch contiene l'eccezione

La e in catch (ArithmeticException e) è un oggetto vero. Contiene informazioni su cosa è andato storto, e la più utile è un messaggio:

e.getMessage() restituisce una breve descrizione. Durante il debug, e.printStackTrace() stampa lo stack trace completo che mostra esattamente da dove è arrivata l'eccezione: usalo quando il solo messaggio non basta per trovare la causa.

Cattura il tipo specifico, non tutto

Un blocco catch cattura solo le eccezioni che corrispondono al tipo dichiarato (o a una sua sottoclasse). L'errore più grande di chi inizia è catturare Exception per far "sparire" un problema:

// Non farlo: nasconde bug reali
try {
    doWork();
} catch (Exception e) {
    // ingoia NullPointerException, errori di battitura, errori di logica... tutto
}

Cattura il tipo più ristretto che sei davvero in grado di gestire. Se ti aspetti un input numerico sbagliato, cattura NumberFormatException. Tutto ciò che non avevi previsto deve poter propagarsi, così te ne accorgi davvero invece di continuare in silenzio in uno stato rotto.

Gestire più tipi di eccezione

Puoi mettere più blocchi catch uno dopo l'altro. Java li controlla dall'alto in basso ed esegue il primo che corrisponde, quindi ordinali dal più specifico al più generale:

Quando due tipi richiedono la stessa gestione, usa un solo multi-catch con | invece di duplicare il blocco:

Una trappola: un tipo più generale deve venire dopo quelli specifici. Mettere catch (Exception e) per primo rende irraggiungibili i blocchi successivi più specifici, e il compilatore lo rifiuta.

finally viene sempre eseguito

Un blocco finally viene eseguito dopo il try e l'eventuale catch, qualunque cosa sia successa: successo, eccezione catturata o perfino un return anticipato. È il posto giusto per le operazioni di pulizia che devono sempre avvenire.

"Closing resource" viene stampato che l'eccezione si verifichi o no. Evita però di usare return dentro finally: può scartare in silenzio un'eccezione o sovrascrivere un valore restituito dal blocco try.

try-with-resources chiude le cose al posto tuo

Quando lavori con qualcosa che va chiuso (un file, una connessione di rete, un'istruzione di database), dichiararlo dentro try (...) lo chiude automaticamente alla fine del blocco, anche se viene lanciata un'eccezione. Funziona con qualsiasi tipo che implementa l'interfaccia AutoCloseable.

Sostituisce il vecchio schema in cui si apriva nel try e si chiudeva nel finally, ed è meno soggetto a errori perché non puoi dimenticare la chiusura. Preferiscilo ogni volta che hai a che fare con una risorsa da chiudere.

Non usare le eccezioni per il normale flusso di controllo

try-catch serve per situazioni eccezionali, non per condizioni normali. Catturare un'eccezione costa più di un semplice controllo e rende il codice più difficile da leggere. Se puoi verificare prima la condizione con un controllo if-else, fai così:

// Da evitare: usare un catch per verificare se manca una chiave
try {
    process(map.get(key).trim());
} catch (NullPointerException e) {
    // gestisci la chiave mancante
}

// Meglio: controllare in modo esplicito
String value = map.get(key);
if (value != null) {
    process(value.trim());
}

Riserva catch alle cose davvero fuori dal tuo controllo: input sbagliati dell'utente, file mancanti, errori di rete.

Prossima pagina: NullPointerException

L'eccezione più comune che catturerai (e provocherai) in Java è la NullPointerException: compare nell'istante in cui chiami un metodo su qualcosa che si è rivelato null. Nella prossima pagina vedremo esattamente cosa la provoca, come leggere il suo stack trace e le abitudini che impediscono che si verifichi.

Domande frequenti

Come si usa try-catch in Java?

Metti il codice che potrebbe lanciare un'eccezione dentro un blocco try, poi aggiungi un blocco catch che indica il tipo di eccezione da gestire: try { risky(); } catch (IOException e) { ... }. Se il codice nel try lancia un'eccezione corrispondente, Java salta direttamente al blocco catch invece di andare in crash. La variabile (e) contiene l'oggetto eccezione, così puoi leggerne il messaggio con e.getMessage().

A cosa serve il blocco finally in Java?

Un blocco finally viene eseguito dopo try/catch in ogni caso: che il codice sia andato a buon fine, abbia lanciato un'eccezione o sia uscito prima con un return. È il posto per le operazioni di pulizia che devono sempre avvenire, come chiudere un file o rilasciare un lock. Per chiudere le risorse, try-with-resources di solito è più pulito perché le chiude automaticamente.

Conviene catturare Exception o un tipo di eccezione specifico?

Cattura il tipo più specifico che sei davvero in grado di gestire. Catturare Exception (o peggio Throwable) ingoia ogni problema, compresi bug come NullPointerException, e nasconde la causa reale. Cattura NumberFormatException se è ciò che ti aspetti, e lascia propagare le eccezioni inattese così te ne accorgi.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA