Java 的 System.exit() 如何与 try/catch/finally 块一起工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1410951/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does Java's System.exit() work with try/catch/finally blocks?
提问by Thomas Owens
I'm aware of headaches that involve returning in try/catch/finally blocks - cases where the return in the finally is always the return for the method, even if a return in a try or catch block should be the one executed.
我知道涉及在 try/catch/finally 块中返回的头痛 - 在 finally 中的返回始终是方法的返回的情况下,即使应执行 try 或 catch 块中的返回。
However, does the same apply to System.exit()? For example, if I have a try block:
但是,这同样适用于 System.exit() 吗?例如,如果我有一个 try 块:
try {
//Code
System.exit(0)
}
catch (Exception ex) {
//Log the exception
}
finally {
System.exit(1)
}
If there are no exceptions, which System.exit() will be called? If the exit was a return statement, then the line System.exit(1) would always (?) be called. However, I'm not sure if exit behaves differently than return.
如果没有异常,将调用哪个 System.exit()?如果退出是一个 return 语句,那么 System.exit(1) 行将始终 (?) 被调用。但是,我不确定 exit 的行为是否与 return 不同。
The code is in an extreme case that is very difficult, if not impossible, to reproduce, so I can't write a unit test. I'm going to try to run an experiment later today, if I get a few free minutes, but I'm curious anyway, and perhaps someone on SO knows the answer and can provide it before or in case I can't run an experiment.
代码处于极端情况下,即使不是不可能,也很难重现,因此我无法编写单元测试。如果我有几分钟的空闲时间,我将尝试在今天晚些时候运行一个实验,但无论如何我很好奇,也许 SO 上的某个人知道答案并且可以在我无法运行之前或万一提供它实验。
采纳答案by erickson
No. System.exit(0)
doesn't return, and the finally block is not executed.
No.System.exit(0)
不会返回,并且 finally 块不会被执行。
System.exit(int)
can throw a SecurityException
. If that happens, the finally block willbe executed. And since the same principal is calling the same method from the same code base, another SecurityException
is likely to be thrown from the second call.
System.exit(int)
可以扔一个SecurityException
。如果发生这种情况,finally 块将被执行。并且由于相同的主体从相同的代码库调用相同的方法SecurityException
,因此第二次调用可能会抛出另一个方法。
Here's an example of the second case:
这是第二种情况的示例:
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);
}
}
}
回答by Jér?me Verstrynge
Simple tests including catch
too reveal that if system.exit(0)
does not throw a security exception, it will be the last executed statement (catch
and finally
are not executed at all).
包括catch
too在内的简单测试表明,如果system.exit(0)
不抛出安全异常,它将是最后执行的语句(catch
并且finally
根本不执行)。
If system.exit(0)
does throw a security exception, catch
and finally
statements are executed. If both catch
and finally
contain system.exit()
statements, only statements preceding these system.exit()
statements are executed.
如果system.exit(0)
不抛出一个安全异常,catch
和finally
语句执行。如果catch
和finally
都包含system.exit()
语句,则只system.exit()
执行这些语句之前的语句。
In both cases decribed above, if the try
code belongs to a method called by another method, the called method does not return.
在上面描述的两种情况下,如果try
代码属于另一个方法调用的方法,则被调用的方法不会返回。
More details here(personal blog).
更多细节在这里(个人博客)。
回答by Chinmoy
finally block will be executed no matter what....even if try block throws any throwable(exception or error).....
无论如何,finally 块都会被执行....即使 try 块抛出任何可抛出的(异常或错误).....
only case finally block does not execute...is when we call System.exit() method..
唯一的情况是 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!!!");
}
It will not execute finally block. The program will be terminated after System.exit() statement.
它不会执行 finally 块。程序将在 System.exit() 语句后终止。
回答by honey
In example below, if
System.exit(0)
is before the exception line, the program will be terminated normally, so the FINALLY will not execute.If the
System.exix(0)
is the last line of the try block, here we have 2 scenarios- when exception is present then finally block is executed
- when exception is not present then finally block is not executed
在下面的例子中,如果
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
回答by rgettman
Other answers have covered how the catch
and finally
blocks don't run if System.exit
exits the JVM without throwing a SecurityException
, but they don't show what happens in a "try-with-resources" block to the resources: Are they closed?
其他答案涵盖了如果退出 JVM 而不抛出 a ,catch
andfinally
块如何不运行,但它们没有显示资源的“try-with-resources”块中会发生什么:它们是否已关闭?System.exit
SecurityException
According to the JLS, Section 14.20.3.2:
The effect of the translation is to put the resource specification "inside" the try statement. This allows a catch clause of an extended try-with-resources statement to catch an exception due to the automatic initialization or closing of any resource.
Furthermore, all resources will have been closed (or attempted to be closed) by the time the finally block is executed, in keeping with the intent of the finally keyword.
翻译的效果是将资源规范“放在”try 语句中。这允许扩展 try-with-resources 语句的 catch 子句捕获由于任何资源的自动初始化或关闭而引起的异常。
此外,在执行 finally 块时,所有资源都将已关闭(或试图关闭),这与 finally 关键字的意图保持一致。
That is, resources will be close
d before a catch
or finally
block runs. What if they are close
d somehow even if catch
and finally
don't run?
也就是说,资源将close
在 acatch
或finally
块运行之前被删除。如果它们close
以某种方式运行catch
并且finally
不运行怎么办?
Here's some code to demonstrate that the resources in a "try-with-resources" statement aren't closed either.
下面是一些代码来证明“try-with-resources”语句中的资源也没有关闭。
I use a simple subclass of BufferedReader
that prints a statement before calling super.close
.
我使用了一个简单的子类,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();
}
}
Then I set up the test case of calling System.exit
in the try-with-resources statement.
然后我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!");
}
}
Output:
输出:
In try
在试
Therefore, not only do catch
and finally
blocks not run, a "try-with-resources" statement won't get a chance to close
its resources if System.exit
succeeds.
因此,不仅 docatch
和finally
blocks 不运行,close
如果System.exit
成功,“try-with-resources”语句也不会获得其资源的机会。
回答by Groostav
If you consider this behaviour problematic, and you need fine control over your System.exit
calls, then the only thing you can do is wrap the System.exit functionality in your own logic. If we do that, we can get finally blocks executed and get resources closed as part of our exit flow.
如果您认为这种行为有问题,并且需要对System.exit
调用进行精细控制,那么您唯一可以做的就是将 System.exit 功能包装在您自己的逻辑中。如果我们这样做,我们可以执行 finally 块并关闭资源作为退出流程的一部分。
What I'm considering doing is wrapping the System.exit
call & functionality in my own static method. In my implementation of exit
I would throw a custom subclass of Throwable
or Error
, and implement a custom Uncaught exception handler with Thread.setDefaultUncaughtExceptionHandler
to handle that exception. Thus my code becomes:
我正在考虑做的是将System.exit
调用和功能包装在我自己的静态方法中。在我的实现中,exit
我会抛出Throwable
or 的一个自定义子类Error
,并实现一个自定义的 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;
}
}
This strategy has the added "benefit" of making this logic testable: to test that the method containing our "main flow" actually requests the system to exit, all we have to do is catch a throwable and assert that is the write type. For example, a test for our business logic wrapper might look like:
此策略具有使此逻辑可测试的附加“好处”:要测试包含我们的“主流”的方法是否实际请求系统退出,我们所要做的就是捕获一个 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)
The major downside to this strategy is that it is a way of getting functionality out of exception flow, which often has unintended consequences. The most obvious one, in this case, is that anywhere you've written try { ... } catch(Throwable ex){ /*doesnt rethrow*/ }
will have to be updated. In the case of libraries that have custom execution contexts, they will need to be retrofitted to also understand this exception.
这种策略的主要缺点是它是一种从异常流中获取功能的方法,这通常会产生意想不到的后果。在这种情况下,最明显的一点是,您编写的任何地方try { ... } catch(Throwable ex){ /*doesnt rethrow*/ }
都必须更新。对于具有自定义执行上下文的库,它们将需要进行改造以也理解此异常。
On balance, this seems like a good strategy to me. Does anybody else here think so?
总的来说,这对我来说似乎是一个很好的策略。这里还有人这么认为吗?