Java中20個常見的錯誤及規避方法
譯者注:本文介紹了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.");
}
}
} // 從這裡刪掉它
}
4. “Cannot Find Symbol”
這是一個非常常見的問題,因為Java中的所有識別符號都需要在使用之前進行宣告。出現這個錯誤是因為,在編譯程式碼時,編譯器不明白該識別符號的含義。
有很多原因可能會產生“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);
}
}
要解決這個問題,可以:
- 把類和檔案命名為相同的名字。
- 確保兩個名稱始終保持一致。
6. “Incompatible Types”
“Incompatible Types”是賦值語句嘗試對變數與表示式進行型別匹配時發生的邏輯錯誤。通常,將字串賦值給一個整數時會產生這個錯誤,反之亦然。這不是一個Java語法錯誤。
test.java:78: error: incompatible types
return stringBuilder.toString();
^
required: int
found: String
1 error
當編譯器丟擲“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”作為返回型別。
- 建構函式不需要宣告型別。但是,如果建構函式名稱中存在錯誤,那麼編譯器會把建構函式看成是沒有指定型別的方法。
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
在呼叫方法時,應傳入在其宣告時定義的那些引數。請檢查方法宣告和方法的呼叫,以確保它們是匹配的。
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”錯誤](https://stackoverflow.com/questions/21256469/java-missing-return-statement)這個例子。
<div class="se-preview-section-delimiter"></div>
#### 10. “Possible Loss of Precision”
當賦值給變數的資訊超過了該變數可以承載的上限時,就會觸發“Possible Loss of Precision”錯誤。一旦發生這種情況,部分資訊將被丟棄。如果這樣做沒問題的話,那麼在程式碼上應該將變數顯式地宣告為新的型別。
<center>![possible-loss-of-precision-error-11501](https://stackify.com/wp-content/uploads/2017/05/possible-loss-of-precision-error-11501.jpg)</center>
以下情況通常會發生“possible loss of precision”錯誤:
- 嘗試將一個實數賦值給整型型別的變數。
- 嘗試將一個double資料賦值給整型型別的變數。
[Java中的基本資料型別](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html)解釋了不同資料型別的特點。
<div class="se-preview-section-delimiter"></div>
#### 11. “Reached End of File While Parsing”
這個錯誤訊息通常在程式缺少右大括號(“}”)時觸發。有時,在程式碼的末尾增加右大括號可以快速地修復此錯誤。
<div class="se-preview-section-delimiter"></div>
```java
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 }
編碼工具和適當的程式碼縮排可以更容易地找到這些不匹配的大括號。
12. “Unreachable Statement”
當一條語句出現在一個它不可能被執行的地方時,會觸發“Unreachable statement”錯誤。通常,是在一個break或return語句之後。
for(;;){
break;
... // unreachable statement
}
int i=1;
if(i==1)
...
else
... // dead code
13. “Variable Might Not Have Been Initialized”
在方法中宣告的區域性變數如果沒有初始化,就會發生這種錯誤。如果在if語句中包含沒有初始值的變數時,就會發生這種錯誤。
int x;
if (condition) {
x = 5;
}
System.out.println(x); // x可能尚未初始化
14. “Operator … Cannot be Applied to ”
當操作符作用於未在其定義範圍內的型別時,會出現此問題。
operator < cannot be applied to java.lang.Object,java.lang.Object
當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;
^
例如,布林型別不能轉換為整形。
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
通常,這個錯誤的出現是因為有某個返回語句沒有返回任何東西。
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;
}
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};
未完待續
今天我們討論了編譯器的錯誤,下次我們將深入討論各種可能會出現的執行時異常。像本文的結構一樣,下次也會包含程式碼片段、解釋,以及相關的連結來幫助你儘快修復程式碼。