Blocchi try/catch annidati in Java

Concetti Chiave
  • I blocchi try possono essere annidati per gestire eccezioni in contesti complessi.
  • Un blocco try interno può intercettare eccezioni che non sono state gestite da un blocco try esterno.
  • Le eccezioni vengono propagate dal blocco try interno al blocco try esterno se non vengono gestite.
  • Le istruzioni try annidate possono essere utilizzate per gestire eccezioni in metodi chiamati, consentendo una gestione più flessibile delle eccezioni.

Istruzioni try annidate

L'istruzione try può essere annidata.

Vale a dire, un'istruzione try può essere all'interno del blocco di un'altra try. Ogni volta che viene eseguita un'istruzione try, il contesto di quell'eccezione viene inserito nello stack.

Se un'istruzione try interna non ha un gestore catch per una particolare eccezione, lo stack viene smontato e i gestori catch dell'istruzione try successiva vengono ispezionati alla ricerca di una corrispondenza.

Questo continua finché uno dei blocchi catch ha successo, oppure finché tutti i blocchi try annidati sono stati esauriti. Se nessun blocco catch ha successo, allora il sistema di runtime di Java gestirà l'eccezione. Ecco un esempio che utilizza istruzioni try annidate:

// Un esempio di istruzioni try annidate.
class ProvaAnnidata {
    public static void main(String[] args) {
        try {
            int a = args.length;

            /* Se non sono presenti argomenti da linea di comando,
               l'istruzione seguente genererà
               un'eccezione di divisione per zero. */
            int b = 42 / a;

            System.out.println("a = " + a);

            try { // blocco try annidato
                /* Se viene usato un argomento da linea di comando,
                   verrà generata una divisione per zero dal codice seguente. */
                if(a==1) a = a/(a-a); // divisione per zero

                /* Se vengono usati due argomenti da linea di comando,
                   viene generata un'eccezione di indice fuori dai limiti. */
                if(a==2) {
                    int[] c = { 1 };
                    c[42] = 99; // genera un'eccezione di indice fuori dai limiti
                }
            } catch(ArrayIndexOutOfBoundsException e) {
                System.out.println("Indice fuori dai limiti: " + e);
            }

        } catch(ArithmeticException e) {
            System.out.println("Divisione per 0: " + e);
        }
    }
}

Come possiamo vedere, questo programma annida un blocco try all'interno di un altro.

Il programma funziona come segue. Quando il programma viene eseguito senza argomenti da linea di comando, si verifica una divisione per zero che viene intercettata dal blocco try esterno.

L'esecuzione del programma con un argomento da linea di comando genera una divisione per zero all'interno del blocco try annidato. Poiché il blocco interno non intercetta questa eccezione, essa viene passata al blocco try esterno, dove viene gestita. Se invece il programma viene eseguito con due argomenti da linea di comando, viene generata un'eccezione di limite dell'array all'interno del blocco try interno. Ecco alcuni esempi di esecuzione che illustrano ciascun caso:

$ java ProvaAnnidata
Divisione per 0: java.lang.ArithmeticException: / by zero

$ java ProvaAnnidata Uno
a = 1
Divisione per 0: java.lang.ArithmeticException: / by zero

$ java ProvaAnnidata Uno Due
a = 2
Indice fuori dai limiti:
    java.lang.ArrayIndexOutOfBoundsException:
    Indice 42 fuori dai limiti per la lunghezza 1

L'annidamento delle istruzioni try può verificarsi in modi meno ovvi quando sono coinvolte chiamate a metodi. Ad esempio, è possibile racchiudere una chiamata a un metodo all'interno di un blocco try.

All'interno del metodo si trova un'altra istruzione try. In questo caso, la try all'interno del metodo è ancora annidata all'interno del blocco try esterno, che chiama il metodo.

Ecco il programma precedente riscritto in modo che il blocco try annidato venga spostato all'interno del metodo provaAnnidata():

/* Le istruzioni try possono essere annidate implicitamente tramite
   chiamate a metodi. */
class MetodoProvaAnnidata {
    static void provaAnnidata(int a) {
        try { // blocco try annidato
            /* Se viene usato un argomento da linea di comando,
               verrà generata una divisione per zero dal codice seguente. */
            if(a==1) a = a/(a-a); // divisione per zero

            /* Se vengono usati due argomenti da linea di comando,
               viene generata un'eccezione di indice fuori dai limiti. */
            if(a==2) {
                int[] c = { 1 };
                c[42] = 99; // genera un'eccezione di indice fuori dai limiti
            }
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Indice fuori dai limiti: " + e);
        }
    }

    public static void main(String[] args) {
        try {
            int a = args.length;

            /* Se non sono presenti argomenti da linea di comando,
               l'istruzione seguente genererà
               una divisione per zero. */
            int b = 42 / a;
            System.out.println("a = " + a);

            provaAnnidata(a);
        } catch(ArithmeticException e) {
            System.out.println("Divisione per 0: " + e);
        }
    }
}

L'output di questo programma è identico a quello dell'esempio precedente.