Excepción arrojada en catch y finalmente cláusula

En una pregunta para Java en la universidad, había un fragmento de código:

class MyExc1 extends Exception {} class MyExc2 extends Exception {} class MyExc3 extends MyExc2 {} public class C1 { public static void main(String[] args) throws Exception { try { System.out.print(1); q(); } catch (Exception i) { throw new MyExc2(); } finally { System.out.print(2); throw new MyExc1(); } } static void q() throws Exception { try { throw new MyExc1(); } catch (Exception y) { } finally { System.out.print(3); throw new Exception(); } } } 

Me pidieron que diera su resultado. Respondí 13Exception in thread main MyExc2 , pero la respuesta correcta es 132Exception in thread main MyExc1 . ¿Por qué es eso? Simplemente no puedo entender a dónde va MyExc2.

Basándome en leer su respuesta y ver cómo se le ocurrió, creo que cree que una “excepción en curso” tiene “precedencia”. Tenga en cuenta:

Cuando se lanza una nueva excepción en un bloque catch o finalmente un bloque que se propagará desde ese bloque, entonces la excepción actual se cancelará (y se olvidará) a medida que la nueva excepción se propague hacia afuera. La nueva excepción comienza a desenrollar la stack como cualquier otra excepción, abortando fuera del bloque actual (el bloque catch o finally) y sujeto a cualquier captura aplicable o finalmente bloquea en el camino.

Tenga en cuenta que los bloques de captura o finalmente aplicables incluyen:

Cuando se lanza una nueva excepción en un bloque catch, la nueva excepción sigue estando sujeta al bloque finally de esa captura, si corresponde.

Ahora vuelva a la ejecución recordando que, cada vez que pulsa throw , debe abortar el rastreo de la excepción actual y comenzar a rastrear la nueva excepción.

Esto es lo que Wikipedia dice sobre la cláusula finally:

Más común es una cláusula relacionada (finalmente, o asegurar) que se ejecuta independientemente de si se produjo una excepción, normalmente para liberar recursos adquiridos dentro del cuerpo del bloque de manejo de excepciones.

Vamos a diseccionar tu progtwig.

 try { System.out.print(1); q(); } 

