Articoli recenti

La fotocamera digitale
28-Ott-2013
(892 visite)

Filtro di Kalman con Python
07-Giu-2012
(2660 visite)

Perché usi Python?
16-Mag-2012
(3602 visite)

Link

AeP Webmaster

 

MyNetSfera

 

banner francescocastaldo

Condividi coi tuoi amici

FacebookMySpaceTwitterGoogle BookmarksLinkedin

Eccezioni in Java – Mettere in pratica (2/2)

( 2 Votes ) 

Come si lanciano, intercettano e gestiscono le eccezioni?

Dopo una breve, e credo esaustiva, panoramica su cosa sono e come funzionano le eccezioni, vediamo come tutti questi discorsi si mettono in pratica in java.

Supponiamo di avere una classe "Divisione" che possiede un metodo che effettua la divisione tra due numeri interi. Come si può ben capire la divisione presenta un evento eccezionale che potrebbe verificarsi quando il divisore è pari a zero, quindi bisogna gestire la situazione. Innanzitutto, bisogna precisare che il metodo può lanciare un'eccezione, e lo si fa attraverso la parola chiave throws, dopo di ché, laddove previsto, deve essere lanciata l'eccezione, come mostra il seguente codice:

 

public class Divisione {
    static int div(int a, int b) throws ArithmeticException{
        if(b==0) throw new ArithmeticException("divisione per zero");
        return (int) a/b;
    }
}

 

Come si può notare, viene lanciata l'eccezione di tipo ArithmeticException una volta controllato che b è uguale a zero. Uno dei costruttori, comune a tutte le classi derivabili da Throwable, consente di passare come parametro una stringa rappresentante il messaggio di eccezione; quest'ultimo, come vedremo, potrà essere usato dal gestore per avvisare l'utente.
A questo punto usiamo il metodo div() appena creato ed inseriamo nel metodo del chiamante il gestore dell'eccezione. Per intercettare e gestire l'eccezione si usa il costrutto try-catch, come nel seguente codice:

 

public class TestEccezioni {
    public static void main(String[] args) {
        try {
            System.out.println(Divisione.div(10, 0));
        }catch (ArithmeticException ex) {
            System.out.println(ex.getMessage());
        }
    }
}

 

Come si può notare, il metodo che può creare eccezione, e che vogliamo intercettare, deve essere messo nel blocco try, mentre, il codice che dovrà gestire l'eccezione è rappresentato dal blocco catch. Bisogna notare che nelle parentesi tonde del gestore catch bisogna specificare, come parametro, quale sarà l'eccezione che dovrà gestire: in questo caso è ArithmeticException. Quando vine lanciato questo codice il metodo div() lancerà un'eccezione che verrà delegata al metodo chiamante (in questo caso il metodo main()) il quale presenta l'apposito gestore che, come notiamo, stampa a video il messaggio di eccezione che precedentemente abbiamo costruito nel metodo div(). In questo esempio il call stack è main->div.

Delegare un'eccezione

A volte può capitare che un metodo chiamante non può o non riesce a gestire l'eccezione di un metodo chiamato, per cui, quello che può fare è delegare l'eccezione al metodo che l'ha invocato. Ciò viene fatto dichiarando, attraverso la parola chiave throws, l'eccezione da delegare come se l'avesse causata egli stesso. L'esempio seguente mostra la delegazione dell'eccezione del metodo div() senza doversi preoccupare di gestirla:

 

public class Operazione {
    static int divisione(int a, int b)throws ArithmeticException{
        int c=Divisione.div(a, b);
        return c;
    }
}

 

Creare una propria classe di eccezione

Delle volte vi è la necessità di creare un'apposita classe di eccezione personalizzata, perché di quelle standard non vi è quella che rispecchia le proprie necessità. La creazione è semplice, basta estendere una delle classi standard che più si avvicina a ciò che si vuole fare. Per esempio, creiamo una classe che serve per lanciare l'eccezione quando un numero è minore di zero. Per fare ciò estendiamo la classe RuntimeException (è la superclasse di tutte quelle eccezioni che possono essere lanciate durante le normali operazioni della JVM):

 

public class NumNegException extends RuntimeException{
    public NumNegException(){
        super("Numero minore di zero");
    }
}

 

