JavaRush /בלוג Java /Random-HE /טעויות של מתכנתי ג'אווה מתחילים. חלק 1
articles
רָמָה

טעויות של מתכנתי ג'אווה מתחילים. חלק 1

פורסם בקבוצה

1. שם המחלקה שונה משם הקובץ בו היא מאוחסנת

כל מסגרות ה-Java שהשתמשתי בהן, כולל ה-Javasoft JDKs, מניחות שקוד המקור של מחלקה עם ה-Public Modifier מאוחסן בקובץ עם אותו שם בדיוק כמו שם המחלקה וסיומת .java. אי ציות למוסכמה זו עלולה לגרום לבעיות רבות שיופיעו במהלך ההידור.
טעויות של מתכנתי ג'אווה מתחילים.  חלק 1 - 1
תלמידים מתחילים (מתכנתים) שוכחים לא פעם מהמוסכמה הזו ולדוגמה, מגדירים את שם הקובץ בהתאם למשימה: Lab6.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;
  }
מקום אפשרי נוסף להתרחשות השגיאה הזו הוא על ידי הגדרת שם פרמטר המתודה להיות זהה לשם שדה המחלקה. זה נראה טוב בבנאים, אבל לא מתאים לשיטות רגילות.

משוער. תִרגוּם

קצת כאוטי, אבל זו התמצית

public class Test {
   private int param = 0;

   public Test(int param) {
      this.param = param;
   }
}

כלומר, הכל נראה יפה בקונסטרוקטור, אבל אסור להשתמש בזה לשיטות רגילות.

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™ קרא את ההמשך קישור למקור: טעויות של מתכנתי ג'אווה מתחילים
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION