为什么Java编译器一次只报一种错误?
我有一个片段
class T{
int y;
public static void main(String... s){
int x;
System.out.println(x);
System.out.println(y);
}
}
,这里有两个错误,但在编译时为什么只显示一个错误?
显示的错误是:
non-static variable y cannot be referenced from a static context
System.out.println(y);
^
但是错误呢
variable x might not have been initialized
System.out.println(x);
^
I've a snippet
class T{
int y;
public static void main(String... s){
int x;
System.out.println(x);
System.out.println(y);
}
}
Here there are two error, but on compilation why only one error is shown?
The error shown is:
non-static variable y cannot be referenced from a static context
System.out.println(y);
^
But what about the error
variable x might not have been initialized
System.out.println(x);
^
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
Java 编译器会分几次编译您的代码。遍。在每次传递中,都会检测到某些类型的错误。在您的示例中,
javac
不会查看x
是否可以初始化,直到代码的其余部分实际通过了先前的编译器传递。The Java compiler compiles your code in several passes. In each pass, certain kinds of errors are detected. In your example,
javac
doesn't look to see whetherx
may be initialised or not, until the rest of the code actually passes the previous compiler pass.@Greg Hewgill 已经成功了。
特别是,对正在初始化的变量、正在声明的异常、无法访问的代码和其他一些事情的检查发生在稍后的过程中。如果之前的传递中存在错误,则此传递不会运行。
这是有充分理由的。前面的传递构建了一个修饰的解析树,它代表编译器理解的程序。如果之前出现错误,那么该树将无法准确表示开发人员所理解的程序。 (这不可能!)。如果编译器继续运行后面的过程以产生更多错误消息,则许多错误消息很可能是不正确的解析树的误导性产物。这只会让开发者感到困惑。
无论如何,这就是大多数编程语言的大多数编译器的工作方式。修复某些编译错误可能会导致其他(以前未报告的)错误出现。
@Greg Hewgill has nailed it.
In particular, the checks for variables being initialized, exceptions being declared, unreachable code and a few other things occur in a later pass. This pass doesn't run if there were errors in earlier passes.
And there's a good reason for that. The earlier passes construct a decorated parse tree that represent the program as the compiler understands it. If there were errors earlier on, that tree will not be an accurate representation of the program as the developer understands it. (It can't be!). If the compiler then were to go on to run the later pass to produce more error messages, the chances are that a lot of those error messages would be misleading artifacts of the incorrect parse tree. This would only confuse the developer.
Anyway, that's the way that most compilers for most programming languages work. Fixing some compilation errors can cause other (previously unreported) errors to surface.
The Dragon Book(“编译器:原理、技术和工具”作者: Aho、Sethi 和 Ullman)描述了编译器编写者可以用来在给定不符合语言规范的输入文件时改进错误检测和报告的几种方法。
他们提供的一些技术:
紧急模式恢复:跳过输入流中的所有输入令牌,直到找到“同步令牌”——想想
;
、} 或
end
语句和块终止符或分隔符,或do
、while
、for
、function
等可以显示新代码预期开始的关键字希望从该点开始的输入有足够的意义来解析并返回有用的错误消息。短语级别恢复:猜测短语可能的含义:插入分号、将逗号改为分号、插入
=
赋值运算符等,并希望结果足以解析并返回有用的错误消息。错误产生式:除了识别允许的语法运算符的“合法”产生式之外,还包括识别语言中的错误的“错误产生式”,这些错误会违反语法,但编译器作者认识到很可能发生的错误。这些错误消息可能非常好,但可能会大大增加解析器的大小以优化输入错误(这应该是例外,而不是常见情况)。
全局修正:尝试对输入字符串进行最小程度的修改,以尝试将其恢复为可以解析的程序。由于可以通过多种不同的方式进行更正(插入、更改和删除任意数量的字符),因此尝试生成所有这些并发现“最小成本”的更改,使程序解析得足够好以继续解析。
这些选项出现在解析语法一章(我的版本中的第 161 页)中——显然,一些错误只有在解析输入文件后才会发现,但开始转换为基本块以进行优化和代码生成。早期语法级别中发生的任何错误都会阻止典型编译器启动代码优化和生成阶段,并且在这些阶段中可能检测到的任何错误都必须等待固定输入才能运行。
我强烈建议您找到一本《The Dragon Book》,它会给您一些同情,并希望对我们友好的编译器作者产生新的尊重。
The Dragon Book ("Compilers: Principles, Techniques, and Tools" by Aho, Sethi, and Ullman) describe several methods that compiler writers can employ to try to improve error detection and reports when given input files that don't conform to the language specification.
Some of the techniques they give:
Panic mode recovery: skip all input tokens in the input stream until you have found a "synchronizing token" -- think of
;
,}
, orend
statement and block terminators or separators, ordo
,while
,for
,function
, etc. keywords that can show intended starts of new code blocks, etc. Hopefully the input from that point forward will make enough sense to parse and return useful error messages.Phrase level recovery: guess at what a phrase might have meant: insert semicolons, change commas to semicolons, insert
=
assignment operators, etc., and hope the result makes enough sense to parse and return useful error messages.Error productions: in addition to the "legitimate" productions that recognize allowed grammar operators, include "error productions" that recognize mistakes in the language that would be against the grammar, but that the compiler authors recognize as very likely mistakes. The error messages for these can be very good, but can drastically grow the size of the parser to optimize for mistakes in input (which should be the exception, not the common case).
Global correction: try to make minimal modifications to the input string to try to bring it back to a program that can be parsed. Since the corrections can be made in many different ways (inserting, changing, and deleting any number of characters), try to generate them all and discover the "least cost" change that makes the program parse well enough to continue parsing.
These options are presenting in the chapter on parsing grammars (page 161 in my edition) -- obviously, some errors are only discovered after the input file has been parsed, but is beginning to be converted into basic blocks for optimization and code generation. Any errors that occur in the earlier syntactic levels will prevent the typical compiler from starting the code optimization and generation phases, and any errors that might be detected in these phases must wait for fixed input before they can be run.
I strongly recommend finding a copy of The Dragon Book, it'll give you some sympathy and hopefully new-found respect for our friendly compiler authors.
错误的全部要点是编译器不知道如何正确解释您的代码。这就是为什么您应该始终忽略第一个错误之后的错误,因为编译器的混淆可能会导致额外/丢失错误。
The whole point of an error is that the compiler doesn't know how to interpret your code correctly. That's why you should always ignore errors after the first one, because the compiler's confusion may result in extra/missing errors.