Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
I corsi di Ingegneria
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Stefano Russo » 4.Gestione delle eccezioni


Sommario

Argomenti

  • Definizioni
  • Classificazione delle eccezioni
  • Gestione delle eccezioni

Gestione eccezioni

  • Definizione di Eccezioni
  • Classificazione delle Eccezioni
  • Eccezioni definite dall’utente
  • Sollevare un’eccezione
  • Intercettare un’eccezione

Errori

Errore: risultato “inaspettato” ottenuto al runtime dipendente dalle condizioni in cui un’applicazione viene eseguita.

Errori non gestiti possono manifestersi come risultati non corretti, o come comportamento non previsto di un programma.

  • Entrambi possono portare alla terminazione non prevista del programma.

Gli errori devono essere gestiti dal programmagore per evitare che possano raggiungere l’utente, es. attraverso la terminazione del programma.

Errori

Alcune frequenti cause di errori:

  • Memory errors (i.e. memoria non allocata correttamente, memory leaks, “null pointer”).
  • File system errors (i.e. disk full, file non presente).
  • Network errors (i.e. network disconnessa, URL inesistente).
  • Calculation errors (i.e. divisione per zero 0).
  • Array errors (i.e. accesso ad un elemento in posizione –1).
  • Conversion errors (i.e. convertire ‘q’ a un numero).

Ce ne sono altre?

Gestione degli errori

Idealmente il momento migliore per rilevare degli errori in un codice è a tempo di compilazione, ma questo non è possibile in tutti i casi.
es.

FILE* temp = fopen("pippo.txt", "w");
temp >> mystring;

ma pippo.txt non esiste e il programma viene terminato!!

In questo caso si richiede che il blocco di codice che ha generato l’errore restituisca delle informazioni su quanto è accaduto a chi lo ha invocato, in modo che si possa agire per gestire opportunamente l’errore senza che il programma debba necessariamente interrompere la sua esecuzione.

Gestione degli errori

In linguaggi come il C, una condizione anomala è gestita da “codici d’errore” restituiti dalla funzione:

  • un valore di tipo booleano (flag), oppure
  • uno tra n valori opportunamente codificati

es.: -1 = file_non_trovato, -2: privilegi_insufficienti, etc.);

La procedura chiamante deve controllare il valore ritornato per capire l’esito dell’esecuzione.


Gestione degli errori

Tale approccio è poco efficace:

  1. pochi programmatori controllano il valore di ritorno di una funzione per verificare casi di errori;
  2. un semplice flag booleano contiene poche informazioni al fine di comprendere e poter gestire opportunamente l’errore;
  3. se vi sono molti codici d’errore, la procedura chiamante si “appesantisce”.

Eccezioni

La soluzione è di tipizzare le condizioni anomale, e forzare il programmatore ad invocare la soluzione dell’errore generato. Ciò è realizzato per mezzo di eccezioni.

Un’eccezione esprime una condizione anormale che si genera in una sequenza di codice durante l’esecuzione.

Tale situazione blocca la regolare esecuzione e richiede un’opportuna gestione.

Sollevare un’eccezione

Il processo di notifica e trattamento di una eccezione è detta “throwing” dell’eccezione.

Java consente di sollevare un’eccezione quando si verificano delle condizioni mediante l’istruzione throw:

throw new IOExeception ("File not found")

Un’eccezione può essere gestita in due modalità:

  • diretta: l’eccezione viene rilevata e “processata” nello stesso metodo;
  • indiretta: l’esecuzione del metodo in cui è stata rilevata l’eccezione ha termine e l’eccezione viene “passata” al metodo chiamante.

Sollevare un’eccezione

Le eccezioni “sollevabili” da un metodo vanno opportunamente dichiarate nel prototipo del metodo mediante la clausola throws:

public void metodo(..) throws IOExeception

public void dividi(int x, int y) throws ArithmeticExeception {
if (y!=0)
this.r = x/y;
else
throw new ArithmeticException("Divided by zero");
}

L’istruzione throws ArithmeticException indica che in Java non si può mentire sulla specifica delle eccezioni che un metodo può sollevare. Il compilatore imporrà di gestire nel metodo l’eccezione sollevata o di specificare che il metodo solleva un’eccezione che il chiamante dovrà gestire.

Classificazione delle eccezioni


Classificazione delle eccezioni


Classificazione delle eccezioni


Classificazione delle eccezioni


