什么是“找不到符号”的编译错误是什么意思?

请解释以下关于“无法find符号”错误:

  • 这个错误是什么意思?
  • 什么东西可以导致这个错误?
  • 程序员如何去解决这个错误?

这个问题被devise成一个关于Java中“找不到符号”编译错误的综合性问题。

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

首先,这是一个编译错误 1 。 这意味着您的Java源代码中存在问题, 或者您编译的方式存在问题。

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

  • 关键字:像truefalseclasswhile等等。
  • 文字:像42'X'"Hi mum!"
  • 运算符和其他非字母数字令牌:如+={等等。
  • 标识符:像ReaderitoStringprocessEquibalancedElephants等。
  • 评论和空白。

“无法find符号”错误是关于标识符。 编译代码时,编译器需要确定代码中每个标识符的含义。

“无法find符号”错误意味着编译器不能这样做。 你的代码似乎是指编译器不理解的东西。

2.什么会导致“无法find符号”错误?

作为第一个订单,只有一个原因。 编译器在应该定义标识符的所有地方查找,并找不到定义。 这可能是由一些事情造成的。 常见的有:

  • 对于一般的标识符:
    • 也许你拼错了名字; 即StringBiulder而不是StringBuilder 。 Java不能也不会试图弥补不好的拼写或打字错误。
    • 也许你弄错了; 即stringBuilder而不是StringBuilder 。 所有Java标识符都区分大小写。
    • 也许你用不恰当的下划线; 即mystringmy_string是不同的。 (如果你坚持Java风格的规则,你将会在很大程度上免受这个错误的影响…)
    • 也许你正在尝试使用被宣布为“别的地方”的东西; 即在不同的上下文中,隐式地告诉编译器看看。 (一个不同的类?一个不同的范围?一个不同的包?一个不同的代码库?)
  • 对于应该引用variables的标识符:
    • 也许你忘了声明variables。
    • 也许variables声明超出了你试图使用它的地步。 (见下面的例子)
  • 对于应该是方法或字段名称的标识符:
    • 也许你正在试图引用父类/祖先类或接口中没有声明的inheritance方法或字段。
    • 也许你正在尝试使用一种方法作为一个领域,反之亦然; 例如"someString".lengthsomeArray.length()
  • 对于应该是类名称的标识符:

    • 也许你忘了导入课程。
    • 也许你使用了“明星”导入,但是这个类没有在你导入的任何包中定义。
    • 也许你忘了一个new如:

       String s = String(); // should be 'new String()' 
  • 对于types或实例似乎没有您期望的成员的情况:

    • 也许你已经声明了一个嵌套的类或者一个通用的参数来影响你想要使用的types。
    • 也许你正在遮蔽一个静态或实例variables。
    • 也许你input了错误的types; 例如由于IDE完成或自动更正。
    • 也许你正在使用(编译)一个API的错误版本。
    • 也许你忘了把你的对象转换成适当的子类。

问题往往是上述的组合。 例如,也许你“星”import的java.io.*然后试图使用Files类…这是在java.nio不是java.io.* 或者也许你打算写File …这 java.io一个类。


下面是一个不正确的variables范围如何导致“无法find符号”错误的例子:

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

这将给iif语句中的“无法find符号”错误。 虽然我们以前曾经宣布过i ,但是这个声明只是声明和声明的范围之内 。 在if语句中对i的引用不能看到 i声明。 这是超出范围

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


这是一个例子,导致拼写错误导致看似莫名的“无法find符号”的错误:

 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的声明不在块的范围内


这是另一个由错字引起的“无法find符号”错误的例子。

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

尽pipe前面的声明, tmp(...)expression式中的tmp(...)是错误的。 编译器会查找一个名为tmp的方法,并且找不到一个方法。 之前声明的tmp在variables的名称空间中,而不是方法的名称空间。

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

 int res = tmp * (a + b); 

编译器如果从命令行编译时可能找不到符号,还有另一个原因。 你可能只是忘记了编译或重新编译其他类。 例如,如果您有FooBarFoo使用Bar 。 如果你从来没有编译过Bar并运行javac Foo.java ,那么你很容易发现编译器找不到符号Bar 。 简单的答案是FooBar在一起; 例如javac Foo.java Bar.javajavac *.java 。 或者更好的还是使用Java构build工具; 比如Ant,Maven,Gradle等等。

还有一些其他更晦涩的原因,我将在下面讨论。

3.如何解决这些错误?

一般来说,你首先弄清楚是什么导致了编译错误。

  • 查看编译错误消息指示的文件中的行。
  • 确定错误消息正在讨论哪个符号。
  • 找出为什么编译器说它找不到符号; 往上看!

然后你想想你的代码应该说什么。 然后,最后你找出你需要做的修正你的源代码做你想做的。

请注意,并非每个“更正”都是正确的。 考虑这个:

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

假设编译器对j表示“无法find符号”。 有很多方法可以“修复”:

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

重点是你需要了解你的代码试图做什么,以find正确的修复。

4.模糊的原因

这里有一些情况下,“找不到符号”看起来似乎莫名其妙,直到你仔细观察。

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

  2. IDE问题 :人们已经报告了他们的IDE感到困惑的情况,IDE中的编译器找不到存在的类…或相反的情况。

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

    • 这可能是一个IDE错误。 例如@Joel Costigliola描述了一个Eclipse不能正确处理Maven“testing”树的场景: 看到这个答案

  3. 重新定义系统类 :我已经看到了编译器抱怨substring是下面类似的未知符号的情况

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

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

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

  4. 同态语言:如果你为你的源文件使用UTF-8编码,有可能使标识符看起来相同,但实际上是不同的,因为它们包含同类映射。 请参阅此页面了解更多信息。

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


1 – 如果偶然发现运行时exception或错误消息,那么要么您已经configuration了IDE来运行带有编译错误的代码,要么您的应用程序正在运行时生成并编译代码。

如果你忘记了一个new你也会得到这个错误:

 String s = String(); 

 String s = new String(); 

“variables超出范围”的另一个例子是,

正如我已经多次看到这样的问题,也许再多一个例子,即使它可能感觉不错,也是非法的。

考虑这个代码:

 if(somethingIsTrue()) { String message = "Everything is fine"; } else { String message = "We have an error"; } System.out.println(message); 

这是无效的代码。 因为没有一个名为message的variables在它们各自的作用域之外是可见的 – 在这种情况下,这可能就是括号{}

你可能会说:“但是一个名为消息的variables是以任何方式定义的 – 所以消息if之后定义的。

但是你会错的

Java没有free()delete操作符,所以它必须依靠追踪variables范围来找出何时不再使用variables(连同对这些variables的原因的引用)。

如果你认为自己做得很好,那就特别糟糕。 在“优化”代码之后,我看到了这样的错误:

 if(somethingIsTrue()) { String message = "Everything is fine"; System.out.println(message); } else { String message = "We have an error"; System.out.println(message); } 

“哦,有重复的代码,让我们把这条共同的线” – “并在那里它。

处理这种范围问题的最常见的方法是预先将其他值分配给外部范围中的variables名称,然后在以下情况下重新分配:

 String message = "We have an error"; if(somethingIsTrue()) { message = "Everything is fine"; } System.out.println(message); 

在Eclipse中得到这个错误的一种方法是:

  1. src/test/java定义一个类A
  2. src/main/java中定义另一个使用class AB

结果:Eclipse会编译代码,但是maven会给出“找不到符号”。

基本原因:Eclipse正在为主要和testing树使用组合构buildpath。 不幸的是,它不支持为Eclipse项目的不同部分使用不同的构buildpath,这正是Maven需要的。

解答:

  1. 不要这样定义你的依赖关系; 即不要犯这个错误。
  2. 定期使用Maven构build您的代码库,以便尽早找出错误。 一种方法是使用CI服务器。

如果在构build的其他位置出现此错误,而您的IDE说明一切正常,则请检查您是否在两个位置使用相同的Java版本。

例如,Java 7和Java 8具有不同的API,因此在较旧的Java版本中调用不存在的API会导致此错误。

我也得到这个错误。 (为此我google了,我被导向到这个页面)

问题:我正在从另一个项目B中定义的类调用项目A的类中定义的静态方法。我收到以下错误:

 error: cannot find symbol 

解决scheme:我解决了这个问题,首先构build方法定义的项目,然后调用方法的项目。

对于提示,请仔细查看引发错误的类名称和行号,例如:编译失败[错误] \ applications \ xxxxx.java:[44,30]错误:无法find符号

另一个原因是java版本不支持的方法,比如说jdk7和8.检查你的%JAVA_HOME%