Dalam pemrograman Java, beberapa pengetahuan tidak dapat dipelajari hanya melalui spesifikasi bahasa atau dokumentasi API standar. Dalam artikel ini, saya akan mencoba mengumpulkan beberapa idiom yang paling umum digunakan, terutama yang sulit ditebak.
Saya memasukkan semua kode di artikel ini di tempat umum. Anda dapat menyalin dan memodifikasi cuplikan kode apa pun sesuai dengan preferensi Anda, tanpa kredensial.
Implement Equals ()
class person {string name; Int Birthdayyear; byte [] mentah; Public Boolean Equals (Object Obj) {if (! Obj instance dari Person) Return False; Orang lain = (orang) obj; return name.equals (Other.name) && ulang tahunYear == Other.birthyear && arrays.equals (RAW, Other.raw); } public int hashCode () {...}} Parameter harus dari tipe objek, bukan dari kelas periferal.
foo.equals (null) harus mengembalikan false dan tidak dapat melempar nullpointerexception. (Perhatikan bahwa null instance dari kelas apa pun selalu mengembalikan false, sehingga kode di atas dapat dijalankan.)
Perbandingan domain tipe dasar (misalnya, int) digunakan ==, dan perbandingan domain array tipe dasar digunakan oleh arrays.equals ().
Ketika overwriting equals (), ingatlah untuk menimpa kode hashcode () sesuai, untuk konsisten dengan Equals ().
Referensi: java.lang.object.equals (objek).
Implementasikan hashcode ()
class person {string a; Objek B; byte c; int [] d; public int hashCode () {return a.hashcode () + b.hashcode () + c + arrays.hashcode (d); } public boolean sama (objek o) {...}} Ketika objek X dan Y memiliki x.equals (y) == Benar, Anda harus memastikan x.hashcode () == y.hashcode ().
Menurut proposisi terbalik, jika x.hashcode ()! = Y.hashcode (), maka x.equals (y) == Salah harus benar.
Anda tidak perlu menjamin x.hashCode ()! = Y.HashCode () ketika x.equals (y) == false. Namun, ini meningkatkan kinerja tabel hash jika Anda dapat membuatnya selama mungkin.
Implementasi hukum hashCode () paling sederhana adalah dengan sekadar mengembalikan 0; Meskipun implementasi ini benar, ini akan menyebabkan struktur data seperti hashmap berjalan sangat lambat.
Implementasikan compareto ()
Class Person mengimplementasikan <Fone> yang sebanding {String firstName; String LastName; ulang tahun int; // Bandingkan dengan FirstName, Break Ties by LastName, akhirnya dasi break by BirthDate public int compareto (orang lain) {if (firstName.compareto (Other.firstName)! = 0) return firstName.compareto (Other.firstname); lain if (lastname.compareto (Other.LastName)! = 0) return LastName.Competo (Other.LastName); lain jika (kelahiran tDate <Other.birthDate) kembali -1; lain jika (BirthDate> Other.birthDate) kembali 1; lain kembali 0; }} Selalu terapkan versi generik yang sebanding, bukan tipe primitif yang sebanding. Karena ini dapat menyimpan volume kode dan mengurangi kerumitan yang tidak perlu.
Peduli tentang tanda -tanda (negatif/nol/positif) yang mengembalikan hasilnya, ukurannya tidak masalah.
Implementasi Comparator.compare () mirip dengan yang ini.
Menerapkan klon ()
Nilai kelas mengimplementasikan kloning {String ABC; foo ganda; int [] bar; Tanggal disewa; nilai publik clone () {try {values result = (values) super.clone (); result.bars = result.bars.clone (); result.hired = result.hired.clone (); hasil pengembalian; } catch (clonenotsupportedException e) {// mustahil melempar assertionerror baru (e); }}} Gunakan super.clone () untuk membuat kelas objek bertanggung jawab untuk membuat objek baru.
Domain tipe dasar telah disalin dengan benar. Sekali lagi, kita tidak perlu mengkloning tipe abadi seperti string dan biginteger.
Secara manual melakukan penyalinan mendalam dari semua bidang tipe non-primitif (objek dan array).
Kelas kloning diimplementasikan, dan metode klon () tidak boleh melempar clonenotsupportedException. Karena itu, Anda perlu menangkap pengecualian ini dan mengabaikannya, atau membungkusnya dengan pengecualian yang tidak terkendali.
Tidak apa -apa dan legal untuk mengimplementasikan metode klon () secara manual tanpa menggunakan metode objek.clone ().
Gunakan StringBuilder atau StringBuffer
// gabungkan (["a", "b", "c"]) -> "a dan b dan c" string gabung (daftar <string> strs) {stringBuilder sb = new stringBuilder (); boolean pertama = true; untuk (string s: strs) {if (first) first = false; lain SB.Append ("dan"); SB.Append (s); } return sb.toString ();} Jangan gunakan duplikat gabungan string seperti ini: s += item, karena efisiensi waktunya adalah o (n^2).
Saat menggunakan StringBuilder atau StringBuffer, Anda dapat menggunakan metode Append () untuk menambahkan teks dan menggunakan metode ToString () untuk mendapatkan seluruh teks yang terhubung.
Prioritas diberikan kepada StringBuilder karena lebih cepat. Semua metode StringBuffer disinkronkan, dan Anda biasanya tidak memerlukan metode yang disinkronkan.
Menghasilkan bilangan bulat acak dalam kisaran
Acak rand = new random (); // Antara 1 dan 6, termasuk inint diceroll () {return rand.nextint (6) + 1;} Selalu gunakan metode Java API untuk menghasilkan nomor acak dalam kisaran bilangan bulat.
Jangan mencoba menggunakan math.abs (rand.nextint ()) %n untuk penggunaan yang tidak pasti ini, karena hasilnya bias. Selain itu, nilai hasilnya mungkin negatif, seperti ketika rand.nextInt () == integer.min_value.
Gunakan iterator.remove ()
void filter (daftar <string> daftar) {untuk (iterator <string> iter = list.iterator (); iter.hasnext ();) {string item = iter.next (); if (...) iter.remove (); }}Metode Remove () bertindak pada entri yang baru saja dikembalikan dari metode berikutnya (). Setiap entri hanya dapat menggunakan metode lepas () sekali.
Pengembalian string
String reverse (string s) {return new stringBuilder (s) .reverse (). ToString ();}Metode ini mungkin harus ditambahkan ke pustaka standar Java.
Mulai utas
Tiga contoh berikut melakukan hal yang sama dengan cara yang berbeda.
Cara menerapkan runnnable:
void startAthread0 () {utas baru (myRunnable baru ()). start ();} kelas myRunnable mengimplementasikan runnable {public void run () {...}}Cara mewarisi utas:
void startathread1 () {new mythread (). start ();} class mythread memperluas utas {public void run () {...}}Cara mewarisi utas secara anonim:
void startAthread2 () {thread baru () {public void run () {...}} .start ();}Jangan memanggil metode run () secara langsung. Metode thread.start () selalu dipanggil, yang membuat utas baru dan menyebabkan utas yang baru dibuat untuk dipanggil ().
Gunakan mencoba-akhir
Contoh aliran I/O:
void writestuff () melempar ioException {outputStream out = FileOutputStream baru (...); coba {out.write (...); } akhirnya {out.close (); }}Contoh kunci:
void dowithlock (lock lock) {lock.acquire (); coba {...} akhirnya {lock.release (); }} Jika pernyataan sebelum percobaan gagal berjalan dan pengecualian dilemparkan, maka blok pernyataan akhirnya tidak akan dieksekusi. Tapi tidak peduli apa, dalam contoh ini, tidak perlu khawatir tentang pelepasan sumber daya.
Jika pernyataan dalam blok pernyataan coba melempar pengecualian, berjalan program akan melompat ke blok pernyataan akhirnya untuk menjalankan sebanyak mungkin pernyataan, dan kemudian melompat keluar dari metode ini (kecuali metode ini memiliki blok pernyataan akhirnya akhirnya).
Baca data byte dari aliran input
InputStream in = (...); coba {while (true) {int b = in.read (); if (b == -1) break; (... proses b ...)}} akhirnya {in.close ();}Metode baca () baik mengembalikan jumlah byte berikutnya yang dibaca dari aliran (0 hingga 255, termasuk 0 dan 255), atau mengembalikan -1 ketika ujung aliran tercapai.
Baca data blok dari aliran input
InputStream in = (...); coba {byte [] buf = byte baru [100]; while (true) {int n = in.read (buf); if (n == -1) break; (... proses buf dengan offset = 0 dan panjang = n ...)}} akhirnya {in.close ();}Ingatlah bahwa metode baca () tidak selalu mengisi seluruh buf, jadi Anda harus mempertimbangkan panjang pengembalian dalam logika pemrosesan.
Baca teks dari file
BufferedReader di = BufferedReader baru (inputStreamReader baru (FileInputStream baru (...), "UTF-8")); coba {while (true) {string line = in.readline (); if (line == null) break; (... baris proses ...)}} akhirnya {in.close ();} Penciptaan objek BufferedReader tampaknya sangat bertele -tele. Ini karena Java memperlakukan byte dan karakter sebagai dua konsep yang berbeda (ini berbeda dari C).
Anda dapat menggunakan semua jenis InputStream sebagai pengganti FileInputStream, seperti Socket.
BufferedReader.readline () mengembalikan nol ketika akhir aliran tercapai.
Untuk membaca satu karakter pada satu waktu, gunakan metode Reader.read ().
Anda dapat menggunakan pengkodean karakter lain tanpa UTF-8, tetapi lebih baik tidak melakukannya.
Tulis teks ke file
Printwriter out = printwriter baru (outputStreamWriter baru (FileOutputStream baru (...), "UTF-8")); coba {out.print ("halo"); out.print (42); out.println ("World!");} akhirnya {out.close ();} Pembuatan objek printwriter tampaknya sangat bertele -tele. Ini karena Java memperlakukan byte dan karakter sebagai dua konsep yang berbeda (ini berbeda dari C).
Sama seperti System.out, Anda dapat menggunakan print () dan println () untuk mencetak beberapa jenis nilai.
Anda dapat menggunakan pengkodean karakter lain tanpa UTF-8, tetapi lebih baik tidak melakukannya.
Nilai Pemeriksaan Defensif
Int Factorial (int n) {if (n <0) melempar IllegalArgumentException baru ("tidak terdefinisi"); lain jika (n> = 13) melempar arithmeticException baru ("hasil overflow"); lain jika (n == 0) kembali 1; lain return n * factorial (n - 1);} Jangan berpikir bahwa nilai input positif, cukup kecil, dll. Untuk mendeteksi kondisi ini secara eksplisit.
Fungsi yang dirancang dengan baik harus dapat dieksekusi dengan benar untuk semua nilai input yang mungkin. Pastikan bahwa semua situasi diperhitungkan dan tidak ada output yang salah (seperti overflow).
Objek pengujian preventif
int findIndex (daftar <string> daftar, string target) {if (list == null || target == null) lempar nullpointerException baru (); ...}Jangan berpikir bahwa parameter objek tidak akan menjadi nol. Untuk secara eksplisit mendeteksi kondisi ini.
Indeks array deteksi preventif
void frob (byte [] b, index int) {if (b == null) lempar nullpointerexception baru (); if (index <0 || index> = b.length) lempar indexOutofboundsException () baru; ...}Jangan berpikir bahwa indeks array yang diberikan tidak akan melewati batas. Untuk mendeteksinya secara eksplisit.
Interval array deteksi preventif
void frob (byte [] b, int off, int len) {if (b == null) lempar nullpointerException baru (); if (off <0 || off> b.length || len <0 || b.length - off <len) lempar indexoutofboundsException (); ...}Jangan berpikir bahwa interval array yang diberikan (misalnya, mulai dari luar, membaca elemen len) tidak akan melampaui batas. Untuk mendeteksinya secara eksplisit.
Isi elemen array
Menggunakan loop:
// Isi setiap elemen array 'a' dengan 123byte [] a = (...); untuk (int i = 0; i <a.length; i ++) a [i] = 123;
(Preferensi) Metode untuk menggunakan pustaka standar:
Arrays.fill (a, (byte) 123);
Salin elemen array dalam rentang
Menggunakan loop:
// Salin 8 elemen dari array 'a' mulai dari offset 3 // ke array 'b' mulai dari offset 6, // asumsi 'a' dan 'b' adalah arraysbyte yang berbeda [] a = (...); byte [] b = (...); untuk (int i = 0; i <8; i ++) b [6 + i] = a [3 + i];
(Preferensi) Metode untuk menggunakan pustaka standar:
System.arraycopy (a, 3, b, 6, 8);
Ubah Ukuran Array
Gunakan loop (skala up):
// buat array 'a' lebih besar ke newlenbyte [] a = (...); byte [] b = byte baru [newlen]; for (int i = 0; i <a.length; i ++) // naik ke panjang b [i] = a [i]; a = b;
Gunakan loop (kurangi ukurannya):
// buat array 'a' lebih kecil ke newlenbyte [] a = (...); byte [] b = byte baru [newlen]; untuk (int i = 0; i <b.length; i ++) // naik ke panjang b b [i] = a [i]; a = b;
(Preferensi) Metode untuk menggunakan pustaka standar:
a = arrays.copyof (a, newlen);
Mengemas 4 byte menjadi int
int packBigendian (byte [] b) {return (b [0] & 0xff) << 24 | (B [1] & 0xff) << 16 | (B [2] & 0xff) << 8 | (b [3] & 0xff) << 0;} int packlittleendian (byte [] b) {return (b [0] & 0xff) << 0 | (B [1] & 0xff) << 8 | (B [2] & 0xff) << 16 | (B [3] & 0xff) << 24;}Terurai int menjadi 4 byte
byte [] unpackbigendian (int x) {return byte baru [] {(byte) (x >>> 24), (byte) (x >>> 16), (byte) (x >>> 8), (byte) (x >> 0)};} byte [] unpacklittleDian (int x) {lebah) {byte [] {x) {byte [] {x) {byte {x) {x) { (byte) (x >>> 8), (byte) (x >>> 16), (byte) (x >>> 24)};}Selalu gunakan operator pergeseran kanan yang tidak ditandatangani (>>>) untuk membungkus bit, jangan gunakan operator pergeseran kanan aritmatika (>>).