In questo caso è stato semplicemente personalizzato il messaggio dell'eccezione nel costruttore.

Lancio, intercettazione e gestione di eccezioni multiple

Delle volte può accadere che un metodo possa lanciare più eccezioni di diverso tipo, allora bisogna indicare l'elenco delle eccezioni separate da una virgola, come nel seguente esempio:

 

public class Divisione {
    static int div(int a, int b) throws ArithmeticException,NumNegException{
        if(a<0) throw new NumNegException();
        if(b==0) throw new ArithmeticException("b è uguale a zero");
        return (int) a/b;
    }
}

 

Bisogna notare che un metodo termina nel punto in cui viene lanciata un'eccezione, quindi l'ordine di lancio di un'eccezione determina il non verificarsi, a prescindere, delle altre.

Analogamente un metodo chiamante può gestire più eccezioni differenti, ciò lo si fa aggiungendo un altro gestore catch(), come mostra il seguente pezzo di codice:

 

public class TestEccezioni {
    public static void main(String[] args) {
        try {
            System.out.println(Divisione.div(-10, 0));
        }catch (ArithmeticException ex) {
            System.out.println(ex.getMessage());
        }catch (NumNegException ex) {
            System.out.println(ex.getMessage());
        }
    }
}

 

Bisogna tenere presente che i blocchi/gestori catch() vengono analizzati nell'ordine in cui si presentano nel codice. Ciò significa che se un blocco che gestisce un'eccezione di tipo RuntimeException precede blocchi che gestiscono un tipo di eccezione che deriva da RuntimeException, il compilatore java da errore; questo perché la gestione di quest'ultimi è già prevista dal gestore che li precede.

Però, è possibile fare un altro ragionamento e cioè: voglio gestire due eccezioni, in modo diverso, che derivano dalla classe RuntimeException, ma nello stesso tempo voglio gestire, comunque, tutte le altre eccezioni che derivano da RuntimeException; in questo caso il cestore di RuntimeException viene messo in coda, senza dare errori di compilazione, come mostra l'esempio:

 

public class TestEccezioni {
    public static void main(String[] args) {
        try {
            System.out.println(Divisione.div(-10, 0));
        }
        catch (ArithmeticException ex) {
            System.out.println(ex.getMessage());
        }catch (NumNegException ex) {
            System.out.println(ex.getMessage());
        }catch (RuntimeException ex) {
            System.out.println("Può essere una qualsiasi eccezione RuntimeException");
        }
    }
}

 

Il blocco finally

Quando il codice lancia un'eccezione, questa interrompe la restante parte del codice rimanente ed esce dal metodo. Questa situazione è un problema, soprattutto quando il nostro metodo ha acquisito precedentemente una risorsa condivisa, per esempio, un dispositivo il cui accesso è concorrenziale (cioè può essere usato da uno alla volta). In questo caso, nonostante il metodo non può più usufruirne, il dispositivo non viene rilasciato per poi poter essere usato da altre parti software.

Per risolvere questa tipologia di problemi si utilizza il blocco finally{}, che può essere usato solo in presenza del blocco try{}, il quale viene eseguito indipendentemente dal fatto se si verifica un'eccezione o meno. Vi sono tre casi:

  1. non si verifica un'eccezione, quindi viene eseguito prima tutto il codice del blocco try e poi quello del blocco finally;
  2. si verifica un'eccezione che viene gestita da un blocco catch, quindi viene eseguito prima tutto il codice del blocco catch e poi quello del blocco finally;
  3. si verifica un'eccezione ma non c'è un blocco catch appropriato per la gestione, quindi viene eseguito tutto il codice del blocco finally.

Di seguito un esempio di utilizzo del blocco finally:

 

public class TestEccezioni {
    public static void main(String[] args) {
        try {
            System.out.println(Divisione.div(10, 0));
        }
        catch (ArithmeticException ex) {
            System.out.println(ex.getMessage());
        }catch (NumNegException ex) {
            System.out.println(ex.getMessage());
        }catch (RuntimeException ex) {
            System.out.println("Può essere una qualsiasi eccezione RuntimeException");
        }
        finally{
            System.out.println("Qualsiasi sia stata la sorte, è stato un piacere!");
        }
    }
}