提问者:小点点

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


请解释以下有关“找不到符号”和“无法解析符号”错误的内容:

  • 它们是什么意思?
  • 哪些因素会导致它们?
  • 程序员如何着手修复它们?

这个问题的设计是为了种子一个全面的Q&;a关于这些常见的编译错误在Java。


共3个答案

匿名用户

不是真的。 “找不到符号”和“解析不了符号”是一回事。 有些Java编译器使用一个短语,有些则使用另一个。

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

您的Java源代码由以下内容组成:

  • 关键字:如truefalseclasswhile等。
  • 字面意思:如42“X”“Hi mum!”
  • 运算符和其他非字母数字标记:如+={等。
  • 标识符:如ReaderIToStringProcesseQuiBalanceDelephants等。
  • 批注和空格。

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

“找不到符号”错误意味着编译器无法做到这一点。 您的代码似乎引用了编译器不理解的内容。

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

  • 对于一般标识符:
    • 也许你把名字拼错了; 即StringBiulder而不是StringBuilder。 Java不能也不会试图弥补拼写或打字错误。
    • 也许你弄错了案子; 即StringBuilder而不是StringBuilder。 所有Java标识符都区分大小写。
    • 也许您不恰当地使用了下划线; 即mystringmy_string是不同的。 (如果你坚持Java风格的规则,你将在很大程度上避免犯这种错误……)
    • 也许你试图使用被声明为“其他地方”的东西; 即与您隐式地告诉编译器查找的上下文不同。 (不同的类?不同的作用域?不同的包?不同的代码基?)
    • 可能您忘了声明变量。
    • 可能在您试图使用变量声明时,它超出了范围。 (见下面的示例)

    对于应为方法名或字段名的标识符:

    >

  • 可能您试图引用的是未在父/祖类或接口中声明的继承方法或字段。
  • 可能您正在尝试引用您正在使用的类型中不存在(即尚未声明)的方法或字段; 例如“someString”.push()2.
  • 也许您正在尝试使用方法作为字段,反之亦然; 例如“SomeString”.lengthSomeArray.length().
  • 您可能错误地操作了数组而不是数组元素; 例如。

    String strings[] = ...
    if (strings.charAt(3)) { ... }
    // maybe that should be 'strings[0].charAt(3)'
    

    对于应为类名的标识符:

    >

  • 可能您忘记导入类。
  • 您可能使用了“星型”导入,但在导入的任何包中都没有定义该类。
  • 您可能忘记了一个,如:

    String s = String();  // should be 'new String()'
    

    对于类型或实例似乎没有您期望的成员的情况:

    • 您可能声明了嵌套类或泛型参数,该参数隐藏了您要使用的类型。
    • 您可能正在隐藏静态变量或实例变量。
    • 也许您导入了错误的类型; 例如,由于IDE完成或自动更正。
    • 可能您正在使用(编译)错误版本的API.
    • 您可能忘记将对象转换为适当的子类。

    问题往往是以上几个因素的综合。 例如,可能您“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);
    

    如果从命令行编译,编译器可能找不到符号还有另一个原因。 您可能只是忘记编译或重新编译其他类。 例如,如果您有类foobar,其中foo使用bar。 如果您从未编译过bar,并且运行javac foo.java,您可能会发现编译器找不到符号bar。 简单的答案是将foobar一起编译; 例如javac foo.Java bar.Javajavac*.。 或者更好地使用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);