20個常見的Java錯誤以及規避方法

NO IMAGE

原文50 Common Java Errors and How to Avoid Them (Part 1)
作者:Angela Stringfellow
翻譯:雁驚寒

譯者注:本文介紹了20個常見的Java編譯器錯誤,每種錯誤都包含了程式碼片段、問題說明,並給出了相關的連結來幫助你快速地理解並解決這些問題。以下是譯文。

在開發Java軟體的時候可能會遇到很多型別的錯誤,但大多數可以避免。我們精心挑選了20個最常見的Java軟體錯誤,包含了程式碼示例和教程,以幫助你解決一些常見的編碼問題。

要獲得更多編寫Java程式的提示和技巧,你可以下載我們的“Comprehensive Java Developer’s Guide”這本書,它裡面包含了所有你需要的東西,從各類工具到最佳網站和部落格、YouTube頻道、Twitter影響者、LinkedIn組、播客、must-attend events等等。

如果你正在使用.NET,你應該閱讀我們的50個最常見的.NET軟體錯誤指南,以避免出現這些錯誤。但是如果你當前遇到的挑戰是與Java相關的,那麼請閱讀下面的文章以瞭解最常見的問題及其解決方法。

編譯器錯誤

在編譯器執行Java程式碼的時候會建立編譯器錯誤訊息。有一點很重要,編譯器可能會因為一個錯誤丟擲多個錯誤訊息。所以修復一個錯誤,然後重新編譯,這樣可以解決很多問題。

1. “… Expected”

當程式碼中缺少某些東西時,會產生這個錯誤。通常這是因為缺少一個分號或右括號。

private static double volume(String solidom, double alturam, double areaBasem, double raiom) {
double vol;
if (solidom.equalsIgnoreCase("esfera"){
vol=(4.0/3)*Math.pi*Math.pow(raiom,3);
}
else {
if (solidom.equalsIgnoreCase("cilindro") {
vol=Math.pi*Math.pow(raiom,2)*alturam;
}
else {
vol=(1.0/3)*Math.pi*Math.pow(raiom,2)*alturam;
}
}
return vol;
}

通常,這種錯誤訊息不會指出產生問題的確切位置。要找出問題所在,需要:

  • 確保所有的左括號都有相應的右括號。
  • 檢視錯誤所指示的那一行前面的程式碼。這個錯誤通常是在後面的程式碼中才會被編譯器發現。
  • 有的時候,有些字元(例如左括號)不應該位於Java程式碼的第一個。

例項:一個丟失的圓括號引發的錯誤

2. “Unclosed String Literal”

當字串結尾缺少引號時,會產生“unclosed string literal”錯誤訊息,並且該訊息就顯示在出錯的那一行上。

 public abstract class NFLPlayersReference {
private static Runningback[] nflplayersreference;
private static Quarterback[] players;
private static WideReceiver[] nflplayers;
public static void main(String args[]){
Runningback r = new Runningback("Thomlinsion");
Quarterback q = new Quarterback("Tom Brady");
WideReceiver w = new WideReceiver("Steve Smith");
NFLPlayersReference[] NFLPlayersReference;
Run();// {
NFLPlayersReference = new NFLPlayersReference [3];
nflplayersreference[0] = r;
players[1] = q;
nflplayers[2] = w;
for ( int i = 0; i < nflplayersreference.length; i   ) {
System.out.println("My name is "   " nflplayersreference[i].getName());
nflplayersreference[i].run();
nflplayersreference[i].run();
nflplayersreference[i].run();
System.out.println("NFL offensive threats have great running abilities!");
}
}
private static void Run() {
System.out.println("Not yet implemented");
}     
}

通常,這種錯誤在以下這些情況下會產生:

  • 字串不是以引號結尾。這很容易修改,用指定的引號來結束字串即可。
  • 字串超出一行。長字串可以分成多個短串,並用加號(“ ”)連線。
  • 作為字串一部分的引號沒有使用反斜槓(“\”)來進行轉義。

請閱讀這篇文章:未封閉的字串錯誤訊息

3. “Illegal Start of an Expression”

出現“Illegal Start of an Expression”錯誤的原因有很多。它已經成為不太有用的錯誤訊息之一。一些開發者認為這是由壞的程式碼味道造成的。

通常,建立一個表示式是為了生成一個新值或給其他變數賦值。編譯器期望找到一個表示式,但是因為語法不符合預期而找不到表示式。在下面這些程式碼中可以找到這種錯誤。

} // 把它新增到這裡
public void newShape(String shape) {
switch (shape) {
case "Line":
Shape line = new Line(startX, startY, endX, endY);
shapes.add(line);
break;
case "Oval":
Shape oval = new Oval(startX, startY, endX, endY);
shapes.add(oval);
break;
case "Rectangle":
Shape rectangle = new Rectangle(startX, startY, endX, endY);
shapes.add(rectangle);
break;
default:
System.out.println("ERROR. Check logic.");
}
}
} // 從這裡刪掉它
}

