“找不到符号”或“无法解析符号”错误是什么意思?

新手上路,请多包涵

请解释以下有关“找不到符号”、“无法解析符号”或“未找到符号”错误(在 Java 中):

  • 他们的意思是什么?
  • 什么事情会导致他们?
  • 程序员如何修复它们?

这个问题旨在为有关 Java 中这些常见编译错误的全面问答提供种子。

原文由 Stephen C 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 4k
2 个回答

0. 这些错误有什么区别吗?

并不真地。 “找不到符号”、“无法解析符号”和“找不到符号”都是同一个意思。 (不同的Java编译器是由不同的人编写的,不同的人用不同的措辞来表达同样的事情。)

1. “找不到符号”错误是什么意思?

首先,这是一个 编译错误 1 。这意味着 要么 你的 Java 源代码有问题, 要么 你编译它的方式有问题。

您的 Java 源代码包含以下内容:

  • 关键字:如 classwhile 等。
  • Literals: like true , false , 42 , 'X' and "Hi mum!" .
  • 运算符和其他非字母数字标记:如 +={ 等等。
  • 标识符:喜欢 Readeri toString processEquibalancedElephants
  • 注释和空格。

“找不到符号”错误与标识符有关。编译代码时,编译器需要弄清楚代码中每个标识符的含义。

“找不到符号”错误意味着编译器无法执行此操作。您的代码似乎指的是编译器不理解的东西。

2. 什么会导致“找不到符号”错误?

作为第一顺序,只有一个原因。编译器查看了所有 应该 定义标识符的地方,但找不到定义。这可能是由多种原因造成的。常见的有以下几种:

  • 对于一般标识符:

    • 也许你拼错了名字;即 StringBiulder 而不是 StringBuilder 。 Java 不能也不会尝试弥补拼写错误或输入错误。
    • 也许你搞错了;即 stringBuilder 而不是 StringBuilder 。所有 Java 标识符都区分大小写。
    • 也许你不恰当地使用了下划线;即 mystringmy_string 是不同的。 (如果你坚持 Java 风格的规则,你将在很大程度上避免这个错误……)
    • 也许您正在尝试使用被声明为“其他地方”的东西;即在与您隐式告诉编译器查看的不同上下文中。 (不同的类?不同的范围?不同的包?不同的代码库?)
  • 对于应该引用变量的标识符:

    • 也许你忘了声明变量。
    • 也许变量声明在您尝试使用它时超出了范围。 (见下面的例子)
  • 对于应该是方法或字段名称的标识符:

    • 也许您正在尝试引用未在父/祖先类或接口中声明的继承方法或字段。

    • 也许您正试图引用您正在使用的类型中不存在(即尚未声明)的方法或字段;例如 "rope".push() 2 。

    • 也许您正在尝试将方法用作字段,反之亦然;例如 "rope".lengthsomeArray.length()

    • 也许您错误地对数组而不是数组元素进行了操作;例如

        String strings[] = ...
        if (strings.charAt(3)) { ... }
        // maybe that should be 'strings[0].charAt(3)'
    
    
    
  • 对于应该是类名的标识符:

    • 也许您忘记了导入该类。

    • 也许您使用了“星号”导入,但该类未在您导入的任何包中定义。

    • 也许你忘记了 new 如下:

         String s = String();  // should be 'new String()'
    
    
    
  • 对于类型或实例似乎没有您期望的成员(例如方法或字段)的情况:

    • 也许您已经声明了一个嵌套类或一个泛型参数,这些参数 隐藏 了您打算使用的类型。
    • 也许您正在隐藏静态或实例变量。
    • 也许你输入了错误的类型;例如,由于 IDE 完成或自动更正可能建议使用 java.awt.List 而不是 java.util.List
    • 也许您正在使用(编译)错误版本的 API。
    • 也许您忘记将对象转换为适当的子类。
    • 也许您已经将变量的类型声明为您正在寻找的成员的超类型。

问题通常是上述问题的组合。 For example, maybe you “star” imported java.io.* and then tried to use the Files class … which is in java.nio not 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 循环的主体,因此 ifor 的语句中 前一个声明块中的 _范围_。


这是由拼写错误引起的“找不到符号”错误的另一个示例。

 int tmp = ...
int res = tmp(a + b);

尽管前面有声明,但 tmp tmp(...) 是错误的。编译器将寻找一种名为 tmp 的方法,但找不到。先前声明的 tmp 位于变量的命名空间中,而不是方法的命名空间中。

在我遇到的示例中,程序员实际上遗漏了一个运算符。他想写的是这样的:

 int res = tmp * (a + b);


如果您从命令行编译,编译器可能找不到符号还有另一个原因。您可能只是忘记了编译或重新编译其他一些类。例如,如果您有类 FooBar 其中 Foo 使用 Bar 。如果您从未编译过 Bar 并运行 javac Foo.java ,您很容易发现编译器找不到符号 Bar 。简单的答案是将 FooBar 一起编译;例如 javac Foo.java Bar.javajavac *.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. 不明原因

以下是一些“找不到符号”看似莫名其妙的情况……直到你仔细观察。

  1. 不正确的依赖关系:如果您使用管理构建路径和项目依赖关系的 IDE 或构建工具,您可能在依赖关系方面犯了错误;例如,遗漏了一个依赖项,或者选择了错误的版本。如果您使用的是构建工具(Ant、Maven、Gradle 等),请检查项目的构建文件。如果您使用的是 IDE,请检查项目的构建路径配置。

  2. 找不到符号“var” :您可能正在尝试使用较旧的编译器或较旧的 --source 级别编译使用局部变量类型推断(即 var 声明)的源代码。 var 是在 Java 10 中引入的。检查您的 JDK 版本和构建文件,以及(如果发生在 IDE 中),IDE 设置。

  3. 您没有编译/重新编译:有时会发生新的 Java 程序员不了解 Java 工具链是如何工作的,或者没有实现可重复的“构建过程”;例如使用 IDE、Ant、Maven、Gradle 等。在这种情况下,程序员最终可能会追着尾巴寻找 实际上是 由于没有正确重新编译代码等造成的虚幻错误。

另一个例子是当您使用 (Java 9+) java SomeClass.java 编译和运行一个类时。如果该类依赖于您尚未编译(或重新编译)的另一个类,则您可能会收到涉及第二类的“无法解析符号”错误。其他源文件不会自动编译。 java 命令的新“编译和运行”模式不适用于运行具有多个源代码文件的程序。

  1. 较早的构建问题:较早的构建可能以某种方式失败,导致 JAR 文件缺少类。如果您使用的是构建工具,通常会注意到这种失败。但是,如果您从其他人那里获取 JAR 文件,则您依赖于 他们 正确构建并注意到错误。如果您怀疑这一点,请使用 tar -tvf 列出可疑 JAR 文件的内容。

  2. IDE 问题:人们报告了他们的 IDE 被混淆并且 IDE 中的编译器找不到存在的类的情况……或者相反的情况。

    • 如果 IDE 配置了错误的 JDK 版本,则可能会发生这种情况。

    • 如果 IDE 的缓存与文件系统不同步,就会发生这种情况。有特定于 IDE 的方法来解决这个问题。

    • 这可能是一个 IDE 错误。例如,@Joel Costigliola 描述了 Eclipse 没有正确处理 Maven“测试”树的场景: 见这个答案。 (显然,那个特定的错误很久以前就被修复了。)

  3. Android 问题:当您为 Android 编程时,出现与 R 相关的“找不到符号”错误,请注意 R 符号由 context.xml 定义 --- 文件。检查您的 context.xml 文件是否正确且在正确的位置,以及相应的 R 类文件是否已生成/编译。请注意,Java 符号区分大小写,因此相应的 XML id 也区分大小写。

    Android 上的其他符号错误可能是由于前面提到的原因;例如,缺少或不正确的依赖项、不正确的包名称、特定 API 版本中不存在的方法或字段、拼写/键入错误等等。

  4. 隐藏系统类:我见过编译器抱怨 substring 是一个未知符号的情况,如下所示

   String s = ...
   String s1 = s.substring(1);

事实证明,程序员已经创建了他们自己版本的 String 并且他的类版本没有定义 substring 方法。我见过人们用 SystemScanner 和其他类来做这个。

教训: 不要使用与公共库类相同的名称定义自己的类!

该问题也可以通过使用完全限定名称来解决。例如,在上面的示例中,程序员 可以 编写:

    java.lang.String s = ...
   java.lang.String s1 = s.substring(1);

  1. Homoglyphs: 如果您对源文件使用 UTF-8 编码,则可能会有 看起来 相同但实际上不同的标识符,因为它们包含同形文字。请参阅 此页面 了解更多信息。

您可以通过将自己限制为 ASCII 或 Latin-1 作为源文件编码并使用 Java \uxxxx 对其他字符进行转义来避免这种情况。


1 - 如果您 确实 在运行时异常或错误消息中看到了这一点,那么您已经将 IDE 配置为运行带有编译错误的代码,或者您的应用程序正在生成和编译代码……在运行时。

2 - 土木工程的三个基本原则:水不会上坡,木板的侧面更坚固, _你不能推动绳索_。

原文由 Stephen C 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果您忘记了 new ,您也会收到此错误:

 String s = String();

相对

String s = new String();

因为没有 new 关键字的调用将尝试查找名为 String 的(本地)方法,没有参数 - 并且该方法签名可能未定义。

原文由 thinkterry 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