Como é que System.exit () do Java trabalho com try / catch / finally blocos? [duplicado]

StackOverflow https://stackoverflow.com/questions/1410951

Pergunta

Esta questão já tem uma resposta aqui:

Eu estou ciente de dores de cabeça que envolvem retornando em try / catch / finally blocos - casos em que o retorno no finalmente é sempre o retorno para o método, mesmo se um retorno em um bloco try ou captura deve ser o executado .

No entanto, se o mesmo se aplica a System.exit ()? Por exemplo, se eu tiver um bloco try:

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

Se não há exceções, que System.exit () será chamado? Se a saída foi uma declaração de retorno, então o System.exit line (1) sempre (?) Ser chamado. No entanto, eu não tenho certeza se saída se comporta de forma diferente do retorno.

O código está em um caso extremo que é muito difícil, se não impossível, para reproduzir, por isso não posso escrever um teste de unidade. Vou tentar executar uma experiência mais tarde hoje, se eu ficar alguns minutos livres, mas estou curioso de qualquer maneira, e talvez alguém no SO sabe a resposta e pode fornecê-la antes ou no caso de eu não pode executar um experimento.

Foi útil?

Solução

No. System.exit(0) não retorna, e finalmente bloco não é executado.

System.exit(int) pode jogar uma SecurityException. Se isso acontecer, o bloco finally irá ser executada. E uma vez que o mesmo princípio está chamando o mesmo método a partir da mesma base de código, outro SecurityException é susceptível de ser jogado a partir da segunda chamada.


Aqui está um exemplo do 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);
    }
  }

}

Outras dicas

Testes simples incluindo catch também revelam que se system.exit(0) não lança uma exceção de segurança, será a última instrução executada (catch e finally não são executados em todos).

Se system.exit(0) faz jogar uma exceção de segurança, declarações catch e finally são executados. Se ambos catch e finally conter declarações system.exit(), apenas declarações anteriores essas demonstrações system.exit() são executados.

Em ambos os casos descrito acima, se o código try pertence a um método chamado por outro método, o método chamado não retornar.

Mais detalhes aqui (blog pessoal).

Outras respostas cobrimos como os catch e finally blocos não são executadas se System.exit sai da JVM sem jogar um SecurityException, mas eles não mostram o que acontece em um bloco "try-with-recursos" para os recursos: Are eles fecharam?

De acordo com a JLS , Seção 14.20.3.2 :

O efeito da tradução é colocar a especificação de recursos "dentro" da instrução try. Isso permite que uma cláusula catch de um prolongado try-with-recursos para capturar uma exceção devido à inicialização automática ou fechamento de qualquer recurso.

Além disso, todos os recursos terão sido fechada (ou tentou ser fechado) pelo tempo que o último bloco é executado, de acordo com a intenção da finalmente palavra-chave.

Isto é, os recursos serão closed antes de um catch ou finally bloco é executado. E se eles são closed de alguma forma, mesmo que catch e finally não correr?

Aqui está algum código para demonstrar que os recursos em um comunicado "try-with-recursos" não estão fechadas também.

Eu uso uma subclasse simples de BufferedReader que imprime uma declaração antes de chamar super.close.

class TestBufferedReader extends BufferedReader {
    public TestBufferedReader(Reader r) {
        super(r);
    }

    @Override
    public void close() throws IOException {
        System.out.println("close!");
        super.close();
    }
}

Então eu configurar o caso de teste de chamar System.exit na instrução try-with-recursos.

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!");
    }
}

Output:

Na tentativa

Portanto, não só catch e finally blocos não correr, uma declaração "try-with-recursos" não vai ter a chance de close seus recursos se System.exit êxito.

bloco finally será executado não importa o que .... mesmo bloco try joga qualquer throwable (exceção ou erro) .....

único caso bloco finally não executar ... é quando chamamos System.exit método () ..

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!!!");
}

Ele não executará finalmente bloco. O programa será encerrado após System.exit () declaração.

Se você considerar esta problemática comportamento, e você precisa de um bom controle sobre suas chamadas System.exit, então a única coisa que você pode fazer é envolver a funcionalidade System.exit em sua própria lógica. Se fizermos isso, podemos obter finalmente blocos executados e se os recursos fechados, como parte de nosso fluxo de saída.

O que eu estou pensando em fazer é embalar a chamada System.exit & funcionalidade em meu próprio método estático. Na minha implementação de exit eu iria jogar uma subclasse personalizada de Throwable ou Error, e implementar um manipulador de exceção personalizada Uncaught com Thread.setDefaultUncaughtExceptionHandler para lidar com essa exceção. Assim, o meu código torna-se:

//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 estratégia tem o "benefício" adicional de tornar este testável lógica: para testar se o método que contém o nosso "fluxo principal" na verdade pede o sistema para saída, todos nós temos que fazer é pegar um assert throwable e que é a gravação tipo. Por exemplo, um teste para o nosso envoltório lógica de negócios pode parecer:

//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)

A principal desvantagem desta estratégia é que é uma maneira de obter a funcionalidade fora do fluxo de exceção, que muitas vezes tem consequências inesperadas. A mais óbvia, neste caso, é que em qualquer lugar que você escreveu try { ... } catch(Throwable ex){ /*doesnt rethrow*/ } terá que ser atualizado. No caso de bibliotecas que têm contextos de execução personalizados, eles terão de ser adaptados também entender essa exceção.

Em suma, esta parece ser uma boa estratégia para mim. Mais alguém aqui acha?

  1. No exemplo abaixo, se System.exit(0) é antes da linha de excepção, o programa vai ser finalizado normalmente, de modo a finalmente, não será executada.

  2. Se o System.exix(0) é a última linha do bloco try, aqui temos 2 cenários

    • quando exceção está presente, em seguida, finalmente bloco é executado
    • quando exceção não estiver presente, em seguida, finalmente bloco não é executado

.

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top