请解释以下有关“找不到符号”和“无法解析符号”错误的内容:
这个问题的设计是为了种子一个全面的Q&;a关于这些常见的编译错误在Java。
不是真的。 “找不到符号”和“解析不了符号”是一回事。 有些Java编译器使用一个短语,有些则使用另一个。
首先,它是一个编译错误1。 这意味着要么你的Java源代码有问题,要么你编译它的方式有问题。
您的Java源代码由以下内容组成:
true
,false
,class
,while
等。42
和“X”
和“Hi mum!”
。+
,=
,{
等。Reader
,I
,ToString
,ProcesseQuiBalanceDelephants
等。“找不到符号”错误与标识符有关。 编译代码时,编译器需要计算出代码中的每个标识符的含义。
“找不到符号”错误意味着编译器无法做到这一点。 您的代码似乎引用了编译器不理解的内容。
首先,只有一个原因。 编译器查找了所有应该定义标识符的地方,但找不到定义。 这可能是由许多因素造成的。 常见的有以下几种:
StringBiulder
而不是StringBuilder
。 Java不能也不会试图弥补拼写或打字错误。StringBuilder
而不是StringBuilder
。 所有Java标识符都区分大小写。mystring
和my_string
是不同的。 (如果你坚持Java风格的规则,你将在很大程度上避免犯这种错误……)对于应为方法名或字段名的标识符:
>
“someString”.push()
2.“SomeString”.length
或SomeArray.length()
.您可能错误地操作了数组而不是数组元素; 例如。
String strings[] = ...
if (strings.charAt(3)) { ... }
// maybe that should be 'strings[0].charAt(3)'
对于应为类名的标识符:
>
您可能忘记了一个新
,如:
String s = String(); // should be 'new String()'
对于类型或实例似乎没有您期望的成员的情况:
问题往往是以上几个因素的综合。 例如,可能您“star”导入了java.io.*
,然后尝试使用files
类。。。该类位于java.nio
而不是java.io
中。 或者您可能打算编写file
.。。它是java.io
中的一个类。
下面是一个示例,说明不正确的变量作用域如何导致“找不到符号”错误:
List<String> strings = ...
for (int i = 0; i < strings.size(); i++) {
if (strings.get(i).equalsIgnoreCase("fnord")) {
break;
}
}
if (i < strings.size()) {
...
}
这将为if
语句中的i
提供“找不到符号”错误。 虽然我们以前声明了i
,但该声明仅在for
语句及其主体的作用域内。 if
语句中对i
的引用看不到i
的声明。 它超出了范围。
(此处适当的更正可能是将if
语句移到循环内部,或者在循环开始之前声明i
。)
下面是一个引起困惑的例子,一个错别字导致了一个似乎无法解释的“找不到符号”错误:
for (int i = 0; i < 100; i++); {
System.out.println("i is " + i);
}
这将在println
调用中给您一个编译错误,说找不到i
。 但是(我听到你说)我确实申报了!
问题在于{
前的分号(;
)。Java语言语法将该上下文中的分号定义为空语句。空语句随后成为for
循环的主体。因此,代码实际上是这样的:
for (int i = 0; i < 100; i++);
// The previous and following are separate statements!!
{
System.out.println("i is " + i);
}
{。。。}
块不是for
循环的主体,因此for
语句中以前的i
声明超出了块中的作用域。
下面是另一个由错别字引起的“找不到符号”错误的例子。
int tmp = ...
int res = tmp(a + b);
尽管有前面的声明,tmp(...)
表达式中的tmp
是错误的。 编译器将查找名为tmp
的方法,但找不到。 前面声明的tmp
位于变量的命名空间中,而不是方法的命名空间中。
在我遇到的例子中,程序员实际上漏掉了一个运算符。 他的意思是这样写的:
int res = tmp * (a + b);
如果从命令行编译,编译器可能找不到符号还有另一个原因。 您可能只是忘记编译或重新编译其他类。 例如,如果您有类foo
和bar
,其中foo
使用bar
。 如果您从未编译过bar
,并且运行javac foo.java
,您可能会发现编译器找不到符号bar
。 简单的答案是将foo
和bar
一起编译; 例如javac foo.Java bar.Java
或javac*.
。 或者更好地使用Java构建工具; 例如Ant,Maven,Gradle等等。
还有其他一些更模糊的原因。。。我将在下面讨论。
一般来说,首先要弄清楚是什么导致了编译错误。
然后思考代码应该说什么。 然后,最后,您确定需要对源代码进行哪些更正,以实现您想要的功能。
注意,并不是每一个“更正”都是正确的。 考虑一下:
for (int i = 1; i < 10; i++) {
for (j = 1; j < 10; j++) {
...
}
}
假设编译器对j
说“找不到符号”。 我有很多方法可以“修复”这个问题:
更改为(int j=1;j<10;j++)
的-可能正确。
for
循环或外部for
循环之前为j
添加声明-可能正确。中将j
更改为i
-可能是错误的!
关键是,您需要理解代码试图做什么,以便找到正确的修复。
下面是几个“找不到符号”的例子,它们看起来是无法解释的……除非你仔细看。
>
不正确的依赖项:如果您使用的是管理生成路径和项目依赖项的IDE或生成工具,则可能在依赖项方面出错; 例如遗漏了一个依赖项,或者选择了错误的版本。 如果您正在使用构建工具(Ant,Maven,Gradle等),请检查项目的构建文件。 如果使用的是IDE,请检查项目的生成路径配置。
你没有重新编译:有时会发生新的Java程序员不理解Java工具链是如何工作的,或者还没有实现可重复的“构建过程”; 例如使用IDE,Ant,Maven,Gradle等等。 在这种情况下,程序员可能会穷追猛打,寻找一个虚幻的错误,而这个错误实际上是由于没有正确地重新编译代码造成的,等等。
早期构建问题:早期构建可能失败,导致JAR文件缺少类。 如果您使用构建工具,通常会注意到这样的失败。 但是,如果您从其他人那里获取JAR文件,则需要正确构建这些文件,并注意到错误。 如果您怀疑这一点,请使用tar-tvf
列出可疑JAR文件的内容。
IDE问题:有人报告过这样的情况:他们的IDE被混淆了,IDE中的编译器找不到存在的类……或者是相反的情况。
>
如果IDE配置了错误的JDK版本,则可能发生这种情况。
如果IDE的缓存与文件系统不同步,就可能发生这种情况。 有IDE特定的方法来解决这个问题。
这可能是IDE错误。 例如,@Joel Costigliola描述了一个场景,其中Eclipse没有正确处理Maven“测试”树:请参阅以下答案。
Android问题:当您为Android编程时,遇到与r
相关的“无法找到符号”错误,请注意r
符号是由context.xml
文件定义的。 检查context.xml
文件是否正确,是否位于正确的位置,以及是否已经生成/编译了相应的R
类文件。 注意,Java符号是区分大小写的,因此相应的XML ID也是区分大小写的。
Android上的其他符号错误很可能是由于前面提到的原因; 例如缺少或不正确的依赖项,不正确的包名称,特定API版本中不存在的方法或字段,拼写/键入错误等等。
重新定义系统类:我见过编译器抱怨substring
是一个未知符号的情况,如下所示
String s = ...
String s1 = s.substring(1);
结果是程序员创建了自己版本的string
,而他的类版本没有定义substring
方法。
教训:不要用与普通库类相同的名称定义自己的类!
同形异形:如果您对源文件使用UTF-8编码,那么可能会有看起来相同,但实际上不同的标识符,因为它们包含同形异形。 有关详细信息,请参阅本页。
您可以通过限制自己使用ASCII或Latin-1作为源文件编码,并对其他字符使用Java\uxxxx
转义符来避免这种情况。
1-如果您在运行时异常或错误消息中确实看到了这一点,那么您可能已经将IDE配置为运行带有编译错误的代码,或者您的应用程序正在生成和编译代码。 在运行时。
2-土木工程的三个基本原则:水不会往上坡流,木板的侧面更结实,你不能推绳子。
如果您忘记了new
:
String s = String();
范瑟丝
String s = new String();
因为没有new
关键字的调用将尝试查找一个没有参数的名为string
的(本地)方法,而该方法签名可能没有定义。
“Variable is out of scope”的另一个示例
我已经见过几次这样的问题了,也许再举一个例子来说明什么是非法的,即使感觉还可以。
请考虑以下代码:
if(somethingIsTrue()) {
String message = "Everything is fine";
} else {
String message = "We have an error";
}
System.out.println(message);
那是无效的代码。 因为这两个名为message
的变量在它们各自的作用域之外都是不可见的--在本例中,作用域就是周围的括号{}
。
您可能会说:“但是一个名为message的变量是以任何方式定义的-所以message是在if
之后定义的”。
但你就错了。
Java没有free()
或delete
运算符,因此它不得不依靠跟踪变量作用域来找出变量何时不再使用(连同对这些原因变量的引用)。
如果你认为自己做了好事,那就更糟了。 我见过这样“优化”代码后的这种错误:
if(somethingIsTrue()) {
String message = "Everything is fine";
System.out.println(message);
} else {
String message = "We have an error";
System.out.println(message);
}
“哦,有重复代码,让我们把公共行拉出来”->; 就在这里。
处理这种作用域问题的最常见方法是将else值预赋给外部作用域中的变量名,然后在下列情况下重新赋值:
String message = "We have an error";
if(somethingIsTrue()) {
message = "Everything is fine";
}
System.out.println(message);