JavaRush /Blog Java /Random-MS /Coffee break #56. Panduan Pantas untuk Amalan Terbaik di ...

Coffee break #56. Panduan Pantas untuk Amalan Terbaik di Jawa

Diterbitkan dalam kumpulan
Sumber: DZone Panduan ini termasuk amalan dan rujukan Java terbaik untuk meningkatkan kebolehbacaan dan kebolehpercayaan kod anda. Pembangun mempunyai tanggungjawab besar untuk membuat keputusan yang betul setiap hari, dan perkara terbaik yang boleh membantu mereka membuat keputusan yang betul ialah pengalaman. Dan walaupun tidak semua daripada mereka mempunyai pengalaman yang luas dalam pembangunan perisian, semua orang boleh menggunakan pengalaman orang lain. Saya telah menyediakan beberapa cadangan untuk anda yang saya perolehi daripada pengalaman saya dengan Java. Saya harap mereka membantu anda meningkatkan kebolehbacaan dan kebolehpercayaan kod Java anda.Coffee break #56.  Panduan Pantas untuk Amalan Terbaik di Jawa - 1

Prinsip Pengaturcaraan

Jangan tulis kod yang hanya berfungsi . Berusaha untuk menulis kod yang boleh diselenggara —bukan hanya oleh anda, tetapi oleh sesiapa sahaja yang mungkin akan bekerja pada perisian pada masa hadapan. Seorang pembangun menghabiskan 80% masanya membaca kod, dan 20% menulis dan menguji kod. Jadi, fokus pada menulis kod yang boleh dibaca. Kod anda tidak sepatutnya memerlukan ulasan untuk sesiapa sahaja memahami fungsinya. Untuk menulis kod yang baik, terdapat banyak prinsip pengaturcaraan yang boleh kita gunakan sebagai garis panduan. Di bawah saya akan senaraikan yang paling penting.
  • • KISS – Singkatan untuk "Keep It Simple, Stupid." Anda mungkin perasan bahawa pembangun pada permulaan perjalanan mereka cuba melaksanakan reka bentuk yang kompleks dan samar-samar.
  • • KERING - "Jangan Ulangi Sendiri." Cuba elakkan sebarang pendua, sebaliknya masukkannya ke dalam satu bahagian sistem atau kaedah.
  • YAGNI - "Anda Tidak Akan Memerlukannya." Jika anda tiba-tiba mula bertanya kepada diri sendiri, "Bagaimana pula dengan menambah lebih banyak lagi (ciri, kod, dll.)?", maka anda mungkin perlu memikirkan sama ada ia benar-benar bernilai menambahnya.
  • Kod bersih dan bukannya kod pintar - Ringkasnya, biarkan ego anda di depan pintu dan lupakan tentang menulis kod pintar. Anda mahu kod bersih, bukan kod pintar.
  • Elakkan Pengoptimuman Pramatang - Masalah dengan pengoptimuman pramatang ialah anda tidak pernah tahu di mana kesesakan akan berada dalam program sehingga ia muncul.
  • Tanggungjawab tunggal - Setiap kelas atau modul dalam program hanya perlu mengambil berat tentang menyediakan sedikit fungsi tertentu.
  • Komposisi dan bukannya warisan pelaksanaan - Objek dengan gelagat kompleks harus mengandungi contoh objek dengan gelagat individu, bukannya mewarisi kelas dan menambah gelagat baharu.
  • Gimnastik objek ialah latihan pengaturcaraan yang direka sebagai satu set 9 peraturan .
  • Fail fast, stop fast - Prinsip ini bermaksud menghentikan operasi semasa apabila sebarang ralat yang tidak dijangka berlaku. Pematuhan prinsip ini membawa kepada operasi yang lebih stabil.

Pakej

  1. Utamakan pakej penstrukturan mengikut bidang subjek dan bukannya mengikut tahap teknikal.
  2. Pilih reka letak yang menggalakkan pengkapsulan dan penyembunyian maklumat untuk melindungi daripada penyalahgunaan daripada menganjurkan kelas atas sebab teknikal.
  3. Rawat pakej seolah-olah ia mempunyai API tidak berubah - jangan dedahkan mekanisme dalaman (kelas) yang dimaksudkan hanya untuk pemprosesan dalaman.
  4. Jangan dedahkan kelas yang bertujuan untuk digunakan hanya dalam pakej.

Kelas