Entonces, 1 será enviado a la pantalla, luego se llama a q() . En q() , se lanza una excepción. La excepción es atrapada por Exception y sin embargo, no hace nada. Una cláusula finally se ejecuta (tiene que), entonces, 3 se imprimirá en la pantalla. Porque (en el método q() hay una excepción lanzada en la cláusula finally , también el método q() pasa la excepción a la stack padre (por la throws Exception en la statement del método new Exception() lanzará y catch ( Exception i ) new Exception() mediante catch ( Exception i ) , se MyExc2 excepción MyExc2 (por ahora agréguela a la stack de excepciones), pero finalmente se ejecutará finalmente en el bloque main .

Entonces en,

 catch ( Exception i ) { throw( new MyExc2() ); } finally { System.out.print(2); throw( new MyExc1() ); } 

Se llama una cláusula finally … (recuerde, acabamos de capturar Exception i y arrojado MyExc2 ) en esencia, 2 está impreso en la pantalla … y después 2 se imprime el 2 en la pantalla, se MyExc1 una excepción MyExc1 . MyExc1 es manejado por el método public static void main(...) .

Salida:

“132Exception en el hilo principal MyExc1”

¡El profesor es correcto! 🙂

En esencia , si tiene finalmente una cláusula try / catch, finalmente se ejecutará ( después de capturar la excepción antes de arrojar la excepción capturada)

Citando del JLS 9: 14.20.2. Ejecución de try-finally y try-catch-finally

Si el bloque catch se completa bruscamente por el motivo R, entonces se ejecuta el bloque finally. Entonces hay una opción:

  • Si el bloque finally se completa normalmente, la instrucción try se completa abruptamente por el motivo R.

  • Si el bloque final se completa abruptamente por la razón S, entonces la instrucción try se completa abruptamente por la razón S (y la razón R se descarta).

Finalmente la cláusula se ejecuta incluso cuando se lanza una excepción desde cualquier lugar del bloque try / catch.

Debido a que es el último en ser ejecutado en main y arroja una excepción, esa es la excepción que ven los que llaman.

De ahí la importancia de asegurarse de que la cláusula finally no arroje nada, porque puede tragarse excepciones del bloque try .

Un method no puede throw dos excepciones al mismo tiempo. Siempre arrojará la última exception lanzada, que en este caso siempre será la del bloque finally .

Cuando se lanza la primera excepción del método q() , será atrapada y luego tragada por la excepción de lanzamiento final del bloque.

q () -> lanzada new Exception -> catch Exception main catch Exception -> throw new Exception -> finally arrojar una nueva exception (y la del catch es “perdida”)

La forma más sencilla de pensar en esto es imaginar que existe una variable global para toda la aplicación que contiene la excepción actual.

 Exception currentException = null; 

A medida que se lanza cada excepción, “currentException” se establece en esa excepción. Cuando la aplicación finaliza, si currentException es! = Null, el tiempo de ejecución informa del error.

Además, los bloques finally siempre se ejecutan antes de que el método finalice. A continuación, puede devolver el fragmento de código a:

 public class C1 { public static void main(String [] argv) throws Exception { try { System.out.print(1); q(); } catch ( Exception i ) { // <-- currentException = Exception, as thrown by q()'s finally block throw( new MyExc2() ); // <-- currentException = MyExc2 } finally { // <-- currentException = MyExc2, thrown from main()'s catch block System.out.print(2); throw( new MyExc1() ); // <-- currentException = MyExc1 } } // <-- At application exit, currentException = MyExc1, from main()'s finally block. Java now dumps that to the console. static void q() throws Exception { try { throw( new MyExc1() ); // <-- currentException = MyExc1 } catch( Exception y ) { // <-- currentException = null, because the exception is caught and not rethrown } finally { System.out.print(3); throw( new Exception() ); // <-- currentException = Exception } } } 

El orden en que se ejecuta la aplicación es:

 main() { try q() { try catch finally } catch finally } 

Es bien sabido que el bloque finally se ejecuta después del try y catch y siempre se ejecuta … Pero como viste que es un poco complicado, a veces echa un vistazo a ese fragmento de código a continuación y verás que las declaraciones return y throw don No siempre hagas lo que deben hacer en el orden en que esperamos que sea el tema.

Aclamaciones.

 /////////////Return dont always return/////// try{ return "In Try"; } finally{ return "In Finally"; } //////////////////////////////////////////// //////////////////////////////////////////// while(true) { try { return "In try"; } finally{ break; } } return "Out of try"; /////////////////////////////////////////// /////////////////////////////////////////////////// while (true) { try { return "In try"; } finally { continue; } } ////////////////////////////////////////////////// /////////////////Throw dont always throw///////// try { throw new RuntimeException(); } finally { return "Ouuuups no throw!"; } ////////////////////////////////////////////////// 

Creo que solo tienes que caminar por los bloques del final:

  1. Imprimir “1”.
  2. finally en q imprime “3”.
  3. finally en la impresión main “2”.
 class MyExc1 extends Exception {} class MyExc2 extends Exception {} class MyExc3 extends MyExc2 {} public class C1 { public static void main(String[] args) throws Exception { try { System.out.print("TryA L1\n"); q(); System.out.print("TryB L1\n"); } catch (Exception i) { System.out.print("Catch L1\n"); } finally { System.out.print("Finally L1\n"); throw new MyExc1(); } } static void q() throws Exception { try { System.out.print("TryA L2\n"); q2(); System.out.print("TryB L2\n"); } catch (Exception y) { System.out.print("Catch L2\n"); throw new MyExc2(); } finally { System.out.print("Finally L2\n"); throw new Exception(); } } static void q2() throws Exception { throw new MyExc1(); } } 

Orden:

 TryA L1 TryA L2 Catch L2 Finally L2 Catch L1 Finally L1 Exception in thread "main" MyExc1 at C1.main(C1.java:30) 

https://www.compilejava.net/

Creo que esto resuelve el problema:

 boolean allOk = false; try{ q(); allOk = true; } finally { try { is.close(); } catch (Exception e) { if(allOk) { throw new SomeException(e); } } }