首页 文章

Java的System.exit()如何与try / catch / finally块一起使用? [重复]

提问于
浏览
55

这个问题在这里已有答案:

我知道涉及在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)行将始终(?)被调用 . 但是,我不确定退出的行为是否与返回不同 .

代码处于极端情况下,即使不是不可能,也很难再现,因此我无法编写单元测试 . 我今天晚些时候会尝试进行一项实验,如果我得到一些免费的分钟,但我很好奇,也许有人知道答案,可以提供它,或者我无法运行实验 .

6 回答

  • 1

    编号 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);
        }
      }
    
    }
    
  • 3

    包括 catch 在内的简单测试也表明如果 system.exit(0) 没有抛出安全异常,它将是最后执行的语句( catchfinally 根本不执行) .

    如果 system.exit(0) 确实抛出安全异常,则执行 catchfinally 语句 . 如果 catchfinally 都包含 system.exit() 语句,则仅执行这些 system.exit() 语句之前的语句 .

    在上面描述的两种情况中,如果 try 代码属于另一个方法调用的方法,则被调用的方法不返回 .

    更多细节here(个人博客) .

  • 3

    其他答案已经涵盖了 catchfinally 块如果 System.exit 在不抛出 SecurityException 的情况下退出JVM时如何运行,但它们没有显示"try-with-resources"块中对资源的影响:它们是否已关闭?

    根据JLS, Section 14.20.3.2

    翻译的效果是将资源规范放在try语句的“内部” . 这允许扩展的try-with-resources语句的catch子句捕获由于自动初始化或关闭任何资源而导致的异常 . 此外,所有资源将在执行finally块时关闭(或尝试关闭),以符合finally关键字的意图 .

    也就是说,在运行 catchfinally 块之前,资源将是 close d . 即使 catchfinally 没有运行,如果它们以某种方式 close d怎么办?

    这里有一些代码来证明“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();
        }
    }
    

    然后我在try-with-resources语句中设置了调用 System.exit 的测试用例 .

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

    输出:

    试试看

    因此,不仅 catchfinally 块不运行,如果 System.exit 成功,"try-with-resources"语句将无法获得 close 其资源的机会 .

  • 70

    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块 . 该程序将在System.exit()语句后终止 .

  • 0

    如果您认为这种行为有问题,并且您需要对 System.exit 调用进行精细控制,那么您唯一能做的就是将System.exit功能包装在您自己的逻辑中 . 如果我们这样做,我们可以最终执行块执行并将资源关闭作为退出流程的一部分 .

    我正在考虑的是在我自己的静态方法中包装 System.exit 调用和功能 . 在我的 exit 实现中,我将抛出 ThrowableError 的自定义子类,并使用 Thread.setDefaultUncaughtExceptionHandler 实现自定义Uncaught异常处理程序来处理该异常 . 因此我的代码变成:

    //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并断言这是write类型 . 例如,我们的业务逻辑包装器的测试可能如下所示:

    //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*/ } . 对于具有自定义执行上下文的库,需要对其进行改进以了解此异常 .

    上 balancer ,这对我来说似乎是一个很好的策略 . 这里有其他人这么认为吗?

  • 7
    • 在下面的示例中,如果 System.exit(0) 在异常行之前,程序将正常终止,因此FINALLY将不会执行 .

    • 如果 System.exix(0) 是try块的最后一行,这里我们有2个场景

    • 当出现异常时,则执行finally块

    • 当异常不存在时,则不执行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
    

相关问题