while(true);当不是't在一片空白中



我用java做了一些小程序。我知道如果我写while(true);,程序将在这个循环中冻结。如果代码是这样的:

测试1:

public class While {
    public static void main(String[] args) {
        System.out.println("start");
        while (true);
        System.out.println("end");
    }
}

编译器向我抛出错误:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Unreachable code
    at While.main(While.java:6)

我不知道这个错误存在。但我明白为什么要扔。当然,第6行无法访问,导致编译问题。然后我测试了这个:

测试2:

public class While {
    public static void main(String[] args) {
        System.out.println("start");
        a();
        b();
    }
    static void a() {
        while(true);
    }
    static void b() {
        System.out.println("end");
    }
}

由于某种原因程序正常运行(控制台打印"启动",然后冻结(。编译器无法检查void a()的内部并确定它不可访问。可以肯定的是,我试过了:

测试3:

public class While {
    public static void main(String[] args) {
        System.out.println("start");
        a();
        System.out.println("end");
    }
    static void a() {
        while(true);
    }
}

与测试2的结果相同。

经过一些研究,我发现了这个问题因此,如果括号内的代码是一个变量,编译器就不会抛出异常。这是有道理的,但我不认为这同样适用于voids

Q:那么,如果无法访问void b()(测试2(和System.out.println("end");

编辑:我尝试了C++中的测试1:

#include <iostream>
using namespace std;
int main()
{
    cout << "start" << endl;
    while(true);
    cout << "end" << endl;
    return 0;
}

编译器没有抛出任何错误,然后我得到了与测试2和测试3相同的结果。所以我想这是java的东西?

语言规范有一个确切的定义,编译器应该将其视为不可访问的代码,另请参阅https://stackoverflow.com/a/20922409/14955.

特别是,它不关心一个方法是否完成,也不查看其他方法内部。

它不会做更多的事情。

然而,你可以使用像FindBugs这样的静态代码分析工具来进行"更深层次"的分析(不过,也不确定它们是否检测到了你描述的模式,而且,正如其他人所指出的那样,无论如何,在所有通用性中的停顿问题都无法通过算法解决,因此必须对"尽最大努力"的一些合理定义划清界限(。

通常,不可能绝对确定地确定某个东西是否可到达。

为什么?这相当于暂停问题。

停顿的问题是:

给定任意计算机程序的描述,决定该程序是结束运行还是永远继续运行。

这个问题已被证明是无法解决的。


X段代码是否可访问,与之前的代码是否会停止是一样的。

因为这是一个无法解决的问题,编译器(在Java或任何其他语言中(都不会很努力地解决它。如果它碰巧确定它真的无法访问,那么你就会收到警告。如果没有,它可能是可访问的,也可能是不可访问的。

在Java中,无法访问的代码是编译器错误。因此,为了保持兼容性,语言规范准确地定义了编译器应该努力的程度。(根据其他答案,这是"不要进入另一个功能"。(

在其他语言(如C++(中,编译器可能会进一步进行优化。(内联函数并发现它永远不会返回后,可能会检测到无法访问的代码。(

不可访问代码是一个编译时错误,它只是说这个程序的流程没有意义;有些东西永远不会到达'

显然,由于无休止的循环,您的测试的执行方式是这样的,但为什么第一个测试会因编译时错误而失败呢?

while语句可以正常完成,如果以下是真的:

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

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

好吧,但是方法调用(比如a()(呢?为什么测试2和3能成功编译?

  • 如果表达式语句是可访问的,则它可以正常完成

由于方法调用被视为一个表达式,因此只要在它阻塞其逻辑执行路径之前没有任何东西,它们将始终是可访问的。


为了更好地说明这种编译机制背后的一些推理,让我们以if语句为例。

if(false)
   System.out.println("Hello!"); // Never executes

以上内容在编译时是正确的(尽管许多IDE肯定会抱怨!(。

Java 1.7规范谈到了这一点:

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

static final boolean DEBUG = false;

然后编写代码,例如:

if (DEBUG) { x=3; }

这个想法是应该有可能改变DEBUG的值从false变为true或从true变为false,然后在不对程序文本进行其他更改的情况下正确编译代码。

此外,实际上还有一个向后兼容性的原因:

这种"有条件编译"的能力对,二元兼容性及其关系(§13(。如果一组类使用这样一个"flag"变量的被编译,条件代码是省略,以后只分发的新版本是不够的包含标志定义的类或接口。A.因此,对标志值的更改是不兼容二进制的具有预先存在的二进制文件(§13.4.9(这种不兼容性,例如在case中使用常量switch语句中的标签;参见§13.4.9.(


Java编译器的大多数(根据规范((如果不是全部的话(实现都会而不是遍历到方法中。当解析Java代码本身时,它将a()视为MethodInvocationElement,这意味着该代码调用其他代码。我真的不在乎,我只是看语法在语法上,在调用a()之后,后续代码的归属是有意义的。

请记住性能成本。编译已经花费了相当长的时间。为了保持快速,Java编译器实际上并没有递归到方法中;这需要很长时间(理论上,编译器必须评估很多很多代码路径(。


为了进一步重申它在语法上是驱动的,需要在a()中的循环之后直接添加一个return;语句。不编译,是吗但是,从语法上来说,没有它是有意义的。

答案在于Java语言规范为可达性制定的规则。它首先声明

如果一个语句由于不可访问而无法执行,则是编译时错误。

然后

while语句可以正常完成,如果以下是真的:

  • while语句是可达的,并且条件表达式不是值为true的常量表达式(§15.28(
  • 有一个可访问的break语句退出while语句

表达式语句可以正常完成,前提是它是可访问的。

在第一个例子中,有一个while循环无法正常完成,因为它有一个条件,该条件是一个值为true的常量表达式,并且其中没有可到达的break

在第二个和第三个示例中,表达式语句(方法调用(是可访问的,因此可以正常完成。


所以我想这是java的东西?

上面的规则是Java的规则。C++可能有自己的规则,其他语言也是如此。

最新更新