Gestione delle Eccezioni
C. Horstmann
Fondamenti di programmazione e Java 2
3^ edizione Apogeo
trad. Nicola Fanizzi
corso di Programmazione, CdS: Informatica TPS
Dip. di Informatica, Università degli studi di Bari
Obiettivi
 Imparare a lanciare eccezioni
 Essere capaci di progettare le proprie classi di
eccezioni
 Capire la differenza tra eccezioni controllate e
incontrollate
 Imparare a catturare le eccezioni
 Sapere quando e dove catturare un'eccezione
2
Gestione Errori
3
 Approccio tradizionale: il metodo restituisce un
codice d'errore
 Problema: dimenticare di controllare il codice-errore

La notifica di errore potrebbe passare inosservata
 Problema: il metodo chiamante potrebbe non essere
in grado di far niente in caso di errore


Anche il programma deve riportare un errore e delegare il
proprio chiamante ad occuparsene
Molte chiamate di metodo necessiterebbero di un controllo
Gestione Errori
4
 Invece di programmare pensando ai casi di
successo:
x.doSomething()
si dovrebbe sempre programmare stando attenti ai
possibili errori:
if (!x.doSomething()) return false;
Lanciare Eccezioni
5
 Eccezioni:


Non si possono ignorare
Inviate direttamente ad un gestore (exception handler)–e
non semplicemente al chiamante del metodo che ha
causato il problema
 Lancia un'eccezione (è un oggetto) per segnalare
una condizione eccezionale
 Esempio: IllegalArgumentException:
Valore illegale per il parametro
IllegalArgumentException exception
= new IllegalArgumentException("Amount exceeds balance");
lancia exception;
Lanciare Eccezioni
6
 Non occorre immagazzinare l'oggetto eccezione in
una variabile:
throw new IllegalArgumentException("Amount exceeds balance");
 Quando si lancia un'eccezione, il metodo termina
immediatamente

