Как работает система Java.exit () работа с блоками try/catch/finally? [дубликат]

этот вопрос уже есть ответ здесь:

Я знаю о головных болях, которые включают возвращение в try/catch / finally blocks-случаи, когда возврат в finally всегда является возвратом для метода, даже если возврат в блоке try или catch должен быть тот, которого казнили.

однако то же самое относится к системе.exit()? Например, если у меня есть блок try:

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

если нет исключений, какая система.exit () будет вызван? Если exit был оператором return, то линейная система.выход(1) всегда (?) назвать. Однако я не уверен, что exit ведет себя иначе, чем return.

код в крайнем случае очень трудно, если не невозможно, воспроизвести, поэтому я не могу написать единицу тест. Я попытаюсь провести эксперимент позже сегодня, если у меня будет несколько свободных минут, но мне все равно любопытно, и, возможно, кто-то знает ответ и может предоставить его раньше или в случае, если я не смогу провести эксперимент.

6 ответов


нет. System.exit(0) не возвращается, и блок finally не выполняется.

System.exit(int) можете кинуть SecurityException. Если это произойдет, блок finally будет быть казнен. И поскольку тот же Принципал вызывает тот же метод из той же базы кода, другой SecurityException вероятно, будет выброшен из второго вызова.


вот пример второго случая:

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

}

простые тесты, в том числе catch тоже показать, что если system.exit(0) не создает исключение безопасности, это будет последний выполненный оператор (catch и finally не выполняется вообще).

если system.exit(0) создает исключение безопасности,catch и finally инструкции выполняются. Если оба catch и finally содержат system.exit() утверждения, только утверждения, предшествующие этим system.exit() инструкции выполняются.

в обоих случаях, описанных выше, если try код принадлежит методу, вызываемому другим методом, вызываемый метод не возвращается.

более подробная информация здесь (личный блог).


блок finally будет выполнен несмотря ни на что....даже если блок try выбрасывает любой throwable (исключение или ошибку).....

только блок case finally не выполняется...когда мы вызываем 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!!!");
}

Он не будет выполнен блок finally. Программа будет прекращена после системы.оператор Exit.


другие ответы охватили как catch и finally блоки не работают, если System.exit выходит из JVM, не бросая SecurityException, но они не показывают, что происходит в блоке "try-with-resources" для ресурсов: они закрыты?

по словам JLS, раздел 14.20.3.2:

эффект перевод ставить спецификация ресурсов "внутри" попробовать заявление. Это позволяет предложение catch расширенного оператор try-with-resources, чтобы поймать исключение из-за автоматической инициализации или закрытия любого ресурса.

кроме того, все ресурсы будут закрыты (или попытаются быть закрыты) к моменту выполнения блока finally в соответствии с намерением ключевого слова finally.

то есть ресурсы будут closed перед catch или finally блок работает. Что, если они ... --6-->д как-то даже если catch и finally не бежать?

вот некоторый код, чтобы продемонстрировать, что ресурсы в операторе "try-with-resources" также не закрыты.

Я использую простой подкласс BufferedReader это печатает заявление перед вызовом super.close.

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

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

затем я установил тестовый случай вызова System.exit в инструкции 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!");
    }
}

выход:

в try

таким образом, не только catch и finally блоки не запускаются, оператор" try-with-resources " не получит шанс close свои ресурсы, если System.exit успешно.


  1. в примере ниже, если System.exit(0) находится перед строкой исключения, программа будет завершена нормально, поэтому, наконец, не будет выполняться.

  2. если System.exix(0) последние строки в блоке try, здесь у нас есть 2 сценария

    • когда исключение присутствует, то, наконец, блок выполняется
    • когда исключение отсутствует, то блок finally не выполняется

.

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

если вы считаете это поведение проблематичным, и вам нужен точный контроль над вашим System.exit звонит, то единственное, что вы можете сделать, это обернуть системе.выход из функциональности в вашей собственной логике. Если мы это сделаем, мы сможем выполнить блоки finally и закрыть ресурсы как часть нашего потока выхода.

то, что я рассматриваю, это обертывание System.exit вызов и функциональность в моем собственном статическом методе. В моей реализации exit Я бы бросил пользовательский подкласс Throwable или Error, и реализовать пользовательский обработчик необработанных исключений с Thread.setDefaultUncaughtExceptionHandler для обработки этого исключения. Таким образом, мой код выглядит так:

//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;
  }
} 

эта стратегия имеет дополнительное "преимущество", делая эту логику проверяемой: чтобы проверить, что метод, содержащий наш" основной поток", фактически запрашивает систему для выхода, все, что нам нужно сделать, это поймать throwable и утверждать, что это тип записи. Например, тест для нашей оболочки бизнес-логики может выглядеть так:

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

главные недостатком этой стратегии является то, что это способ получить функциональность из потока исключений, что часто имеет непреднамеренные последствия. Наиболее очевидным в этом случае является то, что в любом месте вы написали try { ... } catch(Throwable ex){ /*doesnt rethrow*/ } придется обновить. В случае библиотек, имеющих пользовательские контексты выполнения, их необходимо будет обновить, чтобы также понять это исключение.

в целом, это кажется мне хорошей стратегией. Кто-нибудь еще так думает?