请解释以下有关“找不到符号”、“无法解析符号”或“未找到符号”错误(在 Java 中):
他们的意思是什么?
什么事情会导致他们?
程序员如何修复它们?
这个问题旨在为有关 Java 中这些常见编译错误的全面问答提供种子。
0.这两个错误有什么区别吗?
并不真地。 “找不到符号”、“无法解析符号”和“找不到符号”都是同一个意思。不同的 Java 编译器使用不同的措辞。
1. “找不到符号”错误是什么意思?
首先,这是一个编译错误1。这意味着要么你的 Java 源代码有问题,要么你编译它的方式有问题。
您的 Java 源代码包含以下内容:
关键字:like class、while 等。
字面量:如 true、false、42、'X' 和“Hi mum!”。
运算符和其他非字母数字标记:如 +、=、{ 等。
标识符:如 Reader、i、toString、processEquibalancedElephants 等。
注释和空格。
“找不到符号”错误与标识符有关。编译代码时,编译器需要弄清楚代码中每个标识符的含义。
“找不到符号”错误意味着编译器无法执行此操作。您的代码似乎指的是编译器不理解的东西。
2. 什么会导致“找不到符号”错误?
作为第一顺序,只有一个原因。编译器查看了所有应该定义标识符的地方,但找不到定义。这可能是由多种原因造成的。常见的有以下几种:
对于一般标识符:也许您拼错了名称;即StringBiulder 而不是StringBuilder。 Java 不能也不会尝试弥补拼写错误或输入错误。也许你搞错了;即 stringBuilder 而不是 StringBuilder。所有 Java 标识符都区分大小写。也许你不恰当地使用了下划线;即 mystring 和 my_string 是不同的。 (如果你坚持 Java 风格的规则,你将在很大程度上避免这个错误......)也许你正在尝试使用被声明为“其他地方”的东西;即在与您隐式告诉编译器查看的不同上下文中。 (不同的类?不同的范围?不同的包?不同的代码库?)
也许你拼错了名字;即StringBiulder 而不是StringBuilder。 Java 不能也不会尝试弥补拼写错误或输入错误。
也许你搞错了;即 stringBuilder 而不是 StringBuilder。所有 Java 标识符都区分大小写。
也许你不恰当地使用了下划线;即 mystring 和 my_string 是不同的。 (如果你坚持 Java 风格的规则,你将在很大程度上避免这个错误......)
也许您正在尝试使用被声明为“其他地方”的东西;即在与您隐式告诉编译器查看的不同上下文中。 (不同的类?不同的范围?不同的包?不同的代码库?)
对于应该引用变量的标识符:也许您忘记声明变量。也许变量声明在您尝试使用它时超出了范围。 (见下面的例子)
也许你忘了声明变量。
也许变量声明在您尝试使用它时超出了范围。 (见下面的例子)
对于应该是方法或字段名称的标识符:也许您正在尝试引用未在父/祖先类或接口中声明的继承方法或字段。也许您正试图引用您正在使用的类型中不存在(即尚未声明)的方法或字段;例如“绳子”.push()2。也许您正在尝试将方法用作字段,反之亦然;例如“绳索”.length 或 someArray.length()。也许您错误地对数组而不是数组元素进行了操作;例如 String strings[] = ... if (strings.charAt(3)) { ... } // 也许应该是 'strings[0].charAt(3)'
也许您正在尝试引用未在父/祖先类或接口中声明的继承方法或字段。
也许您正试图引用您正在使用的类型中不存在(即尚未声明)的方法或字段;例如“绳子”.push()2。
也许您正在尝试将方法用作字段,反之亦然;例如“绳索”.length 或 someArray.length()。
也许您错误地对数组而不是数组元素进行了操作;例如 String strings[] = ... if (strings.charAt(3)) { ... } // 也许应该是 'strings[0].charAt(3)'
对于应该是类名的标识符:也许您忘记了导入类。也许您使用了“星号”导入,但该类未在您导入的任何包中定义。也许你忘记了一个新的:String s = String(); // 应该是 'new String()'
也许您忘记了导入该类。
也许您使用了“星型”导入,但该类未在您导入的任何包中定义。
也许你忘记了一个新的:String s = String(); // 应该是 'new String()'
对于类型或实例似乎没有您期望它拥有的成员(例如方法或字段)的情况:也许您已经声明了一个嵌套类或一个隐藏您打算使用的类型的泛型参数。也许您正在隐藏静态或实例变量。也许你输入了错误的类型;例如,由于 IDE 完成或自动更正可能建议使用 java.awt.List 而不是 java.util.List。也许您正在使用(编译)错误版本的 API。也许您忘记将对象转换为适当的子类。也许您已经将变量的类型声明为您正在寻找的成员的超类型。
也许您已经声明了一个嵌套类或一个泛型参数,这些参数隐藏了您打算使用的类型。
也许您正在隐藏静态或实例变量。
也许你输入了错误的类型;例如,由于 IDE 完成或自动更正可能建议使用 java.awt.List 而不是 java.util.List。
也许您正在使用(编译)错误版本的 API。
也许您忘记将对象转换为适当的子类。
也许您已经将变量的类型声明为您正在寻找的成员的超类型。
问题通常是上述问题的组合。例如,也许您“星标”导入了 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
。或者最好还是使用 Java 构建工具;例如 Ant、Maven、Gradle 等。
还有一些其他更模糊的原因......我将在下面处理。
3. 如何修复这些错误?
一般来说,您首先要找出导致编译错误的原因。
查看文件中由编译错误消息指示的行。
确定错误消息所指的是哪个符号。
弄清楚为什么编译器说它找不到符号;看上面!
然后你想想你的代码应该说什么。最后,您确定需要对源代码进行哪些更正以执行您想要的操作。
请注意,并非每个“更正”都是正确的。考虑一下:
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 - 可能是错误的!
等等。
关键是您需要了解您的代码试图做什么才能找到正确的修复程序。
4. 不明原因
以下是一些“找不到符号”看似莫名其妙的情况......直到你仔细观察。
不正确的依赖关系:如果您使用管理构建路径和项目依赖关系的 IDE 或构建工具,您可能在依赖关系方面犯了错误;例如,遗漏了一个依赖项,或者选择了错误的版本。如果您使用的是构建工具(Ant、Maven、Gradle 等),请检查项目的构建文件。如果您使用的是 IDE,请检查项目的构建路径配置。找不到符号“var”:您可能正在尝试使用较旧的编译器或较旧的 --source 级别编译使用局部变量类型推断(即 var 声明)的源代码。 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“测试”树的场景:见这个答案。 (显然,这个特定的错误很久以前就已经修复了。) Android 问题:当您为 Android 编程时,遇到与 R 相关的“找不到符号”错误,请注意 R 符号是由 context.xml 定义的文件。检查您的 context.xml 文件是否正确并位于正确的位置,以及是否已生成/编译了相应的 R 类文件。请注意,Java 符号区分大小写,因此相应的 XML id 也区分大小写。 Android 上的其他符号错误可能是由于前面提到的原因;例如,缺少或不正确的依赖项、不正确的包名称、特定 API 版本中不存在的方法或字段、拼写/键入错误等等。隐藏系统类:我见过编译器抱怨子字符串是未知符号的情况,例如以下 String s = ... String s1 = s.substring(1);事实证明,程序员已经创建了他们自己的 String 版本,并且他的类版本没有定义子字符串方法。我见过人们用 System、Scanner 和其他类来做这件事。教训:不要使用与公共库类相同的名称定义自己的类!该问题也可以通过使用完全限定名称来解决。例如,在上面的例子中,程序员可以这样写: java.lang.String s = ... java.lang.String s1 = s.substring(1); Homoglyphs:如果您对源文件使用 UTF-8 编码,则可能会有看起来相同但实际上不同的标识符,因为它们包含同形文字。请参阅此页面了解更多信息。您可以通过将自己限制为 ASCII 或 Latin-1 作为源文件编码并使用 Java \uxxxx 对其他字符进行转义来避免这种情况。
- 如果您确实在运行时异常或错误消息中看到了这一点,那么您已经将 IDE 配置为运行带有编译错误的代码,或者您的应用程序正在生成和编译代码......在运行时。 2 - 土木工程的三个基本原则:水不会向上流动,木板的侧面更坚固,你不能推动绳索。
如果您忘记了 new
,您也会收到此错误:
String s = String();
相对
String s = new String();
因为不带 new
关键字的调用将尝试查找名为 String
且不带参数的(本地)方法 - 并且该方法签名可能未定义。
“变量超出范围”的另一个示例
正如我已经多次看到这类问题的那样,也许再举一个例子来说明什么是非法的,即使它可能感觉还不错。
考虑这段代码:
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 值预先分配给外部范围内的变量名,然后在 if 中重新分配:
String message = "We have an error";
if(somethingIsTrue()) {
message = "Everything is fine";
}
System.out.println(message);
final
变量声明。
在 Eclipse 中获取此错误的一种方法:
在 src/test/java 中定义一个类 A。在 src/main/java 中定义另一个使用类 A 的类 B。
结果:Eclipse 将编译代码,但 maven 会给出“找不到符号”。
根本原因:Eclipse 使用主树和测试树的组合构建路径。不幸的是,它不支持对 Eclipse 项目的不同部分使用不同的构建路径,而这正是 Maven 所需要的。
解决方案 :
不要那样定义你的依赖关系;即不要犯这个错误。定期使用 Maven 构建您的代码库,以便您尽早发现这个错误。一种方法是使用 CI 服务器。
解决了
使用 IntelliJ
选择 Build->Rebuild Project 将解决它
“找不到”意味着编译器找不到合适的变量、方法、类等……如果你得到那个错误消息,首先你要找到得到错误消息的代码行……然后你会能够找到在使用之前没有定义的变量、方法或类。确认后初始化该变量、方法或类可以用于以后的需求...考虑以下示例。
我将创建一个演示类并打印一个名称...
class demo{
public static void main(String a[]){
System.out.print(name);
}
}
现在看看结果..
https://i.stack.imgur.com/zHJdz.jpg
那个错误说,“变量名找不到”..为'name'变量定义和初始化值可以取消那个错误..实际上像这样,
class demo{
public static void main(String a[]){
String name="smith";
System.out.print(name);
}
}
现在看看新的输出......
https://i.stack.imgur.com/Q8fYZ.jpg
好的 成功解决了那个错误..同时,如果你能得到“找不到方法”或“找不到类”的东西,首先,定义一个类或方法,然后使用它..
如果您在其他地方的构建中遇到此错误,而您的 IDE 说一切都很好,那么请检查您是否在两个地方使用相同的 Java 版本。
例如,Java 7 和 Java 8 具有不同的 API,因此在较旧的 Java 版本中调用不存在的 API 会导致此错误。
正如人们上面提到的那样,可能存在各种情况。有几件事帮助我解决了这个问题。
如果您使用 IntelliJ 文件 -> '无效缓存/重新启动'
或者
被引用的类在另一个项目中,并且该依赖项未添加到我项目的 Gradle 构建文件中。所以我使用 compile project(':anotherProject') 添加了依赖项
它奏效了。 !
如果 eclipse Java 构建路径映射到 7、8 并且在 Project pom.xml Maven 属性中提到 java.version 比 7,8 更高的 Java 版本(9、10、11 等),则需要在 pom 中更新。 xml 文件。
在 Eclipse 中,如果 Java 映射到 Java 版本 11,而在 pom.xml 中它映射到 Java 版本 8。通过 eclipse IDE 帮助 -> 安装新软件 -> 中的以下步骤将 Eclipse 支持更新到 Java 11
将以下链接 http://download.eclipse.org/eclipse/updates/4.9-P-builds 粘贴到 Work With
或者
添加(将打开弹出窗口)->
Name:
Java 11 支持 Location:
http://download.eclipse.org/eclipse/updates/4.9-P-builds
然后在 pom.xml 文件的 Maven 属性中更新 Java 版本,如下所示
<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 steps
我也遇到了这个错误。 (为此我用谷歌搜索并被定向到此页面)
问题:我正在从另一个项目 B 中定义的类调用项目 A 的类中定义的静态方法。我收到以下错误:
error: cannot find symbol
解决方案:我解决了这个问题,首先构建定义方法的项目,然后构建调用方法的项目。
您使用 maven compile 编译了代码,然后使用 maven test 运行它工作正常。现在,如果您更改了代码中的某些内容,然后没有编译就运行它,您将收到此错误。
解决方案:再次编译它,然后运行测试。对我来说,它是这样工作的。
mvn test
,则应在运行测试之前重新编译项目中的所有内容...。如果您运行 mvn surefile:test
,则不应重新编译任何内容。
就我而言 - 我必须执行以下操作:
将 context.xml 文件从 src/java/package 移动到资源目录 (IntelliJ IDE) 清理目标目录。
有关提示,请仔细查看引发错误的类名名称和行号,例如:Compilation failure [ERROR] \applications\xxxxx.java:[44,30] error: cannot find symbol
另一个原因是 java 版本不支持的方法说 jdk7 vs 8。检查你的 %JAVA_HOME%
我们在设置为 Gradle 多项目构建的 Java 项目中遇到错误。结果发现其中一个子项目缺少 Gradle Java Library plugin。这阻止了子项目的类文件对构建中的其他项目可见。
通过以下方式将Java库插件添加到子项目的build.gradle
后,错误消失了:
plugins {
...
id 'java-library'
}
回复:4.4:Stephen C 出色答案中的早期构建问题:
我在开发 osgi 应用程序时遇到了这种情况。
我有一个 java 项目 A
,它是 B
的依赖项。构建 B
时,出现错误:
Compilation failure: org.company.projectA.bar.xyz does not exist
但是在eclipse中,根本没有编译问题。
调查
当我查看 A.jar
时,有 org.company.projectA.foo.abc
的类,但没有 org.company.projectA.bar.xyz
的类。
缺少类的原因是,在 A/pom.xml
中,是导出相关包的条目。
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
...
<configuration>
<instructions>
....
<Export-Package>org.company.projectA.foo.*</Export-Package>
</instructions>
</configuration>
</plugin>
解决方案像这样添加缺少的包:
<Export-Package>org.company.projectA.foo.*,org.company.projectA.bar.*</Export-Package>
并重建一切。
现在 A.jar
包含所有预期的类,并且所有内容都可以编译。
我在编译我的项目时遇到了同样的问题,我检查了我的 java 编译器版本,它曾经在 java 1.8 上工作,但不小心设置为 java 17,我将它改回版本 1.8,我的问题得到了解决。
https://i.stack.imgur.com/bM7uY.png
我像这样解决了这个错误......android的疯狂。我将包名称作为适配器,并且我将名称重构为使用“a”而不是“A”的适配器并解决了错误。
System.out.println
中的println
放在标准编译器下的类级别,我们会得到<identifier> expected
(demo),但在 IntelliJ 中我们会看到Cannot resolve symbol 'println'
(demo)。