为啥即使在 System.exit(0) 之后也需要返回;
Posted
技术标签:
【中文标题】为啥即使在 System.exit(0) 之后也需要返回;【英文标题】:Why is return needed even after System.exit(0);为什么即使在 System.exit(0) 之后也需要返回; 【发布时间】:2012-07-14 06:57:26 【问题描述】:考虑这个函数:
public boolean foo()
System.exit(1);
//The lines beyond this will not be read
int bar = 1; //L1
//But the return statement is required for syntactically correct code
return false; //L2
//error here for unreachable code
//int unreachable = 3; //L3
有人可以解释一下为什么 L1 和 L2 明显不可访问不会发出警告,但 L3 会发出警告。
【问题讨论】:
哪个编译器给出了警告? @ThorbjørnRavnAndersen 在 Eclipse 中使用 java 编译器 @NitinChhajer,你在使用 Eclipse 吗? 没有人阻止您将System.exit()
更改为 NOP。现在,它可以抛出安全异常/或 ThreadDeath。
【参考方案1】:
编译器检查某些代码是否仅针对 return 关键字是可访问的(通常还有 throw 和 break(在循环的情况下))。对于编译器来说exit方法调用只是另一个调用,它不知道它的含义,所以它不知道后面的代码永远不会到达。
【讨论】:
【参考方案2】:因为就编译器而言,System.exit()
只是另一个方法调用。
它所做的是结束进程的事实只能从实现中找到(这是本机代码,而不是它有任何区别)。
如果你必须把System.exit()
放在你的代码中(通常最好避免它,除非你想返回一个不是0的代码),它应该真正放在一个返回void
,main()
的方法中例如。这样更好。
关于可达性,解释是一样的:return
是Java语言的一个关键字,所以IDE使用的编译器或者解析器可以判断出return
语句后面的代码理论上是不可能的执行。这些规则定义为here。
【讨论】:
事实上,您甚至可以调用另一个方法foo()
,而该方法又调用System.exit(..)
,这样我们就知道foo()
永远不会返回,但编译器无法检查所有可能的情况。跨度>
或者你甚至可以有自己的本地方法来做同样的事情。
是的,通常不需要致电System.exti()
。只需从main
方法返回即可退出程序。
不,应该始终使用 System.exit()! 如果你只是让程序自然死亡,你会每隔一段时间得到这个错误输出(花了我在 Windows 上尝试了不到 10 次,Java 1.8.0_74):“错误:JDWP 无法获取 JNI 1.2 环境,jvm->GetEnv() 返回码 = -2 [等]”这是一个 known Java 错误。此外,一些 Swing 组件(我认为例如文件对话框。)一旦使用,就可以防止程序永远自然死亡。参见例如here。
" 它应该在一个返回 void 的方法中,例如 main()。这样更好。" - 如果 Guice 出现严重的 ProvisionException,我需要停止应用程序怎么办?在这种情况下是否值得使用 ThrowingProviders(这并不容易,因为我不能像这样导入新的 libe)?还是你有更好的主意?【参考方案3】:
Java 编译器对System.exit
一无所知。就它而言,它只是一种方法 - 所以语句的结尾是可以到达的。
您说L1
和L2
“明显无法访问”,但那只是,因为您知道System.exit
的作用。该语言不知道 - 而它确实知道return
语句的作用,因此它知道L3
确实不可访问。
我有时认为能够声明一个方法不仅仅是void
,而是从不正常终止-它从不只是返回(尽管它可能会抛出异常)会很有用.然后,编译器将能够使用该信息使任何调用表达式的结尾无法访问,从而防止此类事情成为问题。然而,这只是我对语言设计的梦想——Java 没有有类似的东西,编译器“知道”特定的 JRE 方法永远不会正常返回是一个非常糟糕的主意,当这个概念不能直接用语言表达。
相反,编译器受section 14.21 of the JLS的规则约束,包括:
如果非空块不是 switch 块,则该块是可访问的,则该块中的第一条语句是可访问的。 如果 S 之前的语句可以正常完成,则非空块中不是 switch 块的所有其他语句 S 都是可访问的。
...
表达式语句只要可达,就可以正常完成。
(方法调用是一个表达式语句。)
然后从第 8.4.7 节开始:
如果方法被声明为具有返回类型,则如果方法的主体可以正常完成,则会发生编译时错误(第 14.1 节)。
在 14.1 中:
除非另有说明,否则如果它评估的所有表达式和它执行的所有子语句都正常完成,则该语句将正常完成。
所以对于编译器来说System.exit()
的调用可以正常完成,也就是说foo
方法的主体可以正常完成,从而导致错误。
【讨论】:
【参考方案4】:从语言的角度来看,只有两种方法可以逃脱当前范围:return
和 throw
。方法调用永远不会被视为相同的方式,即使它们由唯一的代码行组成:
void method()
throw new RuntimeException();
更多。理论上,任何方法调用都可能导致RuntimeException
被抛出。在这种情况下,编译器可能应该警告您绝对任何不在try
块内的方法调用:
...
method(); // WARNING: may throw in theory
anotherMethod(); // WARNING: possible unreachable code, also may throw
anotherMethod2(); // WARNING: possible unreachable code, also may throw
// etc
...
对于你的问题逻辑是一样的。
【讨论】:
【参考方案5】:我:“可以在 return 语句之后执行任何操作吗?” Java:“没有。”
我:“调用 System.exit 后可以执行任何操作吗?” Java:“那是一种方法,我不知道它做了什么——它不是保留关键字,据我所知它不会影响程序流程”(它甚至可能不起作用(我不知道是否退出可以抛出异常(或它的未来变体)))
【讨论】:
【参考方案6】:我知道这没有意义,当您调用方法时,Java 编译器仍然是这样工作的。 编译器此时不知道 Sytem.exist 做了什么(从这个意义上说,为什么 rt.jar 与您编译的其他 jar 不同?)。 例如,这与下一段代码形成对比 -
public int test()
throw new NullPointerException("aaaa");
编译器可以判断总是抛出异常,因此不需要返回。
【讨论】:
【参考方案7】:如果一个方法被声明为返回一个非空值,那么它必须在某处包含一个return
语句,即使它从未到达过(就像问题中的代码一样)。
从编译器的角度来看,System.exit()
只是另一个方法调用,没有什么特别之处,表明程序一到达就结束。只有你,作为程序员,知道这个事实——但这是编译器不知道的。
关于您问题的第二部分 - 在方法内的代码块中的 return
语句之后没有任何内容,因为这将始终是无法访问的代码。这就解释了为什么编译器会抱怨 L3 行。
【讨论】:
【参考方案8】:静态代码分析工具可能没有考虑到应用程序正在终止。
【讨论】:
以上是关于为啥即使在 System.exit(0) 之后也需要返回;的主要内容,如果未能解决你的问题,请参考以下文章
System.exit(0) 导致 Activity 在应用启动时快速退出