JavaRush /Java blogi /Random-UZ /Yangi boshlanuvchi java dasturchilarining xatolari. 2-qis...
articles
Daraja

Yangi boshlanuvchi java dasturchilarining xatolari. 2-qism

Guruhda nashr etilgan
Yangi boshlanuvchi java dasturchilarining xatolari. 1-qism

9. Statik bo'lmagan sinf usullarini main() usulidan chaqirish

Har qanday Java dasturining kirish nuqtasi statik usul bo'lishi kerak main:
Yangi boshlanuvchi java dasturchilarining xatolari.  2-1-qism
public static void main(String[] args) {
  ...
}
Ushbu usul statik bo'lgani uchun siz undan statik bo'lmagan sinf usullarini chaqira olmaysiz. Talabalar ko'pincha buni unutishadi va sinf misolini yaratmasdan usullarni chaqirishga harakat qilishadi. Bu xato odatda mashg'ulotlarning eng boshida, talabalar kichik dasturlarni yozganda sodir bo'ladi. Noto'g'ri misol:
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);
        }
    }
}
Xatoni tuzatishning 2 yo'li mavjud: kerakli usulni statik qilish yoki sinfning namunasini yaratish. To'g'ri usulni tanlash uchun o'zingizdan so'rang, bu usul maydon yoki boshqa sinf usullaridan foydalanadimi. Ha bo'lsa, siz sinfning namunasini yaratishingiz va unga usulni chaqirishingiz kerak, aks holda siz usulni statik qilishingiz kerak. Tuzatilgan 1-misol:
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);
        }
    }
}
Tuzatilgan 2-misol:
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 sinfi obyektlarini metod parametrlari sifatida ishlatish.

Java-da sinf java.lang.Stringstring ma'lumotlarini saqlaydi. Biroq, Java-dagi satrlar
  1. doimiylikka ega (ya'ni ularni o'zgartirib bo'lmaydi),
  2. ob'ektlardir.
Shuning uchun ularni faqat belgilar buferi sifatida ko'rib chiqish mumkin emas; ular o'zgarmas ob'ektlardir. Ba'zida talabalar string ob'ekti mos yozuvlar bo'yicha belgilar massivi sifatida uzatiladi (C yoki C++ da bo'lgani kabi) deb noto'g'ri kutish bilan satrlarni uzatadilar. Kompilyator odatda buni xato deb hisoblamaydi. Noto'g'ri misol.
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();
}
Yuqoridagi misolda talaba metoddagi test1parametrga yangi qiymat berish orqali mahalliy o'zgaruvchining qiymatini o'zgartirmoqchi . Tabiiyki, bu ishlamaydi. Ma'no o'zgaradi, lekin ma'no o'zgarishsiz qoladi. Bu xatolik (1) java ob'ektlari har doim havola orqali uzatilishi va (2) javadagi satrlar o'zgarmasligini noto'g'ri tushunish tufayli yuzaga keladi. Siz tushunishingiz kerakki, string ob'ektlari hech qachon o'z qiymatini o'zgartirmaydi va satrlardagi barcha operatsiyalar yangi ob'ektni yaratadi. Yuqoridagi misoldagi xatoni tuzatish uchun siz usuldan satrni qaytarishingiz yoki ob'ektni o'rniga metodga parametr sifatida o'tkazishingiz kerak . Tuzatilgan 1-misol:lineappendTodaysDatelinetest1StringBufferString
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());
}
Tuzatilgan 2-misol:
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());
}

taxminan. tarjima
Aslida, xato nima ekanligini tushunish unchalik oson emas. Ob'ektlar mos yozuvlar orqali uzatilganligi sababli, u linexuddi shu joyga tegishli ekanligini anglatadi test1. Bu shuni anglatadiki, yangisini yaratish orqali linebiz yangisini yaratamiz test1. Noto'g'ri misolda, hamma narsa uzatish Stringmos yozuvlar bo'yicha emas, balki qiymat bo'yicha bo'lib ko'rinadi.

11. Konstruktorni metod sifatida e’lon qilish

