1. שם המחלקה שונה משם הקובץ בו היא מאוחסנת
כל מסגרות ה-Java שהשתמשתי בהן, כולל ה-Javasoft JDKs, מניחות שקוד המקור של מחלקה עם ה-Public Modifier מאוחסן בקובץ עם אותו שם בדיוק כמו שם המחלקה וסיומת .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, מחרוזות הן אובייקטים של המחלקה 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++, כאשר יצירת מערך של אובייקטים מביאה ליצירת האובייקטים עצמם על ידי קריאה לבנאי ברירת המחדל שלהם.) בדוגמה למטה, התלמיד רוצה ליצור 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 מאוחסנת בקובץ אחד לא יותר. בכל קובץ קוד מקור אתה יכול למקם לא יותר מכיתה אחת עם ה-modifier 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;
}
}
ישנן מספר דרכים לתקן שגיאה זו. הפשוטה ביותר היא לגשת לשדות המחלקה באמצעות ה-implicit this
: pointer this.Name_поля
. הדרך הטובה ביותר היא לשנות את שם שדה המחלקה או המשתנה המקומי, ואז ההחלפה לא תתרחש. (בערך תרגום: השיטה ה-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. שכחתי לקרוא לבנאי האב (סופר-class).
כאשר מחלקה מרחיבה מחלקה אחרת, כל בנאי תת מחלקה חייב לקרוא לבנאי מחלקות-על כלשהו. זה מושג בדרך כלל על ידי קריאה לבנאי מחלקת העל עם השיטהsuper(x)
הממוקמת בשורה הראשונה של הבנאי. אם אין קריאה בשורה הראשונה של הבנאי super(x)
, המהדר עצמו מכניס קריאה זו, אך ללא פרמטרים: super()
. (בערך טרנס.: x...se, אבל לא ידעתי) לפעמים תלמידים שוכחים מהדרישה הזו. בדרך כלל זו לא בעיה: הקריאה לבנאי ה- superclass מוכנסת על ידי המהדר והכל עובד בסדר. עם זאת, אם למחלקת העל אין בנאי ברירת מחדל, המהדר יזרוק שגיאה. בדוגמה למטה, לכל הבנאים של מחלקות העל 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++ או עדה בדרך כלל אינם מתקשים באותם קשיים כמו מתכנתי 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
קובע את הסדר שבו ייתפסו חריגים. יש לקחת בחשבון שכל בלוק כזה יתפוס את כל החריגים של המחלקה שצוינה או כל אחת מתת המחלקות שלה. אם לא תיקח את זה בחשבון, אתה עלול להסתיים עם בלוק קאץ' בלתי ניתן להשגה, עליו יצביע המהדר. בדוגמה להלן 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™ קרא את ההמשך קישור למקור: טעויות של מתכנתי ג'אווה מתחילים
GO TO FULL VERSION