JavaRush /Blog Java /Random-MS /Kesilapan pengaturcara java pemula. Bahagian 2
articles
Tahap

Kesilapan pengaturcara java pemula. Bahagian 2

Diterbitkan dalam kumpulan
Kesilapan pengaturcara java pemula. Bahagian 1

9. Memanggil kaedah kelas bukan statik daripada kaedah main().

Titik masuk mana-mana program Java mestilah kaedah statik main:
Kesilapan pengaturcara java pemula.  Bahagian 2 - 1
public static void main(String[] args) {
  ...
}
Oleh kerana kaedah ini adalah statik, anda tidak boleh memanggil kaedah kelas bukan statik daripadanya. Pelajar sering melupakan perkara ini dan cuba memanggil kaedah tanpa membuat contoh kelas. Kesilapan ini biasanya dibuat pada awal latihan, apabila pelajar menulis program kecil. Contoh yang salah:
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);
        }
    }
}
Terdapat 2 cara untuk membetulkan ralat: buat kaedah yang diingini statik atau buat contoh kelas. Untuk memilih kaedah yang betul, tanya diri anda sama ada kaedah itu menggunakan medan atau kaedah kelas lain. Jika ya, maka anda harus membuat contoh kelas dan memanggil kaedah padanya, jika tidak, anda harus membuat kaedah statik. Contoh yang diperbetulkan 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);
        }
    }
}
Contoh yang diperbetulkan 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. Menggunakan objek kelas String sebagai parameter kaedah.

Di Java, kelas java.lang.Stringmenyimpan data rentetan. Walau bagaimanapun, rentetan di Jawa
  1. mempunyai kekal (iaitu, mereka tidak boleh diubah),
  2. adalah objek.
Oleh itu, mereka tidak boleh dianggap sebagai penampan aksara sahaja; mereka adalah objek yang tidak boleh diubah. Kadangkala pelajar melepasi rentetan dalam jangkaan yang salah bahawa objek rentetan akan diluluskan sebagai tatasusunan aksara dengan rujukan (seperti dalam C atau C++). Pengkompil biasanya tidak menganggap ini sebagai ralat. Contoh yang salah.
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();
}
Dalam contoh di atas, pelajar ingin menukar nilai pembolehubah tempatan test1dengan memberikan nilai baharu kepada parameter linedalam kaedah appendTodaysDate. Sememangnya ini tidak akan berfungsi. Makna lineakan berubah, tetapi makna test1akan tetap sama. Ralat ini berlaku disebabkan oleh salah faham bahawa (1) objek java sentiasa diluluskan oleh rujukan dan (2) rentetan dalam java tidak boleh diubah. Anda perlu memahami bahawa objek rentetan tidak pernah mengubah nilainya, dan semua operasi pada rentetan mencipta objek baharu. Untuk membetulkan ralat dalam contoh di atas, anda perlu sama ada mengembalikan rentetan daripada kaedah atau menghantar objek StringBuffersebagai parameter kepada kaedah dan bukannya String. Contoh yang diperbetulkan 1:
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());
}
Contoh yang diperbetulkan 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());
}

lebih kurang terjemahan
Sebenarnya, tidak begitu mudah untuk memahami apa kesilapan itu. Memandangkan objek diluluskan melalui rujukan, ini bermakna lineia merujuk kepada tempat yang sama seperti test1. Ini bermakna dengan mencipta yang baharu line, kami mencipta yang baharu test1. Dalam contoh yang salah, segala-galanya kelihatan seolah-olah pemindahan Stringadalah mengikut nilai, dan bukan dengan rujukan.

11. Mengisytiharkan pembina sebagai kaedah

Pembina objek di Java adalah serupa dalam penampilan dengan kaedah biasa. Satu-satunya perbezaan ialah pembina tidak menyatakan jenis nilai pulangan dan nama adalah sama dengan nama kelas. Malangnya, Java membenarkan nama kaedah sama dengan nama kelas. Dalam contoh di bawah, pelajar ingin memulakan medan kelas Vector listapabila mencipta kelas. Ini tidak akan berlaku kerana kaedah 'IntList'bukan pembina. Contoh yang salah.
public class IntList {
    Vector list;

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

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
Kod akan membuang pengecualian NullPointerExceptionpada kali pertama medan itu diakses list. Ralat ini mudah dibetulkan: anda hanya perlu mengalih keluar nilai pulangan daripada pengepala kaedah. Contoh yang diperbetulkan:
public class IntList {
    Vector list;

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

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

12. Terlupa untuk membuang objek ke jenis yang diperlukan

Seperti semua bahasa berorientasikan objek lain, dalam Java anda boleh merujuk kepada objek sebagai superclassnya. Ini dipanggil 'upcasting', ia dilakukan secara automatik di Jawa. Walau bagaimanapun, jika pembolehubah, medan kelas atau nilai pulangan kaedah diisytiharkan sebagai superclass, medan dan kaedah subkelas akan tidak kelihatan. Merujuk kepada superclass sebagai subclass dipanggil 'downcasting', anda perlu mendaftarkannya sendiri (iaitu, bawa objek ke subclass yang dikehendaki). Pelajar sering lupa tentang subkelas objek. Ini paling kerap berlaku apabila menggunakan tatasusunan Objek dan koleksi daripada pakej java.util(bermaksud Rangka Kerja Koleksi ). Contoh di bawah Stringmeletakkan objek ke dalam tatasusunan dan kemudian mengeluarkannya daripada tatasusunan untuk membandingkannya dengan rentetan lain. Pengkompil akan mengesan ralat dan tidak akan menyusun kod sehingga jenis cast dinyatakan secara eksplisit. Contoh yang salah.
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]);
}
Maksud pemutus jenis adalah sukar bagi sesetengah orang. Kaedah dinamik terutamanya sering menyebabkan kesukaran. Dalam contoh di atas, jika kaedah telah digunakan equalsdan bukannya compareTo, pengkompil tidak akan membuang ralat, dan kod itu akan berfungsi dengan betul, kerana kaedah equalskelas akan dipanggil String. Anda perlu memahami bahawa pautan dinamik adalah berbeza daripada downcasting. Contoh yang diperbetulkan:
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. Menggunakan antara muka.

