Eclipse中Java中无法访问代码错误与死代码警告?

Uri*_*Uri 53 java eclipse dead-code unreachable-code

有谁知道原因:

public void foo()
{
    System.out.println("Hello");
    return;
    System.out.println("World!");
}
Run Code Online (Sandbox Code Playgroud)

将被报告为Eclipse下的"无法访问的错误",但是

public void foo()
{
    System.out.println("Hello");
    if(true) return;
    System.out.println("World!");
}
Run Code Online (Sandbox Code Playgroud)

只触发"死代码"警告?

我能想到的唯一解释是Java编译器只标记第一个,而Eclipse中的一些额外分析计算出第二个.但是,如果是这种情况,为什么Java编译器不能在编译时弄清楚这种情况呢?

Java编译器不会在编译时弄清楚if(true)没有效果,从而产生基本相同的字节码吗?在什么时候应用可达代码分析?

我想一个更通用的方法来思考这个问题是:"什么时候应用可达代码分析"?在将第二个Java代码片段转换为最终字节码时,我确信在某些时候删除了"if(true)"运行时等效项,并且两个程序的表示变得相同.然后,Java编译器不会再次应用其可访问的代码分析吗?

Bal*_*usC 34

第一个没有编译(你得到一个错误),第二个编译(你刚收到警告).这就是区别.

至于为什么Eclipse会检测死代码,那就是集成开发工具带有内置编译器的便利性,与JDK相比,它可以更好地进行微调,以检测这种代码.

更新:JDK实际上消除了死代码.

public class Test {
    public void foo() {
        System.out.println("foo");
        if(true)return;
        System.out.println("foo");
    }
    public void bar() {
        System.out.println("bar");
        if(false)return;
        System.out.println("bar");
    }
}
Run Code Online (Sandbox Code Playgroud)

javap -c 说:

public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

public void foo();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc             #3; //String foo
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/StrV
   8:   return

public void bar();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc             #5; //String bar
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  ldc             #5; //String bar
   13:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16:  return

}

至于为什么它(Sun)没有给出警告,我不知道:)至少JDK编译器实际上内置了DCE(死代码消除).

  • Java语言规范特别允许将常量`if`表达式计算为"true"或"false",作为打开或关闭代码的方法,例如调试或诊断代码,使用"翻转开关".表达式"if(true)"和"if(DEBUGS)"属于这一类.因此,Eclipse和任何其他Java编译器都不允许*将它们视为错误.更重要的是,它特别要求在评估为"false"时删除这种常量保护代码. (5认同)
  • +1 java编译器不会为未执行的块生成字节码.如果表达式在运行时的计算结果为false,则仍会生成代码.不生成字节码的唯一时间是表达式在编译时的计算结果为false.至少那是我对它是如何工作的理解. (3认同)
  • 嘿Uri,你可以用"boolean x = true; if(x)"替换"if(true)".消除它并不是编译器的工作,因为例如,您可以在执行期间放置断点并更改x的值 (2认同)

Pau*_*and 25

根据Java语言规范,无法访问的代码是错误.

引用JLS:

我们的想法是,从构造函数,方法,实例初始化程序或包含语句本身的静态初始化程序开始,必须有一些可能的执行路径.分析考虑了陈述的结构.除了对while,do和for条件表达式具有常量值true的语句的特殊处理外,在流分析中不考虑表达式的值.

这意味着,if不考虑该块,因为如果您通过该if语句的其中一个路径,则可以达到最终的打印语句.如果您将代码更改为:

public void foo() {
    System.out.println("Hello");
    if (true)
        return;
    else
        return;
    System.out.println("World!");
}
Run Code Online (Sandbox Code Playgroud)

然后它突然不再编译,因为没有通过if语句的路径允许到达最后一行.

也就是说,不允许Java兼容的编译器编译您的第一个代码片段.进一步引用JLS:

例如,以下语句导致编译时错误:

while (false) { x=3; }
Run Code Online (Sandbox Code Playgroud)

因为声明x = 3; 无法到达; 但表面上类似的情况:

if (false) { x=3; }
Run Code Online (Sandbox Code Playgroud)

不会导致编译时错误.优化编译器可以实现语句x = 3; 将永远不会执行,并可能选择从生成的类文件中省略该语句的代码,但语句x = 3; 在此处指定的技术意义上,不被视为"无法到达".

关于死代码,Eclipse给出的第二个警告是由编译器生成的警告,根据JLS,它不是"无法访问",但实际上是.这是Eclipse提供的额外lint样式检查.这完全是可选的,并且通过使用Eclipse配置,可以禁用或转换为编译器错误而不是警告.

第二个块是"代码味道",if (false)通常放置块以禁用代码以进行调试,将其留下通常是偶然的,因此警告.

事实上,Eclipse会进行更高级的测试,以确定if语句的可能值,以确定是否可以采用这两种路径.例如,Eclipse也会在以下方法中抱怨死代码:

public void foo() {
    System.out.println("Hello");
    boolean bool = Random.nextBoolean();
    if (bool)
        return;
    if (bool || Random.nextBoolean())
      System.out.println("World!");
}
Run Code Online (Sandbox Code Playgroud)

它将为第二个if语句生成无法访问的代码,因为它可以说明bool只能false在代码中的这一点.在这么短的代码片段中,很明显两个if语句正在测试相同的东西,但是如果中间有10-15个代码行,它可能就不那么明显了.

总而言之,两者之间存在差异:一个是JLS禁止的,一个不是,但是Eclipse会将其检测为程序员的服务.

  • 哦,我明白了:特殊的处理是检测明显的无限循环.'如果'不是循环语句.(今天我有点慢......) (2认同)

use*_*421 14

这是为了允许一种有条件的编译.
这不是错误if,但编译器将标记错误while,do-whilefor.
还行吧:

if (true) return;    // or false
System.out.println("doing something");
Run Code Online (Sandbox Code Playgroud)

这是错误

while (true) {
}
System.out.println("unreachable");

while (false) {
    System.out.println("unreachable");
}

do {
} while (true);
System.out.println("unreachable");

for(;;) {
}
System.out.println("unreachable");
Run Code Online (Sandbox Code Playgroud)

JLS 14.21结尾处解释:无法访问的语句:

这种不同处理的基本原理是允许程序员定义"标志变量",例如:

 static final boolean DEBUG = false;
Run Code Online (Sandbox Code Playgroud)

然后编写如下代码:

   if (DEBUG) { x=3; }
Run Code Online (Sandbox Code Playgroud)

我们的想法是,应该可以将DEBUG的值从false更改为true或从true更改为false,然后正确编译代码而不对程序文本进行其他更改.

  • +1表示行为差异背后的基本原理. (2认同)