Dettagli sul funzionamento

La classe Exception e la classe Error estendono la classe Throwable per implementare il meccanismo con cui la Java Virtual Machine reagisce quando si imbatte in una eccezione-errore.

In caso di un’eccezione, la JVM istanzia un oggetto dalla classe eccezione relativa al problema (es. IOException), e “lancia” (throws) eccezione appena istanziata (tramite la parola chiave throw).

Dettagli sul funzionamento

Se il codice non “cattura” l’eccezione(tramite il blocco “catch”), il gestore automatico della JVM interromperà il programma generando in output informazioni dettagliate sull’eccezione generata.

  • Es. divisione per zero. La JVM istanzierà un oggetto di tipo ArithmeticException (inizializzandolo opportunamente) es eseguirà il throw.
  • E’ come se la JVM eseguisse le seguenti righe di codice:

ArithmeticException exc = new ArithmeticException();
throw exc;

Es. System-Defined Exception

IndexOutOfBoundsException

  • Relativa a array, string, e vector

ArrayStoreException

  • Quando si assegna un oggetto di un tipo non corretto ad un elemento di un array

NegativeArraySizeException

  • Quando si usa una dimensione negativa di un array

NullPointerException

  • Quando si fa riferimento ad un oggetto non istanziato

SecurityException

  • Quando la security viene violata, sollevata dal security manager

Tipologie di Java Exceptions

Unchecked Exceptions

Non è richiesto che questi tipi di eccezioni siano catturate o dichiarate in un metodo. Esempi sono:

  • Runtime exceptions the possono essere generate dai metodi o dalla JVM;
  • Errors che sono generati dall’interno della JVM e che spesso indicano un vero e proprio stato fatale dell’esecuzione;
  • le Runtime exceptions sono la sorgente delle maggiori controversie!

Checked Exceptions

Possono essere catturate da un metodo o dichiarate nella sua firma.

  • Aumentano la robustezza del codice a situazioni impreviste

Unchecked Exception

Come sono usate?
Rappresentano condizioni di errore che sono considerate “fatali” per l’esecuzione del programma.

Non si può fare nulla per queste eccezioni. Il programma terminerà con un messaggio di errore.

Esempi

  • Checked exceptions includono gli errori del tipo “array index out of bounds”, “file not found” e”number format conversion”.
  • Unchecked exceptions includono errori del tipo “null pointer exception”.

Gestione delle Eccezioni

La gestione delle eccezioni si basa sul meccanismo “try – catch” o attraverso, la clausola “throws” nella dichiarazione di un metodo.
Try-catch

  • Wherever your code may trigger an exception, the normal code logic is placed inside a block of code starting with the “try” keyword.
  • After the try block, the code to handle the exception should it arise is placed in a block of code starting with the “catch” keyword.

Esempio eccezioni

public class Ecc1 {

public static void main(String args[]) {
int a = 10;
int b = 0;
int c = a/b;
System.out.println(c);
}

}

Questa classe può essere compilata senza problemi, ma genererà un’eccezione durante la sua esecuzione, dovuto alla divisione per zero.

Esempio eccezioni

La JVM dopo aver interrotto il programma produrrà il seguente output:

Exception in thread "main"
java.lang.ArithmeticException: / by zero
Ecc1.main(Ecc1.java:6)

Evidenziando:

  • il tipo di eccezione (java.lang.ArithmeticException);
  • un messaggio descrittivo (/ by zero);
  • il metodo che ha sollevato l’eccezione (at Ecc1.main);
  • il file in cui è stata sollevata l’eccezione (Ecc1.java);
  • la riga in cui è stata sollevata l’eccezione (:6).

Esempio eccezioni

Ma… il programma è terminato prematuramente!
Utilizzando le parole chiave try e catch sarà possibile gestire l’eccezione in maniera personalizzata:

public class Ecc2 {

public static void main(String args[]) {
int a = 10;
int b = 0;
try {

int c = a/b;
System.out.println(c);
} catch (ArithmeticException exc) {

System.out.println("Divisione per zero...");
exc.printStackTrace();
//stesso output di come se l'eccezione non fosse
// catturata, ma senza interrompere il programma
}

}

}

Coding delle eccezioni

Quindi…
try {
... normal program code
}
catch(Exception e) {
... exception handling code
}

Intercettare le eccezioni

try {}

catch {}

