1. ชื่อของคลาสแตกต่างจากชื่อของไฟล์ที่เก็บไว้
กรอบงาน Java ทั้งหมดที่ฉันใช้ รวมถึง Javasoft JDK ถือว่าซอร์สโค้ดสำหรับคลาสที่มีตัวแก้ไขสาธารณะนั้นถูกจัดเก็บไว้ในไฟล์ที่มีชื่อเดียวกันกับชื่อคลาสและนามสกุล .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();
}
}
โปรดทราบ:ชื่อคลาสจะถือว่าขึ้นต้นด้วยตัวพิมพ์ใหญ่ ระบบปฏิบัติการที่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ในชื่อไฟล์อาจทำให้เกิดปัญหาเพิ่มเติม โดยเฉพาะอย่างยิ่งสำหรับนักเรียนที่เรียน Java บน Unix ซึ่งคุ้นเคยกับระบบการตั้งชื่อไฟล์ DOS คลาสMotorVehicle
ควรถูกเก็บไว้ในไฟล์MotorVehicle.java
แต่ไม่ใช่ในmotorvehicle.java
.
2. การเปรียบเทียบโดยใช้==
java.lang.String
ใน Java สตริงเป็นวัตถุของคลาส ตัวดำเนินการนำ==
ไปใช้กับวัตถุจะตรวจสอบความเท่าเทียมกันของการอ้างอิงถึงวัตถุ! บางครั้งนักเรียนไม่เข้าใจความหมายของโอเปอเรเตอร์==
และพยายามใช้มันเพื่อเปรียบเทียบสตริง ตัวอย่างที่ไม่ถูกต้อง:
// проверим, 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++ โดยที่การสร้างอาร์เรย์ของอ็อบเจ็กต์ส่งผลให้เกิดการสร้างอ็อบเจ็กต์ด้วยตนเองโดยการเรียกคอนสตรัคเตอร์เริ่มต้น) ในตัวอย่างด้านล่าง นักเรียนต้องการสร้างวัตถุ 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) บางครั้งนักเรียนลืมกฎข้อที่ 2 ซึ่งนำไปสู่ข้อผิดพลาด ในการรวบรวมเวที ข้อความแสดงข้อผิดพลาดสำหรับกฎข้อที่ 2 และ 3 จะเหมือนกัน (ซึ่งทำให้ยากต่อการจดจำข้อผิดพลาดนี้)
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;
}
}
มีหลายวิธีในการแก้ไขข้อผิดพลาดนี้ วิธีที่ง่ายที่สุดคือการเข้าถึงฟิลด์คลาสโดยใช้ implicit this
: this.Name_поля
pointer วิธีที่ดีที่สุดคือการเปลี่ยนชื่อฟิลด์คลาสหรือตัวแปรโลคัล จากนั้นการทดแทนจะไม่เกิดขึ้น (แปลโดยประมาณ: วิธีที่ 2 ไม่ใช่วิธีของเรา ยิ่งไปกว่านั้นไม่รับประกันว่าสักวันหนึ่งฉันจะไม่เปลี่ยนเขตข้อมูลของตัวแปรโดยไม่ตั้งใจ ความยากที่ยิ่งกว่านั้นเกิดขึ้นกับการสืบทอดเมื่อฉันไม่เห็นเลยว่าช่องใด ชั้นเรียนมี ) ตัวอย่างที่แก้ไขแล้ว:
// 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)
ที่วางอยู่บนบรรทัดแรกของตัวสร้าง ถ้าไม่มีการเรียกในบรรทัดแรกของ Constructor 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 block ที่ไม่สามารถเข้าถึงได้ ซึ่งคอมไพลเลอร์จะชี้ให้เห็น ในตัวอย่างด้านล่าง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
( ซึ่งไม่จำเป็นสำหรับข้อยกเว้น - คลาสย่อยของคลาส ) บางครั้งนักเรียนลืมไปว่าการเรียก method อาจทำให้เกิดข้อยกเว้นได้ 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