Perangkap Jawa. Bagian 1

Halo Saya ingin menyampaikan kepada Anda sebuah artikel pendek. Artikel ini ditujukan untuk pemula. Tetapi bahkan jika Anda adalah pengembang yang berpengalaman, jangan langsung menyimpulkan.
Semoga publikasi ini bermanfaat tidak hanya bagi pemula.

Tujuan publikasi ini:
Perlihatkan kesalahan pemula yang paling umum dan beberapa trik untuk memperbaikinya. Jelas bahwa beberapa kesalahan bisa rumit dan terjadi karena satu dan lain alasan. Tujuan publikasi ini sampai taraf tertentu untuk menganalisisnya dan membantu mengidentifikasi pada tahap awal. Semoga publikasi ini bermanfaat bagi pemula.

Daftar Periksa Kesalahan:

1. Kesalahan ketik. Kesalahan ketik yang mengganggu yang tidak segera muncul
2. Penugasan dalam kondisi alih-alih perbandingan
3. Kesalahan logis dalam kondisi tersebut
4. Perbandingan string salah
5. Inisialisasi variabel variabel primitif salah
6. Penggunaan ganda yang tidak benar
7. Jenis nilai pengembalian yang salah dalam konstruktor.
8. Pembagian dengan nol. POSITIF_INFINITAS
9. Tidak memperhitungkan urutan inisialisasi kelas
10. Variabel lokal menyembunyikan variabel kelas
11. Mengabaikan casting otomatis dalam ekspresi aritmatika
12. Infinite loop dengan byte, yang sulit dideteksi.
13. Nama kelas berbeda dari nama file tempat penyimpanannya.
14. Objek yang merupakan elemen array tidak diinisialisasi.
15. Menempatkan beberapa kelas dalam satu file sekaligus dengan pengubah publik

Java Pitfalls


Semua bahasa pemrograman memiliki kelebihan dan kekurangan. Ini karena banyak alasan. Jawa tidak terkecuali. Saya mencoba untuk mengumpulkan beberapa kesulitan yang jelas dan tidak jelas yang dihadapi oleh seorang programmer Java pemula. Saya yakin bahwa programmer yang berpengalaman juga akan menemukan sesuatu yang bermanfaat dalam artikel saya. Berlatih, perhatian, dan mendapatkan pengalaman pemrograman akan membantu menyelamatkan Anda dari banyak kesalahan. Tetapi beberapa kesalahan dan kesulitan sebaiknya dipertimbangkan terlebih dahulu. Saya akan memberikan beberapa contoh dengan kode dan penjelasan. Banyak penjelasan akan menjadi jelas bagi Anda dari komentar pada kode. Latihan memberi banyak, karena beberapa aturan tidak begitu jelas. Beberapa terletak di permukaan, ada yang tersembunyi di perpustakaan bahasa atau di mesin virtual java. Ingat bahwa java tidak hanya bahasa pemrograman dengan satu set perpustakaan, itu juga mesin virtual java.

Untuk artikel itu, saya secara khusus menulis kode kerja dengan komentar terperinci. Untuk menulis artikel dengan contoh kode, java 8 digunakan Untuk pengujian, kode java ditempatkan dalam paket terpisah.

Contoh: "paket underwaterRocks.simple;"

Kesulitan apa yang dihadapi pemula?

Salah ketik


Itu terjadi bahwa programmer pemula membuat kesalahan ketik yang sulit untuk dideteksi sekilas.


Contoh kode:

File: "Simple.java"

/*   ;     */ package underwaterRocks.simple; /** * * @author Ar20L80 */ public class Simple { public static void main(String[] args) { int ival = 10; if(ival>0); { System.out.println("     "); } } } 


Penjelasan : β€œTanda titik koma menunjukkan akhir dari pernyataan. Dalam hal ini; Apakah akhir dari pernyataan kosong? Ini adalah kesalahan logis. Kesalahan seperti itu bisa sulit dideteksi.

