Sei sulla pagina 1di 9

Eccezioni

Introduzione
Il termine eccezione un'abbreviazione della frase "evento eccezionale"; esso pu essere definito nel seguente modo: Un'eccezione un evento che si verifica durante l'esecuzione di un programma (a runtime) e che interrompe il normale flusso di istruzioni. Generalizzando, possiamo classificare gli errori che generano eventi eccezionali, in quattro tipi: 1. errori di input dell'utente; 2. errori dei dispositivi (ad es. file non esistente, stampante spenta, ecc.); 3. limitazioni fisiche (ad es. memoria esaurita); 4. errori di codice (ad es. elaborazione di un indice di array non valido). Quando in un metodo si verifica un errore del tipo descritto, il metodo crea un oggetto particolare, detto oggetto eccezione o semplicemente eccezione (exception) e lascia al sistema runtime il compito di occuparsi di tale errore. In altre parole si dice che il metodo lancia un'eccezione (throwing an exception). L'oggetto eccezione contiene informazioni riguardanti l'errore e lo stato nel quale si trovava il programma quando l'errore si verificato. L'errore originato da un metodo pu essere gestito in diverse maniere:

1. Si pu decidere di non occuparsi dell'errore. In tal caso, il programma viene


terminato con una segnalazione dell'errore verificatosi. 2. Il metodo pu terminare restituendo uno speciale codice di errore, che sar analizzato dal chiamante. 3. Lerrore pu essere intercettato e gestito. Per comprendere meglio ci che accade, vediamo come viene gestito o non gestito un errore nei tre casi, mediante alcuni esempi.

Caso1: nessuna gestione dellerrore


Supponiamo che un programmatore A fornisca la classe Vettore, una classe pubblica, non eseguibile, con un metodo statico la cui intestazione : public static int getElemento(int index)

Il metodo getElemento restituisce l'elemento che si trova, all'interno dell'array, all'indice specificato come parametro.

Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

public class Vettore { static int[] a= {0,1,2,3,4,5,6,7,8,9}; public static int getElemento(int index) { return a[index]; } }

Supponiamo che un programmatore B scriva un'applicazione che stampa l'elemento di un'array in una data posizione, utilizzando la classe fornita dal programmatore A.
import java.util.*; public class UsaVettore { public static void main(String[] args) { Scanner console= new Scanner(System.in); int index, elemento; System.out.println("Inserire l'indice dell'elemento da stampare"); index= console.nextInt(); elemento= Vettore.getElemento(index); System.out.println("elemento di indice " +index+ " = " +elemento); System.out.println("FINE ELABORAZIONE"); } }

L'esecuzione del metodo main pu dare origine ad un errore, nel caso in cui l'utente inserisca un indice maggiore di 9 o negativo. Nel caso in cui il valore inserito per l'indice sia 10, il programma termina producendo in output la seguente segnalazione:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10 at Vettore.getElemento <Vettore.java: 6> at UsaVettore.main <UsaVettore.java: 10>

Il messaggio ci avverte che: durante l'esecuzione del metodo main si verificata un'eccezione
Exception in thread "main";

l'errore stato determinato dal tentativo di accedere ad un elemento di indice 10, cio al di fuori della dimensione dell'array l'istruzione che ha causato l'eccezione si trova alla riga 6 del file Vettore.java e si verificata durante l'esecuzione del metodo getElemento l'eccezione si propagata al metodo main, precisamente alla riga 11, della classe UsaVettore, contenuta all'interno del file UsaVettore.java

ArrayIndexOutOfBoundsException : 10;

at Vettore.getElemento <Vettore.java: 6>;

at UsaVettore.main <UsaVettore.java: 10>.

Se si decide di non gestire l'eccezione, durante l'esecuzione dell'istruzione


return a[index];

del metodo getElemento, viene lanciata un'eccezione di tipo


ArrayIndexOutOfBoundsException
Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

L'eccezione viene passata al chiamante (main), il quale, non prevedendo alcun strumento di gestione dell'eccezione, termina bruscamente con la segnalazione dell'errore riscontrato.

Caso 2: gestione dellerrore mediante codice di errore


Supponiamo che un programmatore A, nello scrivere il metodo getElemento, segua le indicazioni del punto 2, controllando la validit dell'indice passato come parametro.
public class Vettore { static int[] a= {0,1,2,3,4,5,6,7,8,9}; public static int getElemento(int index) { final int ERRORE= -1; if(index>=0 && index<=9) { return a[index]; } else { return ERRORE; } } }

Il metodo getElemento restituisce -1 se l'indice non valido. Il programma chiamante dovr controllare il valore restituito per sapere se l'operazione andata a buon fine o se si verificato un errore.
import java.util.*; class UsaVettore { public static void main(String [] args) { Scanner console= new Scanner(System.in); int index, elemento; System.out.println("Inserire l'indice dell'elemento da stampare"); index= console.nextInt(); elemento= Vettore.getElemento(index); if(elemento!=-1) { System.out.println("elemento di indice " +index+ " = " +elemento); System.out.println("FINE ELABORAZIONE"); } else { System.out.println("ERRORE: INDICE NON VALIDO"); } } }

