Argomenti
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.
Gli errori devono essere gestiti dal programmagore per evitare che possano raggiungere l’utente, es. attraverso la terminazione del programma.
Alcune frequenti cause di errori:
Ce ne sono altre?
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.
In linguaggi come il C, una condizione anomala è gestita da “codici d’errore” restituiti dalla funzione:
es.: -1 = file_non_trovato, -2: privilegi_insufficienti, etc.);
La procedura chiamante deve controllare il valore ritornato per capire l’esito dell’esecuzione.
Tale approccio è poco efficace:
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à:
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.
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).
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.
ArithmeticException exc = new ArithmeticException();
throw exc;
IndexOutOfBoundsException
ArrayStoreException
NegativeArraySizeException
NullPointerException
SecurityException
Unchecked Exceptions
Non è richiesto che questi tipi di eccezioni siano catturate o dichiarate in un metodo. Esempi sono:
Checked Exceptions
Possono essere catturate da un metodo o dichiarate nella sua firma.
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
La gestione delle eccezioni si basa sul meccanismo “try – catch” o attraverso, la clausola “throws” nella dichiarazione di un metodo.
Try-catch
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.
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:
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
}
}
}
Quindi…
try {
... normal program code
}
catch(Exception e) {
... exception handling code
}
try {}
catch {}
Nel blocco “try”, viene eseguita la sequenza di istruzioni che potenzialmente lancia un’eccezione.
try {
...
Istruzione;
...
}
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
}
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?
Il flusso di controllo di un blocco try/catch non è predicibile:
try
;Il blocco finally
permette al programmatore di scrivere delle istruzioni che verranno eseguite incondizionatamente, a prescindere dal verificarsi di 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{...}
Ogni blocco dovrebbe avere almeno un blocco catch o finally.
Se una eccezione è sollevata durante un blocco try:
Nel caso di nessuna eccezione, e nessuno statement System.exit()
:
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
}
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);
}
}
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.
2. La modellazione a oggetti e il linguaggio UML (richiami)
3. Generalità su Java e la programmazione ad oggetti
6. Regole di traduzione da UML a Java/C++
7. Programmazione multi-thread
8. Sincronizzazione tra thread
9. Programmazione client-server con socket TCP/IP (Java networkin...
10. Programmazione di applicazioni client-server: il Pattern Proxy...
12. Design Patterns
13. Pattern architetturali - Esempi
14. Design pattern creazionali. Esempi
15. Design pattern strutturali. Esempi
16. Introduzione alle tecnologie middleware
17. Modelli di middleware: RPC, MOM, TP, TS
Bruce Eckel, “Thinking in Java” capitolo 9
J. Cohoon e J. Davidson, “Java – Guida alla Programmazione” capitolo 10