Eccezione generata all'interno del blocco catch: verrà catturata di nuovo?


180

Potrebbe sembrare una domanda di programmazione 101 e pensavo di conoscere la risposta, ma ora mi trovo a dover ricontrollare. In questa parte di codice riportata di seguito, l'eccezione generata nel primo blocco catch verrà catturata dal blocco catch Eccezione generale di seguito?

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

Ho sempre pensato che la risposta sarebbe stata no, ma ora ho alcuni comportamenti strani che potrebbero essere causati da questo. La risposta è probabilmente la stessa per la maggior parte delle lingue, ma sto lavorando in Java.


2
Forse potresti descrivere il "comportamento strano"?
Jeffrey L Whitledge,

Sei sicuro che ApplicationException non venga lanciata altrove e non si propaghi fino a questo blocco?
sblundy,

L'ho notato nel mio IDE Eclipse. È tirante costringermi a mettere il "lancio nuova eccezione" in un blocco di prova, ma non so perché. L'ho fatto in passato senza farlo. Non vedo perché sarebbe richiesto un blocco try. Molti esempi su Google mostrano alle persone che non necessitano di un blocco try. È perché sto gettando dentro un'istruzione if?
Djangofan,

Risposte:


214

No, poiché il nuovo thrownon è trydirettamente nel blocco.


Diciamo se il codice è così provare {} catch (Exception e) {System.err.println ("Eccezione in catch:" + e.getClass ()); } catch (IOException e) {System.err.println ("In catch IOException:" + e.getClass ()); } e il codice nel blocco try genera Eccezione IO, andrà al blocco Eccezione generale immediato o passerà sopra al blocco catch IOException?
sofs1,

4
@ user3705478 Il codice non verrà compilato, per evitare quel tipo di situazione errata. In generale, non è consentito avere una cattura per una sottoclasse dopo una cattura della sua superclasse nello stesso blocco try.
Chris Jester-Young,

Grazie. Quindi avrò un errore di compilazione, giusto? Lo proverò quando torno a casa.
sofs1,

Dipende da @ user3705478, se un blocco RuntimeExceptionviene lanciato dal catchblocco non si verificherà alcun errore di compilazione.
Randy the Dev,

@AndrewDunn Non penso che sia la domanda di user3705478, ma piuttosto, cosa succede se una catchclausola di eccezione padre viene elencata prima di una catchclausola di eccezione figlio . La mia comprensione è che Java non lo consente e viene rilevato al momento della compilazione.
Chris Jester-Young,

71

No. È molto facile da controllare.

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

Dovrebbe stampare:

In cattura IOException: classe java.io.IOException
Alla fine
Eccezione nel thread "main" java.lang.RuntimeException
        at Catch.main (Catch.java:8)

Tecnicamente quello avrebbe potuto essere un bug del compilatore, dipendente dall'implementazione, un comportamento non specificato o qualcosa del genere. Tuttavia, il JLS è abbastanza ben definito e i compilatori sono abbastanza buoni per questo tipo di cose semplici (il caso d'angolo generici potrebbe essere una questione diversa).

Inoltre, se si scambiano i due blocchi di cattura, non verranno compilati. La seconda cattura sarebbe completamente irraggiungibile.

Si noti che il blocco finally viene sempre eseguito anche se viene eseguito un blocco catch (diverso dai casi stupidi, come loop infiniti, collegamento tramite l'interfaccia degli strumenti e uccisione del thread, riscrittura tramite codice, ecc.).


3
Il modo più ovvio per evitare un finallyè, ovviamente, chiamare System.exit. :-P
Chris Jester-Young,

3
@Chris Jester-Young for(;;);è più breve, contenuto nella lingua, non introduce molto in termini di effetti collaterali e, per me, più ovvio.
Tom Hawtin - tackline

1
System.exitè più amichevole con la CPU! Eeeek !!! Sì, va bene, chiaramente questo è un criterio soggettivo. Inoltre, non sapevo che tu fossi un golfista di codice. ;-)
Chris Jester-Young,

28

La specifica del linguaggio Java dice nella sezione 14.19.1:

Se l'esecuzione del blocco try viene completata bruscamente a causa di un lancio di un valore V, è possibile scegliere:

  • Se il tipo di runtime di V è assegnabile al parametro di qualsiasi clausola catch dell'istruzione try, viene selezionata la prima (estrema sinistra) tale clausola catch. Il valore V viene assegnato al parametro della clausola catch selezionata e viene eseguito il blocco di quella clausola catch. Se quel blocco si completa normalmente, allora l'istruzione try si completa normalmente; se quel blocco si completa bruscamente per qualsiasi motivo, l'istruzione try si completa bruscamente per lo stesso motivo.

Riferimento: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

In altre parole, la prima cattura racchiusa in grado di gestire l'eccezione fa, e se un'eccezione viene eliminata da quella cattura, non è nell'ambito di qualsiasi altra cattura per il tentativo originale, quindi non proveranno a gestirla.

Una cosa correlata e confusa da sapere è che in una struttura try- [catch] -finally, un blocco finally può generare un'eccezione e, in tal caso, qualsiasi eccezione generata dal blocco try o catch viene persa. Ciò può confondere la prima volta che lo vedi.


Volevo solo aggiungere che da Java 7 è possibile evitare l'uso di try-with-resources. Quindi, se tryAND finallylancia entrambi, finallyviene soppresso, ma viene anche AGGIUNTO all'eccezione try. Se catchlancia anche tu, non hai fortuna, a meno che tu non lo gestisca tramite "addSuppressed" e aggiungendo l' tryeccezione, quindi hai tutti e tre.
LAFK dice Reinstate Monica il

6

Se vuoi lanciare un'eccezione dal blocco catch devi informare il tuo metodo / classe / ecc. che deve gettare detta eccezione. Così:

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

E ora il tuo compilatore non ti urlerà contro :)


4

No - Come ha detto Chris Jester-Young, verrà lanciato al prossimo tentativo di cattura nella gerarchia.


2

Come detto sopra ...
Aggiungerei che se hai problemi a vedere cosa sta succedendo, se non riesci a riprodurre il problema nel debugger, puoi aggiungere una traccia prima di ri-lanciare la nuova eccezione (con il buon vecchio sistema peggio .out.println, con un buon sistema di log come log4j altrimenti).


2

Non verrà catturato dal secondo blocco di cattura. Ogni eccezione viene rilevata solo all'interno di un blocco try. Puoi nidificare i tentativi (non che sia generalmente una buona idea):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}

Ho scritto un codice simile. Ma non sono convinto. Voglio chiamare un Thread.sleep () nel mio blocco catch. Ma Thread.sleep si lancia un'eccezione Interrupted. È giusto (una buona pratica) farlo come hai mostrato nel tuo esempio?
riroo,

1

No, poiché tutti i catch si riferiscono allo stesso blocco try, quindi il lancio dall'interno di un blocco catch verrebbe catturato da un blocco try incluso (probabilmente nel metodo che ha chiamato questo)


-4

Il vecchio post ma la variabile "e" deve essere unica:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

5
Questo dovrebbe essere un commento e non una risposta. Non fornisce nulla nel modo di affrontare la vera domanda - è solo una critica alla domanda dei PO.
Derek W,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.