我在用java编写一些小程序。我知道如果我写while(true)
程序将在此循环中冻结。如果代码是这样的:
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行无法访问,导致编译问题。然后我测试了这个:
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");
}
}
由于某种原因,程序运行正常(控制台打印了“开始”,然后冻结了)。编译器无法检查
uluala()
的内部,并看到它是不可访问的。为了确保我尝试了:
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
。
问:那么,如果
void b()
(测试2)和系统,为什么编译器会在测试1中抛出错误呢。出来println(“结束”)
(测试3)无法访问?
编辑:我试过C中的测试1:
#include <iostream>
using namespace std;
int main()
{
cout << "start" << endl;
while(true);
cout << "end" << endl;
return 0;
}
编译器没有抛出任何错误,然后我得到了与测试2和测试3相同的结果。所以我想这是java的事情?
无法访问的代码是一个编译时错误,简单地说“这个程序的流程没有意义;永远不会到达的东西”。
很明显,由于无休止的循环,您的测试执行得很好,但是为什么第一个测试会因为编译时错误而失败呢?
如果至少满足以下条件之一,则while语句可以正常完成:
>
当语句是可达的,并且条件表达式不是一个值为true的常量表达式(§15.28)。
有一个可到达的break语句退出while语句。
好的,但是关于方法调用(比如a()
)呢?为什么测试2和3成功编译?
因为方法调用被认为是一个表达式,所以只要在它阻塞其逻辑执行路径之前没有任何东西,它们就始终是可访问的。
为了更好地说明这种编译机制背后的一些原因,让我们以if
语句为例。
if(false)
System.out.println("Hello!"); // Never executes
以上内容在编译时是正确的(尽管许多IDE肯定会抱怨!)。
Java1.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编译器实际上并不递归到方法中;这将需要很长时间(理论上,编译器必须评估许多许多代码路径)。
要进一步重申它是语法驱动的,可以直接在a()
中的循环之后添加一个返回;
语句。不编译,是吗?不过,从语法上来说,没有它是有意义的。
一般来说,用绝对肯定来确定某样东西是否可到达是不可能的。
为什么?这相当于停顿问题。
停顿的问题是:
给出一个任意计算机程序的描述,决定该程序是结束运行还是永远继续运行。
这个问题已被证明是无法解决的。
X段代码是否可访问,就等于说它之前的代码是否会停止。
因为这是一个无法解决的问题,编译器(用Java或任何其他语言)并不努力解决它。如果它碰巧确定确实无法访问,那么您将收到警告。如果没有,可能也可能无法访问。
在Java,无法访问的代码是编译器错误。所以为了保持兼容性,语言规范准确地定义了编译器应该尝试的“多努力”。(根据其他答案,是“不要进入另一个函数”。)
在其他语言(如C)中,编译器可能会进一步进行优化。(内联函数并发现它永远不会返回后,可能会检测到无法访问的代码。)
语言规范有一个确切的定义,编译器应该将其视为不可访问的代码,另请参见https://stackoverflow.com/a/20922409/14955.
特别是,它不关心一个方法是否完成,也不查看其他方法的内部。
它不会做更多。
然而,您可以使用像FindBugs这样的静态代码分析工具来进行“更深层次”的分析(虽然也不确定它们是否检测到您所描述的模式,而且,正如其他人所指出的,无论如何,停止问题在所有方面都不能通过算法来解决,所以人们必须在“最大努力”的合理定义上划清界限)。
问题内容: 我在用Java做一些小程序。我知道,如果我编写 程序,程序将在此循环中冻结。如果代码是这样的: 测试1: 编译器抛出错误: 我不知道这个错误存在。但是我知道为什么会抛出它。当然, 第6行无法访问 ,从而导致编译问题。然后,我对此进行了测试: 测试2: 由于某种原因 ,程序正常运行 (控制台先打印“开始”,然后冻结)。编译器无法检查内部, 并无法访问。确保我尝试过: 测试3: 与测试2相
我试图创建一个函数,使用不同的字段作为权重将数据分成十分之一,这样我就可以有相等的暴露桶。在这样做的时候,我创建了一个简单的例子,我试图进入3个桶。我确实在第一部分遇到了困难,那就是让这个while循环工作起来: 我的成绩是0,0 谢谢!
hasNext()的定义是“如果此扫描仪的输入中有另一个标记,则返回true。此方法可能会在等待输入扫描时阻塞。扫描仪不会前进超过任何输入。” 当我把 standardInput.hasNext() 放在 for 循环中时,程序会向无穷大运行。但是如果我把它放在 while-loop 中,它不会运行到无穷大。这两个程序之间的区别在哪里,为什么其中一个有效而另一个无效? for循环: while-l
在生命数==0之后,while语句为false,必须退出循环。但在这种情况下,即使循环为false,也会执行循环。
我有3个div。当我点击特定的按钮时,每个人都会有类活动。它们的每个div都有一个状态变量,每当它是真的时,我都会向连接到它的div添加class Name='active': 当我点击按钮时,状态改变,但是div没有激活类 当我将函数更改为此时,它会工作: ================================================ =====================
我正在尝试编写一个函数,该函数将取一个整数,将其除以数字,将其相加,如果总和为 我并不是真的在寻找一个最佳的解决方案,我只是想知道为什么这不起作用。