87

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

该代码处于极端情况下,即使不是不可能,也很难重现,因此我无法编写单元测试。如果我有几分钟的空闲时间,我将在今天晚些时候尝试运行一个实验,但我还是很好奇,也许 SO 上的某个人知道答案并且可以在我无法运行之前提供它实验。

4

6 回答 6

85

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

}
于 2009-09-11T13:45:31.617 回答
11

简单的测试catch也表明如果system.exit(0)不抛出安全异常,它将是最后执行的语句(catch并且finally根本不执行)。

Ifsystem.exit(0)确实引发安全异常,catchfinally执行语句。如果 bothcatchfinallycontainssystem.exit()语句,则仅system.exit()执行这些语句之前的语句。

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

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

于 2012-07-28T20:26:33.663 回答
8

其他答案涵盖了如果退出 JVM 而不抛出 a ,则catchandfinally块如何不运行,但它们没有显示资源的“try-with-resources”块中发生的情况:它们是否关闭?System.exitSecurityException

根据JLS,第 14.20.3.2 节

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

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

也就是说,资源将在 a或块运行close之前被 d 。如果他们以某种方式被 d 即使并且不运行怎么办?catchfinallyclosecatchfinally

下面是一些代码来演示“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!");
    }
}

输出:

在尝试

因此,不仅 docatch和块不运行,如果成功,“try-with-resources”语句也不会获得其资源finally的机会。closeSystem.exit

于 2015-11-20T20:00:23.173 回答
4

finally 块无论如何都会被执行......即使 try 块抛出任何可抛出的(异常或错误)......

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

于 2014-07-06T08:50:03.060 回答
3

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

我正在考虑做的是将System.exit调用和功能包装在我自己的静态方法中。在我的实现中,exit我将抛出 or 的自定义子类ThrowableError并实现自定义 Uncaught 异常处理程序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 并断言它是 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*/ }都必须更新。对于具有自定义执行上下文的库,需要对其进行改造以理解此异常。

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

于 2016-09-19T17:46:05.747 回答
0
  1. 在下面的示例中,如果System.exit(0)在异常行之前,程序将正常终止,因此 FINALLY 不会执行。

  2. 如果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
于 2015-08-15T05:17:10.940 回答