1.類別的名稱與儲存它的檔案的名稱不同
我使用過的所有 Java 框架(包括 Javasoft JDK)都假設帶有 public 修飾符的類別的源代碼存儲在與類別名稱完全相同且擴展名為 .java 的檔案中。不遵循此約定可能會導致編譯期間出現的許多問題。Lab6.java
public class Airplane extends Vehicle
Seat pilot;
public Airplane() {
pilot = new Seat();
}
}
更正範例:檔案名稱Airplane.java
public class Airplane extends Vehicle
Seat pilot;
public Airplane() {
pilot = new Seat();
}
}
請注意:假定類別名稱以大寫字母開頭。檔案名稱區分大小寫的作業系統可能會帶來額外的問題,特別是對於習慣了 DOS 檔案命名系統的在 Unix 上學習 Java 的學生來說。該類別MotorVehicle
應該儲存在檔案中MotorVehicle.java
,而不是motorvehicle.java
.
2. 比較使用==
在Java中,字串是類別的物件java.lang.String
。==
應用於物件的運算子檢查物件引用的相等性!有時學生不理解運算子的語意==
而嘗試使用它來比較字串。 錯誤範例:
// проверим, equals ли первый аргумент "-a"
if (args[0] == "-a") {
optionsAll = true;
}
比較 2 個字串是否相等的正確方法是使用equals()
類別方法java.lang.String
。true
如果字串長度相同且包含相同字符,則傳回。(注意:實際上這並不能保證相等。事實上,equals
它逐個字元地檢查 2 個字串是否相等) 更正範例:
// проверим, equals ли первый аргумент "-a"
if ("-a".equals(args[0])) {
optionsAll = true;
}
這個錯誤很愚蠢,因為事實上Java程式碼在語法上是正確的,但最終卻沒有如預期運作。有些學生也嘗試使用比較運算子>
來取代類別<=
方法。此錯誤更容易檢測,因為它會在編譯階段造成錯誤。 compareTo()
java.lang.String
3. 忘記初始化作為陣列元素的物件。
在Java中,物件數組其實是物件引用的陣列。創建數組只是創建一組不指向任何內容的引用(即它們為空)。要實際建立一個「完整」物件數組,您需要初始化數組的每個元素。很多同學不明白這一點;他們相信,透過創建物件數組,他們會自動創建物件本身。(在大多數情況下,學生從 C++ 中引入了這個概念,在 C++ 中建立物件陣列會導致透過呼叫預設建構函式來建立物件本身。)在下面的範例中,學生想要建立 3 個類別的物件StringBuffer
。程式碼編譯時不會出現錯誤,但在最後一行會出現異常NullPointerException
,因為該行存取了不存在的物件。 錯誤範例:
// Создаем массив из StringBuffer
StringBuffer [] myTempBuffers;
myTempBuffers = new StringBuffer[3];
myTempBuffers[0].add(data);
為了避免此錯誤,您必須記住初始化數組元素。 更正範例:
// Создаем массив из StringBuffer и инициализируем элементы
StringBuffer [] myTempBuffers;
myTempBuffers = new StringBuffer[3];
for (int ix = 0; ix < myTempBuffers.length; ix++)
myTempBuffers[ix] = new StringBuffer();
myTempBuffers[0].add(data);
4. 同時將多個帶有修飾符的類別放入一個檔案中public
Java 原始檔以某種方式與這些文件中包含的類別相關聯。這種關係的特徵如下:任何 Java 類別都儲存在不超過一個檔案中。在任何原始碼檔案中,您最多可以放置 1 個帶有修飾符的類別public
。如果原始碼檔案中存在帶有修飾符的類public
,則檔案名稱和類別名稱必須嚴格相同(翻譯註:根據情況,請參閱第1點)有時學生會忘記第二條規則,從而導致錯誤在編譯階段。第二條和第三條規則的錯誤訊息將是相同的(這實際上使得識別此錯誤變得困難)。
5. 用局部變數取代類別欄位。
Java 允許您在方法內宣告變數,其名稱與類別的欄位相符。在這種情況下,局部變數將優先並代替欄位使用。如果具有相同名稱的變數屬於不同類型,編譯器將拋出錯誤。如果是相同的類型,就不會出現編譯錯誤,程式執行不正確的原因也不清楚。 錯誤範例:public class Point3 {
int i = 0;
int j = 0;
int k = 0;
public boolean hits(Point[] p2list) {
for(int i = 0; i < p2list.length; i++) {
Point p2 = p2list[i];
if (p2.x == i && p2.y == j)
return true;
}
return false;
}
}
有多種方法可以修復此錯誤。最簡單的是使用隱式this
:指標存取類別欄位this.Name_поля
。最好的方法是重命名類別欄位或局部變量,這樣就不會發生替換。(大約翻譯:第二種方法不是我們的方法。而且,它並不能保證我有一天不會意外地替換變數的字段。當我根本看不到什麼字段時,繼承會產生更大的困難該類有) 更正的範例:
// One way to fix the problem
int i = 0;
int j = 0;
int k = 0;
public boolean hits(Point[] p2list) {
for(int i = 0; i < p2list.length; i++) {
Point p2 = p2list[i];
if (p2.x == this.i && p2.y == this.j)
return true;
}
return false;
}
// *****************************
// Лучший способ
int x = 0;
int y = 0;
int z = 0;
public boolean hits(Point[] p2list) {
for(int i = 0; i < p2list.length; i++) {
Point p2 = p2list[i];
if (p2.x == x && p2.y == y)
return true;
}
return false;
}
發生此錯誤的另一個可能的地方是將方法參數名稱設為與類別欄位名稱相同。這在構造函數中看起來不錯,但不適合普通方法。
約。翻譯 有點混亂,但這就是重點
也就是說,建構函式中的一切看起來都很漂亮,但這不應該用於普通方法。 |
6.忘記呼叫父類別(超類別)建構函數
當一個類別擴展另一個類別時,每個子類別建構子必須呼叫某個超類別建構子。這通常是透過呼叫超類別建構函式並將方法super(x)
放在建構函式第一行來實現的。如果建構函數的第一行沒有調用super(x)
,編譯器本身會插入此調用,但不帶參數:super()
。(大約譯:x...se,但我不知道)有時學生會忘記這個要求。通常這不是問題:編譯器插入對超類別構造函數的調用,一切正常。但是,如果超類別沒有預設建構函數,編譯器將會拋出錯誤。在下面的範例中,所有超類別建構函式java.io.File
都有 1 或 2 個參數: 錯誤範例:
public class JavaClassFile extends File {
String classname;
public JavaClassFile(String cl) {
classname = cl;
}
}
問題的解決方案是插入對正確的超類別建構函數的明確呼叫: 更正的範例:
public class JavaClassFile extends File {
String classname;
public JavaClassFile(String cl) {
super(cl + ".class");
classname = cl;
}
}
當超類別有預設建構函數,但它沒有完全初始化物件時,會發生更令人不快的情況。在這種情況下,程式碼可以編譯,但程式的輸出可能不正確或可能出現異常。
7. 錯誤捕獲異常
Java的異常處理系統相當強大,但對於初學者來說很難理解。精通 C++ 或 Ada 的學生通常不會遇到與 C 和 Fortran 程式設計師相同的困難。下面的範例顯示了一些常見錯誤。在此範例中,異常未命名。編譯器會在編譯階段指出這個錯誤,因此很容易自行修復。 錯誤範例:try {
stream1 = new FileInputStream("data.txt");
} catch (IOException) {
message("Could not open data.txt");
}
更正範例:
try {
stream1 = new FileInputStream("data.txt");
} catch (IOException ie) {
message("Could not open data.txt: " + ie);
}
區塊的順序catch
決定了捕捉異常的順序。必須考慮到每個這樣的區塊將捕獲指定類別或其任何子類別的所有異常。如果不考慮這一點,最終可能會出現無法存取的 catch 區塊,編譯器會指出這一點。下面的範例SocketException
是 的子類別IOException
。 錯誤範例:
try {
serviceSocket.setSoTimeout(1000);
newsock = serviceSocket.accept();
} catch (IOException ie) {
message("Error accepting connection.");
} catch (SocketException se) {
message("Error setting time-out.");
}
更正範例:
try {
serviceSocket.setSoTimeout(1000);
newsock = serviceSocket.accept();
} catch (SocketException se) {
message("Error setting time-out.");
} catch (IOException ie) {
message("Error accepting connection.");
}
如果程式碼中可能發生未被任何區塊捕獲的異常try-catch
,則應在方法頭中聲明此異常。(這對於異常 - 類別的子類別來說RuntimeException
不是必需的)。學生有時會忘記呼叫方法可能會引發異常。解決此問題的最簡單方法是將方法呼叫放在區塊中try-catch
。 錯誤範例:
public void waitFor(int sec) {
Thread.sleep(sec * 1000);
}
更正範例:
public void waitFor(int sec) throws InterruptedException {
Thread.sleep(sec * 1000);
}
8. 存取方法有類型void
這是一個非常簡單的錯誤。學生創建一個方法來存取變量,但指定該方法不返回任何內容(void
在方法頭中放置修飾符)。若要修復此錯誤,您必須指定正確的回傳類型。 錯誤範例:
public class Line {
private Point start, end;
public void getStart() {
return start;
}
}
更正範例:
public class Line {
private Point start, end;
public Point getStart() {
return start;
}
}
指定錯誤的回傳類型會產生一整類錯誤。通常,編譯器會識別這些錯誤並報告它們,以便學生可以自行更正它們。作者:A. Grasoff™ 閱讀續篇 原始碼 連結:Java 初學者的錯誤
GO TO FULL VERSION