Kompiler akan mempertimbangkan bahwa semuanya sudah benar. Kondisi jika (ival> 0); dalam hal ini tidak masuk akal. Karena itu berarti: jika ival lebih dari nol, jangan lakukan apa-apa dan lanjutkan. ”

Tugas dalam kondisi alih-alih perbandingan


Kondisi ini adalah penugasan variabel.

Ini bukan kesalahan, tetapi penggunaan teknik seperti itu harus dibenarkan.

  boolean myBool = false; if(myBool = true) System.out.println(myBool); 

Dalam kode ini, if (myBool = true) berarti: "Setel variabel myBool menjadi true,
jika ekspresinya benar, maka ikuti kondisi mengikuti tanda kurung. "

Dalam kode ini, kondisinya akan selalu benar. Dan System.out.println (myBool); akan selalu dieksekusi, terlepas dari kondisinya.

== adalah perbandingan untuk kesetaraan.
= Apakah tugas, Anda dapat mengatakan a = 10; seperti: "tetapi tetapkan nilai 10".

Kondisi dalam tanda kurung mengembalikan nilai boolean.
Tidak masalah urutan apa yang Anda tulis. Anda dapat membandingkan seperti ini: (0 == a) atau (5 == a)
Jika Anda lupa satu tanda sama dengan, misalnya (0 = a) atau (5 = a), maka kompiler akan memberi tahu Anda tentang kesalahan. Anda menetapkan nilai, bukan perbandingan.
Anda juga dapat menulis dalam bentuk beberapa interval yang dapat dibaca.
Misalnya: Anda perlu menulis: lebih besar dari 5 dan kurang dari 10.
Anda menulis seperti ini: (a> 4 && a <10), tetapi dengan kesuksesan yang sama Anda dapat menulis: (4 <a && a <10),
sekarang Anda melihat bahwa a adalah antara 4 dan 10, tidak termasuk nilai-nilai ini. Ini lebih jelas. Segera terbukti bahwa, a berada dalam interval antara 4 dan 10, tidak termasuk nilai-nilai ini.

Contoh kode (interval) 3,9 [):
jika (3 <a && a <9) mengeksekusi;

Kesalahan logis


if (kondisi) {} if (kondisi) {} else {} - else merujuk ke if paling dekat.
Ini sering menjadi penyebab kesalahan pemula.

Perbandingan string tidak valid

Pemula cukup sering menggunakan == daripada .equals untuk membandingkan string.

Inisialisasi variabel


Pertimbangkan menginisialisasi variabel tipe primitif.

Primitif (byte, pendek, int, panjang, char, float, double, boolean).

Nilai awal.

 byte 0 short 0 int 0 long 0L float 0.0f double 0.0d char '\u0000' String (or any object) null boolean false (  jvm) 


Catatan:

Variabel lokal sedikit berbeda;
Kompiler tidak pernah memberikan nilai default ke variabel lokal yang tidak diinisialisasi.

Jika Anda tidak dapat menginisialisasi variabel lokal Anda di tempat itu dinyatakan,
Ingatlah untuk menetapkan nilai sebelum mencoba menggunakannya.

Akses ke variabel lokal yang tidak diinisialisasi akan menghasilkan kesalahan waktu kompilasi.

Konfirmasi catatan ini dalam kode:

