Java中的无限循环

Java中的无限循环,java,halting-problem,Java,Halting Problem,查看Java中的无限while循环。它会导致下面的语句出现编译时错误 while(true) { System.out.println("inside while"); } System.out.println("while terminated"); //Unreachable statement - compiler-error. 但是,以下相同的无限while循环工作正常,不会出现任何错误,我只是用布尔变量替换了条件 boolean b=true; while(b) {

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

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

System.out.println("while terminated"); //Unreachable statement - compiler-error.

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

boolean b=true;

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

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

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


Edit:以下版本的
while
明显陷入无限循环,但不会对其下的语句发出编译器错误,即使循环中的
if
条件始终为
false
,因此,循环永远不会返回,并且可以由编译器在编译时自行确定

while(true) {

    if(false) {
        break;
    }

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

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



编辑:
if
while
相同

if(false) {
    System.out.println("inside if"); //No error here.
}



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

while(true) {

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

这是因为
finally
块始终被执行,即使
return
语句在
try
块本身中遇到它之前。

编译器不够复杂,无法运行
b
可能包含的值(尽管只分配一次)。第一个例子很容易让编译器看到,它将是一个无限循环,因为条件不是变量。

因为分析变量状态很困难,所以编译器基本上放弃了,让你做你想做的事。此外,Java语言规范有明确的规则

boolean b=true;

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

System.out.println("while terminated"); //No error here.
有很多方法可以欺骗编译器-另一个常见的例子是

public void test()
{
    return;
    System.out.println("Hello");
}
这是行不通的,因为编译器会意识到该区域是不可读取的。相反,你可以这样做

public void test()
{
    if (2 > 1) return;
    System.out.println("Hello");
}

这将起作用,因为编译器无法意识到表达式永远不会为false。

后者不是不可访问的。
布尔b仍然有可能在循环中的某个地方被更改为false,从而导致结束条件。

我很惊讶您的编译器拒绝编译第一个案例。我觉得这很奇怪


但是第二种情况没有优化到第一种情况,因为(a)另一个线程可能会更新
b
(b)被调用函数可能会修改
b
的值作为副作用。

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

  • 变量的内容是从文件中读取的吗
  • 该变量不是本地变量,可以由其他线程修改
  • 变量依赖于某些用户输入
编译器显然没有检查您的简单案例,因为它完全放弃了这条路。为什么?因为它更难被规范禁止。请参见:

(顺便说一句,当变量被声明为
final
时,我的编译器确实会抱怨)

我猜变量“b”有可能改变它的值,所以编译器认为
System.out.println(“终止时”)

可以达到。

因为true是常量,而b可以在循环中更改。

从编译器的角度来看,
中的
b
,而(b)
可能在某个地方更改为false。编译器只是不费心检查


为了好玩,请尝试
而(1<2)
则用于(int i=0;i<1;i--)
等。

如果编译器能够最终确定布尔值在运行时将计算为
true
,它将抛出该错误。编译器假定您声明的变量可以更改(尽管我们在这里知道它不会更改)


为了强调这一事实,如果变量在Java中声明为
final
,大多数编译器将抛出与替换值相同的错误。这是因为变量是在编译时定义的(在运行时不能更改),因此编译器可以最终确定表达式在运行时的计算结果为
true

编译器不是完美的,也不应该是完美的

编译器的职责是确认语法,而不是确认执行。编译器最终可以捕获并防止强类型语言中的许多运行时问题,但它们无法捕获所有此类错误

实际的解决方案是使用一组单元测试来补充编译器的检查,或者使用面向对象的组件来实现已知健壮的逻辑,而不是依赖于原始变量和停止条件

强类型和OO:提高编译器的效率

有些错误本质上是语法性的——在Java中,强类型化使得许多运行时异常可以捕获。但是,通过使用更好的类型,您可以帮助编译器执行更好的逻辑

如果您希望编译器在Java中更有效地实施逻辑,那么解决方案是构建能够实施此类逻辑的健壮、必需的对象,并使用这些对象来构建应用程序,而不是原语

一个典型的例子是迭代器模式的使用,与Java的foreach循环相结合。与简化的while循环相比,该构造不易受到您所说明的错误类型的影响

以下是关于while语句的说明

while语句通常可以在至少一个 事实如下:

  • while语句是可访问的,并且条件表达式不是值为true的常量表达式
  • 有一个可到达的break语句退出while语句\
因此,编译器只会说while语句后面的代码是
while(true) {

    try {
        System.out.println("inside while");
        return;   //Replacing return with break makes no difference here.
    } finally {
        continue;
    }
}
public void test()
{
    return;
    System.out.println("Hello");
}
public void test()
{
    if (2 > 1) return;
    System.out.println("Hello");
}