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"); } } }