Kesilapan pengaturcara java pemula. Bahagian 1
Pengarang: A.Grasoff™ Pautan ke sumber: Kesilapan pengaturcara java pemula
9. Memanggil kaedah kelas bukan statik daripada kaedah main().
Titik masuk mana-mana program Java mestilah kaedah statikmain
:
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, kelasjava.lang.String
menyimpan data rentetan. Walau bagaimanapun, rentetan di Jawa
- mempunyai kekal (iaitu, mereka tidak boleh diubah),
- adalah objek.
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 test1
dengan memberikan nilai baharu kepada parameter line
dalam kaedah appendTodaysDate
. Sememangnya ini tidak akan berfungsi. Makna line
akan berubah, tetapi makna test1
akan 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 StringBuffer
sebagai 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 |
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 kelasVector list
apabila 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 NullPointerException
pada 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 String
meletakkan 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 equals
dan bukannya compareTo
, pengkompil tidak akan membuang ralat, dan kod itu akan berfungsi dengan betul, kerana kaedah equals
kelas 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 sepertiObserver
atau Runnable
menggunakan 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 hasiltoString()
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 menggunakanadd()
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 menggunakanjava.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 Runnable
adalah 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: Reader
dan 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.DataInputStream
tidak 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:double
untuk nombor dengan ketepatan 64-bit mengikut piawaian IEEE, dan float
untuk 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 int
pembolehubah jenis byte
tanpa 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 double
kepada pembolehubah jenis float
boleh 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 double
dan 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 float
ialah 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 double
kepada 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 -- |
GO TO FULL VERSION