“Cannot find symbol”编译错误是什么意思?


“Cannot find symbol”编译错误是什么意思?


1.“Cannot find symbol”错误是什么意思?

首先,它是编译错误1。这意味着,无论有在Java源代码中的问题,或有在你编译它的方式有问题。

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

  • 关键词:像true,false,class,while,等。
  • 文字:像42和'X'和"Hi mum!"。
  • 运营商和其他非字母数字标记:像+,=,{,等等。
  • 标识符:像Reader,i,toString,processEquibalancedElephants,等。
  • 注释和空白。

“Cannot find symbol”错误与标识符有关。编译代码时,编译器需要确定代码中每个标识符的含义。

“Cannot find symbol”错误意味着编译器无法执行此操作。您的代码似乎是指编译器无法理解的内容。

2.什么可能导致“Cannot find symbol”错误?

编译器查找了应该定义标识符的所有位置,并且找不到定义。这可能是由许多事情引起的。常见的如下:

  • 对于标识符一般:
    • 也许你拼错了名字; 即StringBiulder代替StringBuilder。Java不能也不会尝试弥补拼写错误或输入错误。
    • 也许你弄错了; 即stringBuilder代替StringBuilder。所有Java标识符都区分大小写。
    • 也许你不恰当地使用了下划线; 即mystring和my_string不同。(如果你遵守Java风格规则,你将在很大程度上受到这种错误的保护...)
    • 也许你正在尝试使用被宣称为“其他地方”的东西; 即在一个不同的上下文中,你隐含地告诉编译器。(一个不同的类?一个不同的范围?一个不同的包?一个不同的代码库?)
  • 对于应引用变量的标识符:
    • 也许你忘了申报变量了。
    • 也许变量声明在您尝试使用它时不在范围内。(见下面的例子)
    • 对于应该是方法或字段名称的标识符:
    • 也许您正在尝试引用未在父/祖先类或接口中声明的继承方法或字段。
    • 也许您正在尝试将方法用作字段,反之亦然; 例如"someString".length或someArray.length()。
  • 对于应该是类名的标识符:
    • 也许你忘了导入课程。
    • 也许您使用了“星型”导入,但未在您导入的任何包中定义该类。
    • 也许你忘记了new:
String s = String();  // should be 'new String()'
  • 对于类型或实例似乎没有您期望的成员的情况:
    • 也许您已经声明了一个嵌套类或泛型参数,它们会影响您要使用的类型。
    • 也许您正在隐藏静态或实例变量。
    • 也许你导入了错误的类型; 例如,由于IDE完成或自动更正。
    • 也许您正在使用(编译)错误版本的API。
    • 也许您忘了将对象转换为适当的子类。

问题通常是上述的组合。例如,也许你“*”导入java.io.*,然后尝试使用Files类...这java.nio不是java.io。或许你打算写File...这是一个班级java.io。

以下是不正确的变量范围如何导致“Cannot find symbol”错误的示例:

for (int i = 0; i < strings.size(); i++) {
    if (strings.get(i).equalsIgnoreCase("fnoord")) {
        break;
    }
}
if (i < strings.size()) {
    ...
}

这将i在if语句中给出“Cannot find symbol”错误。虽然我们之前已经声明i,但该声明仅适用于for声明及其正文。以参考i在if声明中无法看到的那个声明i。它超出了范围。

(这里适当的修正可能是if在循环中移动语句,或者i在循环开始之前声明。)


这是一个导致困惑的例子,其中拼写错误导致看似无法解释的“Cannot find symbol”错误:

for (int i = 0; i < 100; i++); {
    System.out.println("i is " + i);
}

这将在println调用中显示i无法找到的编译错误。但是(我听到你说)我确实宣布了!

问题是之前的鬼鬼祟祟的分号{。Java语言将其定义为空语句。所以代码实际上意味着:

for (int i = 0; i < 100; i++);

{
    System.out.println("i is " + i);
}

该{ ... }块不是for循环的主体,因此声明i不在块的范围内。

这是由拼写错误引起的“Cannot find symbol”错误的另一个示例。

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

尽管与先前的声明中,tmp在tmp(...)表达是错误的。编译器将查找一个名为的方法tmp,但不会找到一个方法。先前声明的tmp是变量的名称空间,而不是方法的名称空间。

在我遇到的例子中,程序员实际上已经遗漏了一个操作员。他打算写的是这样的:

int res = tmp * (a + b);

如果从命令行编译,编译器可能Cannot find symbol还有另一个原因。您可能只是忘记编译或重新编译其他类。举例来说,如果你有类Foo和Bar地方Foo使用Bar。如果您从未编译过Bar并且运行过javac Foo.java,则可能会发现编译器找不到该符号Bar。简单的答案就是Foo和Bar在一起; 例如javac Foo.java Bar.javajavac *.java。或者更好的是仍然使用Java构建工具; 例如Ant,Maven,Gradle等。

还有其他一些比较模糊的原因......我将在下面讨论。

3.如何修复这些错误?

一般来说,您首先要弄清楚导致编译错误的原因。

  • 查看编译错误消息指示的文件中的行。
  • 确定错误消息正在讨论的符号。
  • 弄清楚为什么编译器说它Cannot find symbol; 往上看!

然后你想想你的代码应该说些什么。最后,您需要确定需要对源代码进行哪些更正才能执行您想要的操作。

请注意,并非每个“校正”都是正确的。考虑一下:

for (int i = 1; i < 10; i++) {
    for (j = 1; j < 10; j++) {
        ...
    }
}

假设编译器说“Cannot find symbol” j。有很多方法可以“修复”:

  • 我可以改变内for到for (int j = 1; j < 10; j++)-可能是正确的。
  • 我可以在内循环或外循环j 之前添加一个声明- 可能是正确的。forfor
  • 我可以改变j,以i在内部for循环-可能是错的!
  • 等等。

关键是您需要了解代码尝试执行的操作才能找到正确的修复方法。

4.模糊的原因

以下是一些“Cannot find symbol”看似莫名其妙的情况......直到你仔细观察。

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

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

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

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

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

这可能是一个IDE错误。例如,@ Joel Costigliola描述了Eclipse无法正确处理Maven“测试”树的场景:请参阅此答案。

5.重新定义系统类:我已经看到了编译器抱怨这substring是一个未知符号的情况,如下所示

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

事实证明,程序员已经创建了自己的版本,String并且他的类版本没有定义substring方法。

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

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

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