Java中的无限循环

Lio*_*ion 82 java halting-problem

while在Java中查看以下无限循环.它会导致它下面的语句出现编译时错误.

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

System.out.println("while terminated"); //Unreachable statement - compiler-error.
Run Code Online (Sandbox Code Playgroud)

以下相同的无限while循环,但工作正常,并不会发出任何错误,我只是用布尔变量替换条件.

boolean b=true;

while(b) {
    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,循环后的语句显然无法访问,因为布尔变量b为true,编译器根本不会抱怨.为什么?


编辑:下面的版本while陷入了无限循环,但是对于它下面的语句没有发出编译器错误,即使if循环中的条件总是false因此,循环也永远不会返回并且可以由编译器在编译时本身.

while(true) {

    if(false) {
        break;
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.
Run Code Online (Sandbox Code Playgroud)
while(true) {

    if(false)  { //if true then also
        return;  //Replacing return with break fixes the following error.
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.
Run Code Online (Sandbox Code Playgroud)
while(true) {

    if(true) {
        System.out.println("inside if");
        return;
    }

    System.out.println("inside while"); //No error here.
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.
Run Code Online (Sandbox Code Playgroud)

编辑:if和相同的事情while.

if(false) {
    System.out.println("inside if"); //No error here.
}
Run Code Online (Sandbox Code Playgroud)
while(false) {
    System.out.println("inside while");
    // Compiler's complain - unreachable statement.
}
Run Code Online (Sandbox Code Playgroud)
while(true) {

    if(true) {
        System.out.println("inside if");
        break;
    }

    System.out.println("inside while"); //No error here.
}      
Run Code Online (Sandbox Code Playgroud)

以下版本while也陷入了无限循环.

while(true) {

    try {
        System.out.println("inside while");
        return;   //Replacing return with break makes no difference here.
    } finally {
        continue;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是因为finally即使return语句在try块本身内遇到它,也始终执行该块.

Way*_*ett 105

编译器可以轻松明确地证明第一个表达式总是导致无限循环,但对于第二个表达式来说并不容易.在你的玩具示例中它很简单,但如果:

  • 变量的内容是从文件中读取的?
  • 变量不是本地的,可以被另一个线程修改?
  • 变量依赖于一些用户输入?

编译器显然没有检查你的简单案例,因为它完全放弃了这条道路.为什么?因为规范禁止它更难.见14.21节:

(顺便说一句,我的编译器在声明变量时抱怨final.)

  • -1 - 它与编译器无法做什么有关.检查更容易或更难.它是关于Java语言规范允许编译器*做什么的.根据JLS,代码的第二个版本是**有效的Java**,因此编译错误是错误的. (25认同)
  • @StephenC - 感谢您提供的信息.高兴地更新以反映尽可能多. (10认同)
  • @Oak - 我在答案中承认*"在[OP的]玩具示例中它很简单"*并且根据Stephen的评论,JLS是限制因素.我很确定我们同意. (2认同)

Kib*_*bee 55

根据规范,以下是关于while语句的说法.

如果至少满足下列条件之一,则while语句可以正常完成:

  • while语句是可访问的,条件表达式不是值为true的常量表达式.
  • 有一个可到达的break语句退出while语句.

因此,编译器只会说如果while条件是具有true值的常量,或者while内有break语句,则while语句后面的代码无法访问.在第二种情况下,由于b的值不是常数,因此不认为其后面的代码是不可达的.这个链接背后有更多的信息,可以为您提供有关什么是什么,什么不被认为无法到达的更多细节.

  • +1注意到它不仅仅是编译器编写者能够或不能想到的东西--JLS告诉他们他们可以做什么,不能认为无法访问. (3认同)

hop*_*rim 14

因为true是常数,b可以在循环中改变.


kba*_*kba 10

因为分析变量状态很难,所以编译器几乎只是放弃了,让你按照自己的意愿行事.此外,Java语言规范有关于如何允许编译器检测无法访问的代码的明确规则.

有许多方法可以欺骗编译器 - 另一个常见的例子是

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

这是行不通的,因为编译器会意识到该区域是不可行的.相反,你可以做到

public void test()
{
    if (2 > 1) return;
    System.out.println("Hello");
}
Run Code Online (Sandbox Code Playgroud)

这可以工作,因为编译器无法意识到表达式永远不会是假的.

  • 正如其他答案所述,这与编译器检测无法访问的代码可能很容易或很难相关.JLS竭尽全力*指定用于检测无法访问语句的规则.根据这些规则,第一个示例不是有效的Java,第二个示例是有效的Java.而已.编译器只是按指定的方式实现规则. (6认同)
  • @emory - 当你通过第三方注释处理器提供Java程序时,它是......在非常真实的意义上......不再是Java了.相反,它是Java*覆盖*与注释处理器实现的任何语言扩展和修改.但这不是这里发生的事情.OP的问题是关于vanilla Java,JLS规则管理什么是有效的vanilla Java程序,什么不是有效的vanilla Java程序. (2认同)

Che*_*err 6

后者无法到达.布尔值b仍有可能在循环内的某处变为false,从而导致结束条件.