Excepción atrapada y finalmente cláusula

155

En una pregunta para Java en la universidad, había este 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 salida. 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 MyExc2va.

Jubstuff
fuente

Respuestas:

167

Basándome en leer su respuesta y ver cómo se le ocurrió, creo que cree que una "excepción en progreso" tiene "prioridad". Tenga en cuenta:

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

Tenga en cuenta que capturas aplicables o finalmente bloques incluyen:

Cuando se lanza una nueva excepción en un bloque catch, la nueva excepción todavía está sujeta al bloqueo de esa captura, si corresponde.

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

Bert F
fuente
77
«Basado en la lectura de su respuesta y viendo cómo se le ocurrió, creo que cree que una" excepción en progreso "tiene" prioridad "» Gracias ... eso fue exactamente lo que pensé :)
Jubstuff
39

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

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

Analicemos su programa.

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

Por lo tanto, se 1mostrará en la pantalla, luego q()se llama. En q(), se lanza una excepción. La excepción queda atrapada Exception ypero no hace nada. A finalmente se ejecuta entonces cláusula (tiene que), por lo que, 3se va a imprimir a la pantalla. Debido a que (en el método q()hay una excepción lanzada en la cláusula finalmente , también el q()método pasa la excepción a la pila principal (mediante la throws Exceptiondeclaración en el método) new Exception()será lanzada y atrapada catch ( Exception i ), se MyExc2lanzará una excepción (por ahora agréguela a la pila de excepciones) ), pero finalmentemain se ejecutará un finalmente en el bloque.

Entonces en

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

Una cláusula finalmente se llama ... (recuerde, acabamos de atrapar Exception iy lanzar MyExc2) en esencia, 2se imprime en la pantalla ... y después de que 2se imprime en la pantalla, MyExc1se lanza una excepción. MyExc1se maneja por el public static void main(...)método

Salida:

"132Excepción en el hilo principal MyExc1"

Profesor es correcto! :-)

En esencia , si finalmente tiene una cláusula try / catch, se ejecutará un finalmente ( después de atrapar la excepción antes de descartar la excepción atrapada)

Buhake Sindi
fuente
El catchse ejecuta desde que q()lanzó un Exceptiondesde su propio finallybloque.
Péter Török
"En q (), se lanza una excepción, pero antes de que se lance por completo, primero se ejecuta una cláusula, por lo que se imprimirá 3 en la pantalla." Er ... no, la primera excepción lanzada qpasa la ejecución a catchbloque vacío en q(que se traga esta excepción), luego al finallybloque en q. Dicho bloque finalmente imprime 3, luego lanza una nueva excepción, que gracias a q's throws Exceptionse pasa por la pila al padre.
Powerlord
38

Las excepciones en el bloque finalmente reemplazan las excepciones en el bloque catch.

Citando de la edición 14 de Java Language Specification :

Si el bloque catch se completa abruptamente por la razón R, entonces el bloque finalmente se ejecuta. Entonces hay una opción:

  • Si el bloque finalmente se completa normalmente, entonces la instrucción try se completa abruptamente por la razón R.

  • Si el bloque finalmente 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).

Roland
fuente
21

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

Debido a que es el último en ejecutarse en el mainy arroja una excepción, esa es la excepción que ven las personas que llaman.

De ahí la importancia de asegurarse de que la finallycláusula no arroje nada, ya que puede tragar excepciones del trybloque.

Alexander Pogrebnyak
fuente
55
También se ejecutará INCLUSO si no hay una excepción lanzada en el bloque try / catch
nanda
2
+1: Directo y al punto sin serpentear por toda la pila que el OP ya parece entender.
Powerlord
9

Una methodno puede throwdos excepciones al mismo tiempo. Siempre lanzará el último lanzamiento exception, que en este caso siempre será el del finallybloque.

Cuando q()se lanza la primera excepción del método , se atrapará y luego será tragada por la excepción lanzada finalmente bloqueada.

q () -> arrojado new Exception -> main catch Exception -> throw new Exception -> finally lanzar un nuevo exception(y el del catch"se pierde")

Garis M Suero
fuente
3

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

Exception currentException = null;

A medida que se produce cada excepción, "currentException" se establece en esa excepción. Cuando finaliza la aplicación, si currentException es! = Nulo, el tiempo de ejecución informa el error.

Además, los bloques finalmente siempre se ejecutan antes de que salga el método. Luego puede solicitar 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
}
CodingWithSpike
fuente
1

Es bien sabido que el bloque finalmente se ejecuta después de intentar y atrapar y siempre se ejecuta ... Pero como viste, es un poco complicado a veces echa un vistazo a ese fragmento de código a continuación y verás que las declaraciones de devolución y lanzamiento no No siempre hagas lo que deberían hacer en el orden que esperamos que haga el tema.

Salud.

/////////////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!";

}
////////////////////////////////////////////////// 
Astuto
fuente
1
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/

Luiz Fernando
fuente
1
Si bien este fragmento de código puede ser la solución, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que usted está respondiendo a la pregunta de los lectores en el futuro, y esa gente puede no saber las razones de su sugerencia de código
Rahul Gupta
1

La lógica es clara hasta que termine de imprimirse 13. Entonces la excepción lanzada en q()es capturado por catch (Exception i)en main()y new MyEx2()está listo para ser lanzado. Sin embargo, antes de lanzar la excepción, el finallybloque debe ejecutarse primero. Entonces la salida se convierte 132y finallypide lanzar otra excepción new MyEx1().

Como un método no puede lanzar más de uno Exception, siempre arrojará lo último Exception. En otras palabras, si ambos catchy los finallybloques intentan lanzar Exception, entonces Exceptionse traga la captura y solo se finallylanzará la excepción .

Por lo tanto, en este programa, la excepción MyEx2se traga y MyEx1se lanza. Esta excepción se expulsa main()y ya no se captura, por lo que JVM se detiene y el resultado final es 132Exception in thread main MyExc1.

En esencia, si tiene un finallyen una try/catchcláusula, finallyse ejecutará un DESPUÉS de atrapar la excepción , pero ANTES de lanzar cualquier excepción atrapada , y SOLAMENTE la última excepción se lanzará al final .

yyFred
fuente
0

Creo que solo tienes que caminar los finallybloques:

  1. Imprimir "1".
  2. finallyen qimpresión "3".
  3. finallyen mainimpresión "2".
Uwe Keim
fuente
0

Para manejar este tipo de situación, es decir, manejar la excepción planteada por finalmente bloquear. Puedes rodear el bloque finalmente por el bloque try: mira el siguiente ejemplo en python:

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print "Going to close the file"
      fh.close()
except IOError:
   print "Error: can\'t find file or read data"

fuente
-1

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);
     }
  }
}
Vouze
fuente
3
¿Qué problema vas a "resolver"? ¿Te refieres a la pregunta del examen? pues ya respondió. Si te refieres al problema del código dado, ya que es solo una pregunta de examen, no tiene sentido culparlo.
Earth Engine el