Gestione delle eccezioni in Java
Quando si verifica un errore, il metodo che lo ha generato non sempre ha la possibilità di porre rimedio ad esso, ma potrà delegare il chiamante a gestire direttamente l’eccezione o a rilanciarla a sua volta. La gestione può essere delegata a uno dei metodi la cui cascata di chiamate invoca il metodo che lancia l’eccezione. La gestione delle eccezioni consente di trasferire il controllo dell’errore (rilanciare) dal punto in cui si manifesta al punto del programma in cui è possibile gestire (catturare) l’errore, ripristinando la situazione corretta.
Catch or Specify Requirement
Un buon programma Java dovrebbe rispettare il Catch or Specify Requirement, ossia il codice che può generare una eccezione dovrebbe:
- Catturarla e gestirla (try-catch)
- Essere racchiuso in un metodo che la rilancia (throws)
Quindi si va a risolvere l’eccezione da un’altra parte del programma. Throws potrebbe essere utile per la riusabilità del codice. Se l’eccezione non viene gestita da qualche parte, allora verrà propagata fino al main dove verrà interrotto producendo un messaggio di errore. Il messaggio include il tipo di eccezione lanciata e la pila delle chiamate ai metodi che consentono di individuare il punto del programma in cui l’eccezione si è verificata.
Tipi di eccezioni
- Checked: sono quelle per cui è già previsto che un certo codice potrà generare un'eccezione. I metodi che le possono generare sono obbligati a stabilire una politica per gestirle (o propagando l’eccezione indietro nello stack di chiamate, o gestendola in qualche modo). Un metodo che non gestisce localmente un’eccezione controllata è obbligato a dichiarare che può sollevare quel tipo di eccezione, mediante la keyword throws, così l’eccezione viene propagata a chi chiamerà quel metodo, che dovrà trovare un modo per risolverla. Esempio: apertura di file inesistente.
- Runtime: sono non note e vengono scoperte in fase di esecuzione e sta alla sensibilità dello sviluppatore prevederle. I metodi non sono obbligati a stabilire una politica per le eccezioni non controllate lanciate dalla loro implementazione (e spesso non le gestiscono). Le eccezioni a runtime non siamo obbligati a gestirle, le altre si, ad esempio nel caso della IOException è sicuro che ci potrebbe essere un problema se provo ad aprire un file aperto con un altro programma di scrittura, quel file non può essere editato.
Esempi di eccezioni
- ArithmeticException: errori in operazioni aritmetiche, es. divisione per zero.
- IllegalArgumentException: quando il chiamante passa un argomento inappropriato.
- NumberFormatException: quando si tenta di convertire una stringa in un formato numerico e la stringa non ha un valore appropriato.
- ArrayIndexOutOfBoundsException: quando si utilizza un indice esterno al range valido per un array.
- ClassCastException: si verifica quando proviamo a fare male il cast di qualcosa.
- StringIndexOutOfBoundsException: quando si tenta di accedere ad una posizione inesistente di una stringa.
- NegativeArraySizeException: quando si tenta di creare un array di dimensione negativa.
- NullPointerException: quando si tenta di accedere ad un oggetto tramite riferimento null.
Si può lanciare un’eccezione, ad esempio:
static int calc(int num1, int num2, int div) {
if(div == 0)
throw new IllegalArgumentException("Divisore non valido");
int val = (num1 + num2)/div;
return val;
}
Con l'operatore new alloco l'oggetto che rappresenta l'eccezione e con l'operatore throw lo lanciamo. Potrei anche non mettere throw, con il throw trasformo la mia eccezione in un'eccezione controllata perché sono io sviluppatore che la sto controllando dichiarandolo ufficialmente.
Blocco try-catch
Il blocco try-catch è un blocco di gestione delle eccezioni. Con questo costrutto possiamo provare a risolvere le eccezioni. Mediante il try-catch è come se stessimo dicendo che c'è un percorso principale, se però si verifica un problema allora cattura un problema e fai qualcosa, è sostanzialmente un percorso alternativo nel caso in cui ci sia un problema. Il blocco try-catch si legge come "prova a eseguire le istruzioni presenti nel blocco try, se c'è un'eccezione ti dico io come risolvere, te lo dico nel catch". La soluzione presente nel catch è detta in maniera custom, cioè il catch ha bisogno di sapere un template cioè quale eccezione devo sistemare, posso essere generico e dirgli di lavorare su qualsiasi eccezione. Possiamo aggiungere quanti catch vogliamo e l'ordine di esecuzione è l'ordine di dichiarazione.
Bisogna fare attenzione a ciò che mettiamo nel try, se infatti mettiamo qualcosa che va a manipolare dati del programma stesso e si genera un'eccezione, tutto ciò che abbiamo scritto nel try e che è stato eseguito decade. Se invece nel try c'è qualcosa di “esterno”, quindi ad esempio si fa una chiamata a un sito web, ciò che è stato fatto ormai è stato fatto.
Propagazione delle eccezioni
Le eccezioni si propagano finché si incontra un metodo che intercetta e gestisce l’eccezione, oppure finché non termina anche il metodo main. Per intercettare e gestire un’eccezione ad un livello più esterno, il metodo che produce l’eccezione deve essere invocato all’interno di un blocco try che possieda la clausola catch appropriata per gestirla. Try-catch può essere usato anche nel caso dei cast, infatti invece di scrivere qualcosa del tipo if (p instanceof studente), che è sbagliato perché mi intrometto tra l’interazione dello studente e del metodo in cui si trova l’if, posso fare un try-catch, nel try metto un cast del tipo s=(studente)p.
Clausola finally
La clausola finally può essere associata al try, indica una parte del codice che deve essere eseguita a prescindere da cosa sia accaduto nel try. La finally si usa poco, può essere utile solo se abbiamo una qualche istruzione che vogliamo sia eseguita sia se si segue il try che si segue uno qualunque del catch. Il finally si mette dopo il catch del tipo:
try {
// codice
} catch (ExceptionType e) {
// gestione eccezione
} finally {
// codice da eseguire sempre
}
-
La gestione delle eccezioni in Java
-
Metodi ausiliari progettare classi eccezioni java
-
Eccezioni all'ereditarietà mendeliana: Genetica Medica
-
Tolleranze dimensionali (errori di manifattura, errori dimensionali) - Appunti completi