Nel blocco “try”, viene eseguita la sequenza di istruzioni che potenzialmente lancia un’eccezione.

try {

...
Istruzione;
...

}

Intercettare le eccezioni

try {}
catch {}

Nel blocco “catch”, si inserisce il codice per il trattamento dell’eccezione (exception handler)

try {
...
Istruzione;
...
} catch(TipoEccezione1 e1){
//trattamento eccezione 1
} catch(TipoEccezione2 e2){
//trattamento eccezione 2
}

Intercettare le eccezioni

Se un’eccezione non è intercettata dai blocchi “catch”, essa sarà inoltrata al metodo chiamante.

try {
...
Istruzione;
...
} catch(TipoEccezione1 e1){
//trattamento eccezione 1
} catch(TipoEccezione2 e2){
//trattamento eccezione 2
}

Un blocco try può essere seguito da una successione di blocchi catch: in quale ordine collocarli?

Intercettare le eccezioni

Il flusso di controllo di un blocco try/catch non è predicibile:

  • dipende dalle eccezioni che vengono sollevate nel blocco try;

Il blocco finally permette al programmatore di scrivere delle istruzioni che verranno eseguite incondizionatamente, a prescindere dal verificarsi di eccezioni.

Intercettare le eccezioni

Nel caso venga sollevata un’eccezione, il blocco “finally” verrà eseguito dopo quello di “try-catch“.

Il blocco “finally” verrà sempre eseguito anche nel caso non ci siano state eccezioni

L’uso dei blocchi finally è quello di realizzare operazioni di clean-up.

Quindi la sequenza dei blocchi sarà:

Try{...}
Catch{...}
Finally{...}

Passi di try…catch…finally

Ogni blocco dovrebbe avere almeno un blocco catch o finally.
Se una eccezione è sollevata durante un blocco try:

  • il resto del codice nel try non è eseguito;
  • se c’è un blocco catch mappato sul tipo di eccezione sollevata o un suo derivato nello stesso stack frame, allora si salta alla sua prima istruzione;
  • se c’è un blocco finally, si esegue;
  • se non c’è alcun blocco, la JVM cerca un blocco più esterno sullo stack frame, cioé, fa gorgogliare l’eccezione!

Nel caso di nessuna eccezione, e nessuno statement System.exit() :

  • Se c’è un blocco finally, viene eseguito.

Come gestire le eccezioni?

In un blocco di codice che “throws” una checked exception si puà decidere di servire l’eccezione (handling) o di passare l’eccezione alla classe base (in gergo “up the chain” alla classe padre).

Per servire l’eccezione si scrive il blocco try-catch. Per passare l’eccezione alla classe base, si dichiara la clausola throws nella dichiarazione del metodo o della classe in questione.

Se il metodo contiente del condice che può causare una checket exception, bisogna gestire l’eccezione o passare la gestione alla classe base.

Es.
public void myMethod throws IOException {
... codice con operazioni di I/O
}

Eccezioni definite dall’utente

Il programmatore non è vincolato ad usare le eccezioni predefinite da Java, ma può crearne di proprie.

Per creare un tipo di eccezione d’utente, il programmatore deve derivarla da un tipo esistente, preferibilmente quella dal significato più vicino alla nuova eccezione.

import java.lang.Exception;

class MyException extends Exception {
public MyException() {}
public MyException(String msg) {
super(msg);
}
}

Intercettare le eccezioni

Consideriamo il diagramma delle classi a lato in cui un programmatore definisce una proprio eccezione a partire dalla classe Exception presente in java.lang.

Inoltre definisce il metodo:

void do_smtg() throws MyException;

Consideriamo il seguente frammento di codice:
try{
...
do_smtg();
...
}catch(Exception ex) {...}
catch(MyException my_ex) {...}

Java con l’istruzione }catch(Exception ex) tenta sempre di eseguire il primo blocco catch in grado di gestire l’eccezione appena sollevata. Il catch di Exception è utilizzabile per risolvere eccezioni di tipo MyException, quindi viene sempre eseguito, mentre l’altro mai.

Pertanto l’ordine dei blocchi catch deve essere l’inverso dell’ordine di derivazione: il catch di eccezioni derivate deve prece-dere quelli di eccezioni base.


I materiali di supporto della lezione

Bruce Eckel, “Thinking in Java” capitolo 9

J. Cohoon e J. Davidson, “Java – Guida alla Programmazione” capitolo 10

  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion