为啥即使在 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的代码),它应该真正放在一个返回voidmain()的方法中例如。这样更好。

关于可达性,解释是一样的: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(这并不容易,因为我不能像这样导入新的 l​​ibe)?还是你有更好的主意?【参考方案3】:

Java 编译器对System.exit 一无所知。就它而言,它只是一种方法 - 所以语句的结尾是可以到达的。

L1L2“明显无法访问”,但那只是,因为您知道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】:

从语言的角度来看,只有两种方法可以逃脱当前范围:returnthrow。方法调用永远不会被视为相同的方式,即使它们由唯一的代码行组成:

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() 之后线程仍在运行

在 System.exit(0) 处解除绑定

关于JUnit4无法支持多线程测试的解决方法

System.exit(0) 导致 Activity 在应用启动时快速退出

System.exit(0) 之间的区别;和 Platform.exit();? [复制]

在 java 中使用 System.exit(0) 退出程序