Java常见错误列表

时间:2023-01-08 23:17:44
Java常见错误列表

Java常见错误列表:

遇见过上面没有列举的错误?如果有,那就在Piazza上提问吧,要在错误专栏(error folder)下吆。

1. 找不到符号(symbol)

当你在代码中引用一个没有声明的变量时一般会报这个错误。考虑下面的例子:

1
2
3
4
5
6
7
8
9
10
public class Test {
    public static void main(String[] args) {
        int a = 3;
        int b = 4;
        int c = 20;
     
        average = (a + b + c)/5.0;
        System.out.println(average);
    }
}
1
2
3
4
5
1 error found:
File: Test.java  [line: 7]
Error: Test.java:7: cannot find symbol
symbol  : variable average
location: class Test

在上面的例子中,变量average没有被声明——也就是说你需要告诉编译器average的类型是什么,例如:

1
double average = (a + b + c)/5.0;

此外,当你在代码中引用一个方法但没有在方法名后加上括号时也会报这个错误,加上括号用以表明引用的是个函数,即使当函数没有参数时也不能省略括号。例如:

1
2
3
4
5
6
7
8
9
public class Test {
    public static void main(String[] args) {
        my_method;
    }
    public static void my_method() {
        System.out.println("Hello, world!");
    }
}
1
2
3
4
5
1 error found:
File: Test.java  [line: 7]
Error: Test.java:7: cannot find symbol
symbol  : variable my_method
location: class Test

在上面的例子中,编译器在main方法中查找名为my_method的变量,实际上,你是想调用一个叫做my_method的方法:

1
2
3
4
5
6
7
8
9
    public class Test {
        public static void main(String[] args) {
            my_method();
        }
        public static void my_method() {
            System.out.println("Hello, world!");
        }
}

第三种情况,如果你忘记导入你所使用的包时也会出现这个错误。例如,考虑下面这个从用户那里读入一个整数的例子:

1
2
3
4
5
6
public class Test {    
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        int n = console.nextInt();
    }
}
1
2
3
4
5
6
7
8
9
2 errors found:
    File: Test.java  [line: 3]
    Error: cannot find symbol
      symbol:   class Scanner
      location: class Test
    File: Test.java  [line: 3]
    Error: cannot find symbol
      symbol:   class Scanner
      location: class Test   

这里的问题是程序必须导入java.util.Scanner(或者java.util.)。否则,编译器不知道Scanner是什么类型。当你在处理文件的输入/输出时,如果忘记导入java.util.Arrays或者java.io.,也会遇到这个错误。

1
2
3
4
5
6
7
import java.util.*; // or --> import java.util.Scanner;   
public class Test {    
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        int n = console.nextInt();
    }
}

最后,当我们在使用大小敏感的变量名时也会遇到这个错误。Java中所有的标识符(identifiers)都是区分大小写的。这就意味着,如果我们声明了一个名为average的变量,然后在后面用Average引用它时,编译器就会报找不到Average这个变量的错误。

2. 类X是public的,应该被声明在名为X.java的文件中

在一个Java程序中,如果类名与文件名不匹配时会报这个错。例如,下面这个Foo.java程序:

1
2
3
4
5
public class Bar {    
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}
1
2
3
1 error found:
File: Foo.java  [line: 1]
Error: class Bar is public, should be declared in a file named Bar.java

由于Foo与Bar不匹配,这段代码会编译失败。修改这个错误,我们既可以修改类名,也可以修改文件名。

3. 缺失类、接口或枚举类型

这个错误是一种与大括号有关的错误,一般来说,这个错误发生在程序最后有太多大括号时;例如:

1
2
3
4
5
6
public class Test {    
    public static void main(String[] args) {
        System.out.println("Hello!");
    }
}
}
1
2
3
1 error found:
File: Test.java  [line: 6]
Error: class, interface, or enum expected

一种找出这种错误的方式是正确的缩进代码(因为这种错误总是与大括号有关)。我们可以在Dr.java中按组合键CTRL-A(去选中这个程序),然后按TAB键(来正确地缩减代码)。在我们上面的实例代码中,程序的最后有两个大括号,这在一个合法的程序中是不可能出现的。因此,我们仅仅去掉一个大括号就能够让程序正确的编译。

1
2
3
4
5
public class Test {    
       public static void main(String[] args) {
           System.out.println("Hello!");
       }
   }

4. 缺失X