Statik

  1. Jangan benarkan penciptaan kelas statik. Sentiasa buat pembina peribadi.
  2. Kelas statik harus kekal tidak berubah, jangan benarkan subkelas atau kelas berbilang benang.
  3. Kelas statik harus dilindungi daripada perubahan orientasi dan harus disediakan sebagai utiliti seperti penapisan senarai.

Warisan

  1. Pilih gubahan daripada warisan.
  2. Jangan tetapkan medan yang dilindungi . Sebaliknya, nyatakan kaedah akses selamat .
  3. Jika pembolehubah kelas boleh ditandakan sebagai final , lakukannya.
  4. Jika warisan tidak dijangka, jadikan kelas muktamad .
  5. Tandakan kaedah sebagai muktamad jika tidak dijangka bahawa subkelas akan dibenarkan untuk mengatasinya.
  6. Jika pembina tidak diperlukan, jangan buat pembina lalai tanpa logik pelaksanaan. Java secara automatik akan menyediakan pembina lalai jika satu tidak ditentukan.

Antara muka

  1. Jangan gunakan antara muka corak pemalar kerana ia membenarkan kelas untuk melaksanakan dan mencemarkan API. Gunakan kelas statik sebaliknya. Ini mempunyai faedah tambahan untuk membolehkan anda melakukan pemulaan objek yang lebih kompleks dalam blok statik (seperti mengisi koleksi).
  2. Elakkan menggunakan antara muka secara berlebihan .
  3. Mempunyai satu dan hanya satu kelas yang melaksanakan antara muka berkemungkinan akan menyebabkan penggunaan antara muka secara berlebihan dan mendatangkan lebih banyak mudarat daripada kebaikan.
  4. "Program untuk antara muka, bukan pelaksanaan" tidak bermakna anda harus menggabungkan setiap kelas domain anda dengan antara muka yang lebih kurang sama, dengan melakukan ini anda melanggar YAGNI .
  5. Sentiasa pastikan antara muka kecil dan khusus supaya pelanggan hanya tahu tentang kaedah yang menarik minat mereka. Semak ISP dari SOLID.

Penyelesai

  1. Objek #finalize() harus digunakan dengan bijak dan hanya sebagai cara untuk melindungi daripada kegagalan semasa membersihkan sumber (seperti menutup fail). Sentiasa sediakan kaedah pembersihan yang jelas (seperti close() ).
  2. Dalam hierarki warisan, sentiasa panggil parent's finalize() dalam try block . Pembersihan kelas hendaklah dalam blok akhirnya .
  3. Jika kaedah pembersihan eksplisit tidak dipanggil dan pemuktamad menutup sumber, log ralat ini.
  4. Jika pembalak tidak tersedia, gunakan pengendali pengecualian benang (yang akhirnya melepasi ralat standard yang ditangkap dalam log).

Peraturan umum

Kenyataan

Penegasan, biasanya dalam bentuk semakan prasyarat, menguatkuasakan kontrak "gagal cepat, berhenti cepat". Ia harus digunakan secara meluas untuk mengenal pasti ralat pengaturcaraan sehampir mungkin dengan puncanya. Keadaan objek:
  • • Objek tidak boleh dibuat atau diletakkan dalam keadaan tidak sah.
  • • Dalam pembina dan kaedah, sentiasa menerangkan dan menguatkuasakan kontrak menggunakan ujian.
  • • Kata kunci Java menegaskan harus dielakkan kerana ia boleh dilumpuhkan dan biasanya merupakan binaan yang rapuh.
  • • Gunakan kelas utiliti Assertions untuk mengelakkan syarat-syarat if-else verbose untuk semakan prasyarat.

Generik