請閱讀這篇文章:如何排除“illegal start of an expression”錯誤

4. “Cannot Find Symbol”

這是一個非常常見的問題,因為Java中的所有識別符號都需要在使用之前進行宣告。出現這個錯誤是因為,在編譯程式碼時,編譯器不明白該識別符號的含義。

cannot-find-symbol-error-screenshot-11495

有很多原因可能會產生“cannot find symbol”錯誤資訊:

  • 識別符號宣告時的拼寫可能與程式碼中使用時的拼寫不一致。
  • 變數從未被宣告。
  • 未在同一作用域內宣告該變數。
  • 沒有匯入類。

請閱讀這篇文章:關於“cannot find symbol”錯誤的討論

5. “Public Class XXX Should Be in File”

當XXX類和Java程式檔名不匹配時,就會產生“public class XXX should be in file”錯誤訊息。 只有當類名和Java檔名相同時,才能編譯程式碼。

package javaapplication3;  
public class Robot {  
int xlocation;  
int ylocation;  
String name;  
static int ccount = 0;  
public Robot(int xxlocation, int yylocation, String nname) {  
xlocation = xxlocation;  
ylocation = yylocation;  
name = nname;  
ccount  ;         
} 
}
public class JavaApplication1 { 
public static void main(String[] args) {  
robot firstRobot = new Robot(34,51,"yossi");  
System.out.println("numebr of robots is now "   Robot.ccount);  
}
}

要解決這個問題,可以:

  • 把類和檔案命名為相同的名字。
  • 確保兩個名稱始終保持一致。

請閱讀這篇文章:“Public class XXX should be in file”錯誤的示例

6. “Incompatible Types”

“Incompatible Types”是賦值語句嘗試對變數與表示式進行型別匹配時發生的邏輯錯誤。通常,將字串賦值給一個整數時會產生這個錯誤,反之亦然。這不是一個Java語法錯誤。

test.java:78: error: incompatible types
return stringBuilder.toString();
^
required: int
found:    String
1 error

當編譯器丟擲“incompatible types”訊息時,確實不太容易解決這個問題:

  • 使用型別轉換函式。
  • 開發人員可能需要修改程式碼原有的功能。

看一下這個例子:將一個字串賦值給整數會出現“incompatible types”錯誤

7. “Invalid Method Declaration; Return Type Required”

這個錯誤訊息的意思是,在方法宣告中未顯示地宣告方法的返回型別。

public class Circle
{
private double radius;
public CircleR(double r)
{
radius = r;
}
public diameter()
{
double d = radius * 2;
return d;
}
}

有這幾種情況會觸發“invalid method declaration; return type required”錯誤:

  • 忘記宣告型別。
  • 如果方法沒有返回值,那麼需要在方法宣告中指定“void”作為返回型別。
  • 建構函式不需要宣告型別。但是,如果建構函式名稱中存在錯誤,那麼編譯器會把建構函式看成是沒有指定型別的方法。

看一個這個例子:建構函式的命名問題觸發“invalid method declaration; return type required“問題

8. “Method in Class Cannot Be Applied to Given Types”

這個錯誤訊息比較有用,它的意思是某個方法呼叫了錯誤的引數。

RandomNumbers.java:9: error: method generateNumbers in class RandomNumbers cannot be applied to given types;
generateNumbers();
required: int[]
found:generateNumbers();
reason: actual and formal argument lists differ in length