File: "MyInitLocal.java"

 /*         */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ public class MyInitLocal { float classes_f; int classes_gi; public static void main(String[] args) { float f; int i; MyInitLocal myInit = new MyInitLocal(); /*         .*/ System.out.println("myInit.classes_f = " + myInit.classes_f); System.out.println("myInit.classes_gi = " + myInit.classes_gi); // System.out.println("f = " + f); // .     // System.out.println("f = " + i); // .     } } 


Rentang nilai:

byte ( , 1 , [-128, 127])
short ( , 2 , [-32768, 32767])
int ( , 4 , [-2147483648, 2147483647])
long ( , 8 , [-922372036854775808,922372036854775807])
float ( , 4 )
double ( , 8 )
char ( Unicode, 2 , 16 , [0, 65535])
boolean ( /, int, JVM)

char: Tipe data char adalah karakter Unicode 16-bit tunggal. Ini memiliki nilai minimum '\ u0000' (atau 0) dan nilai maksimum '\ uffff' (atau 65.535 inklusif).


Dokumentasi Oracle >>

Mari kita coba menginisialisasi variabel tipe lama dengan nomor: 922372036854775807.
Tidak ada yang berhasil bagi kami. Karena, itu adalah integer literal dari tipe int.
Inisialisasi yang benar dengan literal yang panjang: 922372036854775807L;

Contoh kode:

File: "MyInitLocalLong.java"

 /*    long  */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ public class MyInitLocalLong { public static void main(String[] args) { // long al = 922372036854775807; // integer number too large long bl = 922372036854775807L; //   } } 


Apa yang harus dicari ketika menginisialisasi variabel.

Rentang nilai variabel jenis ini. Fakta bahwa variabel diinisialisasi dengan literal dari jenis tertentu. Untuk gips yang eksplisit dan implisit. Jenis kompatibilitas.

Saat menggunakan cangkang Integer tipe, Anda harus memperhatikan pengepakan otomatis dan pembongkaran otomatis jenis ini.

Penggunaan ganda yang tidak pantas


Di sini Anda perlu mengklarifikasi. Ini bukan tentang menyalahgunakan tipe ganda.
Kami menggunakan dengan benar. Hanya hasilnya yang bisa mengejutkan seorang programmer pemula.
File: "MinusDouble.java"
 /*   */ package underwaterRocks.tstDouble; /** * * @author vvm */ public class MinusDouble { public static void main(String[] args) { double a = 4.64; double b = 2.64; System.out.println("ab = "+(ab)); } } /*   run: ab = 1.9999999999999996 */ 


Perhatikan tentang tipe ganda. Titik mengambang memungkinkan Anda untuk menghitung dengan kesalahan relatif yang diberikan dan rentang yang sangat besar. Dalam perhitungan ilmiah, kesalahan relatif sering diperlukan.

Perbandingan ganda tidak valid


Pertimbangkan jenis ganda.

Contoh kode:

File: "MyDouble.java"

 /*    double  - double. */ package underwaterRocks.myDouble; /** * * @author Ar20L80 */ public class MyDouble { public static void main(String[] args) { double dx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1; System.out.println("dx = " + dx); // dx = 0.9999999999999997 System.out.print(" (dx == 1.0):"); System.out.println(dx == 1.0); // false,   1.0   0.9999999999999997 /*   double*/ final double EPSILON = 1E-14; double xx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1; double xy = 1.0; /*  xx c xy */ if (Math.abs(xx - xy) < EPSILON) System.out.println(xx + "    " + xy + " EPSILON = " + EPSILON); } } 

Jenis ganda nyaman di mana presisi tinggi tidak diperlukan. Untuk transaksi keuangan, jenis ini tidak cocok. Meskipun beberapa, bukan perusahaan yang sangat jujur, gunakan tipe ganda untuk membulatkan ke sisi yang mereka butuhkan. Untuk operasi keuangan, kelas BigDecimal digunakan dalam perhitungan keuangan, karena tipe primitif nyata tidak cocok untuk tujuan ini karena alasan hilangnya akurasi dan kesalahan dalam hasil pembulatan. Namun, hasil yang lebih akurat diperoleh dengan menggunakan kelas BigInteger.

Konstruktor kelas


Konstruktor kelas cocok dengan nama kelas dan tidak mengembalikan apa pun, bahkan tidak batal.

Contoh kode:

File: "MyConstructor.java"

 /*      ,  void    void -    */ package underwaterRocks.myConstructor; /** * * @author Ar20L80 */ public class MyConstructor { public MyConstructor(){ System.out.println("   void"); } public void MyConstructor(){ System.out.println("  c void"); } public static void main(String[] args) { MyConstructor myconst = new MyConstructor(); myconst.MyConstructor(); //    } } 

Seperti yang kita lihat dalam kode, dua metode dengan nama yang sama: MyConstructor () dan MyConstructor (). Salah satu metode tidak mengembalikan apa pun. Ini adalah konstruktor dari kelas kami. Metode lain dengan void adalah metode kelas reguler. Dalam kasus ketika Anda tidak membuat konstruktor atau, menurut pendapat Anda, membuat konstruktor kelas dengan void, kompiler akan membuat konstruktor default dan Anda akan terkejut mengapa konstruktor Anda tidak berfungsi.

Pembagian dengan nol


Menurut Anda apa yang akan menjadi hasil dari eksekusi kode tersebut.

File: "DivisionByZero.java"

 /* */ package divisionByZero; import static java.lang.Double.POSITIVE_INFINITY; /** * * @author Ar20L80 */ public class DivisionByZero { public static void main(String[] args) { try{ float f = 12.2f; double d = 8098098.8790d; System.out.println(f/0); System.out.println(d/0); System.out.println(POSITIVE_INFINITY == f/0); System.out.println(POSITIVE_INFINITY == d/0); } catch (NumberFormatException ex) { System.out.println("NumberFormatException"); } catch (ArithmeticException ex) { System.out.println("ArithmeticException"); } } } 

Menjalankan kode akan menampilkan:

 Infinity Infinity true true 

Membagi tipe integer dengan nol akan memberikan ArithmeticException.

Kelas java.lang.Double mendefinisikan POSITIVE_INFINITY; konstan POSITIVE_INFINITY;

 public static final float POSITIVE_INFINITY = 1.0d / 0.0d; 

Itu dikonversi ke string yang sama dengan Infinity.

Urutan inisialisasi


File: "InitClass.java"

 /*     */ package myInitClass; /** * * @author Ar20L80 */ public class InitClass { InitClass(){ //   System.out.print(""); } { //   System.out.print("3 "); } public static void main(String[] args) { System.out.print("2"); new InitClass(); } static { //    System.out.print("1"); } } 

Pertama, semua blok statis dieksekusi, lalu blok inisialisasi, lalu konstruktor kelas.

Ini akan ditampilkan: "123 Pembuat"

Variabel lokal menyembunyikan variabel kelas
Meskipun IDE modern mudah mendeteksi kesalahan seperti itu, saya ingin mempertimbangkan kesalahan seperti itu secara lebih rinci. Mari kita mulai dengan penugasan variabel klasik di konstruktor. Contohnya benar. Tidak ada kesalahan.
 public class MyClass { private int val = 0; public MyClass(int val) { this.val = val; } } 

Namun, apa yang terjadi jika Anda menggunakan teknik ini dalam metode dan bukan dalam konstruktor kelas? Dalam metode yang biasa, menggunakan teknik ini tidak dianjurkan. Pertanyaannya berkaitan dengan desain kelas yang tepat.

Penjelasan sederhana: Dalam suatu metode, variabel dengan nama yang sama dengan variabel kelas adalah lokal untuk metode tersebut. Anda dapat mengakses variabel kelas menggunakan this.val. Namun, daya tarik dari metode ini, jika kelas dirancang dengan tidak tepat, hanya akan menyebabkan efek samping dan dapat menurunkan keterbacaan kode.

Pengecoran tipe aritmatika dilakukan secara otomatis

Ini dapat menyebabkan kesalahan yang mengganggu.
 // byte a = 1; // byte b = 1; // byte  = a + b; //  // byte a = (byte) 1; // byte b = (byte) 1; // byte  = a + b; //  


 //     β€”     . byte a = 1; byte b = 1; byte c = (byte) (a + b); 


 //     β€”  final // final byte a = 1; // final byte b = 1; // byte c = a + b; //    ,  a  b final 


Salah satu solusi yang mungkin saat bekerja dengan string:
 byte bHundr = Byte.parseByte("100"); //      byte 


Kesalahan lain diberikan dalam kode berikut.
 for (byte i = 1; i <= 128; i++) { System.out.println(i); } 

Dalam hal ini, kita mendapatkan loop tak terbatas.

Penjelasannya. Ketik byte [-128, 127]. 128 tidak lagi dalam kisaran ini. Overflow terjadi dan siklus berulang. Perlunya menggunakan byte dalam kasus ini diragukan. Meskipun terjadi dalam kasus yang jarang terjadi. Rekomendasi adalah untuk menggunakan int, bukan byte. Rekomendasi lain adalah jangan menggunakan loop dalam algoritme Anda.

Objek yang merupakan elemen array tidak diinisialisasi
 int[] cats = new int[10]; for(int i=0; i<cats.length;i++){ System.out.println("cats " + i + " = " + cats[i]); } 


Dalam contoh ini, kami memiliki array elemen tipe primitif. Dan tidak ada hal buruk terjadi ketika kita tidak menginisialisasi mereka. Mereka akan diberi nilai default. Dalam hal ini, nilai = 0.

Mari kita perhatikan contoh lain, tidak dengan primitif dalam array, tetapi dengan objek dalam array.
 public class ArrInitObj { public static void main(String[] args) { MyObj[] cats = new MyObj[10]; for(int i=0; i<cats.length;i++){ System.out.println("cats " + i + " = " + cats[i]); System.out.println("cats " + i + ".val = " + cats[i].val); //    java.lang.NullPointerException } } } class MyObj{ public int val; } 


Solusi untuk masalah ini adalah menginisialisasi semua variabel objek sebelum menggunakannya. Inisialisasi dapat dilakukan dalam konstruktor dari kelas MyObj.

Nama kelas berbeda dari nama file tempat penyimpanannya
IDE modern dengan mudah mendeteksi jenis kesalahan ini. Namun, kesalahan seperti itu ditemui, meskipun sangat jarang. Ini akan membantu perhatian, dengan mempertimbangkan perbedaan dalam nama huruf besar dan kecil.

Menempatkan beberapa kelas dalam satu file sekaligus dengan pengubah publik
Kesalahannya sangat jarang. IDE akan segera memberi Anda peringatan.
Nama file harus cocok dengan nama kelas publik.

Kesimpulan
Banyak kesalahan tidak jelas pada pandangan pertama. Bahkan programmer berpengalaman melakukannya, tetapi dalam jumlah yang lebih kecil. Kehati-hatian, pengalaman praktis, menggunakan debugger dan membaca dokumentasi akan membantu Anda menghindari banyak kesalahan.

Saya harap Anda menikmati artikel ini dan bermanfaat. Saya akan senang atas komentar, komentar, saran, keinginan Anda. Untuk dilanjutkan. Sebaliknya, tambahannya mengikuti.

Referensi
Pedoman desain kode Java Java >>>

PS. Teman saya Saya tidak punya cara untuk terus menerbitkan tanpa bantuan Anda. Artinya, tidak ada peluang finansial. Jika publikasi benar-benar membantu Anda dan Anda ingin melanjutkan, tolong dukung saya. Di suatu tempat ada tombol: "Dukung penulis."
Saya berharap atas pengertian Anda. Terima kasih Terima kasih kepada Habr atas kesempatan untuk menerbitkan.

Penulis akan dipaksa untuk menghapus publikasi atau menyembunyikannya dalam draf salinan jika tidak ada dukungan. Ini bukan ultimatum. Jika Anda memiliki kesempatan, itu berguna, Anda dapat membantu, lalu klik tombol dukungan penulis.

Source: https://habr.com/ru/post/id439642/


All Articles