Java tilidagi obyekt konstruktorlari tashqi ko'rinishi bo'yicha oddiy usullarga o'xshaydi. Yagona farq shundaki, konstruktor qaytarish turini belgilamaydi va nom sinf nomi bilan bir xil. Afsuski, Java metod nomini sinf nomi bilan bir xil bo'lishiga imkon beradi. Quyidagi misolda talaba Vector listsinf yaratishda sinf maydonini ishga tushirmoqchi. Bu sodir bo'lmaydi, chunki usul 'IntList'konstruktor emas. Noto'g'ri misol.
public class IntList {
    Vector list;

    // Выглядит How конструктор, но на самом деле это метод
    public void IntList() {
        list = new Vector();
    }

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
NullPointerExceptionKod birinchi marta maydonga kirishda istisno qiladi list. Xatoni tuzatish oson: siz faqat usul sarlavhasidan qaytarish qiymatini olib tashlashingiz kerak. To'g'rilangan misol:
public class IntList {
    Vector list;

    // Это конструктор
    public IntList() {
        list = new Vector();
    }

    public append(int n) {
        list.addElement(new Integer(n));
    }
}

12. Ob'ektni kerakli turga chiqarishni unutdingiz

Boshqa barcha ob'ektga yo'naltirilgan tillar singari, Java-da siz ob'ektni uning yuqori sinfi deb atashingiz mumkin. Bu 'upcasting'Java-da avtomatik ravishda amalga oshiriladi. Biroq, agar o'zgaruvchi, sinf maydoni yoki usulning qaytish qiymati yuqori sinf sifatida e'lon qilinsa, pastki sinfning maydonlari va usullari ko'rinmas bo'ladi. Subklass deb ataladigan supersinfga murojaat qilish 'downcasting', uni o'zingiz ro'yxatdan o'tkazishingiz kerak (ya'ni ob'ektni kerakli kichik sinfga keltiring). Talabalar ko'pincha ob'ektni kichik sinflarga ajratishni unutishadi. Bu ko'pincha ob'ektlar massivlari va to'plamdagi to'plamlardan foydalanilganda sodir bo'ladi ( To'plam ramkasinijava.util anglatadi ). Quyidagi misol ob'ektni massivga qo'yadi va keyin uni boshqa satr bilan solishtirish uchun uni massivdan olib tashlaydi. Kompilyator xatoni aniqlaydi va translatsiya turi aniq belgilanmaguncha kodni kompilyatsiya qilmaydi. Noto'g'ri misol.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]);
}
Turi quyishning ma'nosi ba'zilar uchun qiyin. Dinamik usullar, ayniqsa, ko'pincha qiyinchiliklarga olib keladi. equalsYuqoridagi misolda, agar usul o'rniga ishlatilsa compareTo, kompilyator xatoga yo'l qo'ymagan bo'lardi va kod to'g'ri ishlagan bo'lardi, chunki equalssinfning usuli chaqirilgan bo'lar edi String. Dinamik ulanish dan farq qilishini tushunishingiz kerak downcasting. To'g'rilangan misol:
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. Interfeyslardan foydalanish.

Ko'pgina talabalar uchun sinflar va interfeyslar o'rtasidagi farq to'liq aniq emas. Shuning uchun, ba'zi talabalar ilovalar o'rniga kengaytirilgan kalit so'zdan foydalanib Observeryoki kabi interfeyslarni amalga oshirishga harakat qilishadi . Xatoni tuzatish uchun siz faqat kalit so'zni to'g'ri so'z bilan to'g'rilashingiz kerak. Noto'g'ri misol:Runnable
public class SharkSim extends Runnable {
    float length;
    ...
}
To'g'rilangan misol:
public class SharkSim implements Runnable {
    float length;
    ...
}
Tegishli xato: bloklarni kengaytirish va amalga oshirishning noto'g'ri tartibi . Java spetsifikatsiyasiga ko'ra, sinf kengaytmalari deklaratsiyasi interfeysni amalga oshirish deklaratsiyasidan oldin kelishi kerak. Bundan tashqari, interfeyslar uchun implements kalit so'zi faqat bir marta yozilishi kerak, bir nechta interfeyslar vergul bilan ajratiladi. Yana bir nechta noto'g'ri misollar:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
    float length;
    ...
}

// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
    int airLeft;
    ...
}
Tuzatilgan misollar:
// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
    float length;
    ...
}

// Несколько интерфейсов разделяются запятыми
public class DiverSim implements Swimmer, Runnable {
    int airLeft;
    ...
}

