1. Overview
1.概述
In this tutorial, we’ll review what compilation errors are. Then we’ll specifically explain the “cannot find symbol” error and how it’s caused.
在本教程中,我们将回顾什么是编译错误。然后我们将具体解释”找不到符号“的错误以及它是如何造成的。
2. Compile Time Errors
2.编译时的错误
During compilation, the compiler analyses and verifies the code for numerous things, such as reference types, type casts, and method declarations, to name a few. This part of the compilation process is important, since during this phase we’ll get a compilation error.
在编译过程中,编译器会分析和验证代码中的许多东西,如引用类型、类型转换和方法声明等等。编译过程的这一部分很重要,因为在这个阶段我们会得到一个编译错误。
Basically, there are three types of compile-time errors:
基本上,有三种类型的编译时错误。
- We can have syntax errors. One of the most common mistakes any programmer can make is forgetting to put the semicolon at the end of the statement. Some other mistakes include forgetting imports, mismatching parentheses, or omitting the return statement.
- Next, there are type-checking errors. This is the process of verifying type safety in our code. With this check, we’re making sure that we have consistent types of expressions. For example, if we define a variable of type int, we should never assign a double or String value to it.
- Finally, there’s the possibility that the compiler crashes. This is very rare, but it can happen. In this case, it’s good to know that our code might not be the problem, and that it’s an external issue instead.
3. The “cannot find symbol” Error
3.“找不到符号”错误
The “cannot find symbol” error comes up mainly when we try to use a variable that’s not defined or declared in our program.
找不到符号“的错误主要出现在我们试图使用一个没有在程序中定义或声明的变量时。
When our code compiles, the compiler needs to verify all the identifiers we have. The error “cannot find symbol” means we’re referring to something that the compiler doesn’t know about.
当我们的代码编译时,编译器需要验证我们所有的标识符。错误 “找不到符号“意味着我们 指的是编译器不知道的东西。
3.1. What Can Cause the “cannot find symbol” Error?
3.1.什么会导致“找不到符号”错误?
There’s really only one cause; the compiler couldn’t find the definition of a variable we’re trying to reference.
其实只有一个原因;编译器找不到我们试图引用的变量的定义。
But there are many reasons why this happens. To help us understand why, let’s remind ourselves what our Java source code consists of:
但发生这种情况的原因有很多。为了帮助我们理解原因,让我们提醒自己,我们的Java源代码由什么组成。
- Keywords: true, false, class, while
- Literals: numbers and text
- Operators and other non-alphanumeric tokens: -, /, +, =, {
- Identifiers: main, Reader, i, toString, etc.
- Comments and whitespace
4. Misspelling
4.拼写错误
The most common issues are all spelling-related. If we recall that all Java identifiers are case-sensitive, we can see that the following would all be different ways to incorrectly refer to the StringBuilder class:
最常见的问题都是与拼写有关的。如果我们回想一下,所有的Java标识符都是区分大小写的,我们可以看到,下面这些都是错误地引用StringBuilder类的不同方式。
- StringBiulder
- stringBuilder
- String_Builder
5. Instance Scope
5.实例范围
This error can also be caused when using something that was declared outside of the scope of the class.
当使用在类的范围之外声明的东西时,也会引起这个错误。
For example, let’s say we have an Article class that calls a generateId method:
例如,假设我们有一个Article类,它调用一个generateId方法。
public class Article {
private int length;
private long id;
public Article(int length) {
this.length = length;
this.id = generateId();
}
}
But we declare the generateId method in a separate class:
但我们在一个单独的类中声明generateId方法。
public class IdGenerator {
public long generateId() {
Random random = new Random();
return random.nextInt();
}
}
With this setup, the compiler will give a “cannot find symbol” error for generateId on line 7 of the Article snippet. This is because the syntax of line 7 implies that the generateId method is declared in Article.
通过这样的设置,编译器将在Article片段的第7行为generateId给出一个”找不到符号“的错误。这是因为第7行的语法暗示了generateId方法是在Article中声明的。
Like in all mature languages, there’s more than one way to address this issue, but one way would be to construct IdGenerator in the Article class and then call the method:
像所有成熟的语言一样,解决这个问题的方法不止一种,但有一种方法是在Article类中构造IdGenerator,然后调用该方法。
public class Article {
private int length;
private long id;
public Article(int length) {
this.length = length;
this.id = new IdGenerator().generateId();
}
}
6. Undefined Variables
6.未定义的变量
Sometimes we forget to declare the variable. As we can see from the snippet below, we’re trying to manipulate the variable we haven’t declared, which in this case is text:
有时我们会忘记声明变量。从下面的片段中我们可以看到,我们正试图操作我们没有声明的变量,在这个例子中是text。
public class Article {
private int length;
// ...
public void setText(String newText) {
this.text = newText; // text variable was never defined
}
}
We solve this problem by declaring the variable text of type String:
我们通过声明text类型的变量String来解决这个问题。
public class Article {
private int length;
private String text;
// ...
public void setText(String newText) {
this.text = newText;
}
}
7. Variable Scope
7.可变范围
When a variable declaration is out of scope at the point we tried to use it, it’ll cause an error during compilation. This typically happens when we work with loops.
当一个变量声明在我们试图使用它的时候超出了范围,它将在编译过程中引起一个错误。这通常发生在我们使用循环的时候。
Variables inside the loop aren’t accessible outside the loop:
循环内的变量在循环外是无法访问的。
public boolean findLetterB(String text) {
for (int i=0; i < text.length(); i++) {
Character character = text.charAt(i);
if (String.valueOf(character).equals("b")) {
return true;
}
return false;
}
if (character == "a") { // <-- error!
...
}
}
The if statement should go inside the for loop if we need to examine characters more:
如果我们需要检查更多的字符,if语句应该放在for循环内。
public boolean findLetterB(String text) {
for (int i = 0; i < text.length(); i++) {
Character character = text.charAt(i);
if (String.valueOf(character).equals("b")) {
return true;
} else if (String.valueOf(character).equals("a")) {
...
}
return false;
}
}
8. Invalid Use of Methods or Fields
8.方法或字段的无效使用
The “cannot find symbol” error will also occur if we use a field as a method or vice versa:
如果我们把一个字段作为一个方法使用,也会出现”找不到符号“的错误,反之亦然。
public class Article {
private int length;
private long id;
private List<String> texts;
public Article(int length) {
this.length = length;
}
// getters and setters
}
If we try to refer to the Article’s texts field as if it were a method:
如果我们试图引用文章的texts字段,就好像它是一个方法。
Article article = new Article(300);
List<String> texts = article.texts();
Then we’d see the error.
然后我们就会看到这个错误。
This is because the compiler is looking for a method called texts, and there isn’t one.
这是因为编译器正在寻找一个名为texts的方法,但并没有这个方法。
Actually, there’s a getter method we can use instead:
实际上,我们可以用一个getter方法来代替。
Article article = new Article(300);
List<String> texts = article.getTexts();
Mistakenly operating on an array rather than an array element is also an issue:
错误地对数组而不是数组元素进行操作也是一个问题。
for (String text : texts) {
String firstLetter = texts.charAt(0); // it should be text.charAt(0)
}
And so is forgetting the new keyword:
而忘记了新关键词也是如此。
String s = String(); // should be 'new String()'
9. Package and Class Imports
9.包和类的进口
Another problem is forgetting to import the class or package, like using a List object without importing java.util.List:
另一个问题是忘记导入类或包,比如使用一个List对象而没有导入java.util.List。
// missing import statement:
// import java.util.List
public class Article {
private int length;
private long id;
private List<String> texts; <-- error!
public Article(int length) {
this.length = length;
}
}
This code won’t compile, since the program doesn’t know what List is.
这段代码不会被编译,因为程序不知道List是什么。
10. Wrong Imports
10.错误的进口
Importing the wrong type, due to IDE completion or auto-correction is also a common issue.
由于IDE完成或自动纠正而导入错误的类型也是一个常见的问题。
Think of a scenario where we want to use dates in Java. A lot of times, we could import a wrong Date class, which doesn’t provide the same methods and functionalities as other date classes that we might need:
想一想我们想在Java中使用日期的情景。很多时候,我们可能会导入一个错误的Date类,它没有提供与我们可能需要的其他日期类一样的方法和功能。
Date date = new Date();
int year, month, day;
To get the year, month, or day for java.util.Date, we also need to import the Calendar class and extract the information from there.
为了获得java.util.Date的年、月、日,我们还需要导入Calendar类,并从那里提取信息。
Simply invoking getDate() from java.util.Date won’t work:
简单地从java.util.Date调用getDate()不会起作用。
...
date.getDay();
date.getMonth();
date.getYear();
Instead, we use the Calendar object:
相反,我们使用Calendar对象。
...
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));
cal.setTime(date);
year = cal.get(Calendar.YEAR);
month = cal.get(Calendar.MONTH);
day = cal.get(Calendar.DAY_OF_MONTH);
However, if we’ve imported the LocalDate class, we won’t need additional code to provide us the information we need:
然而,如果我们已经导入了LocalDate类,我们就不需要额外的代码来提供我们需要的信息。
...
LocalDate localDate=date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
year = localDate.getYear();
month = localDate.getMonthValue();
day = localDate.getDayOfMonth();
11. Conclusion
11.结语
Compilers work on a fixed set of rules that are language-specific. If a code doesn’t stick to these rules, the compiler can’t perform a conversion process, which results in a compilation error. When we face the “cannot find symbol” compilation error, the key is to identify the cause.
编译器在一套固定的规则上工作,这些规则是特定于语言的。如果一段代码不遵守这些规则,编译器就不能进行转换过程,从而导致编译错误。当我们面对”找不到符号“的编译错误时,关键是要找出原因。
From the error message, we can find the line of code where the error occurs, and which element is wrong. Knowing the most common issues that cause this error will make solving it quick and easy.
从错误信息中,我们可以找到发生错误的那一行代码,以及哪个元素出了问题。了解导致这种错误的最常见的问题将使解决它变得快速而简单。