- 它们的含义是什么?
- 有哪些因素会导致它们出现?
- 程序员应该如何修复它们?
实际上没有。“Cannot find symbol”、“Cannot resolve symbol”和“Symbol not found”都是指同样的意思。(不同的Java编译器由不同的人编写,不同的人使用不同的短语表达同样的意思。)
首先,它是一个编译错误1。这意味着要么您的Java源代码存在问题,要么您编译的方式存在问题。
您的Java源代码包括以下内容:
class
、while
等。true
、false
、42
、'X'
和"Hi mum!"
。+
、=
、{
等。Reader
、i
、toString
、processEquibalancedElephants
等。“Cannot find symbol”错误是关于标识符的。当您的代码被编译时,编译器需要弄清楚您代码中每个标识符的含义。
“Cannot find symbol”错误意味着编译器无法实现这一点。您的代码似乎引用了编译器无法理解的内容。
首要原因只有一个。编译器在应该定义标识符的所有位置都搜索过,但找不到定义。这可能由许多因素引起。以下是常见的原因:
对于标识符通用的情况:
也许您拼写错误了,例如使用了StringBiulder
而不是StringBuilder
。Java 不会尝试弥补拼写错误或打字错误。stringBuilder
而不是StringBuilder
。所有 Java 标识符都区分大小写。mystring
和my_string
是不同的。(如果遵循Java的命名规范,您将在很大程度上免受此类错误的影响...)"rope".push()
2。"rope".length
或 someArray.length()
。 String strings[] = ...
if (strings.charAt(3)) { ... }
// maybe that should be 'strings[0].charAt(3)'
对于应该是类名的标识符:
可能您忘记导入该类。
可能您使用了“星号”导入,但该类未在您导入的任何包中定义。
可能您忘记了 new
例如:
String s = String(); // should be 'new String()'
也许你在尝试导入或使用一个在 默认包 中声明的类;即没有 package
语句的类所在的包。java.awt.List
而不是java.util.List
。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);
如果你在命令行编译代码时,编译器找不到某个符号的另一个原因可能是忘记编译或重新编译其他类。例如,如果你有一个使用了Bar
的Foo
类和Bar
类,如果你从未编译过Bar
而运行javac Foo.java
,你会发现编译器找不到符号Bar
。简单的解决方法是一起编译Foo
和Bar
;例如:javac Foo.java Bar.java
或javac *.java
。或者更好的办法是使用Java构建工具,例如Ant、Maven、Gradle等。
还有其他一些更隐晦的原因...我将在下面详细说明。
通常情况下,你首先需要确定编译错误的原因。
然后,你需要思考代码要表达什么意思。最后,确定需要对源代码进行哪些更改才能实现你的目标。
请注意,并不是每个“更改”都是正确的。例如:
for (int i = 1; i < 10; i++) {
for (j = 1; j < 10; j++) {
...
}
}
假设编译器说找不到符号j
时,可以通过多种方式进行“修复”:
for
更改为for(int j = 1; j < 10; j ++)
- 可能是正确的。for
循环或外部 for
循环之前添加对j
的声明- 可能是正确的。for
循环中将j
更改为i
- 可能是错误的!关键是您需要理解您的代码尝试做什么才能找到正确的修复方法。
以下是一些看起来似乎无法解释“找不到符号”的情况,直到您仔细查看为止。
依赖关系不正确: 如果您正在使用管理构建路径和项目依赖项的IDE或构建工具,则可能会出现依赖项错误;例如省略了依赖项或选择了错误的版本。如果您正在使用构建工具(Ant、Maven、Gradle等),请检查项目的构建文件。如果您正在使用IDE,请检查项目的构建路径配置。
找不到符号“var”: 您可能正在尝试使用局部变量类型推断(即var
声明)编译源代码,其使用旧的编译器或旧的 --source
级别。 var
是在Java 10中引入的。检查您的JDK版本和构建文件以及(如果在IDE中出现此问题)IDE设置。
您没有编译/重新编译: 有时,新的Java程序员不了解Java工具链的工作原理,或者没有实现可重复的“构建过程”,例如使用IDE、Ant、Maven、Gradle等等。在这种情况下,程序员可能会一直寻找由于未正确重新编译代码而导致的虚假错误。
另一个例子是当您使用(Java 9+)java SomeClass.java
编译和运行类时。如果类依赖于您没有编译(或重新编译)的另一个类,则可能会出现指向第二个类的“无法解析符号”错误。其他源文件不会自动编译。 java
命令的新“编译和运行”模式不适用于运行具有多个源代码文件的程序。
早期构建问题: 可能存在早期构建失败的情况,导致 JAR 文件中缺少类。如果您正在使用构建工具,则通常会注意到此类故障。但是,如果您从他人那里获取 JAR 文件,则取决于他们正确构建并注意到错误。如果您怀疑此情况,请使用 tar -tvf
命令列出可疑 JAR 文件的内容。
IDE 问题: 有人报告了这样一种情况,即他们的 IDE 混淆了,IDE 编译器无法找到已存在的类,或者反过来。
如果 IDE 配置了错误的 JDK 版本,则可能会发生这种情况。
如果 IDE 的缓存与文件系统不同步,则可能会发生这种情况。有 IDE 特定的方法可解决此问题。
这可能是 IDE 的错误。例如,@Joel Costigliola 描述了一个场景,在该场景中 Eclipse 未正确处理 Maven “test” 树:请参阅此答案。(显然,该特定错误已经很久以前得到修复。)
Android 问题: 当您为 Android 编程,并且出现与 R
相关的“找不到符号”错误时,请注意 R
符号由 context.xml
文件定义。检查您的 context.xml
文件是否正确并位于正确的位置,并且已生成 / 编译相应的 R
类文件。请注意,Java 符号区分大小写,因此相应的 XML ID 也区分大小写。
其他 Android 上的符号错误可能是由于前面提到的原因导致;例如,缺少或不正确的依赖项、不正确的包名称、特定 API 版本中不存在方法或字段、拼写/打字错误等。
隐藏系统类: 我曾经看到编译器抱怨在类似以下内容中,substring
是一个未知符号:
String s = ...
String s1 = s.substring(1);
原来程序员创建了自己的版本String
,而他定义的这个类没有定义substring
方法。我曾经见过人们在System
、Scanner
和其他类中这样做。
教训:不要定义与常用库类相同名称的自己的类!
该问题也可以通过使用全限定名来解决。例如,在上面的例子中,程序员本可以这样写:
java.lang.String s = ...
java.lang.String s1 = s.substring(1);
同形异义字:如果你在源代码中使用UTF-8编码,就可能会出现看起来相同但实际上不同的标识符,因为它们包含了同形异义字。请参见这个页面以获取更多信息。
您可以通过将源文件编码限制为ASCII或Latin-1,并对其他字符使用Java \uxxxx
转义来避免此问题。
1 - 如果偶然地在运行时异常或错误消息中看到这个问题,那么要么是您已经配置了IDE以运行有编译错误的代码,要么是您的应用程序正在生成和编译代码...在运行时。
2 - 土木工程的三个基本原则:水不会流到山上,木板横放更结实,以及你无法拉动绳子。
如果您忘记使用new
,也会出现这个错误:
String s = String();
对抗
String s = new String();
因为没有使用 new
关键字的调用会尝试查找一个名为 String
的(本地)方法而不带参数 - 而该方法签名很可能未定义。已解决
使用IntelliJ
选择构建->重新构建项目即可解决问题。
“变量超出范围”的另一个例子
由于我已经多次看到这种问题,也许可以举一个即使感觉上可能没问题,但实际上是非法的例子。
考虑以下代码:
if(somethingIsTrue()) {
String message = "Everything is fine";
} else {
String message = "We have an error";
}
System.out.println(message);
那是无效的代码。因为两个名为message
的变量都不在它们各自的范围之外可见 - 在这种情况下,就是包围它们的大括号{}
。
你可能会说:"但是无论如何都定义了一个名为message的变量 - 所以,在if
之后message被定义了"。
但你错了。
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 值预先分配给外部作用域的变量名,然后在 if 中重新分配:
String message = "We have an error";
if(somethingIsTrue()) {
message = "Everything is fine";
}
System.out.println(message);
final
变量声明。 - Daniel Pryden在Eclipse中出现此错误的一种方法:
src/test/java
中定义一个类A
。src/main/java
中定义另一个类B
,并使用类A
。结果:Eclipse将编译代码,但Maven会给出“找不到符号”的错误。
根本原因:Eclipse为主树和测试树使用了合并的构建路径。不幸的是,它不支持为Eclipse项目的不同部分使用不同的构建路径,而这正是Maven所需要的。
解决方案:
class demo{
public static void main(String a[]){
System.out.print(name);
}
}
现在看一下结果...
这个错误提示是指“找不到变量名”...定义和初始化“name”变量的值可以消除这个错误...实际上是这样的:
class demo{
public static void main(String a[]){
String name="smith";
System.out.print(name);
}
}
现在看一下新的输出...
好的,成功解决了那个错误。同时,如果你遇到了“找不到方法”或“找不到类”的错误,首先要定义一个类或方法,然后再使用它。
有很多种情况,如上面的人们所提到的。 以下是帮助我解决此问题的一些方法。
如果您正在使用IntelliJ
文件->'使缓存无效/重启'
或者
所引用的类在另一个项目中,并且该依赖项未添加到我的项目的Gradle构建文件中。因此,我使用以下内容添加了依赖项:
编译项目(':anotherProject')
然后它就可以工作了。希望对你有帮助!
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
最后,在项目上右键,选择“Debug as” -> “Maven clean”,“Maven build” 步骤。
我也遇到了这个错误。(我在谷歌上搜索后被引导到了这个页面)
问题:我正在从项目B中定义的一个类中调用项目A中定义的一个静态方法。我遇到了以下错误:
error: cannot find symbol
解决方案:我通过先构建定义该方法的项目,然后构建调用该方法的项目来解决这个问题。
System.out.println
中的println
,我们将得到<identifier> expected
(演示),但在IntelliJ中,我们将看到Cannot resolve symbol 'println'
(演示)。 - Pshemo