Introduzione alla Gestione delle Eccezioni in Java
- La gestione delle eccezioni in Java è fondamentale per gestire errori e condizioni eccezionali in modo controllato.
- Le eccezioni sono oggetti che rappresentano condizioni di errore e possono essere lanciate e catturate tramite blocchi
try
,catch
,throw
,throws
efinally
. - Esistono due principali categorie di eccezioni: quelle derivate da
Exception
, che possono essere gestite dai programmatori, e quelle derivate daError
, che rappresentano errori gravi del sistema e non dovrebbero essere gestite dai programmatori. - Le eccezioni non gestite causano l'interruzione del programma e la visualizzazione di uno stack trace che aiuta a identificare la causa dell'errore.
Fondamenti della gestione delle eccezioni
Un'eccezione Java è un oggetto che descrive una condizione eccezionale (cioè, di errore) che si è verificata in una sezione di codice.
Quando si verifica una condizione eccezionale, un oggetto che rappresenta quell'eccezione viene creato e lanciato (thrown) nel metodo che ha causato l'errore.
Quel metodo può scegliere di gestire direttamente l'eccezione, oppure passarla ad altri. In entrambi i casi, a un certo punto, l'eccezione viene catturata (catch) e processata.
Le eccezioni possono essere generate dal sistema di esecuzione Java, oppure possono essere generate manualmente dal codice. Le eccezioni generate da Java riguardano errori fondamentali che violano le regole del linguaggio Java o i vincoli dell'ambiente di esecuzione Java. Le eccezioni generate manualmente vengono tipicamente utilizzate per segnalare una condizione di errore al chiamante di un metodo.
La gestione delle eccezioni in Java è gestita tramite cinque parole chiave: try
, catch
, throw
, throws
, e finally
.
Brevemente, ecco come funzionano. Le istruzioni di programma che si desidera monitorare per eventuali eccezioni sono contenute all'interno di un blocco try
. Se un'eccezione si verifica all'interno del blocco try
, essa viene lanciata.
Il codice può catturare questa eccezione (usando catch
) e gestirla in qualche modo razionale. Le eccezioni generate dal sistema sono generate automaticamente dal sistema di esecuzione Java. Per generare manualmente un'eccezione, si utilizza la parola chiave throw
. Qualsiasi eccezione che viene lanciata fuori da un metodo deve essere specificata come tale tramite una clausola throws
. Qualsiasi codice che deve assolutamente essere eseguito dopo che un blocco try
è stato completato, è posto in un blocco finally
.
Questa è la forma generale di un blocco di gestione delle eccezioni:
try {
// blocco di codice da monitorare per errori
}
catch (TipoEccezione1 oggEccezione) {
// gestore di eccezioni per TipoEccezione1
}
catch (TipoEccezione2 oggEccezione) {
// gestore di eccezioni per TipoEccezione2
}
// ...
finally {
// blocco di codice da eseguire dopo la fine del blocco try
}
Qui, TipoEccezione
è il tipo di eccezione che si è verificato. Applicheremo questo schema nel dettaglio delle prossime lezioni.
Gestione automatica delle risorse
Esiste un'altra forma dell'istruzione try
che supporta la gestione automatica delle risorse.
Questa forma di try
, chiamata try-with-resources, verrà approfondita nel contesto della gestione dei file, poiché i file sono alcune delle risorse più comunemente utilizzate.
Tipi di eccezioni
Tutti i tipi di eccezioni sono sottoclassi della classe incorporata Throwable
.
Dunque, Throwable
si trova al vertice della gerarchia delle classi di eccezioni. Immediatamente sotto Throwable
ci sono due sottoclassi che suddividono le eccezioni in due rami distinti:
-
Un ramo è guidato da
Exception
.Questa classe è usata per condizioni eccezionali che i programmi utente dovrebbero intercettare. È anche la classe che si estende per creare i propri tipi di eccezioni personalizzate.
Esiste un'importante sottoclasse di
Exception
, chiamataRuntimeException
. Le eccezioni di questo tipo sono definite automaticamente per i programmi scritti e includono cose come la divisione per zero e l'indicizzazione non valida di array. -
L'altro ramo è guidato da
Error
, che definisce eccezioni che non ci si aspetta siano intercettate in circostanze normali dal programma.Le eccezioni di tipo
Error
sono utilizzate dal sistema di esecuzione Java per indicare errori legati all'ambiente di esecuzione stesso. Un esempio di tale errore è lo stack overflow. Nelle prossime lezioni non ci concentreremo sulle eccezioni di tipoError
, poiché esse sono tipicamente generate in risposta a fallimenti catastrofici che solitamente non possono essere gestiti dal programma.
La figura seguente mostra la gerarchia delle eccezioni e degli errori in Java:
Eccezioni non intercettate
Prima di imparare a gestire le eccezioni in un programma, è utile vedere cosa succede quando non vengono gestite.
Questo piccolo programma include un'espressione che causa intenzionalmente un errore di divisione per zero:
class DivisionePerZero {
public static void main(String[] args) {
int d = 0;
int a = 42 / d;
}
}
Quando il sistema di esecuzione Java rileva il tentativo di dividere per zero, costruisce un nuovo oggetto eccezione e poi lancia questa eccezione.
Ciò causa l'interruzione dell'esecuzione di DivisionePerZero
, poiché una volta che un'eccezione è stata lanciata, deve essere intercettata da un gestore di eccezioni e gestita immediatamente.
In questo esempio, non è stato fornito alcun gestore di eccezioni personalizzato, quindi l'eccezione viene intercettata dal gestore predefinito fornito dal sistema di esecuzione Java. Qualsiasi eccezione non intercettata dal programma verrà infine elaborata dal gestore predefinito. Il gestore predefinito visualizza una stringa che descrive l'eccezione, stampa un trace dello stack dal punto in cui l'eccezione si è verificata e termina il programma.
Ecco l'eccezione generata quando questo esempio viene eseguito:
java.lang.ArithmeticException: / by zero
at DivisionePerZero.main(DivisionePerZero.java:4)
Si noti come il nome della classe, DivisionePerZero
; il nome del metodo, main
; il nome del file, DivisionePerZero.java
; e il numero di riga, 4
, siano tutti inclusi nello stack trace semplice. Inoltre, si noti che il tipo di eccezione lanciata è una sottoclasse di Exception
chiamata ArithmeticException
, che descrive in modo più specifico il tipo di errore verificatosi.
Come vedremo nelle prossime lezioni, Java fornisce diverse classi di eccezione integrate che corrispondono a vari tipi di errori di esecuzione che possono essere generati.
Un'altra osservazione: l'esatto output può variare a seconda della versione del sistema Java in uso e di altre condizioni, poiché la costruzione dell'output dello stack trace può differire tra le varie JDK.
Lo stack trace mostrerà sempre la sequenza delle invocazioni di metodo che hanno portato all'errore. Ad esempio, ecco un'altra versione del programma precedente che genera lo stesso errore ma in un metodo separato da main()
:
class DivisionePerZero2 {
static void sottoroutine() {
int d = 0;
int a = 10 / d;
}
public static void main(String[] args) {
DivisionePerZero2.sottoroutine();
}
}
Lo stack trace risultante dal gestore di eccezioni predefinito mostra come viene visualizzato l'intero stack di chiamate:
java.lang.ArithmeticException: / by zero
at DivisionePerZero2.sottoroutine(DivisionePerZero2.java:4)
at DivisionePerZero2.main(DivisionePerZero2.java:7)
Come si può vedere, in fondo allo stack c'è la riga 7 del metodo main
, che rappresenta la chiamata a sottoroutine()
, che ha causato l'eccezione alla riga 4. Lo stack trace è molto utile per il debug, perché individua la sequenza precisa dei passaggi che hanno portato all'errore.