Pregunta

    

Esta pregunta ya tiene una respuesta aquí:

         

Soy consciente de los dolores de cabeza que implican el retorno en los bloques try / catch / finally - casos en los que el retorno en el finalmente siempre es el retorno del método, incluso si un 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 try:

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 fuera una declaración de retorno, 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 que el retorno.

El código se encuentra en un caso extremo que es muy difícil, si no imposible, de reproducir, por lo que no puedo escribir una prueba unitaria. Intentaré realizar un experimento más tarde hoy, si tengo unos minutos libres, pero tengo curiosidad de todos modos, y tal vez alguien en SO sepa la respuesta y pueda proporcionarla antes o en caso de que no pueda ejecutar un experimento.

¿Fue útil?

Solución

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

System.exit (int) puede lanzar una SecurityException . Si eso sucede, finalmente se ejecutará el bloque . Y dado que el mismo principal está llamando al mismo método desde la misma base de código, es probable que se arroje 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);
    }
  }

}

Otros consejos

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

Si system.exit (0) arroja una excepción de seguridad, se ejecutan las declaraciones catch y finalmente . Si tanto catch como finalmente contienen system.exit () , solo las declaraciones que preceden a estas system.exit () las declaraciones se ejecutan.

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 aquí (blog personal).

Otras respuestas han cubierto cómo los bloques catch y finalmente no se ejecutan si System.exit sale de la JVM sin lanzar un SecurityException , pero no muestran lo que sucede en un " try-with-resources " bloque a los recursos: ¿están cerrados?

Según el JLS , Sección 14.20.3.2 :

  

El efecto de la traducción es poner la especificación del recurso "dentro" La declaración de prueba. Esto permite que una cláusula catch de una declaración extendida de prueba con recursos capture una excepción debido a la inicialización o cierre automático de cualquier recurso.

     

Además, todos los recursos se habrán cerrado (o se habrá intentado cerrar) para cuando se ejecute el último bloque, de acuerdo con la intención de la última palabra clave.

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

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

Utilizo 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 no se ejecutan los bloques catch y finalmente , un " prueba con recursos " la declaración no tendrá la oportunidad de cerrar sus recursos si System.exit tiene éxito.

finalmente el bloque se ejecutará pase lo que pase ... incluso si el bloque de prueba arroja algún elemento arrojable (excepción o error) .....

solo el caso finalmente el bloque 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 se ejecutará finalmente el bloque. El programa finalizará después de la declaración System.exit ().

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, finalmente podremos ejecutar bloques y cerrar recursos como parte de nuestro flujo de salida.

Lo que estoy considerando hacer es envolver la llamada System.exit & amp; funcionalidad en mi propio método estático. En mi implementación de exit , arrojaría una subclase personalizada de Throwable o Error , e implementaría un controlador de excepción 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 agregado de hacer comprobable esta lógica: para probar que el método que contiene nuestro flujo principal en realidad solicita que el sistema salga, todo lo que tenemos que hacer es atrapar un elemento arrojable y afirmar que ese 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)

El principal inconveniente de esta estrategia es que es una forma de sacar funcionalidad del flujo de excepciones, que a menudo tiene consecuencias no deseadas. La más obvia, en este caso, es que en cualquier lugar donde haya escrito intente {...} catch (Throwable ex) {/ * no vuelve a lanzar * /} tendrá que actualizarse. En el caso de las bibliotecas que tienen contextos de ejecución personalizados, será necesario adaptarlas para comprender también esta excepción.

A fin de cuentas, esto me parece una buena estrategia. ¿Alguien más aquí piensa eso?

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

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

    • cuando hay una excepción 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top