当编译器检查到代码中缺失字符时会出现”缺失X”这种形式的错误,错误信息会告诉你在哪行缺失了哪个字符,考虑下面的程序:

1
2
3
4
5
6
7
8
9
public class Test
    public static void main(String[] args) {
        my_method();
    }
    public static void my_method() {
        System.out.println("Hello, world!")    
    }
}
1
2
3
4
5
2 errors found:
File: Test.java  [line: 1]
Error: Test.java:1: '{' expected
File:.java  [line: 7]
Error: Test.java:7: ';' expected

这个错误信息告诉你在第1行缺失了一个大括号,在第7行缺失了一个分号。解决这种错误很简单——只需把缺失的字符在正确的位置上补上即可。

1
2
3
4
5
6
7
8
9
public class Test {
       public static void main(String[] args) {
           my_method();
       }
    
       public static void my_method() {
           System.out.println("Hello, world!");  
       }
   }

5. 缺失标识符

当把代码写在了方法外时会出现这个错误;这种错误一般也是由大括号引起的。考虑下面的例子:

1
2
3
4
5
6
7
public class Test {
       System.out.println("Hello!");
    
       public static void main(String[] args) {
           System.out.println("World!");
       }
   }
1
2
3
4
5
2 errors found:
File: Test.java  [line: 2]
Error: <identifier> expected
File: Test.java  [line: 2]
Error: illegal start of type

在这种情况下,很明显第一个打印语句应该放在main方法里面,这样才能通过编译。然而,当我们的程序中有多于一个方法并且大括号也不匹配时,这种“缺失标识符”的错误就不容易被发现了:

1
2
3
4
5
6
public class Test {    
        public static void main(String[] args) {
            System.out.println("Hello!");}
            System.out.println("World!");
        }
    }
1
2
3
4
5
6
7
3 errors found:
File: Test.java  [line: 4]
Error: <identifier> expected
File: Test.java  [line: 4]
Error: illegal start of type
File: Test.java  [line: 6]
Error: class, interface, or enum expected   

在上面的代码中多了一个大括号,但是因为代码没有正确的缩进,所以很难找出这个错误。这样使得main方法在打印“hello”语句后就结束了,这样打印“world”的语句就变成方法以外的代码了。修改这个错误的方式十分简单——只需要把第三行的大括号删除就可以了:

1
2
3
4
5
6
public class Test {    
    public static void main(String[] args) {
        System.out.println("Hello!");
        System.out.println("World!");
    }
}

6. 非法的表达式开头

当编译器遇到一条不合法的语句时会报“非法的表达式开头”这种错误。考虑下面的例子:

1
2
3
4
5
6
7
8
9
public class Test {
    public static void main(String[] args) {
        my_method();
    public static void my_method() {
        System.out.println("Hello, world!");
    }
}
1
2
3
4
5
6
7
8
9
10
11
5 errors found:
File: Test.java  [line: 6]
Error: Test.java:6: illegal start of expression
File: Test.java  [line: 6]
Error: Test.java:6: illegal start of expression
File: Test.java  [line: 6]
Error: Test.java:6: ';' expected
File: Test.java  [line: 6]
Error: Test.java:6: ';' expected
File: Test.java  [line: 9]
Error: Test.java:9: reached end of file while parsing

这里,缺少了一个关闭main方法大括号。由于main方法没有被关闭,编译器把调用my_method方法之后的代码也当作main方法的一部分。然而,后面的代码是public static void my_method() {,很显然,这在一个方法内不合法。

“非法的表达式开头”这种错误不如我们上面提到的“××缺失”这种信息有帮助。对于这种错误(以及很多其他一些错误),非常有必要检查一下出错代码前面的那几行。对于上面那个例子,我们只需要在编译器报错的那行前面加上大括号关闭main方法就可以了。重新编译,所有的错误都解决了。

1
2
3
4
5
6
7
8
9
public class Test {
    public static void main(String[] args) {
        my_method();
    }  
    public static void my_method() {
        System.out.println("Hello, world!");
    }
}

7. 类型不兼容

当你的程序在处理类型相关的问题时会报这个错。我们可以对一些类型进行相互转化,例如,你可以轻松把一个char类型转为int类型,反之亦然;你也可以通过向上转型把一个double类型转为int类型。但是,你不能把基本类型与像String这样的对象进行相互转换。例如:

1
2
3
4
5
public class Test {
    public static void main(String[] args) {
        int num = "Hello, world!";
    }
}
1
2
3
4
5
1 error found:
File: Test.java  [line: 3]
Error: Test.java:3: incompatible types
found   : java.lang.String
required: int

一般来说,你不能像解决其他一些错误一样解决这种错误。这不是一种语法错误,而是一种关于类型的逻辑错误。把一个String类型转为int类型一般来说都是毫无意义。但是,在一些应用中,你可能需要把String类型转为int类型,比如,当这个字符串代码一个数字时:

1
2
3
4
5
public class Test {
    public static void main(String[] args) {
        int num = "500";
    }
}
1
2
3
4
5
1 error found:
File: Test.java  [line: 3]
Error: Test.java:3: incompatible types
found   : java.lang.String
required: int

解决这种错误一般采用这样的方法:借助于Java中像Integer这样的类,这些基本类型的包装类中有能接受字符串类型的参数的方法,这样就把字符串类型转为整型了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    public class Test {
        public static void main(String[] args) {
            int num = Integer.parseInt(&quot;500&quot;);
        }
    }
```
但是,这种解决“类型不兼容”错误的方案是一种例外,不是什么规则,因为这种错误一般来自于逻辑上的错误。
<a name="invalid-method"/></a>
### 8. 非法的方法声明;需要返回类型
在Java中的每个方法都要求明确的声明返回类型,即使这个方法什么也不返回,也要用void进行标识,就像main方法那样。
当一个方法没有声明返回类型时,会出现这种错误:
&lt;pre class=&quot;brush: java; gutter: true; first-line: 1; highlight: []; html-script: false&quot;&gt;
    public class Test {
        public static void main(String[] args) {
            int x = getValue();
            System.out.println(x);
        }
     
        public static getValue() {
            return 10;
        }
    }
1
2
3
1 error found:
File: Test.java  [line: 7]
Error: Test.java:7: invalid method declaration; return type required

解决这种问题,在方法声明处加上合适的返回类型即可:

1
2
3
4
5
6
7
8
9
10
public class Test {
    public static void main(String[] args) {
        int x = getValue();
        System.out.println(x);
    }
    public static int getValue() {
        return 10;
    }
}

9. 数组越界(java.lang.ArrayIndexOutOfBoundsException)

当你使用不合法的索引访问数组时会报数组越界这种错误,数组arr的合法错误范围是[0, arr.length-1];当你访问这之外的索引时会报这个错。例如:

1
2
3
4
5
6
7
8
public class Test {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        for (int i = 0; i <= arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}
1
2
3
4
5
6
7
java.lang.ArrayIndexOutOfBoundsException: 3
at Test.main(Test.java:5)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at edu.rice.cs.drjava.model.compiler.JavacCompiler.runCommand(JavacCompiler.java:272)

这种错误很像我们下面即将说的字符串索引越界,这种错误的错误信息后面部分与错误不大相关。但是,第1行就告诉我们错误的原因是数组越界了,在我们上面的例子,非法的索引值是3,下面一行的错误信息告诉你错误发生在Test类的第5行上,在main方法之内。

在上面的例子中,因为我们循环过多导致出现这个错误,循环索引i最大可以为4,而4超过了数组的长度,因此越界了。相反,i的上界应该使用<或者相同效果的语句来界定。

1
2
3
4
5
6
7
8
public class Test {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

当处理数组越界时,打印出遍历数组的索引十分有帮助,这样我们就能够跟踪代码找到为什么索引达到了一个非法的值。

10. 字符串索引越界(java.lang.StringIndexOutOfBoundsException)

当你在程序中去访问一个字符串的非法索引时会报字符串索引越界这个错误。一个String的合法索引范围是[0,str.leng()-1];当你访问这之外的索引时会报这个错。例如:

1
2
3
4
5
6
7
8
9
public class Test {
    public static void main(String[] args) {
        String str = "Hello, world!";
        String a = str.substring(-1, 3);
        String b = str.charAt(str.length());
        String c = str.substring(0, 20);
    }
}
1
2
3
4
5
6
7
8
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
    at java.lang.String.substring(Unknown Source)
    at Test.main(Test.java:5)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at edu.rice.cs.drjava.model.compiler.JavacCompiler.runCommand(JavacCompiler.java:271)

这种错误的错误信息后面部分与错误不大相关。但是,第1行就说明了错误的地方是字符串的索引,在我们这个例子,非法的索引是-1,下面一行错误信息告诉我们这个错误是在执行substring方法时抛出的,发生错误的位置是Test类的第5行。这种与错误相关的程序轨迹告诉我们程序是在调用哪个方式时出的错,这样我们就能追踪代码,并最终改正它。

值得注意的是,上面程序中的a,b,c都会抛出这种错误,但是程序在遇到第一个错误时就*终止了。

这不是编译时的错误,而是运行时的错误。换句话说,编译器能正确编译这段程序因为它只是在逻辑上有错,此外,在程序运行之前,我们也没法预料是否会有错误发生。解决这种错误,我们需要改正程序的逻辑来保证没有地方访问非法的索引。

11. 类Y中的方法X参数不匹配

当你在调用函数时参数数量或顺序不对时会报这个错误。例如,考虑下面的程序:

1
2
3
4
5
6
7
8
9
public class Test {    
    public static void main(String[] args) {
        myMethod(1.0, 2, "Hello!");
    }
    public static void myMethod(double d, String s, int x) {
        System.out.println(s + " " + d + " " + x);
    }
}
1
2
3
4
5
6
1 error found:
File: Test.java  [line: 3]
Error: method myMethod in class Test cannot be applied to given types;
  required: double,java.lang.String,int
  found: double,int,java.lang.String
  reason: actual argument int cannot be converted to java.lang.String by method invocation conversion

这种错误的错误信息非常有帮助。“required”这一行错误信息告诉我们方法的参数是什么,方法的参数列表在这后面。在上面的例子中,myMethod方法的参数先后顺序应该是double类型、String类型,最后是一个int类型的变量。
错误信息的下一行(found开头的这一行)告诉我们程序在调用这个方法时用了什么样的参数。在上面的例子中,是一个double类型,一个int类型,最后是一个String类型的变量,很显然顺序是不对的。

解决这种错误,我们需要保证方法的参数个数和类型与函数声明时都一致才行。

1
2
3
4
5
6
7
8
9
public class Test {    
    public static void main(String[] args) {
        myMethod(1.0, "Hello!", 2);
    }
    public static void myMethod(double d, String s, int x) {
        System.out.println(s + " " + d + " " + x);
    }
}

12. 缺少return语句

当你声明一个方法有返回值但是没有写return语句时会报这个错误。例如:

1
2
3
4
5
6
7
8
9
10
public class Test {
    public static void main(String[] args) {
        int x = twice(5);
        System.out.println(x);
    }
    public static int twice(int x) {
        int value = 2 * x;
    }
}
1
2
3
1 error found:
File: Test.java  [line: 9]
Error: Test.java:9: missing return statement

我们通过函数声明告知编译器twice方法会返回一个int值,但是我们没有写return语句:

1
2
3
4
5
6
7
8
9
10
11
public class Test {
    public static void main(String[] args) {
        int x = twice(5);
        System.out.println(x);
    }
    public static int twice(int x) {
        int value = 2 * x;
        return value;
    }
}

在某些if条件句中,编译器也会认为函数没有返回值。像下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
    public static void main(String[] args) {
        int x = absVal(-5);
        System.out.println(x);
    }
    public static int absVal(int x) {
        if (x < 0) {
            return -x;
        }
         
        if (x >= 0) {
            return x;
        }
    }
}
1
2
3
1 error found:
File: Test.java  [line: 15]
Error: Test.java:15: missing return statement

避免这种错误,我们可以选择使用else语句(就像我们在变量没被初始化一样),或者我们可以不用第二个if语句,因为我们知道,如果程序能够执行到这个地方,程序就可以直接返回x了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
    public static void main(String[] args) {
        int x = absVal(-5);
        System.out.println(x);
    }
    public static int absVal(int x) {
        if (x < 0) {
            return -x;
        }
         
        return x;
    }
}

13. 精度损失

当你把信息保存到一个变量中,而信息量超过了这个变量的所能容纳的能力时会报这个错。最常见的例子是把double类型赋值给int类型。

1
2
3
4
5
6
public class Test {
    public static void main(String[] args) {
        int pi = 3.14159;
        System.out.println("The value of pi is: " + pi);
    }
}
1
2
3
4
5
1 error found:
File: Test.java  [line: 3]
Error: Test.java:3: possible loss of precision
found   : double
required: int

这个错误发生的原因是计算机在存储double类型时所需的空间是int类型的两倍。如果你不在乎精度的损失,你可以通过上转型的方法来告知编译器:

1
2
3
4
5
6
public class Test {
    public static void main(String[] args) {
        int pi = (int)3.14159;
        System.out.println("The value of pi is: " + pi);
    }
}

现在编译器不会报错了,但是pi这个变量由于进行了取整,最终值为3。

14. 在解析时到达了文件结尾

当你没有用大括号关闭你的程序时会出现这个错误。错误信息明确的指出编译器在没有明确程序该结束时就到达了文件的结尾。例如:

1
2
3
4
5
6
7
8
public class Test {
    public static void main(String[] args) {
        my_method();
    }
    public static void my_method() {
        System.out.println("Hello, world!");
    }
1
2
3
1 error found:
File: Test.java  [line: 9]
Error: Test.java:9: reached end of file while parsing

解决这个错误,我们只需要在最后加上关闭程序的大括号(“}”)即可。有时仅仅在文件末尾缺少了一个大括号,但也有可能是在程序的中间少写或多写了大括号的缘故。

一种调试的方法是用快捷键CTRL-A + TAB来正确的缩减你的代码。由于程序的问题与大括号有关,这样代码就不能够正确的缩进。找到程序中第一个缩进不正确的地方,这就是错误产生的地方。

一旦大括号正确的匹配上,编译器就不会报错了:

1
2
3
4
5
6
7
8
9
public class Test {
    public static void main(String[] args) {
        my_method();
    }
    public static void my_method() {
        System.out.println("Hello, world!");
    }
}

15. 执行不到的语句

当编译器检测到某些语句在整个程序流程中不可能被执行到时会报这个错。这个错误经常是由return或break后的语句所导致的。例如:

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
    public static void main(String[] args) {
        int value = twice(5);
        System.out.println(value);
    }
    public static int twice(int x) {
        int twice = 2 * x;
        return twice;
        System.out.println("Returning " + twice);
    }
}
1
2
3
4
5
2 errors found:
File: Test.java  [line: 10]
Error: Test.java:10: unreachable statement
File: Test.java  [line: 11]
Error: Test.java:11: missing return statement

编译器报了两个错:一个是说System.out.println(“Returning ” + twice);这一行不可能被法执行到,另一个错误是因为编译器假设可以执行print语句,这样的话我们在它之后也应该有个return语句,但是程序中没有,所以报这个错。

解决这个错误,我们可以把print语句放到return的前面,这样程序就能执行了:

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
    public static void main(String[] args) {
        int value = twice(5);
        System.out.println(value);
    }
    public static int twice(int x) {
        int twice = 2 * x;
        System.out.println("Returning " + twice);
        return twice;
    }
}

15. 变量没被初始化

当你在程序中去引用一个没有被初始化的变量时会报这个错。下面看一个非常简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
    public static void main(String[] args) {
        int x = 2;
        int y;
        System.out.println(x + y);
    }
}
1 error found:
File: Test.java  [line: 5]
Error: Test.java:5: variable y might not have been initialized

在程序中你没有告知编译器y的值,所以y不能被打印,y需要像x一样被初始化以后才能使用。

在一些更复杂的情形下,if语句可能导致变量没有被初始化。例如:

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
    public static void main(String[] args) {
        int x;
        boolean setX = false;
         
        if (setX) {
            x = 10;
        }
         
        System.out.println(x);
    }
}
1
2
3
1 error found:
File: Test.java  [line: 8]
Error: Test.java:8: variable x might not have been initialized

这里很明显,x将不能被正确的初始化,因此编译器报错。但是,在一些情况下虽然我们能够很清楚的知道变量能够被初始化,但是编译器不能和我们一样推测出变量是否会被初始化,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
    public static void main(String[] args) {
        int x;
        boolean setToTen = false;
      
        if (setToTen) {
            x = 10;
        }
     
        if (!setToTen) {
            x = 0;
        }
     
        System.out.println(x);
    }
}
1
2
3
1 error found:
File: Test.java  [line: 14]
Error: Test.java:14: variable x might not have been initialized

很明显,x一定会被两个if语句中的任意一个赋值,但是编译器并不能推测出(译者注:需要在运行时才能知道),一种修改这个错误的方式是使用else语句。当使用else语句时,编译器就有最够的证据推测出x将被初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
    public static void main(String[] args) {
        int x;
        boolean setToTen = false;
     
        if (setToTen) {
            x = 10;
        } else {
            x = 0;
        }
     
        System.out.println(x);
    }
}

原文链接: cs-people.bu.edu 翻译: ImportNew.com刘 家财
译文链接: http://www.importnew.com/12425.html