L'output prodotto dal programma, nel caso in cui il valore inserito per l'indice sia 10, :
ERRORE: INDICE NON VALIDO
Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

Si pone il seguente interrogativo:


Quale valore di ritorno opportuno scegliere nel caso di indice di array non valido? Nel caso specifico, il valore -1 adeguato perch l'array della classe Vettore contiene solo interi da 0 a 9 e quindi non si corre il rischio di confondere un elemento dell'array con un codice di errore. In altre situazioni il valore -1 potrebbe non essere opportuno. Per esempio se l'array contiene numeri interi, senza limitazioni, non possibile capire se il valore restituito effettivamente un elemento dell'array o un codice di errore. Inoltre, potete notare come il codice si appesantisca e divenga meno leggibile, a causa dei controlli (costrutti if) inseriti al suo interno. Vediamo allora come si pu scrivere un codice migliore dei precedenti, attenendoci alle indicazioni del punto 3.

Caso 3: intercettare e gestire lerrore con try e catch


public class Vettore { static int[] a= {0,1,2,3,4,5,6,7,8,9}; public static int getElemento(int index) { return a[index]; } } import java.util.*; public class UsaVettore { public static void main(String[] args) { Scanner console= new Scanner(System.in); int index, elemento; System.out.println("Inserire l'indice dell'elemento da stampare"); index= console.nextInt(); try { elemento= Vettore.getElemento(index); System.out.println("elemento di indice " +index+ " = " +elemento); System.out.println("FINE ELABORAZIONE"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("ERRORE: INDICE NON VALIDO"); } } }

L'output prodotto dal programma, nel caso in cui il valore inserito per l'indice sia 10, :
ERRORE: INDICE NON VALIDO

Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

Le differenze rispetto al codice dell'esempio 2 sono: il metodo getElemento non deve preoccuparsi di restituire un codice di errore opportuno; il metodo chiamante (main) pu stabilire se l'operazione andata a buon fine senza dover controllare il valore restituito dal metodo getElemento, ma semplicemente inserendo il codice che pu generare un'eccezione all'interno di un particolare costrutto try; inserendo il codice che gestisce l'eccezione all'interno di un particolare costrutto catch. Quando il metodo getElemento lancia l'eccezione ArrayIndexOutOfBoundsException, essa viene gestita dal chiamante attraverso il codice presente nel blocco catch. Se nessuna eccezione viene lanciata, il blocco catch ignorato.

Recuperare lerrore
Nell'esempio 3 abbiamo visto come sia possibile rilevare un'eccezione e, conseguentemente, terminare il programma. Questo comportamento non sempre gradito all'utente. Pensate, per esempio, ad una fase molto lunga di inserimento di dati, che viene interrotta bruscamente per un errore di digitazione: l'utente dovr mandare in esecuzione il programma e inserire nuovamente tutti i dati. Dopo un po' di volte, cercher di acquistare un prodotto pi affidabile. Di conseguenza, ove possibile, preferibile cercare di rimediare all'errore. Il codice seguente una versione migliorata del programma dell'esempio 3. Esso comprende un ciclo while che permette all'utente di inserire pi volte il dato di input, fino a quando diventa accettabile.
import java.util.*; public class UsaVettore { public static void main(String[] args) { Scanner console= new Scanner(System.in); int index, elemento; boolean b= true; while(b) { System.out.println("Inserire l'indice dell'elemento da stampare"); index= console.nextInt(); try { elemento= Vettore.getElemento(index); System.out.println("elemento di indice " +index+ " = " +elemento); System.out.println("FINE ELABORAZIONE"); b= false; //consente di uscire dal ciclo } catch(ArrayIndexOutOfBoundsException e) { System.out.println("ERRORE: INDICE NON VALIDO"); } } } }
Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

Tipi di eccezioni
Le eccezioni, in Java, si distinguono in due gruppi: eccezioni controllate; eccezioni incontrollate. Le eccezioni incontrollate sono causate generalmente da errori di codice o da errori interni (per esempio l'esaurimento della memoria). Esempi di eccezioni incontrollate dovute ad errori di programmazione, sono: cast difettoso; accesso ad array fuori dai limiti; accesso a puntatore nullo. Le eccezioni controllate si verificano principalmente durante l'accesso a risorse periferiche. Esempi di tali eccezioni sono: tentativo di leggere oltre la fine di un file; impossibilit di trovare un file; tentativo di aprire un URL errato. Un'ulteriore differenza tra i due tipi di eccezioni si manifesta nel modo in cui devono essere gestite. Se un metodo lancia un'eccezione incontrollata, il programmatore libero di scegliere se gestirla o meno. In altre parole il programmatore pu inserire la chiamata al metodo in un costrutto try, oppure no, con le conseguenze che abbiamo visto negli esempi precedenti. Se un metodo lancia un'eccezione controllata, il programmatore obbligato ad inserire la chiamata ad esso in un costrutto try, altrimenti il compilatore segnaler l'errore. Per capire se un metodo lancia un'eccezione, occorre leggere la sua firma: dopo la lista dei parametri formali compare la parola chiave throws, seguita dalla classe alla quale appartiene l'eccezione lanciata. public <tipo> <nome metodo> (parametri) throws <classe eccezione> Le classi di eccezioni sono sottoclassi della classe Throwable (sottoclasse diretta di Object) del package java.lang. Possiamo provare, per esempio, a modificare il metodo getElemento della classe Vettore affinch lanci uneccezione ArrayIndexOutOfBoundsException.
public class Vettore { static int[] a= {0,1,2,3,4,5,6,7,8,9}; public static int getElemento(int index) throws ArrayIndexOutOfBoundsException { return a[index]; } }

Se decidiamo di non gestire lerrore con try e catch nel main, il compilatore non segnaler alcun errore, in caso di inserimento di input errato, perch l'eccezione non controllata. Linformazione delleccezione lanciata sar invece utile al programmatore della classe usaVettore, che potr predisporre adeguatamente il codice del chiamante.

Lanciare eccezioni
Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

Consideriamo la seguente classe, che descrive un impiegato.


public class Impiegato { private String nome; private double stipendio; public Impiegato(String n, double s) { nome= n; setStipendio(s); } public void setStipendio(double s) { if(s>0) { stipendio= s; } else { System.out.println("ERRORE: stipendio negativo"); System.exit(1); } } }

Se l'utente crea una istanza di Impiegato con stipendio negativo o nullo, il programma termina a causa dellistruzione System.exit(1). E bene osservare che il metodo setStipendio non lancia alcuna eccezione poich linserimeno di un valore numerico negativo o nullo non rappresenta un errore di per s. Tuttavia, lerrore esiste per la logica del programma poich non ha senso attribuire ad un impiegato uno stipendio non positivo. La soluzione proposta artificiosa e sgradita allutente in quanto non previsto alcun meccanismo per il recupero dellerrore: sarebbe sufficiente richiedere linserimento dello stipendio corretto. Daltro canto, non possibile utilizzare try e catch perch il metodo non lancia eccezioni di nessun tipo. Lunica soluzione consiste, allora, nel forzare il metodo a lanciare un eccezione e lasciare la sua gestione al chiamante. La procedura da seguire riassunta in 3 punti: 1. cercare la classe di eccezione adatta allos copo, se esiste, o crearne una exnovo derivandola da una classe esistente; 2. inserire nella firma del metodo la clausola throws seguita dal nome della classe eccezione scelta; 3. inserire nel codice del metodo, ove l'eccezione si verifica, la parola chiave throw, seguita da un'istanza della classe di eccezione scelta. Tra le classi di Java, esiste la classe IllegalArgumentException, che fa il caso nostro. Un'eccezione di questo tipo indica che stato passato un argomento illegale ad un metodo. Il codice della classe Impiegato diventa:

Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

public class Impiegato { private String nome; private double stipendio; public Impiegato(String n, double s) { nome= n; setStipendio(s); } public void setStipendio(double s) throws IllegalArgumentException { if(s>0) { stipendio= s; } else { throw new IllegalArgumentException(); } } }

Il codice del programma che usa la classe Impiegato :


import java.util.*; class UsaImpiegato { public static void main(String[]args) { String nome; double stipendio; boolean b= true; Scanner console= new Scanner(System.in); System.out.println("Immettere il nome"); nome= console.nextLine(); while(b) { System.out.println("Immettere lo stipendio"); stipendio= console.nextDouble(); try { Impiegato imp= new Impiegato(nome,stipendio); b=false; } catch(IllegalArgumentException e) { System.out.println("ERRORE: stipendio negativo"); } } } }

Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11

Classi di eccezioni
Come detto in precedenza, le eccezioni sono istanze di classi derivate da Throwable. La gerarchia si divide in due rami: Error ed Exception. La gerarchia di Error descrive errori interni ed esaurimento delle risorse di sistema. Gli errori di questo tipo non sono recuperabili. Lunica cosa che si pu fare avvisare lutente e terminare il programma. La gerarchia di Exception si divide in altri due rami: eccezioni che derivano da RunTimeException ed eccezioni che non derivano da essa (per esempio IOException). Le eccezioni che derivano da RunTimeException sono dovute ad errori di programmazione. Tutte le altre sono causate da altri eventi, non dipendenti dal codice, per esempio un errore di I/O.

Throwable

Error

Exception

IOException

RunTimeEx ception

Le eccezioni che derivano da Error o da RunTimeException sono incontrollate, mentre tutte le altre sono controllate e devono essere intercettate obbligatoriamente. Il programmatore pu creare classi di eccezioni personalizzate derivandole da quelle esistenti.

Quest'opera stata rilasciata con licenza Creative Commons Attribution-ShareAlike 3.0 Unported. Per leggere una copia della licenza visita il sito web http://creativecommons.org/licenses/by-sa/3.0/ o spedisci una lettera a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

Autore: Cinzia Bocchi Ultimo aggiornamento: 10/10/11