在呼叫方法時,應傳入在其宣告時定義的那些引數。請檢查方法宣告和方法的呼叫,以確保它們是匹配的。

這個討論說明了方法宣告和方法呼叫中引數的不相容性所導致的Java錯誤

9. “Missing Return Statement”

當一個方法缺少return語句時,會觸發“Missing Return Statement”錯誤訊息。有返回值(非void型別)的方法必須要有一條返回某個值的語句,以便在方法之外呼叫該值。

public String[] OpenFile() throws IOException {
Map<String, Double> map = new HashMap();
FileReader fr = new FileReader("money.txt");
BufferedReader br = new BufferedReader(fr);
try{
while (br.ready()){
String str = br.readLine();
String[] list = str.split(" ");
System.out.println(list);               
}
}   catch (IOException e){
System.err.println("Error - IOException!");
}
}

編譯器丟擲“missing return statement”訊息有這幾個原因:

  • 返回語句被錯誤地省略了。
  • 該方法沒有返回任何值,但是在方法宣告中未宣告型別為void。

請檢視如何解決“missing return statement”錯誤這個例子。

10. “Possible Loss of Precision”

當賦值給變數的資訊超過了該變數可以承載的上限時,就會觸發“Possible Loss of Precision”錯誤。一旦發生這種情況,部分資訊將被丟棄。如果這樣做沒問題的話,那麼在程式碼上應該將變數顯式地宣告為新的型別。

possible-loss-of-precision-error-11501

以下情況通常會發生“possible loss of precision”錯誤:

  • 嘗試將一個實數賦值給整型型別的變數。
  • 嘗試將一個double資料賦值給整型型別的變數。

Java中的基本資料型別解釋了不同資料型別的特點。

11. “Reached End of File While Parsing”

這個錯誤訊息通常在程式缺少右大括號(“}”)時觸發。有時,在程式碼的末尾增加右大括號可以快速地修復此錯誤。

public class mod_MyMod extends BaseMod
public String Version()
{
return "1.2_02";
}
public void AddRecipes(CraftingManager recipes)
{
recipes.addRecipe(new ItemStack(Item.diamond), new Object[] {
"#", Character.valueOf('#'), Block.dirt
});
}

上述程式碼會產生以下這個錯誤:

java:11: reached end of file while parsing }

編碼工具和適當的程式碼縮排可以更容易地找到這些不匹配的大括號。

請閱讀這篇文章:缺少的大括號會觸發“reached end of file while parsing”錯誤訊息

12. “Unreachable Statement”

當一條語句出現在一個它不可能被執行的地方時,會觸發“Unreachable statement”錯誤。通常,是在一個break或return語句之後。

for(;;){
break;
... // unreachable statement
}
int i=1;
if(i==1)
...
else
... // dead code

通常,簡單地移動return語句即可修復此錯誤。請閱讀這篇文章:如何修復“Unreachable Statement”錯誤

13. “Variable Might Not Have Been Initialized”

在方法中宣告的區域性變數如果沒有初始化,就會發生這種錯誤。如果在if語句中包含沒有初始值的變數時,就會發生這種錯誤。

int x;
if (condition) {
x = 5;
}
System.out.println(x); // x可能尚未初始化

請閱讀這篇文章:如何避免觸發“Variable Might Not Have Been Initialized”錯誤

14. “Operator … Cannot be Applied to ”

當操作符作用於未在其定義範圍內的型別時,會出現此問題。

operator < cannot be applied to java.lang.Object,java.lang.Object

當Java程式碼嘗試在計算(減法、乘法、大小比較等)中使用字串型別時,經常會觸發這種錯誤。要修復這個問題,需要將字串轉換為整數或浮點數。

請閱讀這篇文章:非數字型別為什麼會導致Java軟體錯誤

15. “Inconvertible Types”

當Java程式碼嘗試執行非法轉換時,會發生“Inconvertible Types”錯誤。

TypeInvocationConversionTest.java:12: inconvertible types
found   : java.util.ArrayList<java.lang.Class<? extends TypeInvocationConversionTest.Interface1>>
required: java.util.ArrayList<java.lang.Class<?>>
lessRestrictiveClassList = (ArrayList<Class<?>>) classList;
^

