而(真); 当不在void中时,循环抛出无法访问的代码

 紫色咖啡调 发布于 2023-01-10 11:05

我在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.

问:那么,为什么编译器只在测试1中抛出错误,如果void b()(测试2)和System.out.println("end");(测试3)无法访问?

编辑:我在C++中尝试过Test 1:

#include 

using namespace std;

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

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

4 个回答
  • 答案就在订出规则的可达性由Java语言规范.它首先说明

    如果由于无法访问语句而无法执行语句,则会发生编译时错误.

    然后

    一个while语句可以正常完成且仅当以下至少有一个为真:

    while语句是可到达和条件表达式不是常量表达式(§15.28)与值true.

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

    如果表达式可以访问,则表达式语句可以正常完成.

    在第一个示例中,您有一个无法正常完成的while循环,因为它具有一个带有值的常量表达式true并且在其中无法访问的条件break.

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


    所以我想这是一个java的东西?

    上面的规则是Java的规则.与其他语言一样,C++可能有自己的规则.

    2023-01-10 11:08 回答
  • 无法访问的代码是编译时错误,简单地说'该程序的流程没有意义; 永远不会达到某种东西'.

    显然,由于无限循环,您的测试执行它们的工作方式,但为什么第一个会因编译时错误而失败?

    如果至少满足下列条件之一,则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"变量的类并且省略了条件代码,则稍后仅仅分发包含该标志定义的类或接口的新版本是不够的.因此,对标志值的更改与预先存在的二进制文件不是二进制兼容的(第13.4.9节).(还有其他原因导致这种不兼容性,例如在switch语句中使用case标签中的常量;参见§13.4.9.)


    大多数(根据规范),如果不是全部,Java编译器的实现不会遍历方法.解析Java代码本身时,它只看到a()一个MethodInvocationElement,意思是"这段代码调用其他代码.我真的不在乎,我只是在看语法..从语法上讲,后续代码在调用之后属于它是有意义的a().

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


    进一步重申它的语法驱动是return;在你的循环后直接添加一个语句a().不编译,是吗?但从语法上讲,没有它就没有意义.

    2023-01-10 11:08 回答
  • 该语言规范有一个确切的定义是什么,编译器应该当作可达代码,也见/sf/ask/17360801/.

    特别是,它不关心方法是否完成,并且它不会查看其他方法.

    它不会做更多的事情.

    但是,您可以使用像FindBugs这样的静态代码分析工具来进行"更深入"的分析(不确定它们是否检测到您描述的模式,或者,正如其他人所指出的那样,所有通用性中的暂停问题都不能无论如何都要通过算法解决,因此必须在"尽力而为"的某种合理定义中划清界限.

    2023-01-10 11:08 回答
  • 通常,不可能绝对肯定地确定某些东西是否可达.

    为什么?这相当于停机问题.

    暂停问题问:

    给定任意计算机程序的描述,确定程序是否完成运行或继续运行.

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


    X代码是否可访问与说明之前的代码是否将停止相同.

    因为它是一个无法解决的问题,所以编译器(用Java或任何其他语言)不会很难解决它.如果碰巧确定它确实无法访问,那么您会收到警告.如果不是,它可能会也可能不会到达.

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

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

    2023-01-10 11:08 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有