有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java-while(true);循环在不处于空状态时抛出无法访问的代码

我在用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");(测试3)无法访问,为什么编译器在测试1时抛出错误

编辑:我用C++尝试了测试1:

#include <iostream>

using namespace std;

int main()
{
    cout << "start" << endl;
    while(true);
    cout << "end" << endl;
    return 0;
}

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


共 (4) 个答案

  1. # 1 楼答案

    答案在于Java Language Specification可达性制定的规则。它首先指出

    It is a compile-time error if a statement cannot be executed because it is unreachable.

    然后

    A while statement can complete normally iff at least one of the following is true:

    • The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true.
    • There is a reachable break statement that exits the while statement.

    An expression statement can complete normally iff it is reachable.

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

    在第二个和第三个示例中,expression statement(方法调用)是可访问的,因此可以正常完成


    So I suppose this is a java thing?

    上面的规则是Java的规则。C++可能有自己的规则,其他语言也一样。p>

  2. # 2 楼答案

    Unreachable code是一个编译时错误,简单地说,“这个程序的流程没有意义;有些东西永远无法到达

    很明显,由于无休止的循环,您的测试执行得很好,但是为什么第一个测试会因为编译时错误而失败呢

    A while statement can complete normally if at least one of the following is true:

    • The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true.

    • There is a reachable break statement that exits the while statement.

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

    • An expression statement can complete normally if it is reachable.

    因为方法调用被认为是一个表达式,所以只要在它之前没有任何东西阻止它的逻辑执行路径,它们就始终是可访问的


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

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

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

    The Java 1.7 Specification talks about this

    The rationale for this differing treatment is to allow programmers to define "flag variables" such as:

    static final boolean DEBUG = false;
    

    and then write code such as:

    if (DEBUG) { x=3; }
    

    The idea is that it should be possible to change the value of DEBUG from false to true or from true to false and then compile the code correctly with no other changes to the program text.

    此外,还有一个向后兼容的原因:

    This ability to "conditionally compile" has a significant impact on, and relationship to, binary compatibility (§13). If a set of classes that use such a "flag" variable are compiled and conditional code is omitted, it does not suffice later to distribute just a new version of the class or interface that contains the definition of the flag. A change to the value of a flag is, therefore, not binary compatible with pre-existing binaries (§13.4.9). (There are other reasons for such incompatibility as well, such as the use of constants in case labels in switch statements; see §13.4.9.)


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

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


    要进一步重申它在语法上是受驱动的,可以直接在^{中的循环之后添加return;语句。不会编译,是吗在语法上,但是,没有它是有意义的

  3. # 3 楼答案

    总的来说,不可能绝对肯定地确定某物是否可以到达

    为什么?它相当于Halting Problem

    停顿的问题是:

    Given a description of an arbitrary computer program, decide whether the program finishes running or continues to run forever.

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


    X段代码是否可访问,这与在代码停止之前说明代码是否可访问是一样的

    因为这是一个无法解决的问题,编译器(用Java或任何其他语言)并不努力解决它。如果它碰巧确定确实无法访问,那么您将收到警告。如果没有,可能也可能无法访问

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

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

  4. # 4 楼答案

    编译器应将language spec has an exact definition视为不可访问代码,另请参见https://stackoverflow.com/a/20922409/14955

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

    它不会做更多的事情

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