Bagi kebanyakan pelajar, perbezaan antara kelas dan antara muka tidak sepenuhnya jelas. Oleh itu, sesetengah pelajar cuba melaksanakan antara muka seperti Observeratau Runnablemenggunakan kata kunci extends dan bukannya implement . Untuk membetulkan ralat, anda hanya perlu membetulkan kata kunci kepada yang betul. Contoh yang salah:
public class SharkSim extends Runnable {
    float length;
    ...
}
Contoh yang diperbetulkan:
public class SharkSim implements Runnable {
    float length;
    ...
}
Ralat berkaitan: Tertib lanjutan dan melaksanakan blok yang salah . Menurut spesifikasi Java, perisytiharan sambungan kelas mesti datang sebelum perisytiharan pelaksanaan antara muka. Selain itu, untuk antara muka, kata kunci pelaksanaan perlu ditulis sekali sahaja; berbilang antara muka dipisahkan dengan koma. Beberapa contoh yang lebih salah:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
    float length;
    ...
}

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

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

14. Terlupa menggunakan nilai pulangan kaedah superclass

Java membolehkan anda memanggil kaedah superclass yang serupa daripada subkelas menggunakan kata kunci kata kunci. Kadang-kadang pelajar perlu memanggil kaedah superclass, tetapi sering lupa untuk menggunakan nilai pulangan. Ini berlaku terutamanya dalam kalangan pelajar yang belum memahami kaedah dan nilai pulangan mereka. Dalam contoh di bawah, seorang pelajar ingin memasukkan hasil toString()kaedah superclass ke dalam hasil toString()kaedah subclass. Walau bagaimanapun, ia tidak menggunakan nilai pulangan kaedah superclass. Contoh yang salah:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Untuk membetulkan ralat, ia biasanya mencukupi untuk menetapkan nilai pulangan kepada pembolehubah tempatan, dan kemudian menggunakan pembolehubah itu apabila mengira hasil kaedah subkelas. Contoh yang diperbetulkan:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. Terlupa menambah komponen AWT

AWT menggunakan model reka bentuk GUI yang ringkas: setiap komponen antara muka mesti dibuat terlebih dahulu menggunakan pembinanya sendiri, dan kemudian diletakkan ke dalam tetingkap aplikasi menggunakan add()kaedah komponen induk. Oleh itu, antara muka pada AWT menerima struktur hierarki. Pelajar kadang-kadang lupa 2 langkah ini. Mereka mencipta komponen tetapi lupa meletakkannya dalam tetingkap pembesaran. Ini tidak akan menyebabkan ralat pada peringkat penyusunan; komponen itu tidak akan muncul dalam tetingkap aplikasi. Contoh yang salah.
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Untuk membetulkan ralat ini, anda hanya perlu menambah komponen pada ibu bapa mereka. Contoh di bawah menunjukkan cara melakukan ini. Perlu diingatkan bahawa selalunya pelajar yang terlupa menambah komponen pada tetingkap aplikasi juga terlupa untuk menetapkan pendengar acara kepada komponen tersebut. Contoh yang diperbetulkan:
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. Terlupa untuk memulakan strim

Multithreading dalam Java dilaksanakan menggunakan java.lang.Thread. Kitaran hayat benang terdiri daripada 4 peringkat: dimulakan, dimulakan, disekat dan dihentikan. Urutan yang baru dibuat berada dalam keadaan dimulakan. Untuk meletakkannya dalam keadaan berjalan, anda perlu memanggilnya start(). Kadang-kadang pelajar membuat benang tetapi lupa untuk memulakannya. Biasanya ralat berlaku apabila pelajar tidak mempunyai pengetahuan yang mencukupi tentang pengaturcaraan selari dan multithreading. (lebih kurang transl.: Saya tidak nampak sambungan) Untuk membetulkan ralat, anda hanya perlu memulakan urutan. Dalam contoh di bawah, seorang pelajar ingin mencipta animasi gambar menggunakan antara muka Runnable, tetapi dia terlupa untuk memulakan urutan. Contoh yang salah
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);
                }
        }
        ...
}
Contoh yang diperbetulkan:
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);
                }
        }
        ...
}
Kitaran hayat benang dan hubungan antara utas dan kelas yang melaksanakan antara muka Runnableadalah bahagian yang sangat penting dalam pengaturcaraan Java, dan bukan idea yang tidak baik untuk memberi tumpuan kepada perkara ini.

