初學java程式設計師常犯的錯誤。第1部分
作者:A.Grasoff™ 來源連結:Java 初學者的錯誤
9. 從main()方法呼叫非靜態類別方法
任何 Java 程式的入口點都應該是靜態方法main
:
public static void main(String[] args) {
...
}
由於此方法是靜態的,因此您不能從中呼叫非靜態類別方法。學生經常忘記這一點,並嘗試在不建立類別實例的情況下呼叫方法。這個錯誤通常發生在訓練之初,當學生編寫小程式時。 錯誤範例:
public class DivTest {
boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
// на следующие строки компилятор выдаст ошибку
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
有兩種方法可以修復錯誤:將所需的方法設定為靜態或建立類別的實例。若要選擇正確的方法,請詢問自己該方法是否使用欄位或其他類別方法。如果是,那麼您應該建立該類別的實例並呼叫它的方法,否則您應該將該方法設為靜態。 更正範例1:
public class DivTest {
int modulus;
public DivTest(int m) {
modulus = m;
}
boolean divisible(int x) {
return (x % modulus == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
DivTest tester = new DivTest(v2);
if (tester.divisible(v1) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
更正範例2:
public class DivTest {
static boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
10.使用String類別物件作為方法參數。
在Java中,類別java.lang.String
儲存字串資料。然而,Java 中的字串
- 具有永久性(即它們不能改變),
- 是對象。
public static void main(String args[]) {
String test1 = "Today is ";
appendTodaysDate(test1);
System.out.println(test1);
}
/* прим. редактора: закомментированный метод должен иметь модификатор
static (здесь автором допущена ошибка №9)
public void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
*/
public static void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
在上面的範例中,學生想要透過為方法中的test1
參數分配新值來更改局部變數的值。這自然是行不通的。含義會改變,但含義將保持不變。發生此錯誤是由於以下誤解:(1) java 物件始終透過引用傳遞,(2) java 中的字串是不可變的。您需要了解字串物件永遠不會更改其值,並且對字串的所有操作都會建立一個新物件。要修復上面範例中的錯誤,您需要從該方法傳回一個字串,或將一個物件作為參數傳遞給該方法而不是. 更正範例1:line
appendTodaysDate
line
test1
StringBuffer
String
public static void main(String args[]) {
String test1 = "Today is ";
test1 = appendTodaysDate(test1);
System.out.println(test1);
}
public static String appendTodaysDate(String line) {
return (line + (new Date()).toString());
}
更正範例2:
public static void main(String args[]) {
StringBuffer test1 = new StringBuffer("Today is ");
appendTodaysDate(test1);
System.out.println(test1.toString());
}
public static void appendTodaysDate(StringBuffer line) {
line.append((new Date()).toString());
}
約。翻譯 |
11. 將建構子宣告為方法
Java 中的物件建構函數在外觀上與常規方法類似。唯一的區別是建構函數沒有指定返回值的類型,並且名稱與類別名稱相同。不幸的是,Java 允許方法名稱與類別名稱相同。在下面的範例中,學生希望Vector list
在建立班級時初始化班級欄位。這不會發生,因為方法'IntList'
不是建構子。 錯誤的例子。
public class IntList {
Vector list;
// Выглядит How конструктор, но на самом деле это метод
public void IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
NullPointerException
第一次存取該欄位時, 程式碼將引發異常list
。該錯誤很容易修復:您只需從方法標頭中刪除返回值即可。 更正範例:
public class IntList {
Vector list;
// Это конструктор
public IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
12. 忘記將物件轉換為所需類型
與所有其他物件導向的語言一樣,在 Java 中,您可以將物件稱為其超類別。這稱為'upcasting'
,它在 Java 中自動完成。但是,如果將變數、類別欄位或方法傳回值宣告為超類,則子類別的欄位和方法將不可見。將超類別引用為子類別被調用'downcasting'
,需要自己註冊(即將物件帶到想要的子類別)。學生經常忘記對物件進行子類化。當使用套件(即集合框架)中的物件陣列和集合時,最常發生這種情況。下面的範例將一個物件放入數組中,然後將其從數組中刪除以將其與另一個字串進行比較。編譯器將偵測到錯誤,並且在明確指定類型轉換之前不會編譯程式碼。 錯誤的例子。java.util
String
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if (arr[0].compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
對某些人來說,類型轉換的意思很困難。動態方法尤其常引起困難。在上面的範例中,如果使用方法equals
而不是compareTo
,編譯器將不會拋出錯誤,並且程式碼將正常工作,因為equals
類別的方法將被呼叫String
。您需要了解動態連結與downcasting
. 更正範例:
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if ( ((String) arr[0]).compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
13.使用接口。
對於許多學生來說,類別和介面之間的區別並不完全清楚。因此,一些學生嘗試實現接口,例如Observer
使用Runnable
extends關鍵字而不是implements。要修正錯誤,您只需將關鍵字修正為正確的關鍵字即可。 錯誤範例:
public class SharkSim extends Runnable {
float length;
...
}
更正範例:
public class SharkSim implements Runnable {
float length;
...
}
相關錯誤:擴展和實作區塊 的順序不正確。根據 Java 規範,類別擴展聲明必須位於介面實作宣告之前。另外,對於接口, implements關鍵字只需寫一次;多個接口之間用逗號分隔。 還有一些錯誤的例子:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
float length;
...
}
// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
int airLeft;
...
}
更正的例子:
// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
float length;
...
}
// Несколько интерфейсов разделяются запятыми
public class DiverSim implements Swimmer, Runnable {
int airLeft;
...
}
14.忘記使用超類別方法的回傳值
Java 允許您使用關鍵字關鍵字從子類別呼叫類似的超類別方法。有時學生必須呼叫超類別方法,但常常忘記使用回傳值。這種情況在那些尚未理解方法及其回傳值的學生中尤其常見。在下面的範例中,學生想要toString()
將超類別方法的結果插入子類別方法的結果中toString()
。但是,它不使用超類別方法的傳回值。 錯誤範例:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
super();
return("color=" + fillColor + ", beveled=" + beveled);
}
}
要修正錯誤,通常只需將返回值指派給局部變量,然後在計算子類別方法的結果時使用該變數即可。 更正範例:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
String rectStr = super();
return(rectStr + " - " +
"color=" + fillColor + ", beveled=" + beveled);
}
}
15.忘記添加AWT組件
AWT 使用簡單的 GUI 設計模型:每個介面元件必須先使用自己的建構函式創建,然後使用add()
父元件方法放置到應用程式視窗中。因此,AWT 上的介面具有層次結構。學生有時會忘記這兩個步驟。他們創建了一個組件,但忘記將其放置在放大視窗中。這不會在編譯階段導致錯誤;該元件根本不會出現在應用程式視窗中。 錯誤的例子。
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
}
}
要修復此錯誤,您只需將元件新增至其父元件即可。下面的範例展示如何執行此操作。應該注意的是,忘記將元件新增至應用程式視窗的學生通常也會忘記為該元件指派事件偵聽器。 更正範例:
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
Panel controlPanel = new Panel();
controlPanel.add(exit);
add("Center", controlPanel);
exit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
17.忘記啟動直播
Java 中的多執行緒是使用java.lang.Thread
. 執行緒的生命週期由4個階段組成:初始化、啟動、阻塞和停止。新建立的執行緒處於初始化狀態。要將其置於運行狀態,您需要調用其start()
. 有時學生創建線程但忘記啟動它們。通常,當學生對並行程式設計和多執行緒知識不足時,就會發生該錯誤。(大約翻譯:我沒有看到連接)要修復錯誤,您只需啟動線程即可。在下面的例子中,一個學生想要使用介面創建圖片的動畫Runnable
,但是他忘記啟動線程。 錯誤的例子
public class AnimCanvas extends Canvas implements Runnable {
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
}
// метод run() не будет вызван,
// потому что поток не запущен.
public void run() {
for(int n = 0; n < 10000; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
更正範例:
public class AnimCanvas extends Canvas implements Runnable {
static final int LIMIT = 10000;
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
myThread.start();
}
public void run() {
for(int n = 0; n < LIMIT; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
執行緒的生命週期以及執行緒與實作介面的類別之間的關係Runnable
是 Java 程式設計中非常重要的部分,關注這一點將會很有用。
18.使用java.io.DataInputStream類別禁止的readLine()方法
readLine()
在 Java 1.0 版本中,您必須使用類別方法來讀取文字字串java.io.DataInputStream
。Java 1.1 版本新增了一整套 I/O 類別來提供文字的 I/O 操作:Reader
和Writer
。因此,從1.1版本開始,要讀取一行文本,必須使用readLine()
類別方法java.io.BufferedReader
。學生可能沒有意識到這種變化,尤其是如果他們是從舊書本上學的。(大約翻譯:實際上不再相關。現在不太可能有人會從 10 年前的書籍中學習)。舊方法readLine()
仍然保留在 JDK 中,但被聲明為非法,這常常讓學生感到困惑。您需要了解的是,使用readLine()
類別方法java.io.DataInputStream
並沒有錯,它只是過時了。您必須使用該類別BufferedReader
。 錯誤範例:
public class LineReader {
private DataInputStream dis;
public LineReader(InputStream is) {
dis = new DataInputStream(is);
}
public String getLine() {
String ret = null;
try {
ret = dis.readLine(); // Неправильно! Запрещено.
} catch (IOException ie) { }
return ret;
}
}
更正範例:
public class LineReader {
private BufferedReader br;
public LineReader(InputStream is) {
br = new BufferedReader(new InputStreamReader(is));
}
public String getLine() {
String ret = null;
try {
ret = br.readLine();
} catch (IOException ie) { }
return ret;
}
}
1.0以後的版本還有其他禁止的方法,但這是最常見的。
19. 使用 double 作為浮點數
與大多數其他語言一樣,Java 支援浮點數(小數)運算。Java 有 2 種浮點數的基本類型:double
根據 IEEE 標準的 64 位元精度的數字,以及float
根據 IEEE 標準的 32 位元精度的數字。困難在於,當使用 1.75、12.9e17 或 -0.00003 等十進制數時,編譯器會將它們指派給 type double
。Java 不會在可能發生精度損失的操作中執行類型轉換。這種類型轉換必須由程式設計師完成。例如,Java 不允許您在沒有類型轉換的情況下將類型值指派給類型int
變量byte
,如下例所示。
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
由於小數由 類型 表示double
,並且double
對類型變數的賦值float
可能會導致精度損失,因此編譯器將抱怨任何將小數用作 的嘗試float
。因此,使用下面的分配將阻止該類別編譯。
float realValue1 = -1.7; /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
這個賦值在 C 或 C++ 中可以工作,但在 Java 中則嚴格得多。有 3 種方法可以消除此錯誤。您可以使用 typedouble
來取代float
. 這是最簡單的解決方案。事實上,使用 32 位元算術而不是 64 位元沒有意義;速度上的差異仍然被 JVM 吞噬(此外,在現代處理器中,所有小數都轉換為 80 位元的格式)任何操作之前處理器暫存器)。使用它們的唯一優點float
是它們佔用更少的內存,這在處理大量小數變數時非常有用。您可以使用數字類型修飾符來告訴編譯器如何儲存數字。類型的修飾符float - 'f'
。因此,編譯器會將類型 1.75 指派給double
、 和1.75f - float
。例如:
float realValue1 = 1.7; /* неправильно! */
float realValue2 = 1.9f; /* правильно */
您可以使用顯式類型轉換。double
這是最不優雅的方式,但在將類型變數轉換為 type時很有用float
。例子:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
您可以在此處和此處閱讀有關浮點數的更多資訊。
——譯者評—— |
GO TO FULL VERSION