【问题标题】:Unreachable statement: while true vs if true [duplicate]无法访问的语句:while true vs if true [重复]
【发布时间】:2017-02-11 12:55:24
【问题描述】:

我应该如何理解这种 Java 编译器行为?

while (true) return;
System.out.println("I love Java");
// Err: unreachable statement

if (true) return;
System.out.println("I hate Java");
// OK.

谢谢。

编辑:

几分钟后我发现了重点:

在第一种情况下,由于无限循环,编译器会抛出错误。在这两种情况下,编译器都不会考虑语句 consequent 中的代码。

编辑二:

javac 现在让我印象深刻的是:

    if (true) return; // Correct
}
    while (true) return; // Correct
}

看起来 javac 知道两个循环内部的内容,如果结果, 但是当您编写另一个命令时(如第一个示例中所示),您会得到非等效行为(看起来 javac 忘记了循环/if 中的内容)。

公共静态最终编辑三: 作为这个答案的结果,我可能会说(希望是正确的): 对于 Java,if (arg) { ...; return;}while (arg) { ...; return;} 在语义和句法上(在字节码中)是等价的,如果 argv 是非常量(或有效的最终类型)表达式。如果argv 是常量表达式,字节码(和行为)可能会有所不同。

免责声明 这个问题不是关于无法到达的语句,而是对逻辑等价表达式的不同处理,例如while true returnif true return

【问题讨论】:

  • 这是一个“如果我写了疯狂的代码,编译器也可能会疯狂”的案例。
  • @Andremoniy 不。请仔细阅读。
  • @marek094,您进行这种比较的目的是什么? Java 不是低级语言,可以说编译器不是机器语言编译器。您正在寻找的确切答案是什么,或者您打算问的确切问题是什么?此外,考虑到当今可用的虚拟机和编译器的数量,在我看来,可以说是非常通用且信息量较少的问题。
  • @SiddharthTyagi 这是为我的考试做准备,我应该回答写在纸上的类似问题。 ¯_(ツ)_/¯
  • @marek094 如果语法正确,您只有合理的语义。没有可编译的代码可能有语义的意图,但没有实际的语义。

标签: java javac unreachable-code


【解决方案1】:

当语句在 java 中可达时,有相当严格的规则。这些规则旨在易于评估,而不是 100% 准确。它应该防止基本的编程错误。要推断 Java 中的可达性,您必须遵守这些规则,“通用逻辑”不适用。

以下是 Java 语言规范 14.21. Unreachable Statements 中的规则

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

因此,如果没有 else,if-then 之后的语句总是可以访问的

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

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

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

条件是一个常量表达式“真”,没有中断。因此它确实无法正常完成

【讨论】:

  • 我记得看到了这样做的理由。基本思想是,您可以拥有if(COMPILATION_SWITCH) return; A,并且无论开关的值如何,都可以正确编译程序。
【解决方案2】:

根据docs

除了特殊处理while、do和条件表达式为真值的语句外,流分析中不考虑表达式的值。

【讨论】:

    【解决方案3】:

    如果您稍微更改代码(删除常量表达式),那么它不会触发 javac 可达性,它实际上会为两者生成相同的字节码。

    static boolean flag = true;
    
    static void twhile(){
        while (flag) return;
        System.out.println("Java");
    }
    static void tif(){
        if (flag) return;
        System.out.println("Java");
    }
    

    生成的字节码:

      static void twhile();
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=2, locals=0, args_size=0
          StackMap locals:
          StackMap stack:
         0: getstatic     #10                 // Field flag:Z
         3: ifeq          7
         6: return
          StackMap locals:
          StackMap stack:
         7: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #26                 // String Java
        12: invokevirtual #28                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: return
          LineNumberTable:
        line 8: 0
        line 9: 7
        line 10: 15
          LocalVariableTable:
        Start  Length  Slot  Name   Signature
          StackMapTable: number_of_entries = 1
        frame_type = 7 /* same */
    
      static void tif();
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=2, locals=0, args_size=0
          StackMap locals:
          StackMap stack:
         0: getstatic     #10                 // Field flag:Z
         3: ifeq          7
         6: return
          StackMap locals:
          StackMap stack:
         7: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #26                 // String Java
        12: invokevirtual #28                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: return
          LineNumberTable:
        line 12: 0
        line 13: 7
        line 14: 15
          LocalVariableTable:
        Start  Length  Slot  Name   Signature
          StackMapTable: number_of_entries = 1
        frame_type = 7 /* same */
    

    【讨论】:

    • 是的,我阅读了常量表达式情况的字节码,它的不同之处非常模糊。
    • @marek094 因为您没有为难以比较的常量表达式情况提供可编译示例。在我的测试中,它会创建“noops”,周围有不同的绒毛。
    • 你说得对,我的意思是我的问题中的第二个例子。我知道,比这不一样的情况还有趣。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    • 2013-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多