L'esecuzione continua con un gestore delle eccezioni
Esempio
public class BankAccount
{
public void withdraw(double amount)
{
if (amount > balance)
{
IllegalArgumentException exception
= new IllegalArgumentException("Amount
exceeds balance");
throw exception;
}
balance = balance - amount;
}
. . .
}
7
Gerarchia delle 8
Classi di Eccezioni
Sintassi 15.1: 9
Lanciare un'Eccezione
throw exceptionObject;
 Esempio:
throw new IllegalArgumentException();
 Scopo: lanciare un'eccezione e trasferire il controllo
al gestore per questo tipo di eccezione
Eccezioni 10
Controllate e Non Controllate
 Due tipi di eccezioni:

Controllate (checked)



Il compilatore controlla che non vengano ignorate
Dovute a cause esterne che il programmatore non saprebbe
prevenire
La maggioranza di essere accade quando si tratta l'I/O
 Ad esempio: IOException
Eccezioni 11
Controllate e Non Controllate
 Due tipi di eccezioni:

Non controllate:



Estendono le classi RuntimeException o Error
Sono da imputare al programmatore
Esempi di eccezioni di runtime:
NumberFormatException
IllegalArgumentException
NullPointerException

Esempio di errore: OutOfMemoryError
Eccezioni 12
Controllate e Non Controllate
 Le categorie non sono precise:



Scanner.nextInt lancia l'eccezione non
controllata InputMismatchException
Il programmatore non può prevenire l'immissione di input
non corretto da parte degli utenti
Questa scelta rende la classe facile da usare per
programmatori principianti
 Si ha a che fare con eccezioni controllate quando si
programma con file e stream
Eccezioni 13
Controllate e Non Controllate
 Ad esempio,
si usi uno Scanner per leggere un file
String filename = . . .;
FileReader reader = new FileReader(filename);
Scanner in = new Scanner(reader);
Ma il costruttore di FileReader può lanciare una
FileNotFoundException
Eccezioni 14
Controllate e Non Controllate
 Due scelte:


Gestire l'eccezione
Dire al compilatore che si vuole che il metodo termini
quando si abbia l'eccezione

Si usa lo specificatore throws affinché il metodo possa
lanciare un'eccezione controllata
public void read(String filename) throws FileNotFoundException
{
FileReader reader = new FileReader(filename);
Scanner in = new Scanner(reader);
. . .
}
Eccezioni 15
Controllate e Non Controllate

Per eccezioni multiple:
public void read(String filename)
throws IOException, ClassNotFoundException

Tenere presente la gerarchia di ereditarietà:
 Meglio dichiarare l'eccezione piuttosto che gestirla in
maniera sprovveduta
Sintassi 15.2: 16
Specifica delle Eccezioni
accessSpecifier returnType
methodName(parameterType parameterName, . . .)
throws ExceptionClass, ExceptionClass, . . .
 Esempio:
public void read(BufferedReader in) throws IOException
 Scopo: indicare le eccezioni controllate che questo
metodo possa lanciare
Catturare Eccezioni
17
 Installare un gestore di eccezioni con l'istruzione
try/catch:


Il blocco try contiene istruzioni che possono causare
un'eccezione
La clausola catch introduce il gestore per un tipo di
eccezione
Catturare Eccezioni
 Esempio:
try
{
String filename = . . .;
FileReader reader = new FileReader(filename);
Scanner in = new Scanner(reader);
String input = in.next();
int value = Integer.parseInt(input);
. . .
}
catch (IOException exception)
{
exception.printStackTrace();
}
catch (NumberFormatException exception)
{
System.out.println("Input was not a number");
}
18
Catturare Eccezioni
19
 Le istruzioni in un blocco try vengono eseguite
 Se non si ha nessuna eccezione,
le clausole catch sono saltate
 Se si ha un'eccezione di uno dei tipi gestiti,
l'esecuzione salta al corrispondente blocco catch
 Se si ha un'eccezione di altro tipo,
essa viene rilanciata fino a quando non venga
catturata da un altro blocco try
Catturare Eccezioni
 catch
block



20
(IOException exception)
exception contiene il riferimento all'oggetto
eccezione che era stato lanciato
La clausola catch può analizzare l'oggetto per scoprire
altri dettagli
exception.printStackTrace(): stampa
della catena di chiamate di metodi che hanno portato ad
exception
Sintassi 15.3: 21
Blocco try Generico
try
{
statement
statement
. . .
}
catch (ExceptionClass exceptionObject)
{
statement
statement
. . .
}
catch (ExceptionClass exceptionObject)
{
statement
statement
. . .
}
. . .
Sintassi 15.3: 22
Blocco try Generico

Example:
try
{
System.out.println("How old are you?");
int age = in.nextInt();
System.out.println("Next year, you'll be " + (age + 1));
}
catch (InputMismatchException exception)
{
exception.printStackTrace();
}

Scopo: eseguire una o più istruzioni che possono generare
eccezioni. Se si ha un'eccezione tra quelle delle clausole
catch si esegue la prima di esse. Se non si hanno eccezioni,
o l'eccezione occorsa non corrisponde con quelle delle
catch, allora si saltano tutte le clausole catch
La clausola finally
 Un'eccezione causa la terminazione del metodo
23
corrente
 Pericolo: può far saltare del codice che è essenziale
Esempio:
reader = new FileReader(filename);
Scanner in = new Scanner(reader);
readData(in);
reader.close();
// si potrebbe non arrivare mai qui
La clausola finally
 Occorre eseguire reader.close()
24
anche se si ha l'eccezione
 Si usa la clausola finally per il codice che deve
essere eseguito "a prescindere"
FileReader reader = new FileReader(filename);
try
{
Scanner in = new Scanner(reader);
readData(in);
}
finally
{
reader.close(); // se si ha un'eccezione, la clausola
// finally viene sempre eseguita
// prima del passaggio al gestore
}
La clausola finally
25
 Eseguita quando si esce dal blocco try in uno dei
tre modi:



Dopo l'ultima istruzione del blocco try
Dopo l'ultima istruzione della clausola catch se il blocco
try ha catturato un'eccezione
Quando un'eccezione è stata lanciata dal blocco try e
non è stata catturata
 Raccomandazione: tenere distinte le clausole
catch e finally dello stesso blocco try
Sintassi 15.4: 26
La clausola finally
try
{
statement
statement
. . .
}
finally
{
statement
statement
. . .
}
Sintassi 15.4: 27
La clausola finally
 Esempio:
FileReader reader = new FileReader(filename);
try
{
readData(reader);
}
finally
{
reader.close();
}
 Scopo: assicurare che le istruzioni nella clausola
finally siano eseguite a prescindere dal fatto
che nel blocco try si lancino eccezioni
Progettare i Propri 28
Tipi d'Eccezione
 Si possono progettare propri tipi di eccezioni–
sottoclassi di Exception o
RuntimeException
if (amount > balance)
{
throw new InsufficientFundsException(
"withdrawal of " + amount + " exceeds balance of
“ + balance);
}
 La si può rendere eccezione non controllata –
il programmatore avrebbe potuto evitarla chiamando
prima getBalance
Progettare i Propri 29
Tipi d'Eccezione
 Si può estende RuntimeException o una
delle sue sottoclassi
 Fornire due costruttori


Costruttore di default
Costruttore che accetta una stringa di messaggio
che descriva la ragione dell'eccezione
Progettare i Propri 30
Tipi d'Eccezione
public class InsufficientFundsException
extends RuntimeException
{
public InsufficientFundsException() {}
}
public InsufficientFundsException(String message)
{
super(message);
}
Programma Completo
31
 Programma





Chiedere all'utente un nome di file
Ci si aspetta che il file contenga dei valori
La prima riga del file contiene il numero totale di valori
Le rimanenti contengono i dati
Tipico file di input:
3
1.45
-2.1
0.05
Programma Completo
32
 Cosa può andar male ?


Il file potrebbe non esistere
Il file potrebbe contenere dati in formato sbagliato
 Chi può accorgersi degli errori ?


Il costruttore FileReader lancerà una eccezione
quando il file non esiste
I metodi che elaborano l'input hanno bisogno di lanciare
una eccezione se trovano un errore nel formato dei dati
Programma Completo
33
 Quali eccezioni si possono lanciare?



FileNotFoundException può essere lanciata
dal costruttore di FileReader
IOException può essere lanciata dal metodo close di
FileReader
BadDataException,
una classe ad hoc d'eccezione controllata
Programma Completo
34
 Chi può rimediare agli errori riportati dalle eccezioni?

Solo il metodo main del programma
DataSetTester interagisce con l'utente



Cattura eccezioni
Stampa messaggi d'errore appropriati
Dà all'utente un'altra possibilità di inserimento di un nome di file
corretto
DataSetTester.java
01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
import
import
import
java.io.FileNotFoundException;
java.io.IOException;
java.util.Scanner;
public class DataSetTester
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
DataSetReader reader = new DataSetReader();
boolean done = false;
while (!done)
{
try
{
35
DataSetTester.java
17:
");
18:
19:
20:
21:
22:
23:
24:
25:
36
System.out.println("Please enter the file name:
String filename = in.next();
double[] data = reader.readFile(filename);
double sum = 0;
for (double d : data) sum = sum + d;
System.out.println("The sum is " + sum);
done = true;
} // blocco try
DataSetTester.java
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
37
catch (FileNotFoundException exception)
{
System.out.println("File not found.");
}
catch (BadDataException exception)
{
System.out.println
("Bad data: " + exception.getMessage());
}
catch (IOException exception)
{
exception.printStackTrace();
}
} // while
} // main
} // classe
Il metodo readFile della 38
classe DataSetTester
 Costruisce l'oggetto Scanner
 Chiama il metodo readData
 Si disinteressa completamente di qualunque
