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

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

Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

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

System.exit(int) может бросить SecurityException.Если это произойдет, то окончательно заблокируется будет быть казненным.И поскольку тот же принципал вызывает тот же метод из той же кодовой базы, другой 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 код принадлежит методу, вызванному другим методом, вызванный метод не возвращает.

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

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

В соответствии с JLS, Раздел 14.20.3.2:

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

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

То есть ресурсы будут closed перед a catch или finally блок работает.Что, если это так closed каким - то образом , даже если 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!");
    }
}

Выходной сигнал:

В попытке

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

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

единственный случай, когда блокировка finally не выполняется execute...is когда мы вызываем метод 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.Программа будет завершена после инструкции System.exit().

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

То, что я подумываю сделать, это обернуть 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*/ } придется обновить.В случае библиотек, которые имеют пользовательские контексты выполнения, их необходимо будет модифицировать, чтобы они также понимали это исключение.

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

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

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

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

.

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top