try - ¿Un bloque finalmente se ejecuta siempre en Java?




try catch finally java return statement (20)

Teniendo en cuenta este código, ¿puedo estar absolutamente seguro de que el bloque finally siempre se ejecuta, sin importar qué es something() ?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("i don't know if this will get printed out.");
}

https://code.i-harness.com


La respuesta es simple, sí.

ENTRADA:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

SALIDA:

catch
finally

Además de las otras respuestas, es importante señalar que 'finalmente' tiene el derecho de anular cualquier excepción / valor devuelto por el bloque try..catch. Por ejemplo, el siguiente código devuelve 12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

Del mismo modo, el siguiente método no lanza una excepción:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

Mientras que el siguiente método lo lanza:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

Además, aunque es una mala práctica, si hay una declaración de retorno dentro del bloque finally, prevalecerá sobre cualquier otro retorno del bloque regular. Es decir, el siguiente bloque devolvería falso:

try { return true; } finally { return false; }

Lo mismo con lanzar excepciones del bloque finalmente.


Aquí están las palabras oficiales de la especificación del lenguaje Java.

14.20.2. Ejecución de try-finally y try-catch-finally

Una sentencia de try con un bloque finally se ejecuta ejecutando primero el bloque de try . Entonces hay una opción:

  • Si la ejecución del bloque try se completa normalmente, [...]
  • Si la ejecución del bloque try se completa abruptamente debido a un throw de un valor V , [...]
  • Si la ejecución del bloque try se completa abruptamente por cualquier otra razón R , entonces se ejecuta el bloque finally . Entonces hay una opción:
    • Si el bloque finalmente se completa normalmente, entonces la instrucción try completa abruptamente por la razón R.
    • Si el bloque final se completa abruptamente por la razón S , entonces la instrucción try completa abruptamente por la razón S ( y la razón R se descarta ).

La especificación para return realmente hace esto explícito:

JLS 14.17 La declaración de retorno

ReturnStatement:
     return Expression(opt) ;

Una declaración de return sin Expression intenta transferir el control al invocador del método o constructor que lo contiene.

Una declaración de return con una Expression intenta transferir el control al invocador del método que lo contiene; El valor de la Expression convierte en el valor de la invocación del método.

Las descripciones anteriores dicen " intentos de transferir el control " en lugar de solo " transferencias de control " porque si hay alguna instrucción de try dentro del método o constructor cuyos bloques de try contienen la instrucción de return , entonces se ejecutarán las cláusulas finally de esas declaraciones de try , en Orden, de lo más interno a lo más externo, antes de transferir el control al invocador del método o constructor. La finalización brusca de una cláusula finally puede interrumpir la transferencia de control iniciada por una declaración de return .


Código de ejemplo:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

Salida:

finally trumps return. 
0

Considere el siguiente programa:

public class someTest {

    private static StringBuilder sb = new StringBuilder();

    public static void main(String args[]) {

        System.out.println(someString());
        System.out.println("---AGAIN---");
        System.out.println(someString());
    }

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}

A partir de Java 1.8.162, el bloque de código anterior da el siguiente resultado:

-abc-
---AGAIN---
-abc-xyz-abc-

esto significa que usar finally para liberar objetos es una buena práctica como el siguiente código:

private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null;
    }
}

De manera concisa, en la documentación oficial de Java (Haga clic here ), está escrito que:

Si la JVM sale mientras se está ejecutando el código try o catch, entonces el bloque finally puede no ejecutarse. Del mismo modo, si el subproceso que ejecuta el código try o catch se interrumpe o se elimina, el bloque finally puede no ejecutarse aunque la aplicación en su totalidad continúe.


El bloque finally se ejecuta siempre a menos que haya una terminación anormal del programa, ya sea como resultado de un bloqueo de JVM o de una llamada a System.exit(0) .

Además de eso, cualquier valor devuelto desde el bloque finally anulará el valor devuelto antes de la ejecución del bloque finally, así que tenga cuidado de verificar todos los puntos de salida al usar try finalmente.


Eso es realmente cierto en cualquier idioma ... finalmente, siempre se ejecutará antes de una declaración de devolución, sin importar dónde se encuentre esa devolución en el cuerpo del método. Si ese no fuera el caso, el bloque finalmente no tendría mucho significado.


Esto se debe a que asignó el valor de i como 12, pero no devolvió el valor de i a la función. El código correcto es el siguiente:

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}

Finalmente, siempre se ejecuta ese es todo el punto, solo porque aparece en el código después de la devolución no significa que así se implementa. El tiempo de ejecución de Java tiene la responsabilidad de ejecutar este código al salir del bloque try .

Por ejemplo, si tiene lo siguiente:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

El tiempo de ejecución generará algo como esto:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

Si se lanza una excepción no detectada, el bloque finally se ejecutará y la excepción continuará propagándose.


No, no siempre un caso de excepción es // System.exit (0); Antes de que el bloque finalmente impida finalmente ser ejecutado.

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

Porque siempre se llamará un bloque finally a menos que llames a System.exit() (o el hilo se cuelgue).


Probé el ejemplo anterior con una ligera modificación.

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

Las salidas del código anterior:

Finalmente triunfa el regreso.
2

Esto es porque cuando return i; se ejecuta i tiene un valor 2. Después de esto, se ejecuta el bloque finally donde se asigna 12 a i y luego se ejecuta System.out .

Después de ejecutar el bloque finally , el bloque try devuelve 2, en lugar de devolver 12, porque esta instrucción de retorno no se ejecuta de nuevo.

Si va a depurar este código en Eclipse, tendrá la sensación de que, después de ejecutar System.out finally bloqueará la declaración de return del bloque try . Pero este no es el caso. Simplemente devuelve el valor 2.


Sí, finally se llamará después de la ejecución de los bloques de código try o catch.

Las únicas veces que finally no se llamarán son:

  1. Si System.exit() ;
  2. Si la JVM se bloquea primero;
  3. Si la JVM alcanza un bucle infinito (o alguna otra instrucción no interrumpible, sin terminación) en el bloque try o catch ;
  4. Si el SO termina por la fuerza el proceso de JVM; por ejemplo, "kill -9" en UNIX.
  5. Si el sistema host muere; Por ejemplo, fallo de alimentación, error de hardware, pánico del sistema operativo, etc.
  6. Si finalmente el bloque va a ser ejecutado por el subproceso del daemon y todos los demás subprocesos que no son del demonio, antes de finalmente se llama.

Sí, finalmente el bloque siempre se ejecuta. La mayoría de los desarrolladores utilizan este bloque para cerrar la conexión de la base de datos, el objeto del conjunto de resultados, el objeto de declaración y también los utilizan en la hibernación de Java para revertir la transacción.


Si lo sera No importa lo que suceda en su bloque try / catch a menos que System.exit () sea llamado o JVM se bloquee. Si hay alguna declaración de devolución en el (los) bloque (es), finalmente se ejecutará antes de esa declaración de devolución.


Si se lanza una excepción, finalmente se ejecuta. Si no se lanza una excepción, finalmente se ejecuta. Si la excepción es atrapada, finalmente se ejecuta. Si la excepción no es atrapada, finalmente se ejecuta.

La única vez que no se ejecuta es cuando sale JVM.


Una manera lógica de pensar en esto es:

  1. El código colocado en un bloque finally debe ejecutarse lo que ocurra dentro del bloque try
  2. Por lo tanto, si el código en el bloque try intenta devolver un valor o lanzar una excepción, el elemento se coloca 'en el estante' hasta que se pueda ejecutar el bloque finally
  3. Debido a que el código en el bloque finally tiene (por definición) una prioridad alta, puede devolver o lanzar lo que quiera. En cuyo caso, todo lo que quede "en el estante" se desecha.
  4. La única excepción a esto es si la VM se apaga completamente durante el bloque de prueba, por ejemplo, por 'System.exit'

Una manera lógica de pensar en esto es:

El código colocado en un bloque finally debe ejecutarse lo que ocurra dentro del bloque try.

Entonces, si el código en el bloque try intenta devolver un valor o lanzar una excepción, el elemento se coloca 'en el estante' hasta que el bloque finally pueda ejecutarse. Debido a que el código en el bloque finally tiene (por definición) una prioridad alta, puede devolver o lanzar lo que le guste En cuyo caso, todo lo que quede "en el estante" se desecha.

La única excepción a esto es si la VM se apaga completamente durante el bloque de prueba, por ejemplo, por 'System.exit'

Nunca lanzar ninguna excepción desde el bloque finalmente

try {
  someMethod();  //Throws exceptionOne
} finally {
  cleanUp();    //If finally also threw any exception the exceptionOne will be lost forever
}

Esto está bien, siempre que cleanUp () nunca pueda lanzar ninguna excepción. En el ejemplo anterior, si someMethod () lanza una excepción, y también en el bloque finally, cleanUp () lanza una excepción, esa segunda excepción saldrá del método y la primera excepción original (razón correcta) se perderá para siempre. Si el código que llama en un bloque finally puede lanzar una excepción, asegúrese de que lo maneja o lo registra. Nunca dejes que salga del bloque finalmente.

En realidad, al salir del programa (ya sea llamando a System.exit () o causando un error fatal que causa que el proceso se interrumpa: algunas veces se hace referencia informalmente como un "punto de acceso" o "Dr Watson" en Windows) evitará que finalmente se bloquee ¡ejecutado!

No hay nada que nos impida anidar los bloqueos try / catch / finally (por ejemplo, poner un bloque try / finally dentro de un bloque try / catch, o viceversa), y no es algo tan inusual.







try-catch-finally