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

时间:2009-09-11 13:43:14

标签: java try-catch-finally system.exit

我知道涉及在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 个答案:

答案 0 :(得分:74)

没有。 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);
    }
  }

}

答案 1 :(得分:8)

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

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

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

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

答案 2 :(得分:4)

其他答案涵盖catchfinally阻止如果System.exit退出JVM而不抛出SecurityException而无法运行的方式,但他们不会#39} ; t显示"尝试使用资源"阻止资源:他们关闭了吗?

根据JLS, Section 14.20.3.2

  

翻译的效果是将资源规范放在内部" try语句。这允许扩展的try-with-resources语句的catch子句捕获由于自动初始化或关闭任何资源而导致的异常。

     

此外,所有资源都将在执行finally块时关闭(或尝试关闭),与finally关键字的意图保持一致。

也就是说,在closecatch块运行之前,资源将为finally d。即使closecatch不运行,如果他们finally某种情况怎么办?

这里提供了一些代码来证明资源中的资源"尝试使用资源"声明也没有结束。

我使用一个简单的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块不仅不会运行,而且#34;尝试使用资源"如果close成功,则声明无法获得System.exit资源。

答案 3 :(得分:3)

最终块将被执行无论什么....即使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()语句之后。

答案 4 :(得分:2)

如果您认为这种行为有问题,并且您需要对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并断言这是写类型。例如,我们的业务逻辑包装器的测试可能如下所示:

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

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

答案 5 :(得分:0)

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

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

    • 当出现异常时,则执行finally块
    • 当异常不存在时,则不执行最终阻止
  3. 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