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

我知道在try / catch / finally块中返回的头痛 – 即使try或catch块中的返回应该是执行的,finally中的返回总是返回方法的情况。

但是,是否也适用于System.exit()? 例如,如果我有一个try块:

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

如果没有例外,哪个System.exit()将被调用? 如果出口是一个返回语句,那么System.exit(1)将始终(?)被调用。 但是,我不确定退出的行为是否与退货不同。

代码是在一个极端的情况下,即使不是不可能重现,也是非常困难的,所以我不能写一个unit testing。 我今天晚些时候会试着做一个实验,如果我有几分钟的空闲时间,但是我还是很好奇的,也许有人会知道答案,可以在之前提供,或者我无法运行实验。

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

简单的testing,包括catch也显示如果system.exit(0)没有抛出一个安全exception,它将是最后一个被执行的语句( catch并且finally根本不被执行)。

如果system.exit(0)确实会抛出一个安全exception, catchfinally语句将被执行。 如果两者都catchfinally包含system.exit()语句,则只执行这些system.exit()语句之前的语句。

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

更多细节在这里 (个人博客)。

终于块将被执行,不pipe是什么….即使尝试块抛出任何可抛出(exception或错误)…..

只有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()语句后终止。

其他答案已经介绍了如果System.exit在不抛出SecurityException情况下退出JVM而catchfinally块不运行,但是它们不显示在资源的“try-with-resources”块中会发生什么情况:它们是closures?

根据JLS,第14.20.3.2节 :

翻译的效果是将资源规范放在try语句的“内部”。 这允许扩展try-with-resources语句的catch子句由于自动初始化或closures任何资源而捕获exception。

而且,在执行finally块时,所有资源都将被closures(或试图closures),这与finally关键字的意图保持一致。

也就是说,资源将在catchfinally块运行之前close 。 如果他们以某种方式close即使被catchfinally不跑?

这里有一些代码来演示“try-with-resources”语句中的资源也没有closures。

我使用一个简单的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的testing用例。

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

输出:

在尝试

因此,如果System.exit成功, catchfinally块不会运行,那么“try-with-resources”语句将不会获得close其资源的机会。

  1. 在下面的例子中,如果System.exit(0)在exception行之前,程序将正常终止,所以FINALLY将不会执行。

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

    • 当存在exception时,finally块将被执行
    • 当exception不存在时,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调用进行良好的控制,那么您唯一可以做的就是将System.exitfunction包装在自己的逻辑中。 如果我们这样做,我们可以终止块执行,并获得资源作为我们的退出stream程的一部分。

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

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

这个策略增加了使这个逻辑可testing的“好处”:为了testing包含我们的“主stream”的方法实际上是否要求系统退出,我们所要做的就是捕获一个throwable并断言是写types。 例如,我们的业务逻辑包装testing可能如下所示:

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

这个策略的主要缺点是它是一种从exceptionstream程中获取function的方式,这往往会带来意想不到的后果。 在这种情况下,最明显的一点就是,你所写的任何地方try { ... } catch(Throwable ex){ /*doesnt rethrow*/ }将不得不被更新。 在具有自定义执行上下文的库的情况下,它们将需要被改装以理解这个例外。

总而言之,这对我来说似乎是一个很好的策略。 这里有其他人是这么想吗?