2013-08-07 18 views
166

Ho un piccolo problema di teorico con le strutture try-catch.Eccezione Java non rilevata?

ho preso un esame pratico di ieri su Java e non capisco seguente esempio:

try { 
    try { 
     System.out.print("A"); 
     throw new Exception("1"); 
    } catch (Exception e) { 
     System.out.print("B"); 
     throw new Exception("2"); 
    } finally { 
     System.out.print("C"); 
     throw new Exception("3"); 
    } 
} catch (Exception e) { 
    System.out.print(e.getMessage()); 
} 

La domanda era: "che cosa l'output sarà simile?"

Ero abbastanza sicuro che sarebbe AB2C3, MA sorpresa a sorpresa, non è vero.

La risposta corretta è ABC3 (testato e davvero è così).

La mia domanda è: dove è finita l'eccezione ("2")?

+8

+1 Ahh uomo, conoscevo questa risposta. Mi è stato chiesto questo in un'intervista. È un'ottima domanda per capire come try/catch/finalmente funziona nello stack. –

+10

C'è solo una dichiarazione di stampa che può stampare un numero (l'ultimo: 'print (e.getMessage())'). Pensavate che l'output sarebbe stato 'AB2C3': pensavate che il blocco 'catch' più esterno sarebbe stato eseguito due volte? –

+0

In java, prima che venga eseguita un'istruzione che trasferisce il controllo dal blocco catch, il blocco finally viene eseguito a condizione che esista. Se solo il codice nel blocco finally non trasferisce il controllo all'esterno, viene eseguita l'istruzione ritardata dal blocco catch. – Thomas

risposta

192

Dalla Java Language Specification 14.20.2.:

Se il blocco di catch si completa bruscamente per la ragione R, viene eseguito il blocco finally. Poi v'è una scelta:

  • Se il blocco finally completa normalmente, allora l'istruzione try completa bruscamente per motivi R.

  • Se il blocco finally completa bruscamente per la ragione S, allora l'istruzione try completa bruscamente per la ragione S (e la ragione R è scartata).

Così, quando c'è un blocco catch che genera un'eccezione:

try { 
    // ... 
} catch (Exception e) { 
    throw new Exception("2"); 
} 

ma c'è anche un blocco finally che genera anche un'eccezione:

} finally { 
    throw new Exception("3"); 
} 

Exception("2") verrà scartato e verrà propagato solo lo Exception("3").

+71

Ciò vale anche per le dichiarazioni 'return'. Se il tuo blocco finally ha un ritorno, sovrascriverà qualsiasi ritorno in un blocco 'try' o' catch'. A causa di queste "caratteristiche", una buona pratica è che il blocco dovrebbe ** mai ** lanciare un'eccezione o avere una dichiarazione di ritorno. – Augusto

+0

Questo è anche il vantaggio ereditario provato con le risorse in Java 7. Conserva l'eccezione iniziale se viene generata un'eccezione secondaria durante la chiusura delle risorse, in genere rendendo più facile il debug. – w25r

19

Le eccezioni attivate nel blocco finally sopprimono l'eccezione generata in precedenza nel blocco try o catch.

Java 7 esempio: http://ideone.com/0YdeZo

Da Javadoc's esempio:


static String readFirstLineFromFileWithFinallyBlock(String path) 
                throws IOException { 
    BufferedReader br = new BufferedReader(new FileReader(path)); 
    try { 
     return br.readLine(); 
    } finally { 
     if (br != null) br.close(); 
    } 
} 

Tuttavia, in questo esempio, se i metodi readLine e chiudere entrambe le gettare eccezioni, allora il metodo readFirstLineFromFileWithFinallyBlock lancia l'eccezione generata dal blocco finally; l'eccezione generata dal blocco try viene soppressa.


La nuova try-with la sintassi di Java 7 aggiunge un altro passo della soppressione eccezione: le eccezioni sollevate nel blocco try sopprimere quelli gettati in precedenza in prova, con parte.

da stesso esempio:

try (
     java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); 
     java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) 
    ) { 
     for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) { 
      String newLine = System.getProperty("line.separator"); 
      String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; 
      writer.write(zipEntryName, 0, zipEntryName.length()); 
     } 
    } 

Un'eccezione può essere lanciato dal blocco di codice associato con l'affermazione try-con-risorse. Nell'esempio precedente, un'eccezione può essere generata da dal blocco try e fino a due eccezioni possono essere generate dall'istruzione try-with-resources quando tenta di chiudere gli oggetti ZipFile e BufferedWriter. Se viene generata un'eccezione dal blocco try e vengono generate una o più eccezioni dall'istruzione try-with-resources, quindi vengono eliminate quelle eccezioni generate dall'istruzione try-with-resources e l'eccezione generata da il blocco è quello che viene lanciato dal metodo writeToFileZipFileContents . È possibile recuperare queste eccezioni soppresse chiamando il metodo Throwable.getSuppressed dall'eccezione generata dal blocco try.