eccezione
Il metodo readFile della 39
classe DataSetTester
 Se c'è un problema con il file di input, passa
semplicemente l'eccezione al chiamante
public double[] readFile(String filename)
throws IOException, BadDataException
// FileNotFoundException è una IOException
{
FileReader reader = new FileReader(filename);
try
{
Scanner in = new Scanner(reader);
readData(in);
}
Il metodo readFile della 40
classe DataSetTester
}
finally
{
reader.close();
}
return data;
Il metodo readData della 41
classe DataSetTester
 Legge il numero di valori
 Costruisce un array
 Chiama readValue per ogni valore
 Controlla due potenziali errori:


Il file potrebbe non cominciare con un intero
Il file potrebbe avere ancora dati dopo la lettura di quelli
indicati nella prima riga
 Non fa nessun tentativo di cattura di eccezioni
Il metodo readData della 42
classe DataSetTester
private void readData(Scanner in) throws BadDataException
{
if (!in.hasNextInt())
throw new BadDataException("Length expected");
int numberOfValues = in.nextInt();
data = new double[numberOfValues];
for (int i = 0; i < numberOfValues; i++)
readValue(in, i);
}
if (in.hasNext())
throw new BadDataException("End of file expected");
Il metodo readValue della 43
classe DataSetTester
private void readValue(Scanner in, int i)
throws BadDataException
{
if (!in.hasNextDouble())
throw new BadDataException("Data value expected");
data[i] = in.nextDouble();
}
Scenario
44
1.DataSetTester.main chiama
DataSetReader.readFile
2.readValue chiama readData
3.readData chiama readValue
4.readValue non trova il valore che si attendeva
e lancia BadDataException
5.readValue non ha un gestore per l'eccezione e
termina
Scenario
45
6. readData non ha un gestore per le eccezioni e
termina
7. readFile non ha un gestore per le eccezioni e
termina dopo aver eseguito la clausola finally
8. DataSetTester.main ha un gestore per
BadDataException; il gestore stampa un
messaggio e si dà all'utente un'altra possibilità di
immissione del nome del file
DataSetReader.java
01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;
/**
legge un insieme di dati da file.
// il file ha il formato
numberOfValues
valore1
valore2
. . .
*/
public class DataSetReader
{
46
DataSetReader.java
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
47
/**
legge un insieme di dati.
@param filename il nome del file contenente i dati
@return i dati del file
*/
public double[] readFile(String filename)
throws IOException, BadDataException
{
FileReader reader = new FileReader(filename);
try
{
Scanner in = new Scanner(reader);
readData(in);
}
finally
{
reader.close();
}
return data;
}
DataSetReader.java
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
48
/**
legge tutti i dati.
@param in lo scanner che scandisce i dati
*/
private void readData(Scanner in) throws BadDataException
{
if (!in.hasNextInt())
throw new BadDataException("Length expected");
int numberOfValues = in.nextInt();
data = new double[numberOfValues];
for (int i = 0; i < numberOfValues; i++)
readValue(in, i);
}
if (in.hasNext())
throw new BadDataException("End of file expected");
DataSetReader.java
53:
54:
55:
56:
57:
58:
49
/**
legge il valore di un dato .
@param in lo scanner che scandisce i dati
@param i la posizione del valore da leggere
*/
private void readValue(Scanner in, int i)
throws BadDataException
{
if (!in.hasNextDouble())
throw new BadDataException("Data value expected");
data[i] = in.nextDouble();
}
59:
60:
61:
62:
63:
64:
65:
private double[] data;
66: } // classe