代码之家  ›  专栏  ›  技术社区  ›  Thomas Owens

Java的System.exit()如何处理try/catch/finally块?[副本]

  •  90
  • Thomas Owens  · 技术社区  · 16 年前

    但是,这同样适用于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上的某个人知道答案,可以在我无法进行实验的情况下提供答案。

    6 回复  |  直到 16 年前
        1
  •  73
  •   erickson    16 年前

    号码 System.exit(0) 不返回,finally块不执行。

    System.exit(int) 可以扔a 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);
        }
      }
    
    }
    
        2
  •  7
  •   Jérôme Verstrynge    13 年前

    简单测试包括 catch 也透露,如果 system.exit(0) 抓住 finally 根本不执行)。

    如果 system.export(0) 确实引发了安全异常, 抓住 最终 语句被执行。如果两者都有 抓住 最终 包含 system.exit() 语句,仅限于这些语句之前的语句 system.exit()

    在上述两种情况下,如果 try 代码属于另一个方法调用的方法,被调用的方法不返回。

    更多详情 here (个人博客)。

        3
  •  4
  •   rgettman    10 年前

    catch finally 如果发生以下情况,则块不会运行 System.exit 退出JVM而不抛出 SecurityException ,但它们没有显示在资源的“尝试资源”块中会发生什么:它们是否关闭?

    根据 JLS, Section 14.20.3.2 :

    翻译的效果是将资源规范“放入”try语句中。这允许扩展trywithresources语句的catch子句捕获由于任何资源的自动初始化或关闭而导致的异常。

    此外,根据finally关键字的意图,在执行finally块时,所有资源都将被关闭(或试图关闭)。

    也就是说,资源将 close d在a之前 抓住 最终 块运行。如果他们是 关闭 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语句中。

    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 with resources”语句将没有机会 关闭 其资源如果 系统退出 成功。

        4
  •  3
  •   mezzodrinker    10 年前

    最后无论如何都会执行block。…即使try块抛出任何可抛出的(异常或错误)。....

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

    它最终不会执行block。程序将终止 在System.exit()语句之后。

        5
  •  2
  •   Groostav    9 年前

    如果你认为这种行为有问题,你需要对你的 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;
      }
    } 
    

    此策略还有一个额外的“好处”,即使此逻辑可测试:为了测试包含我们的“主流”的方法是否确实请求系统退出,我们所要做的就是捕获一个可抛出的对象并断言其为写入类型。例如,我们的业务逻辑包装器的测试可能如下:

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

    总的来说,在我看来,这是一个很好的策略。在座的其他人也这么认为吗?

        6
  •  0
  •   Rohit Gupta    10 年前
    1. 在下面的示例中,如果 System.exit(0) 如果在异常行之前,程序将正常终止,因此FINALY将不会执行。

    2. System.exix(0)

      • 当出现异常时,最终执行块
      • 当异常不存在时,最终块不会执行

    .

    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