18. Menggunakan kaedah readLine() terlarang bagi kelas java.io.DataInputStream

readLine()Dalam Java versi 1.0, anda perlu menggunakan kaedah kelas untuk membaca rentetan teks java.io.DataInputStream. Java versi 1.1 menambah set keseluruhan kelas I/O untuk menyediakan operasi I/O untuk teks: Readerdan Writer. Oleh itu, daripada versi 1.1 untuk membaca baris teks, anda mesti menggunakan kaedah readLine()kelas java.io.BufferedReader. Pelajar mungkin tidak menyedari perubahan ini, terutamanya jika mereka diajar daripada buku lama. (lebih kurang. Terjemahan: sebenarnya tidak lagi relevan. Tidak mungkin sesiapa kini akan belajar daripada buku yang berusia 10 tahun). Kaedah lama readLine()kekal dalam JDK, tetapi diisytiharkan haram, yang sering mengelirukan pelajar. Apa yang anda perlu faham ialah menggunakan kaedah readLine()kelas java.io.DataInputStreamtidak salah, ia hanya ketinggalan zaman. Anda mesti menggunakan kelas BufferedReader. Contoh yang salah:
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;
    }
}
Contoh yang diperbetulkan:
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;
    }
}
Terdapat kaedah terlarang lain dalam versi lewat daripada 1.0, tetapi kaedah ini adalah yang paling biasa.

19. Menggunakan double as float

Seperti kebanyakan bahasa lain, Java menyokong operasi pada nombor titik terapung (nombor pecahan). Java mempunyai 2 jenis primitif untuk nombor titik terapung: doubleuntuk nombor dengan ketepatan 64-bit mengikut piawaian IEEE, dan floatuntuk nombor dengan ketepatan 32-bit mengikut piawaian IEEE. Kesukarannya ialah apabila menggunakan nombor perpuluhan seperti 1.75, 12.9e17 atau -0.00003 - pengkompil menugaskan mereka untuk menaip double. Java tidak melakukan tuangan jenis dalam operasi yang kehilangan ketepatan mungkin berlaku. Pemutus jenis ini mesti dilakukan oleh pengaturcara. Sebagai contoh, Java tidak akan membenarkan anda menetapkan nilai jenis kepada intpembolehubah jenis bytetanpa cast jenis, seperti yang ditunjukkan dalam contoh di bawah.
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Oleh kerana nombor pecahan diwakili oleh jenis double, dan penetapan doublekepada pembolehubah jenis floatboleh menyebabkan kehilangan ketepatan, pengkompil akan mengadu tentang sebarang percubaan untuk menggunakan nombor pecahan sebagai float. Jadi menggunakan tugasan di bawah akan menghalang kelas daripada menyusun.
float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Tugasan ini akan berfungsi dalam C atau C++, tetapi di Java ia lebih ketat. Terdapat 3 cara untuk menghilangkan ralat ini. Anda boleh menggunakan jenis doubledan bukannya float. Ini adalah penyelesaian yang paling mudah. Sebenarnya, tidak ada gunanya menggunakan aritmetik 32-bit dan bukannya 64-bit; perbezaan kelajuan masih dimakan oleh JVM (selain itu, dalam pemproses moden semua nombor pecahan ditukar kepada format pemproses 80-bit mendaftar sebelum sebarang operasi). Satu-satunya kelebihan penggunaannya floatialah ia menggunakan kurang memori, yang berguna apabila bekerja dengan sejumlah besar pembolehubah pecahan. Anda boleh menggunakan pengubah jenis nombor untuk memberitahu pengkompil cara menyimpan nombor. Pengubah suai untuk jenis float - 'f'. Oleh itu, pengkompil akan menetapkan jenis 1.75 kepada double, dan 1.75f - float. Sebagai contoh:
float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
Anda boleh menggunakan penghantaran jenis eksplisit. Ini adalah cara yang paling tidak elegan, tetapi ia berguna apabila menukar pembolehubah jenis doublekepada jenis float. Contoh:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Anda boleh membaca lebih lanjut mengenai nombor titik terapung di sini dan di sini.

-- komen penterjemah --
Itu sahaja.
Dalam contoh 10, ralat 9 sebenarnya telah dibuat. Saya perasan dengan segera, tetapi terlupa untuk menulis nota. tetapi tidak membetulkannya supaya tidak ada percanggahan dengan sumber asal.

Pengarang: A.Grasoff™ Pautan ke sumber: Kesilapan pengaturcara java pemula
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION