Eccezioni Previsione dell`imprevisto Rispondere all

2
Previsione dell’imprevisto
Immaginiamo il seguente codice
Eccezioni
int numberInStock = Integer.parseInt(br.readLine());
Cosa succede se:
– L’oggetto BufferedReader rappresenta un file su floppy disk
che l’utente ha erroneamente estratto ?
– L’oggetto BufferedReader rappresenta una connessione alla
rete che è fallita a causa di un guasto ?
– L’operatore immette una sequenza di cifre troppo lunga ?
– L’operatore non immette una sequenza di cifre ?
3
4
Rispondere all’imprevisto
Exception
Un modo alternativo di terminazione dei metodi basato
sull’uso dell’istruzione throw
Consideriamo il caso in cui l’argomento del
metodo parseInt è “pippo”
Il metodo parseInt non può correggere l’errore
Non può far sapere all’utente che c’è stato un
errore
throw riferimento
– dove riferimento è un oggetto di una sottoclasse della classe
Exception, solitamente:
throw new Exception-Class(Argomento String)
– Il metodo restituisce un int …
– dove Exception-Class è una sottoclasse di Exception
Questo non è compito di parseInt
Quando il metodo esegue l’istruzione throw, si dice che
lancia (throw) un’eccezione.
5
Lanciare un’eccezione
Consideriamo la catena di chiamate
main à method1 à method2
– Supponiamo che method2 incontra un imprevisto e lancia
l’eccezione throw riferimento ad oggetto Exception
– Il metodo in esecuzione termina immediatamente, l’Exception
passa attraverso ogni invocazione della catena, forzando
ogni metodo a terminare. Viene visualizzato:
Some Exception
at TryThrow.method2(TryThrow.java:18)
at TryThrow.method1(TryThrow.java:15)
at TryThrow.main(TryThrow.java:12)
6
Esempio
Il metodo parseInt contiene un’istruzione simile a
throw new NumberFormatException(Argomento String)
Questa istruzione crea un nuovo ogetto
NumberFormatException e lancia il riferimento a
questo oggetto.
NumberFormatException è una sottoclasse di
Exception
7
La clausola throws
8
La classe Exception
Il riferimento che appare nell’istruzione throw deve
riferirsi a un oggetto della classe Exception
Il verbo throw non è del tutto sconosciuto …
– Spesso Java richiede che si includa la clausola throws
nell’intestazione della dichiarazione di un metodo:
– Ogni oggetto istanza di una sottoclasse di Exception è anche
un oggetto Exception
public static void main(String[] a) throws Exception
Perché esistono più classi Exception ?
– Qualsiasi metodo che potrebbe lanciare un’Exception deve
indicarlo nella sua dichiarazione con la clausola throws
– La clausola throws è composta dalla parola chiave e dalla
lista delle sottoclassi di Exception che possono essere
lanciate dal metodo
– Questo requisito si applica anche ai metodi che possno
lanciare l’Exception indirettamente invocando altri metodi
– distinzioni tra i vari tipi di situazioni impreviste, es:
throws IOException
throws FileNotFoundException, RemoteException:
– IOException è una sottoclasse di Exception
– FileNotFoundException e RemoteException sono sottoclassi
di IOException
9
Etichettatura delle eccezioni
10
Trattare l’imprevisto
Come evitare la terminazione di un programma in caso
di eccezione ?
Il costruttore di una Exception prende come
argomento una String che viene stampata
quando si visualizza la catena delle invocazioni
– Java fornisce ai metodi un modo per catturare (catch)
qualsiasi Exception lanciata sul loro percorso dai metodi che
invocano
– Catturando l’Exception si interrompe la catena che porta alla
terminazione del programma
– viene recuperato il controllo e si può trattare la situazione in
modo meno drastico
throw new FileNotFoundException(“log file is” +
“always necessary”);
Per catturare una Exception, le istruzioni contenenti
invocazioni di metodi che lanciano Exception devono
essere tra parentesi e precedute dalla parola chiave try
11
Try
try { // parte try
someObject.someMethod();
} catch (Exception e) { // parte catch
le istruzioni sono eseguite solo se
l’eccezione è lanciata all’interno di try
}
12
Esempio
static Movie readMovie(BufferedReader br) throws IOException {
String name;
int playingTime;
Movie newMovie;
name = br.readLine();
if(name == null)
return null;
playingTime = Integer.parseInt(br.readline());
newMovie = new Movie(name, playingTime);
return newMovie;
}
Come gestire un errore dei dati in ingresso inviando un messaggio all’utente ?
13
Esempio
rivisto
static Movie readMovie(BufferedReader br) throws IOException {
String name;
int playingTime;
Movie newMovie;
name = br.readLine();
if(name == null)
return null;
try {
playingTime = Integer.parseInt(br.readLine());
} catch (NumberFormatException e) { // Skip this movie; do next one
System.err.print("Bad playing time data for "+name);
throw e;
}
newMovie = new Movie(name, playingTime);
return newMovie;
}
static Movie readMovie(BufferedReader br) throws IOException {
String name;
int playingTime;
boolean gotGoodData; // True if name and playingTime have valid data
Movie newMovie;
name = br.readLine();
gotGoodData = false;
while (!(name==null || gotGoodData)) {
gotGoodData = true;
// Optimistic!
try { playingTime = Integer.parseInt(br.readLine());
} catch (NumberFormatException e) { // Skip this movie; do next one.
System.err.print("Bad playing time data for "+name);
System.err.println(" -- movie skipped");
gotGoodData = false;
name = br.readLine();
}
}
if (name==null) return null;
else return new Movie(name,playingTime);
}
14
static Movie readMovie(BufferedReader br) throws IOException {
String name;
int playingTime;
Movie newMovie;
name = br.readLine();
if(name == null) return null;
try { playingTime = Integer.parseInt(br.readLine());
} catch (NumberFormatException e) { // Skip this movie; do next one
System.err.print("Bad playing time data for "+name);
System.err.println(" -- movie skipped");
name = br.readLine();
if(name == null) return null;
playingTime = Integer.parseInt(br.readLine());
newMovie = new Movie(name, playingTime);
return newMovie;
}
// questo codice è eseguito se parseInt non lancia una NumberFormatException
newMovie = new Movie(name, playingTime);
return newMovie;
} // dove è il problema ?
Rimediare
ad un errore
15
Versione corretta
16
Responsabilità per l’imprevisto
Ogni oggetto si assume la responsabilità del suo
comportamento (comprese le circostanze impreviste)
– Ad esempio consideriamo gli oggettI:
• NetHeadlineScanner modella un servizio di titoli in rete. Il suo compito
è visualizzare i titoli che contengono alcune parole chiave. I titoli sono
ottenuti dall’oggetto NetHeadlines
• NetHeadlines ottiene in continuazione i titoli delle notizie da una
qualsiasi delle varie sorgenti alternative ed è implementato usando il
NetReader
• L’oggetto NetReader legge in continuazione le ultime informazioni da
una sorgente della rete. A sua volta, il NetReader può essere
implementato usando il BufferedReader
• L’oggetto BufferedReader è costruito dall’InputStream consegnato
dall’oggetto Socket connesso. Socket è la classe predefinita di java
che modella le connessioni di basso livello alla rete
17
18
Responsabilità per l’imprevisto (2)
Responsabilità per l’imprevisto (3)
L’oggetto NetHeadlineScanner invoca il metodo
readLine della classe NetHeadlines che a sua
volta invoca il metodo readLine della classe
NetReader, che a sua volta invoca il metodo
readLine della classe BufferedReader
In questo caso l’imprevisto è la non disponibilità
improvvisa della sorgente corrente di notizie
– Quali classi dovrebbero assumersi la responsabilità
dell’inconveniente ?
– Quale comportamento dovrebbero seguire ?
Si può risalire al punto in cui è stata generata
l’Exception ed esaminare gli oggetti sulla catena di
invocazioni cercando di individuare quale oggetto
sia in grado di agire rispetto all’eccezione
– BufferedReader.readLine lancia l’Exception
• Non sarà lui a catturarla
• Non è riuscito a trattare la non disponibilità della sorgente di
rete perché il BufferedReader è una sorgente di dati
idealizzata e non sa se i dati che sta fornendo provengano
dalla rete, da un file o dalla tastiera
• Per questo lancia un’Exception
19
Responsabilità per l’imprevisto (4)
20
Le eccezioni non sono sempre errori
Scriviamo un programma che riceve un URL come argomento
dalla linea di comando e stampa “good” se la URL corrisponde
all’indirizzo di una pagina web, “bad” altrimenti
Come si comportano gli altri oggetti ?
– L’oggetto NetReader sa che sta leggendo dalla locazione di rete
della sua sorgente
– Si possono usare i response codes restituiti dai web server
– I codici maggiori o uguali a 300 indicano un errore nella URL
• Ottenere i dati è il suo comportamento
• Se i dati non sono disponibili, lancia un’eccezione
• Ad esempio 404 significa che la pagina non esiste
• 403 significa che la risorsa non è accessibile
– L’ogetto NetHeadlines conosce delle sorgenti di rete alternative
• Potrebbe rispondere catturando l’Exception, selezionando un indirizzo di
rete diverso creando un oggetto NetReader basato sulla nuova sorgente
di rete e continuando
– La classe HttpURLConnection nella libreria java.net modella le
connessioni HTTP a risorse web specificate da URL
– Possiamo creare tale oggetto mandando un messaggio
openConnection ad un oggetto URL
– Avuto un oggetto HttpURLConnection, gli possiamo mandare un
messaggio getResponseCode
– NetHeadlineScanner non deve fare niente …
• Potrebbe trattare l’Exception meglio di NetHeadlines, ma questo non
rientra nelle sue responsabilità (che riguardano solo la selezione dei titoli)
21
CheckURL: prima versione
import java.net.*;
import java.io.*;
class CheckURL {
public static void main(String[] a) throws Exception {
URL u = new URL(a[0]);
HttpURLConnection uc = (HttpURLConnection) u.openConnection();
int responseCode = uc.getResponseCode();
System.out.println(responseCode);
if (responseCode >= 300)
System.out.println("bad");
else
System.out.println("good");
}
Nel caso in cui non è possibile stabilire una connessione perché il server web non
esiste il programma dovrebbe rispondere bad, ma …
22
CheckURL:
versione corretta
import java.net.*;
import java.io.*;
class CheckURL {
public static void main(String[] a) throws Exception {
try {
URL u = new URL(a[0]);
HttpURLConnection uC = (HttpURLConnection) u.openConnection();
int responseCode = uc.getResponseCode();
System.out.println(responseCode);
if (responseCode >= 300)
System.out.println("bad");
else
System.out.println("good");
} catch (Exception e) {
System.out.println("bad");
}
}
}