例如,布林型別不能轉換為整形。

請閱讀這篇文章:如何在Java軟體中轉換不可轉換的型別

16. “Missing Return Value”

當返回語句包含不正確的型別時,你會收到“Missing Return Value”訊息。例如,檢視以下程式碼:

public class SavingsAcc2 {
private double balance;
private double interest;
public SavingsAcc2() {
balance = 0.0;
interest = 6.17;
}
public SavingsAcc2(double initBalance, double interested) {
balance = initBalance;
interest = interested;
}
public SavingsAcc2 deposit(double amount) {
balance = balance   amount;
return;
}
public SavingsAcc2 withdraw(double amount) {
balance = balance - amount;
return;
}
public SavingsAcc2 addInterest(double interest) {
balance = balance * (interest / 100)   balance;
return;
}
public double getBalance() {
return balance;
}
}

返回以下錯誤:

SavingsAcc2.java:29: missing return value 
return; 
^ 
SavingsAcc2.java:35: missing return value 
return; 
^ 
SavingsAcc2.java:41: missing return value 
return; 
^ 
3 errors

通常,這個錯誤的出現是因為有某個返回語句沒有返回任何東西。

請閱讀這篇文章:如何避免“Missing Return Value”錯誤

17. “Cannot Return a Value From Method Whose Result Type Is Void”

當一個void方法嘗試返回任何值時,會發生此Java錯誤,例如在以下程式碼中:

public static void move()
{
System.out.println("What do you want to do?");
Scanner scan = new Scanner(System.in);
int userMove = scan.nextInt();
return userMove;
}
public static void usersMove(String playerName, int gesture)
{
int userMove = move();
if (userMove == -1)
{
break;
}

通常,更改方法的返回型別與返回語句中的型別一致,可以解決這個問題。例如,下面的void可以改為int:

public static int move()
{
System.out.println("What do you want to do?");
Scanner scan = new Scanner(System.in);
int userMove = scan.nextInt();
return userMove;
}

請閱讀這篇文章:如何修復“Cannot Return a Value From Method Whose Result Type Is Void”錯誤

18. “Non-Static Variable … Cannot Be Referenced From a Static Context”

當編譯器嘗試在靜態方法中訪問非靜態變數時,會發生此錯誤:

public class StaticTest {
private int count=0;
public static void main(String args[]) throws IOException {
count  ; //compiler error: non-static variable count cannot be referenced from a static context
}
}

要解決“Non-Static Variable … Cannot Be Referenced From a Static Context”這個錯誤,可以做兩件事情:

  • 可以將變數宣告為靜態。
  • 可以在靜態方法中建立非靜態物件的例項。

請閱讀這個教程:靜態和非靜態變數之間的區別

19. “Non-Static Method … Cannot Be Referenced From a Static Context”

當Java程式碼嘗試在靜態類中呼叫非靜態方法時,會發生此問題。例如,以下程式碼:

class Sample
{
private int age;
public void setAge(int a)
{
age=a;
}
public int getAge()
{
return age;
}
public static void main(String args[])
{
System.out.println("Age is:"  getAge());
}
}

會觸發這個錯誤:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
Cannot make a static reference to the non-static method getAge() from the type Sample

要在靜態方法中呼叫非靜態方法,需要是宣告一個要呼叫的非靜態方法的類的例項。

請閱讀這篇文章:非靜態方法和靜態方法之間的區別

20. “(array) Not Initialized”

當陣列已經宣告但未初始化時,你會得到“(array) Not Initialized”這樣的錯誤訊息。陣列的長度是固定的,因此每個陣列都需要以所需的長度進行初始化。

以下程式碼是正確的:

AClass[] array = {object1, object2}

這樣也可以:

AClass[] array = new AClass[2];
...
array[0] = object1;
array[1] = object2;

但這樣是不正確的:

AClass[] array;
...
array = {object1, object2};

請閱讀這篇文章:關於如何在Java中初始化陣列

未完待續

今天我們討論了編譯器的錯誤,下次我們將深入討論各種可能會出現的執行時異常。像本文的結構一樣,下次也會包含程式碼片段、解釋,以及相關的連結來幫助你儘快修復程式碼。