为什么Java编译器不能为不可达的语句产生无法访问的语句错误?

如果我试图编译

for(;;) { } System.out.println("End"); 

Java编译器产生一个错误,说Unreachable statement 。 但是,如果我添加另一个“ 无法达到 ”(根据我) break语句,并使其:

 for(;;) { if(false) break; } System.out.println("End"); 

它编译。 为什么它不会产生错误? Java试图说两个错误是正确的吗?

该行为在不可达语句的JLS描述中定义:

如果if-then语句可达,那么then语句是可达的。

所以编译器确定then语句( break; )是否可达,而不pipeif中的条件如何。

还有一点,我的重点是:

如果至less满足下列条件之一,则基本for语句可以正常完成:

  • for语句是可访问的,有一个条件expression式,条件expression式不是一个值为true的常量expression式(第15.28节)。
  • 有一个可到达的break语句退出for语句。

所以for可以正常完成,因为then语句包含break 。 正如你注意到的,如果你用return取代break ,这将是行不通的。


理由在本节的结尾解释。 实质上, if有一个特殊的处理允许构造如:

 if(DEBUG) { ... } 

DEBUG可能是一个编译时间常量。

正如我在类似问题的答案中所解释的那样,具体结构if(compile-time-false)作为一个明确的后门免于不可达规则。 在这种情况下,编译器会将您的break视为可访问的。

从JLS

如果至less满足以下条件之一,则if-then语句可以正常完成:

> if-then语句是可达的,条件expression式不是一个常量expression式,其值为true。

> then语句可以正常完成。

所以if(false)被允许。

这种“有条件编译”的能力对二进制兼容性有着重要的影响和关系。 如果一组使用这种“标志”variables的类被编译,并且省略了条件代码,那么仅仅分发包含该标志定义的类或接口的新版本是不够的。 因此,标志值的改变与预先存在的二进制文件不是二进制兼容的。 (这种不兼容性还有其他一些原因,例如在switch语句中使用case标签中的常量;)

基本上,通过静态分析程序来检测不可达代码而不实际运行代码。 而条件将在运行时检查。 所以,当这个分析发生的时候,它实际上并没有考虑到这个条件,而只是检查了这个break; 通过if访问( 可达 )。

Java不检测所有不可达语句的核心原因是通常不可能回答代码是否可访问。 这是由于停止问题在图灵机上是不可判定的。

所以,很显然,所有无法访问的语句都不能被检测到,但为什么不尝试评估条件呢? 现在想象一下,使用的条件不仅仅是false而且像~x == x 。 例如,对于每个int x ( source ),所有这些语句都将显示为true

  System.out.println((x + x & 1) == 0); System.out.println((x + -x & 1) == 0); System.out.println((-x & 1) == (x & 1)); System.out.println(((-x ^ x) & 1) == 0); System.out.println((x * 0x80 & 0x56) == 0); System.out.println((x << 1 ^ 0x1765) != 0); 

这些陈述可能相当复杂; 解决它们需要时间。 这将大大增加构build时间,毕竟,它不会检测到所有无法访问的语句。 编译器的devise是为了花费一些努力,但不花费太多的时间。

唯一的问题是:在哪里停止解决条件? 其原因似乎没有math上的正当理由,并以使用场景为基础。 你的具体情况的理由由JLS-14.21给出