Nel codice domanda, ogni blocco è chiaramente scartando il vecchio eccezione, nemmeno la registrazione di esso, non va bene quando si sta cercando di risolvere alcuni bug:

http://en.wikipedia.org/wiki/Error_hiding

9

Poiché throw new Exception("2"); viene generato dal blocco catch e non try, non verrà catturato nuovamente.
Vedere 14.20.2. Execution of try-finally and try-catch-finally.

Questo è ciò che accade:

try { 
    try { 
     System.out.print("A");   //Prints A 
     throw new Exception("1"); 
    } catch (Exception e) { 
     System.out.print("B");   //Caught from inner try, prints B 
     throw new Exception("2"); 
    } finally { 
     System.out.print("C");   //Prints C (finally is always executed) 
     throw new Exception("3"); 
    } 
} catch (Exception e) { 
    System.out.print(e.getMessage()); //Prints 3 since see (very detailed) link 
} 
+0

sì è vero, vedo che questo sta accadendo, ma stavo cercando una spiegazione - perché si sta comportando in questo modo – Kousalik

2

Il blocco finally corre sempre. O si è return dall'interno del blocco try o viene generata un'eccezione. L'eccezione generata nel blocco finally sostituirà quella generata nel ramo di cattura.

Inoltre, il lancio di un'eccezione non causerà alcun output. La riga throw new Exception("2"); non scriverà nulla.

+1

sì, so lanciare l'output Exception da solo, ma non ho visto il motivo, perché l'eccezione 2 dovrebbe essere eliminata . Sono ancora un po 'più intelligente :-) – Kousalik

+0

è sempre molto tempo e in un tempo molto lungo può succedere qualsiasi cosa (check puzzle http://wouter.coekaerts.be/2012/puzzle-dreams) – Dainius

4

La tua domanda è molto ovvia, e la risposta è semplice nella stessa misura .. L'oggetto Exception con messaggio "2" viene sovrascritto dall'oggetto Exception con messaggio "3".

Spiegazione: Quando verificarsi un'eccezione, il suo oggetto è gettato per la cattura di blocco da gestire. Ma quando si verifica un'eccezione nel blocco catch, il suo oggetto viene trasferito su OUTER CATCH Block (se presente) per l'eccezione. E lo stesso è successo qui. L'oggetto eccezione con il messaggio "2" viene trasferito sul blocco catch ESTERNO. Ma aspetta ..Prima di lasciare il blocco try-catch interno, DEVE ESEGUIRE FINALMENTE. Qui si è verificato il cambiamento che ci preoccupa. Un nuovo oggetto EXCEPTION (con messaggio "3") viene eliminato o questo blocco finally che ha sostituito l'oggetto Exception già lanciato (con il messaggio "2"). Come risultato, quando viene stampato il messaggio dell'oggetto Exception, abbiamo ottenuto valore sovrascritto cioè "3" e non "2".

Ricorda: solo un oggetto eccezione può essere gestito dal blocco CATCH.

0

Secondo il codice:

try { 
    try { 
     System.out.print("A"); 
     throw new Exception("1"); // 1 
    } catch (Exception e) { 
     System.out.print("B");  // 2 
     throw new Exception("2"); 
    } finally {      // 3 
     System.out.print("C");  // 4 
     throw new Exception("3"); 
    } 
} catch (Exception e) {    // 5 
    System.out.print(e.getMessage()); 
} 

Come si può vedere qui:

  1. stampa A e getta un'eccezione # 1;
  2. questa eccezione ha catturato da dichiarazione catch e stampa B - # 2;
  3. il blocco infine # 3 viene eseguito dopo la dichiarazione try-catch (o solo prova, se non si è verificata alcuna eccezione) e viene stampato C - # 4 e generata nuova eccezione;
  4. questo ha catturato da dichiarazione di cattura esterna # 5;

Il risultato è ABC3. E 2 è omesso allo stesso modo di 1

+0

Spiacente, Eccezione (" 1 ") non viene omesso, ma viene catturato correttamente –

+0

@Black Maggie Viene memorizzato nella cache e generato nuova eccezione => questo non viene memorizzato nella cache e il programma viene terminato. E prima che questo blocco sia finalmente eseguito. –

Problemi correlati