¿Siempre se ejecuta un bloque finalmente en Java?
-
09-06-2019 - |
Pregunta
Teniendo en cuenta este código, ¿puedo ser? absolutamente seguro que el finally
El bloque siempre se ejecuta, pase lo que pase. 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");
}
Solución
Sí, finally
será llamado después de la ejecución del try
o catch
bloques de código.
las unicas veces finally
no serán llamados son:
- si invocas
System.exit()
- Si la JVM falla primero
- Si la JVM alcanza un bucle infinito (o alguna otra declaración no interrumpible y sin terminación) en el
try
ocatch
bloquear - Si el sistema operativo finaliza por la fuerza el proceso JVM;p.ej.,
kill -9 <pid>
en UNIX - Si el sistema anfitrión muere;por ejemplo, corte de energía, error de hardware, pánico en el sistema operativo, etc.
- Si el
finally
El bloque será ejecutado por un subproceso demonio y todos los demás subprocesos que no son demonios saldrán antesfinally
se llama
Otros consejos
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.");
}
}
Producción:
finally trumps return.
0
Además, aunque es una mala práctica, si hay una declaración de devolución dentro del bloque finalmente, prevalecerá sobre cualquier otra devolución del bloque normal.Es decir, el siguiente bloque devolvería falso:
try { return true; } finally { return false; }
Lo mismo ocurre con el lanzamiento de excepciones desde el 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
A
try
declaración con unfinally
El bloque se ejecuta ejecutando primero eltry
bloquear.Entonces hay una opción:
- Si la ejecución del
try
el bloque se completa normalmente, [...]- Si la ejecución del
try
El bloque se completa abruptamente debido a unthrow
de un valor V, [...]- Si la ejecución del
try
El bloque se completa abruptamente por cualquier otro motivo. R, entonces elfinally
se ejecuta el bloque.Entonces hay una opción:
- Si el bloque finalmente se completa normalmente, entonces el
try
La declaración se completa abruptamente por alguna razón. R.- Si el
finally
El bloque se completa abruptamente por algún motivo. S, entonces eltry
La declaración se completa abruptamente por alguna razón. S (y razon R esta descartado).
La especificación para return
en realidad hace esto explícito:
JLS 14.17 La Declaración de Retorno
ReturnStatement: return Expression(opt) ;
A
return
declaración sinExpression
intentos para transferir el control al invocador del método o constructor que lo contiene.A
return
declaración con unExpression
intentos transferir el control al invocador del método que lo contiene;el valor de laExpression
se convierte en el valor de la invocación del método.Las descripciones anteriores dicen "intentos transferir el control"en lugar de simplemente"control de transferencias"porque si hay alguno
try
declaraciones dentro del método o constructor cuyotry
Los bloques contienen elreturn
declaración, entonces cualquierfinally
cláusulas de esostry
Las declaraciones se ejecutarán, en orden, desde la más interna a la más externa, antes de que el control se transfiera al invocador del método o constructor.Finalización abrupta de unfinally
cláusula puede interrumpir la transferencia de control iniciada por unreturn
declaración.
Además de las otras respuestas, es importante señalar que 'finalmente' tiene derecho a 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;
}
}
De manera similar, el siguiente método no genera 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();
}
}
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.");
}
}
El código anterior genera:
finalmente triunfa sobre el regreso.
2
Esto se debe a que cuando return i;
es ejecutado i
tiene un valor 2.Después de esto el finally
Se ejecuta el bloque donde se asigna 12 a i
y luego System.out
se ejecuta out.
Después de ejecutar el finally
bloquear el try
El bloque devuelve 2, en lugar de devolver 12, porque esta declaración de devolución no se ejecuta nuevamente.
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
El bloque se ejecuta nuevamente.Pero este no es el caso.Simplemente devuelve el valor 2.
He aquí una elaboración de la respuesta de kevin.Es importante saber que la expresión a devolver 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");
}
}
Producción:
X
finally trumps return... sort of
0
Esa es la idea de un bloque final.Le permite asegurarse de realizar limpiezas que de otro modo podrían omitirse cuando regrese, entre otras cosas, por supuesto.
Finalmente lo llaman independientemente de lo que pase en el bloque de prueba (a menos que llama System.exit(int)
o la máquina virtual Java se desconecta por algún otro motivo).
Una forma lógica de pensar en esto es:
- El código colocado en un bloque finalmente debe ejecutarse pase lo que pase dentro del bloque de prueba
- Entonces, si el código en el bloque try intenta devolver un valor o generar una excepción, el elemento se coloca "en el estante" hasta que el bloque finalmente pueda ejecutarse.
- Debido a que el código en el bloque finalmente tiene (por definición) una alta prioridad, puede devolver o lanzar lo que quiera.En cuyo caso, todo lo que quede "en el estante" se descarta.
- La única excepción a esto es si la VM se apaga por completo durante el bloque de prueba, por ejemplo.por 'Sistema.salida'
Además, un retorno finalmente descartará cualquier excepción. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html
finalmente siempre se ejecuta a menos que haya una terminación anormal del programa (como llamar a System.exit(0)...).Entonces, tu salida del sistema se imprimirá.
El bloque finalmente siempre se ejecuta a menos que haya una terminación anormal del programa, ya sea como resultado de una falla de JVM o de una llamada a System.exit(0)
.
Además de eso, cualquier valor devuelto dentro del bloque finalmente anulará el valor devuelto antes de la ejecución del bloque finalmente, así que tenga cuidado de verificar todos los puntos de salida cuando utilice try finalmente.
No, no siempre un caso de excepción es // System.exit (0);antes de que el bloque finalmente impida que finalmente se ejecute.
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");
}
}
}
Finalmente siempre se ejecuta, ese es el punto, el hecho de que aparezca en el código después del retorno no significa que así sea como se implemente.El tiempo de ejecución de Java tiene la responsabilidad de ejecutar este código al salir del try
bloquear.
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, finally
El bloque se ejecutará y la excepción continuará propagándose.
Esto se debe a que asignaste el valor de i como 12, pero no devolviste 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;
}
}
Porque siempre se llamará a un bloque finalmente a menos que usted llame System.exit()
(o el hilo falla).
la respuesta es simple SÍ.
APORTE:
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");
}
PRODUCCIÓN:
catch
finally
Sí, lo llamarán.Ese es el objetivo de tener una palabra clave finalmente.Si saltar del bloque try/catch pudiera simplemente omitir el bloque finalmente, era lo mismo que poner System.out.println fuera del try/catch.
De manera concisa, en la documentación oficial de Java (haga clic en aquí), está escrito que -
Si el JVM sale mientras se ejecuta el código de prueba o captura, entonces el bloque finalmente no puede ejecutarse.Del mismo modo, si el hilo que ejecuta el código de prueba o de captura se interrumpe o mata, el bloque finalmente puede no ejecutarse a pesar de que la aplicación en su conjunto continúa.
Sí, el bloqueo final 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 lo utilizan en la hibernación de Java para revertir la transacción.
Sí, lo será.No importa lo que suceda en su bloque try o catch a menos que se llame a System.exit() o la JVM falle.si hay alguna declaración de devolución en los bloques, finalmente se ejecutará antes de esa declaración de devolución.
Sí lo será.El único caso en el que no será así es que la JVM se cierre o se bloquee.
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());
System.out.println("---PRINT THE RESULT---");
System.out.println(sb.toString());
}
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 proporciona el siguiente resultado:
-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz
esto significa que usando finally
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; // Just an example, but you can close streams or DB connections this way.
}
}
En realidad, eso es cierto en cualquier idioma... finalmente siempre se ejecutará antes de una declaración de retorno, sin importar dónde se encuentre ese retorno en el cuerpo del método.Si ese no fuera el caso, el bloque finalmente no tendría mucho significado.
finally
se ejecutará y eso es seguro.
finally
no se ejecutará en los siguientes casos:
caso 1 :
cuando estas ejecutando System.exit()
.
caso 2:
Cuando su JVM/Thread falla.
caso 3:
Cuando su ejecución se detiene manualmente.
Si no maneja la excepción, antes de finalizar el programa, JVM ejecuta el bloque finalmente.No se ejecutará solo si la ejecución normal del programa falla, lo que significa la terminación del programa debido a las siguientes razones.
Provocando un error fatal que hace que el proceso se aborte.
Terminación del programa debido a memoria corrupta.
Llamando a System.exit()
Si el programa entra en un bucle infinito.
Sí porque sin declaración de control puede prevenir finally
de ser ejecutado.
Aquí hay un ejemplo de referencia, donde se ejecutarán todos los bloques de código:
| x | Current result | Code
|---|----------------|------ - - -
| | |
| | | public static int finallyTest() {
| 3 | | int x = 3;
| | | try {
| | | try {
| 4 | | x++;
| 4 | return 4 | return x;
| | | } finally {
| 3 | | x--;
| 3 | throw | throw new RuntimeException("Ahh!");
| | | }
| | | } catch (RuntimeException e) {
| 4 | return 4 | return ++x;
| | | } finally {
| 3 | | x--;
| | | }
| | | }
| | |
|---|----------------|------ - - -
| | Result: 4 |
En la variante siguiente, return x;
será omitido.El resultado sigue siendo 4
:
public static int finallyTest() {
int x = 3;
try {
try {
x++;
if (true) throw new RuntimeException("Ahh!");
return x; // skipped
} finally {
x--;
}
} catch (RuntimeException e) {
return ++x;
} finally {
x--;
}
}
Las referencias, por supuesto, rastrean su estado.Este ejemplo devuelve una referencia con value = 4
:
static class IntRef { public int value; }
public static IntRef finallyTest() {
IntRef x = new IntRef();
x.value = 3;
try {
return x;
} finally {
x.value++; // will be tracked even after return
}
}
try
- catch
- finally
son las palabras clave para utilizar casos de manejo de excepciones.
Como explicativo normal.
try {
//code statements
//exception thrown here
//lines not reached if exception thrown
} catch (Exception e) {
//lines reached only when exception is thrown
} finally {
// always executed when the try block is exited
//independent of an exception thrown or not
}
El bloque finalmente impide la ejecución...
- Cuando llamaste
System.exit(0);
- Si JVM sale.
- Errores en la JVM
Añadiendo a La respuesta de @vibhash ya que ninguna otra respuesta explica lo que sucede en el caso de un objeto mutable como el siguiente.
public static void main(String[] args) {
System.out.println(test().toString());
}
public static StringBuffer test() {
StringBuffer s = new StringBuffer();
try {
s.append("sb");
return s;
} finally {
s.append("updated ");
}
}
Salida
sbupdated
Intenté esto, está en un solo rostro.
class Test {
public static void main(String args[]) throws Exception {
Object obj = new Object();
try {
synchronized (obj) {
obj.wait();
System.out.println("after wait()");
}
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
}
El hilo principal estará en estado de espera para siempre, por lo tanto, finalmente nunca será llamado.
entonces la salida de la consola no imprimirá la cadena: after wait()
o finally
De acuerdo con @Stephen C, el ejemplo anterior es uno de los terceros casos mencionados. aquí:
Agregando más posibilidades de bucle infinito en el siguiente código:
// import java.util.concurrent.Semaphore;
class Test {
public static void main(String[] args) {
try {
// Thread.sleep(Long.MAX_VALUE);
// Thread.currentThread().join();
// new Semaphore(0).acquire();
// while (true){}
System.out.println("after sleep join semaphore exit infinite while loop");
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
}
Caso 2:Si la JVM falla primero
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Test {
public static void main(String args[]) {
try {
unsafeMethod();
// Runtime.getRuntime().halt(123);
System.out.println("After Jvm Crash!");
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
unsafe.putAddress(0, 0);
}
}
Árbitro: ¿Cómo se bloquea una JVM?
Caso 6:Si finalmente el bloque va a ser ejecutado por un subproceso demonio y todos los demás subprocesos que no son demonios salen antes de que finalmente se llame.
class Test {
public static void main(String args[]) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
printThreads("Daemon Thread printing");
// just to ensure this thread will live longer than main thread
Thread.sleep(10000);
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
};
Thread daemonThread = new Thread(runnable);
daemonThread.setDaemon(Boolean.TRUE);
daemonThread.setName("My Daemon Thread");
daemonThread.start();
printThreads("main Thread Printing");
}
private static synchronized void printThreads(String str) {
System.out.println(str);
int threadCount = 0;
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for (Thread t : threadSet) {
if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
System.out.println("Thread :" + t + ":" + "state:" + t.getState());
++threadCount;
}
}
System.out.println("Thread count started by Main thread:" + threadCount);
System.out.println("-------------------------------------------------");
}
}
producción:Esto no imprime "finalmente", lo que implica que "Finalmente bloquear" en el "hilo del demonio" no se ejecutó.
main Thread Printing Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED Thread :Thread[main,5,main]:state:RUNNABLE Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE Thread count started by Main thread:3 ------------------------------------------------- Daemon Thread printing Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE Thread count started by Main thread:2 ------------------------------------------------- Process finished with exit code 0