Penjelasan penuh dan sangat terperinci tersedia dalam Soalan Lazim Java Generics . Di bawah ialah senario biasa yang perlu diketahui oleh pembangun.
  1. Apabila boleh, lebih baik menggunakan inferens jenis daripada mengembalikan kelas/antara muka asas:

    // MySpecialObject o = MyObjectFactory.getMyObject();
    public  T getMyObject(int type) {
    return (T) factory.create(type);
    }

  2. Jika jenis tidak dapat ditentukan secara automatik, sebariskannya.

    public class MySpecialObject extends MyObject {
     public MySpecialObject() {
      super(Collections.emptyList());   // This is ugly, as we loose type
      super(Collections.EMPTY_LIST();    // This is just dumb
      // But this is beauty
      super(new ArrayList());
      super(Collections.emptyList());
     }
    }

  3. Kad bebas:

    Gunakan kad bebas lanjutan apabila anda hanya mendapat nilai daripada struktur, gunakan kad bebas super apabila anda meletakkan hanya nilai ke dalam struktur dan jangan gunakan kad bebas apabila anda melakukan kedua-duanya.

    1. Semua orang suka PECS ! ( Pengeluar-lanjutkan, Pengguna-super )
    2. Gunakan Foo untuk penerbit T.
    3. Gunakan Foo untuk pengguna T.

Bujang

Singleton tidak boleh ditulis dalam gaya corak reka bentuk klasik , yang baik dalam C++ tetapi tidak sesuai dalam Java. Walaupun ia selamat untuk benang dengan betul, jangan sekali-kali melaksanakan perkara berikut (ia akan menjadi kesesakan prestasi!):
public final class MySingleton {
  private static MySingleton instance;
  private MySingleton() {
    // singleton
  }
  public static synchronized MySingleton getInstance() {
    if (instance == null) {
      instance = new MySingleton();
    }
    return instance;
  }
}
Jika pemulaan malas benar-benar dikehendaki, maka gabungan kedua-dua pendekatan ini akan berfungsi.
public final class MySingleton {
  private MySingleton() {
   // singleton
  }
  private static final class MySingletonHolder {
    static final MySingleton instance = new MySingleton();
  }
  public static MySingleton getInstance() {
    return MySingletonHolder.instance;
  }
}
Spring: Secara lalai, kacang didaftarkan dengan skop tunggal, yang bermaksud bahawa hanya satu tika akan dibuat oleh bekas dan disambungkan kepada semua pengguna. Ini memberikan semantik yang sama seperti singleton biasa, tanpa sebarang had prestasi atau mengikat.

Pengecualian

  1. Gunakan pengecualian yang disemak untuk keadaan yang boleh dibetulkan dan pengecualian masa jalan untuk ralat pengaturcaraan. Contoh: mendapatkan integer daripada rentetan.

    Buruk: NumberFormatException memanjangkan RuntimeException, jadi ia bertujuan untuk menunjukkan ralat pengaturcaraan.

  2. Jangan lakukan perkara berikut:

    // String str = input string
    Integer value = null;
    try {
       value = Integer.valueOf(str);
    } catch (NumberFormatException e) {
    // non-numeric string
    }
    if (value == null) {
    // handle bad string
    } else {
    // business logic
    }

    Penggunaan yang betul:

    // String str = input string
    // Numeric string with at least one digit and optional leading negative sign
    if ( (str != null) && str.matches("-?\\d++") ) {
       Integer value = Integer.valueOf(str);
      // business logic
    } else {
      // handle bad string
    }
  3. Anda perlu mengendalikan pengecualian di tempat yang betul, di tempat yang betul di peringkat domain.

    CARA SALAH - Lapisan objek data tidak tahu apa yang perlu dilakukan apabila pengecualian pangkalan data berlaku.

    class UserDAO{
        public List getUsers(){
            try{
                ps = conn.prepareStatement("SELECT * from users");
                rs = ps.executeQuery();
                //return result
            }catch(Exception e){
                log.error("exception")
                return null
            }finally{
                //release resources
            }
        }}
    

    CARA YANG DISYORKAN - Lapisan data hanya perlu membuang semula pengecualian dan menyerahkan tanggungjawab untuk mengendalikan pengecualian atau tidak kepada lapisan yang betul.

    === RECOMMENDED WAY ===
    Data layer should just retrow the exception and transfer the responsability to handle the exception or not to the right layer.
    class UserDAO{
       public List getUsers(){
          try{
             ps = conn.prepareStatement("SELECT * from users");
             rs = ps.executeQuery();
             //return result
          }catch(Exception e){
           throw new DataLayerException(e);
          }finally{
             //release resources
          }
      }
    }

  4. Pengecualian secara amnya TIDAK sepatutnya dilog pada masa ia dikeluarkan, tetapi pada masa ia sebenarnya diproses. Pengecualian pembalakan, apabila ia dibuang atau dibuang semula, cenderung untuk mengisi fail log dengan bunyi bising. Juga ambil perhatian bahawa jejak tindanan pengecualian masih merekodkan tempat pengecualian itu dibuang.

  5. Menyokong penggunaan pengecualian standard.

  6. Gunakan pengecualian daripada kembalikan kod.

Equals dan HashCode

Terdapat beberapa isu yang perlu dipertimbangkan semasa menulis kaedah kesetaraan objek dan kod cincang yang betul. Untuk memudahkan penggunaan, gunakan java.util.Objects' equals dan hash .
public final class User {
 private final String firstName;
 private final String lastName;
 private final int age;
 ...
 public boolean equals(Object o) {
   if (this == o) {
     return true;
   } else if (!(o instanceof User)) {
     return false;
   }
   User user = (User) o;
   return Objects.equals(getFirstName(), user.getFirstName()) &&
    Objects.equals(getLastName(),user.getLastName()) &&
    Objects.equals(getAge(), user.getAge());
 }
 public int hashCode() {
   return Objects.hash(getFirstName(),getLastName(),getAge());
 }
}

Pengurusan sumber

Cara untuk melepaskan sumber dengan selamat: Pernyataan cuba-dengan-sumber memastikan setiap sumber ditutup pada penghujung pernyataan. Sebarang objek yang melaksanakan java.lang.AutoCloseable, yang merangkumi semua objek yang melaksanakan java.io.Closeable , boleh digunakan sebagai sumber.
private doSomething() {
try (BufferedReader br = new BufferedReader(new FileReader(path)))
 try {
   // business logic
 }
}

Gunakan Cangkuk Tutup

Gunakan cangkuk penutupan yang dipanggil apabila JVM ditutup dengan anggun. (Tetapi ia tidak akan dapat mengendalikan gangguan secara tiba-tiba, seperti akibat gangguan bekalan elektrik) Ini adalah alternatif yang disyorkan dan bukannya mengisytiharkan kaedah finalize() yang hanya akan dijalankan jika System.runFinalizersOnExit() adalah benar (default is false) .
public final class SomeObject {
 var distributedLock = new ExpiringGeneralLock ("SomeObject", "shared");
 public SomeObject() {
   Runtime
     .getRuntime()
     .addShutdownHook(new Thread(new LockShutdown(distributedLock)));
 }
 /** Code may have acquired lock across servers */
 ...
 /** Safely releases the distributed lock. */
 private static final class LockShutdown implements Runnable {
   private final ExpiringGeneralLock distributedLock;
   public LockShutdown(ExpiringGeneralLock distributedLock) {
     if (distributedLock == null) {
       throw new IllegalArgumentException("ExpiringGeneralLock is null");
     }
     this.distributedLock = distributedLock;
   }
   public void run() {
     if (isLockAlive()) {
       distributedLock.release();
     }
   }
   /** @return True if the lock is acquired and has not expired yet. */
   private boolean isLockAlive() {
     return distributedLock.getExpirationTimeMillis() > System.currentTimeMillis();
   }
 }
}
Benarkan sumber menjadi lengkap (serta boleh diperbaharui) dengan mengedarkannya antara pelayan. (Ini akan membolehkan pemulihan daripada gangguan mengejut seperti gangguan bekalan elektrik.) Lihat kod contoh di atas yang menggunakan ExpiringGeneralLock (kunci biasa untuk semua sistem).

Masa tarikh

Java 8 memperkenalkan API Date-Time baharu dalam pakej java.time. Java 8 memperkenalkan API Tarikh-Masa baharu untuk menangani kelemahan API Masa-Masa yang berikut: bukan benang, reka bentuk yang lemah, pengendalian zon masa yang kompleks, dsb.

Paralelisme

Peraturan umum

  1. Berhati-hati dengan perpustakaan berikut, yang tidak selamat untuk benang. Sentiasa selaraskan dengan objek jika ia digunakan oleh berbilang benang.
  2. Tarikh ( tidak boleh diubah ) - Gunakan API Tarikh-Masa baharu, yang selamat untuk urutan.
  3. SimpleDateFormat - Gunakan API Tarikh-Masa baharu, yang selamat untuk urutan.
  4. Lebih suka menggunakan kelas java.util.concurrent.atomic daripada membuat pembolehubah tidak menentu .
  5. Tingkah laku kelas atom lebih jelas kepada pembangun biasa, manakala tidak menentu memerlukan pemahaman tentang model memori Java.
  6. Kelas atom membungkus pembolehubah yang tidak menentu ke dalam antara muka yang lebih mudah.
  7. Fahami kes penggunaan yang tidak menentu adalah sesuai . (lihat artikel )
  8. Gunakan Boleh Dipanggil apabila pengecualian yang diperiksa diperlukan tetapi tiada jenis pemulangan. Memandangkan Void tidak boleh dibuat seketika, ia menyampaikan maksud dan boleh mengembalikan null dengan selamat .

Aliran

  1. java.lang.Thread harus ditamatkan. Walaupun ini tidak secara rasmi berlaku, dalam hampir semua kes pakej java.util.concurrent menyediakan penyelesaian yang lebih jelas kepada masalah tersebut.
  2. Memperluaskan java.lang.Thread dianggap sebagai amalan buruk - laksanakan Runnable dan buat thread baharu dengan contoh dalam pembina (peraturan komposisi ke atas warisan) .
  3. Lebih suka pelaksana dan benang apabila pemprosesan selari diperlukan.
  4. Ia sentiasa disyorkan untuk menentukan kilang benang tersuai anda sendiri untuk mengurus konfigurasi benang yang dibuat ( butiran lanjut di sini ).
  5. Gunakan DaemonThreadFactory dalam Pelaksana untuk benang tidak kritikal supaya kumpulan benang boleh ditutup serta-merta apabila pelayan ditutup ( butiran lanjut di sini ).
this.executor = Executors.newCachedThreadPool((Runnable runnable) -> {
   Thread thread = Executors.defaultThreadFactory().newThread(runnable);
   thread.setDaemon(true);
   return thread;
});
  1. Penyegerakan Java tidak lagi begitu perlahan (55–110 ns). Jangan elakkannya dengan menggunakan helah seperti mengunci semak dua kali .
  2. Lebih suka penyegerakan dengan objek dalaman daripada kelas, kerana pengguna boleh menyegerakkan dengan kelas/contoh anda.
  3. Sentiasa segerakkan berbilang objek dalam susunan yang sama untuk mengelakkan kebuntuan.
  4. Menyegerakkan dengan kelas tidak secara semula jadi menyekat akses kepada objek dalamannya. Sentiasa gunakan kunci yang sama apabila mengakses sumber.
  5. Ingat bahawa kata kunci yang disegerakkan tidak dianggap sebagai sebahagian daripada tandatangan kaedah dan oleh itu tidak akan diwarisi.
  6. Elakkan penyegerakan yang berlebihan, ini boleh menyebabkan prestasi lemah dan kebuntuan. Gunakan kata kunci yang disegerakkan dengan ketat untuk bahagian kod yang memerlukan penyegerakan.

Koleksi

  1. Gunakan koleksi selari Java-5 dalam kod berbilang benang apabila boleh. Mereka selamat dan mempunyai ciri-ciri yang sangat baik.
  2. Jika perlu, gunakan CopyOnWriteArrayList dan bukannya synchronizedList.
  3. Gunakan Collections.unmodifiable list(...) atau salin koleksi apabila menerimanya sebagai parameter ke ArrayList(list) baru . Elakkan mengubah suai koleksi tempatan dari luar kelas anda.
  4. Sentiasa kembalikan salinan koleksi anda, elakkan mengubah suai senarai anda secara luaran dengan ArrayList (senarai) baharu .
  5. Setiap koleksi mesti dibungkus dalam kelas yang berasingan, jadi kini gelagat yang dikaitkan dengan koleksi mempunyai rumah (mis. kaedah penapisan, menggunakan peraturan pada setiap elemen).

Macam-macam

  1. Pilih lambdas berbanding kelas tanpa nama.
  2. Pilih rujukan kaedah dan bukannya lambdas.
  3. Gunakan enum dan bukannya pemalar int.
  4. Elakkan menggunakan float dan double jika jawapan yang tepat diperlukan, sebaliknya gunakan BigDecimal seperti Wang.
  5. Pilih jenis primitif dan bukannya primitif berkotak.
  6. Anda harus mengelak daripada menggunakan nombor ajaib dalam kod anda. Gunakan pemalar.
  7. Jangan kembalikan Null. Berkomunikasi dengan klien kaedah anda menggunakan `Pilihan`. Sama untuk koleksi - kembalikan tatasusunan atau koleksi kosong, bukan nol.
  8. Elakkan membuat objek yang tidak perlu, gunakan semula objek dan elakkan pembersihan GC yang tidak perlu.

Inisialisasi malas

Inisialisasi malas ialah pengoptimuman prestasi. Ia digunakan apabila data dianggap "mahal" atas sebab tertentu. Dalam Java 8 kita perlu menggunakan antara muka pembekal berfungsi untuk ini.
== Thread safe Lazy initialization ===
public final class Lazy {
   private volatile T value;
   public T getOrCompute(Supplier supplier) {
       final T result = value; // Just one volatile read
       return result == null ? maybeCompute(supplier) : result;
   }
   private synchronized T maybeCompute(Supplier supplier) {
       if (value == null) {
           value = supplier.get();
       }
       return value;
   }
}
Lazy lazyToString= new Lazy<>()
return lazyToString.getOrCompute( () -> "(" + x + ", " + y + ")");
Itu sahaja buat masa ini, saya harap ini berguna!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION