java try-catch-finally system.exit

¿Cómo funciona Java''s System.exit() con los bloques try/catch/finally?



try-catch-finally (6)

Esta pregunta ya tiene una respuesta aquí:

Estoy al tanto de los dolores de cabeza que implican volver en los bloques try / catch / finally - casos en los que el retorno en el final siempre es el retorno del método, incluso si el retorno en un bloque try o catch debe ser el ejecutado.

Sin embargo, ¿se aplica lo mismo a System.exit ()? Por ejemplo, si tengo un bloque de prueba:

try { //Code System.exit(0) } catch (Exception ex) { //Log the exception } finally { System.exit(1) }

Si no hay excepciones, ¿a qué System.exit () se llamará? Si la salida era una instrucción return, entonces la línea System.exit (1) siempre se llamaría (?). Sin embargo, no estoy seguro de si la salida se comporta de manera diferente a la devolución.

El código está en un caso extremo que es muy difícil, si no imposible, de reproducir, por lo que no puedo escribir una prueba unitaria. Voy a tratar de ejecutar un experimento más tarde hoy, si tengo unos minutos gratis, pero de todos modos tengo curiosidad, y tal vez alguien en SO sabe la respuesta y puede proporcionarla antes o en caso de que no pueda ejecutar un experimentar.


  1. En el ejemplo siguiente, si System.exit(0) está antes de la línea de excepción, el programa terminará normalmente, por lo que FINALMENTE no se ejecutará.

  2. Si System.exix(0) es la última línea del bloque try, aquí tenemos 2 escenarios

    • cuando la excepción está presente, finalmente se ejecuta el bloque
    • cuando la excepción no está presente, finalmente el bloque no se ejecuta

.

package com.exception; public class UserDefind extends Exception { private static int accno[] = {1001,1002,1003,1004,1005}; private static String name[] = {"raju","ramu","gopi","baby","bunny"}; private static double bal[] = {9000.00,5675.27,3000.00,1999.00,1600.00}; UserDefind(){} UserDefind(String str){ super(str); } public static void main(String[] args) { try { //System.exit(0); -------------LINE 1--------------------------------- System.out.println("accno"+"/t"+"name"+"/t"+"balance"); for (int i = 0; i < 5; i++) { System.out.println(accno[i]+"/t"+name[i]+"/t"+bal[i]); //rise exception if balance < 2000 if (bal[i] < 200) { UserDefind ue = new UserDefind("Balance amount Less"); throw ue; }//end if }//end for //System.exit(0);-------------LINE 2--------------------------------- }//end try catch (UserDefind ue) { System.out.println(ue); } finally{ System.out.println("Finnaly"); System.out.println("Finnaly"); System.out.println("Finnaly"); } }//end of main }//end of class


Finalmente, el bloque se ejecutará sin importar qué ... incluso si try block arroja cualquier throwable (excepción o error) .....

only case finally block no se ejecuta ... es cuando llamamos al método System.exit () ..

try{ System.out.println("I am in try block"); System.exit(1); } catch(Exception ex){ ex.printStackTrace(); } finally { System.out.println("I am in finally block!!!"); }

No ejecutará finalmente el bloqueo. El programa terminará después de la instrucción System.exit ().


Las pruebas simples, incluida la catch también revelan que si system.exit(0) no arroja una excepción de seguridad, será la última instrucción ejecutada ( catch y finally no se ejecutarán en absoluto).

Si system.exit(0) arroja una excepción de seguridad, se ejecutan las sentencias catch y finally . Si ambos catch y finally contienen system.exit() , solo se ejecutan las sentencias anteriores a estas system.exit() .

En ambos casos descritos anteriormente, si el código try pertenece a un método llamado por otro método, el método llamado no regresa.

Más detalles here (blog personal).


No. System.exit(0) no regresa y el bloque finally no se ejecuta.

System.exit(int) puede arrojar una SecurityException . Si eso sucede, se ejecutará el bloque finally. Y dado que el mismo principal está llamando al mismo método desde la misma base de código, es probable que se genere otra SecurityException desde la segunda llamada.

Aquí hay un ejemplo del segundo caso:

import java.security.Permission; public class Main { public static void main(String... argv) throws Exception { System.setSecurityManager(new SecurityManager() { @Override public void checkPermission(Permission perm) { /* Allow everything else. */ } @Override public void checkExit(int status) { /* Don''t allow exit with any status code. */ throw new SecurityException(); } }); System.err.println("I''m dying!"); try { System.exit(0); } finally { System.err.println("I''m not dead yet!"); System.exit(1); } } }


Otras respuestas han cubierto cómo los bloqueos catch y finally no se ejecutan si System.exit sale de la JVM sin lanzar una SecurityException , pero no muestran qué sucede en un bloque "try-with-resources" con los recursos: ¿Están ¿cerrado?

De acuerdo con el JLS, Sección 14.20.3.2 :

El efecto de la traducción es poner la especificación del recurso "dentro" de la declaración try. Esto permite que una cláusula catch de una instrucción try-with-resources extendida capture una excepción debido a la inicialización o cierre automático de cualquier recurso.

Además, todos los recursos se cerrarán (o intentarán cerrarse) cuando se ejecute el bloque finally, de acuerdo con la intención de la palabra clave finally.

Es decir, los recursos estarán close d antes de que finally ejecute un bloque catch o finally . ¿Qué pasa si están close d de alguna manera, incluso si la catch y, finally , no se ejecuta?

Aquí hay un código para demostrar que los recursos en una declaración "try-with-resources" no están cerrados tampoco.

Uso una subclase simple de BufferedReader que imprime una declaración antes de llamar a super.close .

class TestBufferedReader extends BufferedReader { public TestBufferedReader(Reader r) { super(r); } @Override public void close() throws IOException { System.out.println("close!"); super.close(); } }

Luego configuré el caso de prueba de llamar a System.exit en la declaración try-with-resources.

public static void main(String[] args) { try (BufferedReader reader = new TestBufferedReader(new InputStreamReader(System.in))) { System.out.println("In try"); System.exit(0); } catch (Exception e) { System.out.println("Exception of type " + e.getClass().getName() + " caught: " + e.getMessage()); } finally { System.out.println("finally!"); } }

Salida:

En prueba

Por lo tanto, no solo catch y finally no se ejecutan bloques, una declaración de "prueba con recursos" no tendrá la oportunidad de close sus recursos si System.exit tiene éxito.


Si considera que este comportamiento es problemático y necesita un control preciso sobre sus llamadas a System.exit , entonces lo único que puede hacer es ajustar la funcionalidad System.exit en su propia lógica. Si hacemos eso, podemos finalmente ejecutar bloques y obtener recursos cerrados como parte de nuestro flujo de salida.

Lo que estoy considerando hacer es System.exit llamada y la funcionalidad System.exit en mi propio método estático. En mi implementación de la exit , lanzaría una subclase personalizada de Throwable o Error , e implementaría un controlador de excepciones Uncaught personalizado con Thread.setDefaultUncaughtExceptionHandler para manejar esa excepción. Por lo tanto, mi código se convierte en:

//in initialization logic: Thread.setDefaultUncaughtExceptionHandler((thread, exception) -> { if(exception instanceof SystemExitEvent){ System.exit(((SystemExitEvent)exception).exitCode); } }) // in "main flow" or "close button" or whatever public void mainFlow(){ try { businessLogic(); Utilities.exit(0); } finally { cleanUpFileSystemOrDatabaseConnectionOrWhatever(); } } //... class Utilities { // I''m not a fan of documentaiton, // but this method could use it. public void exit(int exitCode){ throw new SystemExitEvent(exitCode); } } class SystemExitEvent extends Throwable { private final int exitCode; public SystemExitEvent(int exitCode){ super("system is shutting down") this.exitCode = exitCode; } }

Esta estrategia tiene el "beneficio" adicional de hacer que esta lógica sea comprobable: para probar que el método que contiene nuestro "flujo principal" en realidad solicita al sistema que salga, todo lo que tenemos que hacer es capturar un objeto descartable y afirmar que es el tipo de escritura. Por ejemplo, una prueba para nuestro contenedor de lógica de negocios podría verse así:

//kotlin, a really nice language particularly for testing on the JVM! @Test fun `when calling business logic should business the business`(){ //setup val underTest = makeComponentUnderTest(configureToReturnExitCode = 42); //act val thrown: SystemExitEvent = try { underTest.mainFlow(); fail("System Exit event not thrown!") } catch(event: SystemExitEvent){ event; } //assert assertThat(thrown.exitCode).isEqualTo(42)

La principal desventaja de esta estrategia es que es una forma de obtener funcionalidad del flujo de excepciones, que a menudo tiene consecuencias imprevistas. El más obvio, en este caso, es que en cualquier lugar que hayas escrito, try { ... } catch(Throwable ex){ /*doesnt rethrow*/ } tendrá que actualizarse. En el caso de las bibliotecas que tienen contextos de ejecución personalizados, deberán actualizarse para que también comprendan esta excepción.

En resumen, esto me parece una buena estrategia. ¿Alguien más aquí piensa eso?