Java 不可达语句:while true vs if true

Java 不可达语句:while true vs if true,java,javac,unreachable-code,Java,Javac,Unreachable Code,我应该如何理解这种Java编译器行为 while (true) return; System.out.println("I love Java"); // Err: unreachable statement if (true) return; System.out.println("I hate Java"); // OK. 谢谢 编辑: 几分钟后我发现了要点: 在第一种情况下,编译器由于无限循环而抛出错误。在这两种情况下,编译器都不会考虑后续语句中的代码 编辑II: 现在让我对javac

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

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

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

编辑:

几分钟后我发现了要点:

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

编辑II:

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

    if (true) return; // Correct
}
    while (true) return; // Correct
}
看起来javac知道两个循环中都有什么,如果是结果, 但是,当您编写另一个命令(如第一个示例中所示)时,您会得到非等效的行为(看起来javac忘记了loop/if中的内容)

公共静态最终编辑III: 作为回答的结果,我可能会说(希望是正确的): 如果(arg){…;return;}和
而(arg){…;return;}
的表达式在语义和语法(字节码)上都是等价的,那么Java iff
argv
是非常量(或实际上是最终类型)表达式。如果
argv
为常量,则表达式字节码(和行为)可能不同

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

在java中可访问语句时有非常严格的规则。这些规则设计为易于评估,而不是100%准确。它应该可以防止基本的编程错误。为了解释java中的可达性,您仅限于这些规则,“公共逻辑”不适用

下面是Java语言规范中的规则

if-then语句在可到达时可以正常完成

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

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

  • while语句是可访问的,条件表达式不是值为true的常量表达式(§15.28)

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

条件是一个常量表达式“true”,没有中断。因此,根据以下情况,它不会正常完成:

除了对while、do和条件表达式具有常数值true的语句进行特殊处理外,在流分析中不考虑表达式的值


如果您稍微更改代码(删除常量表达式),这样就不会触发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,您这样比较的目的是什么?Java不是低级语言,也就是说编译器不是机器语言编译器。你想要的确切答案是什么,或者你想问的确切问题是什么?另外,考虑到目前可用的虚拟机和编译器的数量,在我看来,这是一个非常通用且信息量较少的问题。@Siddharttyagi这是为我的考试做准备,我希望在考试中回答写在论文上的类似问题。¯_(ツ)_/“@marek094只有在语法正确的情况下,您才有合理的语义。任何可编译代码可能都没有语义意图,但没有实际的语义。是的,我阅读了常量表达式大小写的字节码,其差异非常模糊。@marek094因为您没有提供常量表达式大小写的可编译示例,所以很难理解。”比较。在我的测试中,它会产生“noops”,周围有不同的绒毛。你是对的,我指的是我问题中的第二个例子。我知道,情况不一样,但也很有趣。我记得看到了这一点的基本原理。基本思想是你可以有
if(编译开关)return;A
并让程序正确编译,而不管开关的值如何。