5

当在无限循环之后写了一些语句时,该语句就变成了无法访问的代码。例如:

for(;;) 
{
}
Sytem.out.println("Test-1"); //unreachable code

但我在这里面临一些困难。

看下面的两个代码片段:

代码片段1:

for(final int z=4;z<6;)
{
}
System.out.println("Test-2"); //unreachable code

在这里,最后一条语句一定是不可访问的,因为循环是无限的,并且输出符合预期。

代码片段2:

final int z=4;
for(;;)
{
    if(z<2)
        break;
}
System.out.println("Test-3");  //not unreachable

从概念上讲,上述代码中的 for 循环也是无限的,因为 z 是最终的并且if(z<2)仅在编译时确定。if 条件永远不会为真,循环也永远不会中断。但是,上面代码中的 Last 语句并非不可访问。

问题:

  1. 为什么会这样?

  2. 谁能告诉我我们可以查看代码是否无法访问的确切规则。

4

3 回答 3

3

http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.21中的关键词是:

如果由于无法访问而无法执行语句,则这是编译时错误。

本节专门对“可达”一词进行精确解释。这个想法是,从包含语句的构造函数、方法、实例初始化程序或静态初始化程序的开头到语句本身,必须有一些可能的执行路径。该分析考虑了语句的结构。除了特殊处理while、do和条件表达式为真值的语句外,流分析中不考虑表达式的值。

因此,编译器不会z<2在您的if()语句中评估,也不知道它永远不会评估为true.

就 Java 规范而言,这定义了无法访问的代码。编译器遵守规范很重要,因为更改规则可能会使用于编译的代码无法编译。

然而,编译器可以自由地给出警告而不是编译错误。

如果我在 Eclipse 中键入以下代码:

final int x = 0;
if(x == 1) {
    System.out.println("This never happens");
}

...我收到警告“死代码”。编译器知道无法访问代码 - 但它不能拒绝编译,因为根据 Java 规范,代码并不是正式的“无法访问”。

于 2014-06-12T10:06:32.543 回答
2

来自http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.21

14.21。无法访问的语句

如果由于无法访问而无法执行语句,则这是编译时错误。

本节专门对“可达”一词进行精确解释。这个想法是,从包含语句的构造函数、方法、实例初始化程序或静态初始化程序的开头到语句本身,必须有一些可能的执行路径。该分析考虑了语句的结构。除了特殊处理while、do和条件表达式为真值的语句外,流分析中不考虑表达式的值。

例如,Java 编译器将接受以下代码:

{ int n = 5; 而 (n > 7) k = 2; 尽管 n 的值在编译时是已知的,并且原则上它可以在编译时知道对 k 的赋值永远不会被执行。

本节中的规则定义了两个技术术语:

语句是否可达

语句是否可以正常完成

这里的定义允许一个语句只有在它可达时才能正常完成。

为了缩短对规则的描述,习惯上的缩写“iff”用于表示“当且仅当”。

如果在 break 目标内没有 try 语句的 try 块包含 break 语句,或者存在 try 语句的 try 块包含 break 语句并且这些 try 语句的所有 finally 子句都可以完成,则可到达的 break 语句退出语句一般。

此定义基于第 14.15 节中围绕“尝试转移控制权”的逻辑。

如果在 do 语句中没有 try 语句的 try 块包含 continue 语句,或者存在 try 语句的 try 块包含 continue 语句并且这些 try 语句的所有 finally 子句都可以完成,则 continue 语句继续 do 语句一般。

规则如下:

作为构造函数、方法、实例初始化程序或静态初始化程序的主体的块是可访问的。

一个不是 switch 块的空块可以正常完成,只要它是可达的。

一个不是 switch 块的非空块可以正常完成,前提是它的最后一条语句可以正常完成。

非空块中不是 switch 块的第一条语句是可到达的,前提是该块是可到达的。

如果 S 之前的语句可以正常完成,则非空块中不是 switch 块的所有其他语句 S 都是可访问的。

本地类声明语句可以正常完成,如果它是可访问的。

一个局部变量声明语句可以正常完成,只要它是可达的。

一个空语句可以正常完成,只要它是可达的。

如果以下至少一项为真,则带标签的语句可以正常完成:

包含的语句可以正常完成。

有一个可到达的 break 语句退出带标签的语句。

如果标记的语句是可访问的,则包含的语句是可访问的。

一个表达式语句可以正常完成,只要它是可达的。

一个 if-then 语句可以正常完成,只要它是可达的。

如果 if-then 语句可达,则 then 语句可达。

如果 then 语句可以正常完成或 else 语句可以正常完成,则 if-then-else 语句可以正常完成。

如果 if-then-else 语句是可达的,则 then-语句是可达的。

如果 if-then-else 语句可达,则 else 语句可达。

这种对 if 语句的处理,无论它是否有 else 部分,都是相当不寻常的。基本原理在本节末尾给出。

一个 assert 语句可以正常完成,只要它是可达的。

如果以下至少一项为真,则 switch 语句可以正常完成:

开关块为空或仅包含开关标签。

switch 块中的最后一条语句可以正常完成。

在最后一个 switch 块语句组之后至少有一个 switch 标签。

开关块不包含默认标签。

有一个可到达的 break 语句退出 switch 语句。

一个 switch 块是可达的,如果它的 switch 语句是可达的。

switch 块中的语句是可访问的,当且仅当它的 switch 语句是可访问的并且以下至少一项为真时:

它带有案例或默认标签。

在 switch 块中它前面有一条语句,并且前面的语句可以正常完成。

当以下至少一项为真时,while 语句可以正常完成:

while 语句是可访问的,并且条件表达式不是值为 true 的常量表达式(第 15.28 节)。

有一个可到达的 break 语句退出 while 语句。

如果 while 语句可访问且条件表达式不是值为 false 的常量表达式,则包含的语句是可访问的。

如果以下至少一项为真,则 do 语句可以正常完成:

包含的语句可以正常完成,并且条件表达式不是值为 true 的常量表达式(第 15.28 节)。

do 语句包含一个不带标签的可达 continue 语句,do 语句是包含该 continue 语句的最里面的 while、do 或 for 语句,并且 continue 语句继续该 do 语句,并且条件表达式不是常量表达式值为真。

do 语句包含一个带有标签 L 的可达 continue 语句,do 语句有标签 L,并且 continue 语句继续该 do 语句,并且条件表达式不是值为 true 的常量表达式。

有一个可到达的 break 语句退出 do 语句。

如果 do 语句是可访问的,则包含的语句是可访问的。

如果以下至少一项为真,则基本 for 语句可以正常完成:

for 语句可达,有条件表达式,且条件表达式不是值为 true 的常量表达式(第 15.28 节)。

有一个可到达的 break 语句退出 for 语句。

如果 for 语句可访问且条件表达式不是值为 false 的常量表达式,则包含的语句是可访问的。

增强的 for 语句可以正常完成,前提是它是可访问的。

break、continue、return 或 throw 语句无法正常完成。

如果包含的语句可以正常完成,则同步语句可以正常完成。

如果同步语句是可访问的,则包含的语句是可访问的。

如果以下两个都为真,则 try 语句可以正常完成:

try 块可以正常完成,或者任何 catch 块都可以正常完成。

如果 try 语句有 finally 块,那么 finally 块可以正常完成。

当 try 语句可达时,try 块是可达的。

如果满足以下两个条件,则可以访问 catch 块 C:

C的参数类型要么是未经检查的异常类型,要么是Throwable;或者 try 块中的某些表达式或 throw 语句是可访问的,并且可以抛出一个已检查的异常,其类型可分配给 catch 子句 C 的参数。

如果包含它的最里面的语句是可达的,则表达式是可达的。

有关表达式的正常和突然完成,请参见第 15.6 节。

try 语句中没有更早的catch 块A,使得C 的参数类型与A 的参数类型相同或子类。

如果 catch 块可达,则 catch 块的 Block 是可达的。

如果存在 finally 块,则当 try 语句可访问时,它是可访问的。

于 2014-06-12T09:40:51.477 回答
0
for(final int z=4;z<6;)
{
}
System.out.println("Test-2"); //unreachable code --> z is final. Its value can't change. There is no way out of that loop



final int z=4;
for(;;)
{
  if(z<2)  // z is 4  . The compiler only knows the value of "z". It doesn't know what z<2 evaluates to.. So, it still thinks taht there is a chance of getting out of that loop.
break;
}
System.out.println("Test-3");  //So --> reachable code.
于 2014-06-12T09:36:54.970 回答