14. Superklass usulining qaytish qiymatidan foydalanishni unutdingiz

Java sizga kalit so'z kalit so'zidan foydalangan holda pastki sinfdan shunga o'xshash superklass usulini chaqirish imkonini beradi. Ba'zida talabalar superklass usullarini chaqirishlari kerak, lekin ko'pincha qaytarish qiymatidan foydalanishni unutishadi. Bu, ayniqsa, usullar va ularning qaytish qiymatlarini hali tushunmagan talabalar orasida tez-tez sodir bo'ladi. toString()Quyidagi misolda talaba yuqori sinf usuli natijasini kichik sinf usuli natijasiga kiritmoqchi toString(). Biroq, u superklass usulining qaytish qiymatidan foydalanmaydi. Noto'g'ri misol:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Xatoni to'g'irlash uchun odatda mahalliy o'zgaruvchiga qaytish qiymatini belgilash kifoya, keyin esa subklass usuli natijasini hisoblashda ushbu o'zgaruvchidan foydalaning. To'g'rilangan misol:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. AWT komponentlarini qo'shishni unutdingiz

AWT oddiy GUI dizayn modelidan foydalanadi: har bir interfeys komponenti avvalo o'z konstruktori yordamida yaratilishi kerak, so'ngra add()asosiy komponent usuli yordamida dastur oynasiga joylashtirilishi kerak. Shunday qilib, AWT interfeysi ierarxik tuzilmani oladi. Talabalar ba'zan bu 2 qadamni unutishadi. Ular komponent yaratadilar, lekin uni kattalashtirish oynasiga joylashtirishni unutishadi. Bu kompilyatsiya bosqichida xatolarga olib kelmaydi; komponent oddiygina dastur oynasida ko'rinmaydi. Noto'g'ri misol.
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Ushbu xatoni tuzatish uchun siz shunchaki komponentlarni ota-onalariga qo'shishingiz kerak. Quyidagi misol buni qanday qilishni ko'rsatadi. Shuni ta'kidlash kerakki, ko'pincha dastur oynasiga komponent qo'shishni unutgan talaba ushbu komponentga voqea tinglovchilarini belgilashni ham unutadi. To'g'rilangan misol:
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. Oqimni boshlashni unutib qo'ydim

Java-da multithreading yordamida amalga oshiriladi java.lang.Thread. Ipning hayot aylanishi 4 bosqichdan iborat: ishga tushirilgan, boshlangan, bloklangan va to'xtatilgan. Yangi yaratilgan ip ishga tushirilgan holatda. Uni ishlaydigan holatga keltirish uchun uni chaqirishingiz kerak start(). Ba'zida talabalar mavzular yaratadilar, lekin ularni boshlashni unutishadi. Odatda xatolik talaba parallel dasturlash va ko'p ish zarralari haqida yetarli ma'lumotga ega bo'lmaganda yuzaga keladi. (taxminan tarjima: Men ulanishni ko'rmayapman) Xatoni tuzatish uchun siz shunchaki mavzuni boshlashingiz kerak. Quyidagi misolda talaba interfeysdan foydalanib rasm animatsiyasini yaratmoqchi Runnable, lekin u mavzuni boshlashni unutib qo'ygan. Noto'g'ri misol
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);
                }
        }
        ...
}
To'g'rilangan misol:
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);
                }
        }
        ...
}
Tarmoqning hayot aylanishi va interfeysni amalga oshiradigan iplar va sinflar o'rtasidagi munosabatlar RunnableJava dasturlashning juda muhim qismidir va bunga e'tibor qaratish foydali bo'ladi.

18. Java.io.DataInputStream sinfining taqiqlangan readLine() usulidan foydalanish

