Introduzione
Un’ eccezione è un evento di errore che può verificarsi durante l’esecuzione di un programma e interrompe il suo normale svolgimento. Java fornisce un modo robusto e orientato agli oggetti per gestire scenari di eccezione noto come Gestione delle eccezioni di Java.
Le eccezioni in Java possono derivare da diverse situazioni, come dati errati inseriti dall’utente, guasti hardware, interruzioni della connessione di rete o un server di database non disponibile. Il codice che specifica cosa fare in scenari di eccezione specifici viene chiamato gestione delle eccezioni.
Lancio e cattura delle eccezioni
Java crea un oggetto eccezione quando si verifica un errore durante l’esecuzione di un’istruzione. L’oggetto eccezione contiene molte informazioni di debug, come la gerarchia dei metodi, il numero di riga in cui si è verificata l’eccezione e il tipo di eccezione.
Se si verifica un’eccezione in un metodo, il processo di creazione dell’oggetto eccezione e del suo passaggio all’ambiente di esecuzione viene chiamato “lanciare l’eccezione”. Il flusso normale del programma si interrompe e l’ambiente di esecuzione Java (JRE) cerca di trovare l’handler per l’eccezione. L’Exception Handler è il blocco di codice che può elaborare l’oggetto eccezione.
- La logica per trovare l’Exception Handler inizia cercando nel metodo in cui si è verificato l’errore.
- Se non viene trovato alcun handler appropriato, si sposterà al metodo chiamante.
- E così via.
Quindi, se lo stack delle chiamate del metodo è A->B->C
e si verifica un’eccezione nel metodo C
, la ricerca dell’handler appropriato si sposterà da C->B->A
.
Se viene trovato un handler eccezione appropriato, l’oggetto eccezione viene passato all’handler per elaborarlo. L’handler si dice “catturare l’eccezione”. Se non viene trovato alcun handler eccezione appropriato, il programma termina e stampa le informazioni sull’eccezione sulla console.
Il framework di gestione delle eccezioni Java viene utilizzato solo per gestire gli errori in fase di esecuzione. Gli errori in fase di compilazione devono essere corretti dallo sviluppatore che scrive il codice, altrimenti il programma non verrà eseguito.
Parole chiave per la gestione delle eccezioni in Java
Java fornisce parole chiave specifiche per scopi di gestione delle eccezioni.
- throw – Sappiamo che se si verifica un errore, viene creato un oggetto eccezione e quindi l’esecuzione di Java inizia a gestirli. A volte potremmo voler generare eccezioni esplicitamente nel nostro codice. Ad esempio, in un programma di autenticazione utente, dovremmo lanciare eccezioni ai client se la password è
null
. La parola chiavethrow
viene utilizzata per lanciare eccezioni all’esecuzione per gestirle. - throws – Quando stiamo lanciando un’eccezione in un metodo e non la stiamo gestendo, allora dobbiamo utilizzare la parola chiave
throws
nella firma del metodo per far sapere al programma chiamante le eccezioni che potrebbero essere lanciate dal metodo. Il metodo chiamante potrebbe gestire queste eccezioni o propagarle al proprio metodo chiamante utilizzando la parola chiavethrows
. Possiamo fornire più eccezioni nella clausolathrows
, e può essere utilizzata anche con il metodomain()
. - try-catch – Utilizziamo il blocco
try-catch
per la gestione delle eccezioni nel nostro codice.try
è l’inizio del blocco ecatch
è alla fine del bloccotry
per gestire le eccezioni. Possiamo avere più blocchicatch
con un bloccotry
. Il bloccotry-catch
può essere anche annidato. Il bloccocatch
richiede un parametro che dovrebbe essere di tipoException
. - finalmente – il blocco
finally
è opzionale e può essere usato solo con un bloccotry-catch
. Poiché l’eccezione interrompe il processo di esecuzione, potremmo avere alcune risorse aperte che non verranno chiuse, quindi possiamo usare il bloccofinally
. Il bloccofinally
viene sempre eseguito, sia che si verifichi un’eccezione sia che non si verifichi.
Un Esempio di Gestione delle Eccezioni
- Il metodo
testException()
lancia eccezioni usando la parola chiavethrow
. La firma del metodo utilizza la parola chiavethrows
per far sapere al chiamante il tipo di eccezioni che potrebbe lanciare. - Nel metodo
main()
, sto gestendo le eccezioni usando il bloccotry-catch
nel metodomain()
. Quando non le sto gestendo, le sto propagando a runtime con la clausolathrows
nel metodomain()
. - Il
testException(-10)
non viene mai eseguito a causa dell’eccezione e poi viene eseguito il bloccofinally
.
Il printStackTrace()
è uno dei metodi utili nella classe Exception
per scopi di debug.
Questo codice produrrà il seguente output:
Outputjava.io.FileNotFoundException: Negative Integer -5
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:24)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:10)
Releasing resources
Exception in thread "main" java.io.IOException: Only supported for index 0 to 10
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:27)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:19)
Alcuni punti importanti da notare:
- Non possiamo avere una clausola
catch
ofinally
senza un’istruzionetry
. - A
try
statement should have eithercatch
block orfinally
block, it can have both blocks. - Non possiamo scrivere alcun codice tra i blocchi
try-catch-finally
. - Possiamo avere più blocchi
catch
con un singolo statementtry
. - I blocchi
try-catch
possono essere annidati in modo simile agli statementif-else
. - Possiamo avere solo un blocco
finally
con un’istruzionetry-catch
.
Gerarchia delle eccezioni Java
Come già detto, quando viene generata un’eccezione, viene creato un oggetto eccezione. Le eccezioni Java sono gerarchiche e l’ereditarietà viene utilizzata per categorizzare diversi tipi di eccezioni. Throwable
è la classe padre della gerarchia delle eccezioni Java e ha due oggetti figlio – Error
ed Exception
. Le Exception
sono ulteriormente divise in Exception
controllate e Exception
non controllate.
- Errori:
Error
sono scenari eccezionali che esulano dallo scopo dell’applicazione e non è possibile prevedere e recuperare da essi. Ad esempio, guasto hardware, crash della macchina virtuale Java (JVM) o errore di memoria esaurita. Ecco perché abbiamo una gerarchia separata diError
e non dovremmo cercare di gestire queste situazioni. Alcuni dei comuniError
sonoOutOfMemoryError
eStackOverflowError
. - Eccezioni Verificate: Le eccezioni verificate sono scenari eccezionali che possiamo prevedere in un programma e cercare di recuperare da esso. Ad esempio,
FileNotFoundException
. Dovremmo catturare questa eccezione e fornire un messaggio utile all’utente e registrarla correttamente per scopi di debug. L’Exception
è la classe genitore di tutte le eccezioni verificate. Se stiamo lanciando un’eccezione verificata, dobbiamocatch
arla nello stesso metodo, o dobbiamo propagarla al chiamante utilizzando la parola chiavethrows
. - Eccezione in Tempo di Esecuzione: Le eccezioni in tempo di esecuzione sono causate da una programmazione scorretta. Ad esempio, cercare di recuperare un elemento da un array. Dovremmo controllare prima la lunghezza dell’array prima di tentare di recuperare l’elemento, altrimenti potrebbe lanciare
ArrayIndexOutOfBoundException
durante l’esecuzione.RuntimeException
è la classe genitore di tutte le eccezioni in tempo di esecuzione. Se stiamolanciando
una qualsiasi eccezione in tempo di esecuzione in un metodo, non è necessario specificarle nella clausola di firma del metodothrows
. Le eccezioni in tempo di esecuzione possono essere evitate con una programmazione migliore.
Alcuni metodi utili delle Classi di Eccezione
Java Exception
e tutte le sue sottoclassi non forniscono metodi specifici, e tutti i metodi sono definiti nella classe base – Throwable
. Le classi Exception
vengono create per specificare diversi tipi di scenari di Exception
in modo che possiamo identificare facilmente la causa principale e gestire l’eccezione in base al suo tipo. La classe Throwable
implementa l’interfaccia Serializable
per l’interoperabilità.
Alcuni dei metodi utili della classe Throwable
sono:
- public String getMessage() – Questo metodo restituisce il messaggio
String
diThrowable
e il messaggio può essere fornito durante la creazione dell’eccezione tramite il suo costruttore. - public String getLocalizedMessage() – Questo metodo è fornito in modo che le sottoclassi possano sovrascriverlo per fornire un messaggio specifico per la località al programma chiamante. L’implementazione della classe
Throwable
di questo metodo utilizza il metodogetMessage()
per restituire il messaggio dell’eccezione. - public synchronized Throwable getCause() – Questo metodo restituisce la causa dell’eccezione o
null
se la causa è sconosciuta. - public String toString() – Questo metodo restituisce le informazioni su
Throwable
in formatoString
, laString
restituita contiene il nome della classeThrowable
e il messaggio localizzato. - public void printStackTrace() – Questo metodo stampa le informazioni sulla traccia dello stack sul flusso di errore standard. Il metodo è sovraccaricato, e possiamo passare
PrintStream
oPrintWriter
come argomento per scrivere le informazioni sulla traccia dello stack su file o flusso.
Java 7 Gestione automatica delle risorse e miglioramenti del blocco Catch
Se stai affrontando molte eccezioni in un singolo blocco try
, noterai che il codice nel blocco catch
consiste principalmente in codice ridondante per registrare l’errore. In Java 7, una delle nuove caratteristiche era un miglioramento del blocco catch
in cui possiamo gestire più eccezioni in un unico blocco catch
. Ecco un esempio del blocco catch
con questa funzionalità:
Ci sono alcune restrizioni, come ad esempio l’oggetto eccezione è finale e non possiamo modificarlo all’interno del blocco catch
. Leggi l’analisi completa su Miglioramenti del blocco Catch in Java 7.
La maggior parte delle volte, usiamo il blocco finally
solo per chiudere le risorse. A volte dimentichiamo di chiuderle e otteniamo eccezioni durante l’esecuzione quando le risorse sono esaurite. Queste eccezioni sono difficili da debuggare e potremmo dover controllare ogni punto in cui stiamo utilizzando quella risorsa per assicurarci di chiuderla. In Java 7, uno dei miglioramenti è stato try-with-resources
, dove possiamo creare una risorsa nello stesso blocco try
e usarla all’interno del blocco try-catch
. Quando l’esecuzione esce dal blocco try-catch
, l’ambiente di runtime chiude automaticamente queste risorse. Ecco un esempio del blocco try-catch
con questo miglioramento:
A Custom Exception Class Example
Java fornisce molte classi di eccezioni per noi da usare, ma a volte potremmo avere bisogno di creare le nostre classi di eccezioni personalizzate. Ad esempio, per notificare il chiamante di un tipo specifico di eccezione con il messaggio appropriato. Possiamo avere campi personalizzati per il tracciamento, come codici di errore. Ad esempio, diciamo che scriviamo un metodo per elaborare solo file di testo, quindi possiamo fornire al chiamante il codice di errore appropriato quando viene inviato un altro tipo di file in input.
Prima, crea MyException
:
Poi, crea un CustomExceptionExample
:
Possiamo avere un metodo separato per elaborare diversi tipi di codici di errore che otteniamo da metodi diversi. Alcuni di essi vengono consumati perché potremmo non voler notificare l’utente di quello, o alcuni di essi li rimanderemo per notificare all’utente il problema.
Ecco come sto estendendo Exception
in modo che ogni volta che questa eccezione viene generata, deve essere gestita nel metodo o restituita al programma chiamante. Se estendiamo RuntimeException
, non è necessario specificarlo nella clausola throws
.
Questa è stata una decisione di progettazione. L’uso delle eccezioni controllate Exception
ha il vantaggio di aiutare gli sviluppatori a capire quali eccezioni possono essere attese e intraprendere azioni appropriate per gestirle.
Linee guida per la gestione delle eccezioni in Java
- Usa eccezioni specifiche – Le classi di base dell’ierarchia delle eccezioni non forniscono informazioni utili, ecco perché Java ha molte classi di eccezioni, come
IOException
con ulteriori sottoclassi comeFileNotFoundException
,EOFException
, ecc. Dovremmo semprethrow
ecatch
classi di eccezioni specifiche in modo che il chiamante possa conoscere facilmente la causa radice dell’eccezione e elaborarla. Questo rende il debug più facile e aiuta le applicazioni client a gestire le eccezioni in modo appropriato. - Lancia presto o fallisci in modo rapido – Dovremmo cercare di
throw
eccezioni il prima possibile. Considera il metodoprocessFile()
sopra, se passiamo l’argomentonull
a questo metodo, otterremo la seguente eccezione:
OutputException in thread "main" java.lang.NullPointerException
at java.io.FileInputStream.<init>(FileInputStream.java:134)
at java.io.FileInputStream.<init>(FileInputStream.java:97)
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:42)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
Durante il debug, dovremo prestare attenzione alla traccia dello stack per identificare con precisione la posizione effettiva dell’eccezione. Se cambiamo la logica di implementazione per controllare queste eccezioni anticipatamente come segue:
Allora la traccia dello stack dell’eccezione indicherà dove si è verificata l’eccezione con un messaggio chiaro:
Outputcom.journaldev.exceptions.MyException: File name can't be null
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:37)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
- Cattura Tardiva – Poiché Java impone di gestire l’eccezione controllata o dichiararla nella firma del metodo, talvolta gli sviluppatori tendono a
catch
l’eccezione e registrare l’errore. Ma questa pratica è dannosa perché il programma chiamante non riceve alcuna notifica dell’eccezione. Dovremmocatch
le eccezioni solo quando possiamo gestirle in modo appropriato. Ad esempio, nel metodo sopra, stothrow
ando le eccezioni al metodo chiamante per gestirle. Lo stesso metodo potrebbe essere utilizzato da altre applicazioni che potrebbero voler elaborare l’eccezione in modo diverso. Durante l’implementazione di qualsiasi funzionalità, dovremmo semprethrow
are le eccezioni al chiamante e lasciare che sia lui a decidere come gestirle. - Chiusura delle Risorse – Poiché le eccezioni bloccano l’elaborazione del programma, dovremmo chiudere tutte le risorse nel blocco finally o utilizzare il miglioramento
try-with-resources
di Java 7 per consentire al runtime di Java di chiuderle per voi. - Registrazione delle eccezioni – Dovremmo sempre registrare i messaggi di eccezione e, durante il lancio delle eccezioni, fornire un messaggio chiaro in modo che chi chiama sappia facilmente perché si è verificata l’eccezione. Dovremmo sempre evitare un blocco
catch
vuoto che consuma semplicemente l’eccezione e non fornisce dettagli significativi dell’eccezione per il debug. - Blocco catch singolo per eccezioni multiple – La maggior parte delle volte registriamo i dettagli delle eccezioni e forniamo un messaggio all’utente, in questo caso dovremmo utilizzare la funzione Java 7 per gestire eccezioni multiple in un unico blocco
catch
. Questo approccio ridurrà le dimensioni del nostro codice e avrà un aspetto più pulito. - Utilizzo di eccezioni personalizzate – È sempre meglio definire una strategia di gestione delle eccezioni durante la progettazione e anziché lanciare e catturare eccezioni multiple, possiamo creare un’eccezione personalizzata con un codice di errore e il programma chiamante può gestire questi codici di errore. È anche una buona idea creare un metodo di utilità per elaborare diversi codici di errore e utilizzarli.
- Convenzioni di denominazione e pacchettizzazione – Quando si crea la propria eccezione personalizzata, assicurarsi che termini con
Exception
in modo che sia chiaro dal nome stesso che si tratta di una classe di eccezione. Inoltre, assicurarsi di confezionarle come è fatto nel Java Development Kit (JDK). Ad esempio,IOException
è l’eccezione di base per tutte le operazioni di IO. - Usa le eccezioni con giudizio – Le eccezioni sono costose, e a volte non è necessario lanciare eccezioni affatto, e possiamo restituire una variabile booleana al programma chiamante per indicare se un’operazione è stata eseguita con successo o meno. Questo è utile quando l’operazione è facoltativa e non vuoi che il tuo programma si blocchi se fallisce. Ad esempio, durante l’aggiornamento delle quotazioni di borsa nel database da un servizio web di terze parti, potremmo voler evitare di lanciare eccezioni se la connessione fallisce.
- Documenta le eccezioni lanciate – Usa Javadoc
@throws
per specificare chiaramente le eccezioni lanciate dal metodo. È molto utile quando stai fornendo un’interfaccia per altre applicazioni da utilizzare.
Conclusione
In questo articolo hai appreso come gestire le eccezioni in Java. Hai imparato a utilizzare throw
e throws
. Hai anche imparato su blocchi try
(e try-with-resources
), catch
e finally
.
Source:
https://www.digitalocean.com/community/tutorials/exception-handling-in-java