Pregunta ¿Finalmente siempre se ejecuta en Java?


Teniendo en cuenta este código, ¿puedo ser absolutamente seguro que el finally bloque siempre se ejecuta, no importa qué something() ¿es?

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

1901
2017-09-15 17:43


origen


Respuestas:


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

Las únicas veces finally no se llamará son:

  1. Si invocas System.exit();
  2. Si la JVM falla primero;
  3. Si la JVM alcanza un ciclo infinito (o alguna otra instrucción no interrumpible y no finalizada) en el try o catch bloquear;
  4. Si el sistema operativo finaliza por la fuerza el proceso de JVM; p.ej. "kill -9" en UNIX.
  5. Si el sistema host muere; p.ej. falla de energía, error de hardware, pánico de OS, etcétera.
  6. Si finalmente el bloque va a ser ejecutado por el hilo daemon y todos los otros hilos no daemon salen antes de que se llame finalmente.

2114
2017-09-15 17:45



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

444
2017-09-15 17:59



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

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

Lo mismo con lanzar excepciones del bloque finally.


323
2017-09-15 18:19



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

UN try declaración con un finally bloque se ejecuta ejecutando primero el try bloquear. Entonces hay una opción:

  • Si la ejecución de la try bloque completa normalmente, [...]
  • Si la ejecución de la try bloque se completa abruptamente debido a una throw de un valor V, [...]
  • Si la ejecución de la try bloque se completa abruptamente por cualquier otro motivo R, entonces el finally el bloque se ejecuta. Entonces hay una opción:
    • Si el bloque finally se completa normalmente, entonces el try declaración termina abruptamente por la razón R.
    • Si el finally bloque se completa abruptamente por la razón S, entonces el try declaración termina abruptamente por la razón S (y razon R es descartado)

La especificación para return en realidad lo hace explícito:

JLS 14.17 Declaración de devolución

ReturnStatement:
     return Expression(opt) ;

UN return declaración sin Expression  intentos para transferir el control al invocador del método o constructor que lo contiene.

UN return declaración con un Expression  intentos transferir el control al invocador del método que lo contiene; el valor de la Expression se convierte en el valor de la invocación del método.

Las descripciones anteriores dicen "intentos para transferir el control"en lugar de solo"control de transferencias"porque si hay alguno try declaraciones dentro del método o constructor cuya try bloques contienen el return declaración, luego cualquier finally cláusulas de aquellos try los enunciados se ejecutarán, en orden, desde el más interno hasta el más externo, antes de que el control se transfiera al invocador del método o constructor. Terminación abrupta de una finally cláusula puede interrumpir la transferencia de control iniciada por un return declaración.


229
2018-05-25 06:50



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 arroja una excepción:

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

Mientras que el siguiente método lo arroja:

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

134
2018-05-13 07:11



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 de código anteriores:

finalmente triunfa sobre el retorno.
  2

Esto es porque cuando return i; es ejecutado i tiene un valor 2. Después de esto, el finally bloque se ejecuta donde 12 está asignado a i y entonces System.out fuera se ejecuta.

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

Si depura este código en Eclipse, tendrá la sensación de que después de ejecutar System.out de finally bloquear el return declaracion de try bloque se ejecuta de nuevo. Pero este no es el caso. Simplemente devuelve el valor 2.


88
2017-11-17 16:25



Aquí hay una elaboración de La respuesta de Kevin. Es importante saber que la expresión que se devuelve se evalúa antes finally, incluso si se devuelve después.

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

public static int printX() {
    System.out.println("X");
    return 0;
}

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

Salida:

X
finally trumps return... sort of
0

80
2017-12-03 23:36



Esa es la idea de un bloqueo final. Te permite asegurarte de que haces limpiezas que de otro modo podrían omitirse porque regresas, entre otras cosas, por supuesto.

Finalmente se llama independientemente de lo que pase en el bloque try (a no ser que llama System.exit(int) o la Máquina Virtual Java se retira por alguna otra razón).


46
2018-05-13 06:19



Una forma lógica de pensar sobre esto es:

  1. El código colocado en un bloque finally debe ser ejecutado lo que ocurra dentro del bloque try
  2. 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
  3. Como el código en el bloque finally tiene (por definición) una alta prioridad, puede devolver o lanzar lo que quiera. En cuyo caso, todo lo que queda 'en el estante' se descarta.
  4. La única excepción a esto es si la VM se apaga por completo durante el bloqueo try, p. por 'System.exit'

31
2017-09-15 19:26



Además, un regreso finalmente arrojará cualquier excepción. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html


14
2017-09-15 19:26



finalmente se ejecuta siempre a menos que haya una terminación anormal del programa (como llamar a System.exit (0) ..). entonces, tu sysout se imprimirá


13
2017-09-15 17:46