Java 1.0 versiyasida siz matn qatorini o'qish uchun readLine()sinf usulidan foydalanishingiz kerak edi java.io.DataInputStream. Java 1.1 versiyasi matn uchun kiritish/chiqarish operatsiyalarini ta'minlash uchun I/U sinflarining butun majmuasini qo'shdi: Readerva Writer. Shunday qilib, 1.1 versiyasidan matn qatorini o'qish uchun siz readLine()sinf usulidan foydalanishingiz kerak java.io.BufferedReader. Talabalar bu o'zgarishdan xabardor bo'lmasligi mumkin, ayniqsa eski kitoblardan o'qitilgan bo'lsa. (taxminan. Tarjima: aslida endi ahamiyatli emas. Endi hech kim 10 yoshli kitoblarni o'rganishi dargumon). Eski usul readLine()JDKda qoladi, lekin noqonuniy deb e'lon qilinadi, bu ko'pincha talabalarni chalkashtirib yuboradi. Siz tushunishingiz kerak bo'lgan narsa shundaki, readLine()sinf usulidan foydalanish java.io.DataInputStreamnoto'g'ri emas, u eskirgan. Siz sinfdan foydalanishingiz kerak BufferedReader. Noto'g'ri misol:
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;
    }
}
To'g'rilangan misol:
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 dan keyingi versiyalarda boshqa taqiqlangan usullar mavjud, ammo bu eng keng tarqalgan.

19. Double dan float sifatida foydalanish

Ko'pgina boshqa tillar singari, Java suzuvchi nuqtali raqamlar (kasr raqamlari) bilan ishlashni qo'llab-quvvatlaydi. Java-da suzuvchi nuqtali raqamlar uchun ikkita ibtidoiy tur mavjud: doubleIEEE 64-bitli aniqlik va floatIEEE 32-bitli aniqlik. Qiyinchilik 1.75, 12.9e17 yoki -0.00003 kabi oʻnlik raqamlardan foydalanishda - kompilyator ularni turiga tayinlaydi double. Java aniqlik yo'qolishi mumkin bo'lgan operatsiyalarda turdagi translatsiyalarni amalga oshirmaydi. Ushbu turdagi kasting dasturchi tomonidan amalga oshirilishi kerak. Misol uchun, Java quyida keltirilgan misolda ko'rsatilganidek, turdagi into'zgaruvchiga tur qiymatini belgilashga ruxsat bermaydi .byte
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Kasr sonlar turi bilan ifodalanganligi doubleva doubleturdagi o'zgaruvchiga tayinlash floataniqlikning yo'qolishiga olib kelishi mumkinligi sababli, kompilyator kasr sonlarni sifatida ishlatishga bo'lgan har qanday urinishdan shikoyat qiladi float. Shunday qilib, quyidagi topshiriqlardan foydalanish sinfni kompilyatsiya qilishdan saqlaydi.
float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Bu topshiriq C yoki C++ da ishlaydi, lekin Javada bu ancha qattiqroq. Ushbu xatolikdan xalos bo'lishning 3 yo'li mavjud. doubleSiz o'rniga type dan foydalanishingiz mumkin float. Bu eng oddiy yechim. Aslida, 64-bit o'rniga 32-bitli arifmetikadan foydalanishning ma'nosi yo'q; tezlikdagi farq hali ham JVM tomonidan iste'mol qilinadi (bundan tashqari, zamonaviy protsessorlarda barcha kasr raqamlari 80-bitli protsessor formatiga o'zgartiriladi. har qanday operatsiyadan oldin ro'yxatdan o'ting). Ulardan foydalanishning yagona afzalligi floatshundaki, ular kamroq xotirani egallaydi, bu ko'p sonli kasr o'zgaruvchilari bilan ishlashda foydalidir. Siz kompilyatorga raqamni qanday saqlash kerakligini aytish uchun raqam turini o'zgartiruvchidan foydalanishingiz mumkin. Tur uchun modifikator float - 'f'. doubleShunday qilib, kompilyator 1.75 turini va ga belgilaydi 1.75f - float. Masalan:
float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
Siz aniq turdagi kastingdan foydalanishingiz mumkin. doubleBu eng kam oqlangan usul, ammo u turdagi o'zgaruvchini type ga aylantirishda foydalidir float. Misol:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Suzuvchi nuqta raqamlari haqida koʻproq maʼlumotni bu yerda va bu yerda oʻqishingiz mumkin.

-- tarjimonning izohi --
Hammasi shu.
10-misolda 9-xato haqiqatda sodir bo'ldi.Men buni darhol payqadim, lekin eslatma yozishni unutibman. lekin asl manba bilan nomuvofiqlik bo'lmasligi uchun uni tuzatmagan.

Muallif: A.Grasoff™ Manbaga havola: Yangi boshlanuvchi java dasturchilarining xatolari
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION