JavaRush /Blog Jawa /Random-JV /Ngopi #56. Pandhuan Cepet Praktik Paling Apik ing Jawa

Ngopi #56. Pandhuan Cepet Praktik Paling Apik ing Jawa

Diterbitake ing grup
Sumber: DZone Pandhuan iki kalebu praktik lan referensi Jawa sing paling apik kanggo nambah keterbaca lan linuwih kode sampeyan. Pangembang duwe tanggung jawab gedhe kanggo nggawe keputusan sing bener saben dina, lan sing paling apik sing bisa mbantu nggawe keputusan sing bener yaiku pengalaman. Lan sanajan ora kabeh wong duwe pengalaman ekstensif ing pangembangan piranti lunak, kabeh wong bisa nggunakake pengalaman wong liya. Aku wis nyiapake sawetara Rekomendasi kanggo sampeyan sing aku wis gained saka pengalaman karo Jawa. Muga-muga bisa mbantu sampeyan nambah maca lan linuwih kode Jawa.Ngopi #56.  Pandhuan Cepet Praktek Paling Apik ing Jawa - 1

Prinsip Pemrograman

Aja nulis kode sing mung bisa digunakake . Coba nulis kode sing bisa dijaga - ora mung sampeyan, nanging wong liya sing bakal bisa nggarap piranti lunak kasebut ing mangsa ngarep. Pangembang mbuwang 80% wektu kanggo maca kode, lan 20% nulis lan nguji kode. Dadi, fokus nulis kode sing bisa diwaca. Kode sampeyan ora mbutuhake komentar supaya sapa wae ngerti apa sing ditindakake. Kanggo nulis kode sing apik, ana akeh prinsip pemrograman sing bisa digunakake minangka pedoman. Ing ngisor iki aku bakal dhaptar sing paling penting.
  • • KISS - Singkatan kanggo "Tetep Gampang, Bodho." Sampeyan bisa uga sok dong mirsani manawa pangembang ing wiwitan perjalanan nyoba ngetrapake desain sing rumit lan ambigu.
  • • GARING - "Aja Baleni dhewe." Coba ngindhari duplikat, tinimbang sijine menyang bagean siji saka sistem utawa cara.
  • YAGNI - "Sampeyan Ora Bakal Mbutuhake." Yen dumadakan sampeyan takon dhewe, "Apa babagan nambah liyane (fitur, kode, lan sapiturute)?", Sampeyan mbokmenawa kudu mikir apa iku bener worth nambah.
  • Kode resik tinimbang kode pinter - Cukup, ninggalake ego ing lawang lan lali nulis kode pinter. Sampeyan pengin kode resik, ora kode pinter.
  • Ngindhari Optimasi Prematur - Masalah karo optimasi durung wayahe yaiku sampeyan ora ngerti ing ngendi kemacetan bakal ana ing program nganti katon.
  • tanggung jawab Single - Saben kelas utawa modul ing program kudu mung Care babagan nyediakake siji dicokot saka fungsi tartamtu.
  • Komposisi tinimbang warisan implementasine - Obyek karo prilaku Komplek kudu ngemot kedadean saka obyek karo prilaku individu, tinimbang oleh warisan kelas lan nambah prilaku anyar.
  • Senam obyek minangka latihan pemrograman sing dirancang minangka set 9 aturan .
  • Gagal cepet, mandheg cepet - Prinsip iki tegese mungkasi operasi saiki nalika ana kesalahan sing ora dikarepke. Selaras karo prinsip iki nyebabake operasi sing luwih stabil.

Paket

  1. Prioritasake paket penataan miturut area subyek tinimbang tingkat teknis.
  2. Pilih tata letak sing ningkatake enkapsulasi lan ndhelikake informasi kanggo nglindhungi saka nyalahi panggunaan tinimbang ngatur kelas kanthi alasan teknis.
  3. Nambani paket kaya-kaya duwe API sing ora bisa diganti - aja mbukak mekanisme internal (kelas) mung kanggo pangolahan internal.
  4. Aja mbabarake kelas sing dimaksudake mung digunakake ing paket kasebut.

Kelas

Statis

  1. Aja ngidini nggawe kelas statis. Tansah nggawe konstruktor pribadi.
  2. Kelas statis kudu tetep immutable, ora ngidini subclassing utawa multi-threaded kelas.
  3. Kelas statis kudu direksa saka owah-owahan orientasi lan kudu diwenehake minangka utilitas kayata panyaring dhaptar.

pusaka

  1. Pilih komposisi tinimbang warisan.
  2. Aja nyetel lapangan sing dilindhungi . Nanging, nemtokake cara akses aman .
  3. Yen variabel kelas bisa ditandhani minangka final , tindakake.
  4. Yen warisan ora samesthine, nggawe kelas final .
  5. Tandhani metode minangka final yen ora samesthine yen subkelas bakal diijini ngatasi.
  6. Yen konstruktor ora dibutuhake, aja nggawe konstruktor standar tanpa logika implementasine. Jawa bakal kanthi otomatis nyedhiyakake konstruktor standar yen ora ditemtokake.

Antarmuka

  1. Aja nggunakake antarmuka pola konstanta amarga ngidini kelas kanggo ngetrapake lan ngrusak API. Gunakake kelas statis tinimbang. Iki entuk manfaat tambahan kanggo ngidini sampeyan nindakake inisialisasi obyek sing luwih rumit ing blok statis (kayata ngisi koleksi).
  2. Aja nggunakake antarmuka sing berlebihan .
  3. Duwe siji lan mung siji kelas sing ngleksanakake antarmuka kamungkinan bakal mimpin kanggo overuse antarmuka lan gawe piala luwih saka apik.
  4. "Program kanggo antarmuka, dudu implementasine" ora ateges sampeyan kudu nggabungake saben kelas domain kanthi antarmuka sing luwih utawa kurang padha, kanthi nindakake iki sampeyan bakal nglanggar YAGNI .
  5. Tansah antarmuka sing cilik lan spesifik supaya klien mung ngerti babagan cara sing disenengi. Priksa ISP saka SOLID.

Finalis

  1. Objek #finalize () kudu digunakake kanthi wicaksana lan mung minangka sarana kanggo nglindhungi saka kegagalan nalika ngresiki sumber daya (kayata nutup file). Tansah menehi cara ngresiki eksplisit (kayata close () ).
  2. Ing hirarki warisan, tansah nelpon wong tuwa finalize () ing blok nyoba . Pembersihan kelas kudu ana ing blok pungkasan .
  3. Yen cara ngresiki eksplisit ora disebut lan finalizer nutup sumber daya, log kesalahan iki.
  4. Yen logger ora kasedhiya, gunakake pawang pangecualian thread (sing pungkasane ngliwati kesalahan standar sing dijupuk ing log).

Aturan umum

Pratelan

Pernyataan, biasane ing wangun mriksa prasyarat, ngleksanakake kontrak "gagal cepet, mandheg cepet". Padha kudu digunakake sacara wiyar kanggo ngenali kesalahan pemrograman sing cedhak karo sababe. Kondisi obyek:
  • • Obyek ora tau digawe utawa dilebokake ing negara sing ora bener.
  • • Ing konstruktor lan cara, tansah njlèntrèhaké lan ngleksanakake kontrak nggunakake tes.
  • • Tembung kunci Jawa kudu nyingkiri amarga bisa dipateni lan biasane minangka konstruksi sing rapuh.
  • • Gunakake kelas sarana Pranyatan kanggo ngindhari kahanan verbose yen-liyane kanggo mriksa prasyarat.

Generik

Panjelasan lengkap lan rinci banget kasedhiya ing FAQ Java Generics . Ing ngisor iki minangka skenario umum sing kudu dingerteni para pangembang.
  1. Yen bisa, luwih becik nggunakake inferensi jinis tinimbang mbalekake kelas / antarmuka dhasar:

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

  2. Yen jinis ora bisa ditemtokake kanthi otomatis, inline.

    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. Wildcards:

    Gunakake wildcard lengkap nalika sampeyan mung entuk nilai saka struktur, gunakake wildcard super nalika sampeyan mung nglebokake nilai menyang struktur, lan aja nggunakake wildcard nalika sampeyan nindakake loro-lorone.

    1. Kabeh wong seneng PECS ! ( Produser-extends, Consumer-super )
    2. Gunakake Foo kanggo produser T.
    3. Gunakake Foo kanggo konsumen T.

Singleton

A singleton ngirim tau ditulis ing gaya pola desain klasik , kang nggoleki ing C++ nanging ora cocok ing Jawa. Sanajan wis aman kanggo thread, aja nglakokake ing ngisor iki (bakal dadi bottleneck kinerja!):
public final class MySingleton {
  private static MySingleton instance;
  private MySingleton() {
    // singleton
  }
  public static synchronized MySingleton getInstance() {
    if (instance == null) {
      instance = new MySingleton();
    }
    return instance;
  }
}
Yen initialization malas pancene dikarepake, banjur kombinasi saka loro pendekatan iki bakal bisa.
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: Kanthi gawan, buncis kadhaptar kanthi ruang lingkup tunggal, tegese mung siji conto sing bakal digawe dening wadhah lan disambungake menyang kabeh konsumen. Iki nyedhiyakake semantik sing padha karo singleton biasa, tanpa watesan kinerja utawa ikatan.

Pangecualian

  1. Gunakake pangecualian sing dicenthang kanggo kahanan sing bisa dibenerake lan pangecualian runtime kanggo kesalahan pemrograman. Tuladha: njupuk integer saka string.

    Bad: NumberFormatException ngluwihi RuntimeException, supaya iku dimaksudaké kanggo nunjukaké kasalahan program.

  2. Aja nindakake ing ngisor iki:

    // 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
    }

    Panggunaan sing bener:

    // 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. Sampeyan kudu nangani pangecualian ing panggonan sing bener, ing panggonan sing bener ing tingkat domain.

    WRONG WAY - Lapisan obyek data ora ngerti apa sing kudu ditindakake nalika ana pangecualian database.

    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 RECOMMENDED - Lapisan data mung kudu mbaleni pangecualian lan ngliwati tanggung jawab kanggo nangani pangecualian utawa ora menyang lapisan sing bener.

    === 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. Pangecualian umume ora kudu dicathet nalika diterbitake, nanging nalika diproses. Pengecualian logging, nalika dibuwang utawa dibuwang maneh, cenderung ngisi file log kanthi gangguan. Elinga uga yen tilak tumpukan pangecualian isih nyathet ing ngendi pengecualian kasebut dibuwang.

  5. Ndhukung panggunaan pangecualian standar.

  6. Gunakake pangecualian tinimbang bali kode.

Podo karo lan HashCode

Ana sawetara masalah sing kudu ditimbang nalika nulis obyek sing tepat lan metode kesetaraan kode hash. Kanggo nggampangake nggunakake, gunakake java.util.Objects ' padha lan 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());
 }
}

Manajemen sumber daya

Cara kanggo ngeculake sumber daya kanthi aman: Pernyataan coba-kanggo-sumber mesthekake yen saben sumber ditutup ing pungkasan statement. Sembarang obyek sing ngetrapake java.lang.AutoCloseable, sing kalebu kabeh obyek sing ngetrapake java.io.Closeable , bisa digunakake minangka sumber.
private doSomething() {
try (BufferedReader br = new BufferedReader(new FileReader(path)))
 try {
   // business logic
 }
}

Gunakake Pancing Mati

Gunakake pancing mati sing diarani nalika JVM mati anggun. (Nanging ora bakal bisa kanggo nangani interruptions dadakan, kayata amarga mati daya) Iki alternatif dianjurake tinimbang deklarasi finalize () cara sing mung bakal mbukak yen System.runFinalizersOnExit () bener (standar palsu) .
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();
   }
 }
}
Ngidini sumber daya dadi lengkap (uga bisa dianyari) kanthi nyebarake ing antarane server. (Iki bakal ngidini pemulihan saka gangguan dadakan kayata listrik mati.) Deleng conto kode ing ndhuwur sing nggunakake ExpiringGeneralLock (kunci umum kanggo kabeh sistem).

Tanggal-Wektu

Jawa 8 ngenalaken API Tanggal-Wektu anyar ing paket java.time. Java 8 ngenalake API Tanggal-Wektu anyar kanggo ngatasi kekurangan ing ngisor iki saka API Tanggal-Wektu lawas: non-threading, desain sing ora apik, penanganan zona wektu sing rumit, lsp.

Paralelisme

Aturan umum

  1. Waspada karo perpustakaan ing ngisor iki, sing ora aman. Tansah nyinkronake karo obyek yen digunakake dening macem-macem Utas.
  2. Tanggal ( ora immutable ) - Gunakake API Tanggal-Wektu anyar, kang thread aman.
  3. SimpleDateFormat - Gunakake API Tanggal-Wektu anyar, sing aman thread.
  4. Luwih seneng nggunakake kelas java.util.concurrent.atomic tinimbang nggawe variabel molah malih .
  5. Prilaku kelas atom luwih jelas kanggo pangembang rata-rata, dene molah malih mbutuhake pangerten babagan model memori Jawa.
  6. Kelas atom mbungkus variabel molah malih dadi antarmuka sing luwih trep.
  7. Ngerti kasus panggunaan ing ngendi molah malih cocok . (ndeleng artikel )
  8. Gunakake Callable nalika pangecualian sing dicenthang dibutuhake nanging ora ana jinis bali. Wiwit Void ora bisa instantiated, iku komunikasi maksud lan bisa aman bali null .

lepen

  1. java.lang.Thread kudu ora digunakake. Sanajan iki ora resmi, ing meh kabeh kasus, paket java.util.concurrent nyedhiyakake solusi sing luwih jelas kanggo masalah kasebut.
  2. Ngluwihi java.lang.Thread dianggep laku ala - ngleksanakake Runnable tinimbang lan nggawe thread anyar karo Kayata ing konstruktor (aturan komposisi liwat pusaka).
  3. Luwih milih eksekutor lan benang nalika pangolahan paralel dibutuhake.
  4. Iku tansah dianjurake kanggo nemtokake pabrik thread adat dhewe kanggo ngatur konfigurasi thread digawe ( rincian liyane kene ).
  5. Gunakake DaemonThreadFactory ing Executors kanggo Utas non-kritis supaya blumbang Utas bisa langsung mati nalika server mati ( rincian liyane kene ).
this.executor = Executors.newCachedThreadPool((Runnable runnable) -> {
   Thread thread = Executors.defaultThreadFactory().newThread(runnable);
   thread.setDaemon(true);
   return thread;
});
  1. Sinkronisasi Jawa wis ora alon (55–110 ns). Aja nyingkiri kanthi nggunakake trik kaya ngunci sing dicenthang kaping pindho .
  2. Luwih seneng sinkronisasi karo obyek internal tinimbang kelas, amarga pangguna bisa nyinkronake karo kelas / conto sampeyan.
  3. Tansah nyelarasake macem-macem obyek ing urutan sing padha kanggo ngindhari deadlocks.
  4. Sinkronisasi karo kelas ora mblokir akses menyang obyek internal. Tansah nggunakake kunci sing padha nalika ngakses sumber daya.
  5. Elinga yen tembung kunci sing disinkronake ora dianggep minangka bagian saka tandha cara lan mulane ora bakal diwarisake.
  6. Ngindhari sinkronisasi sing berlebihan, iki bisa nyebabake kinerja sing ora apik lan buntu. Gunakake tembung kunci sing disinkronake kanthi ketat kanggo bagean kode sing mbutuhake sinkronisasi.

Koleksi

  1. Gunakake Java-5 koleksi paralel ing kode multi-threaded sabisa. Padha aman lan nduweni ciri sing apik banget.
  2. Yen perlu, gunakake CopyOnWriteArrayList tinimbang synchronizedList.
  3. Gunakake Collections.unmodifiable list (...) utawa nyalin koleksi nalika nampa minangka parameter kanggo ArrayList anyar (dhaftar) . Aja ngowahi koleksi lokal saka njaba kelas sampeyan.
  4. Tansah mbalekake salinan koleksi sampeyan, supaya ora ngowahi dhaptar kanthi njaba nganggo ArrayList (dhaftar) anyar .
  5. Saben koleksi kudu dibungkus ing kelas sing kapisah, mula saiki prilaku sing ana gandhengane karo koleksi duwe omah (contone, metode nyaring, ngetrapake aturan kanggo saben unsur).

macem-macem

  1. Pilih lambdas saka kelas anonim.
  2. Pilih referensi metode tinimbang lambdas.
  3. Gunakake enum tinimbang konstanta int.
  4. Aja nggunakake float lan pindho yen jawaban sing tepat dibutuhake, tinimbang nggunakake BigDecimal kayata Dhuwit.
  5. Pilih jinis primitif tinimbang primitif kothak.
  6. Sampeyan kudu ngindhari nggunakake nomer sihir ing kode sampeyan. Gunakake konstanta.
  7. Aja bali Null. Komunikasi karo klien metode sampeyan nggunakake `Opsional`. Padha kanggo koleksi - bali array kosong utawa koleksi, ora nulls.
  8. Aja nggawe obyek sing ora perlu, gunakake maneh obyek, lan aja ngresiki GC sing ora perlu.

Inisialisasi malas

Inisialisasi malas minangka optimasi kinerja. Iki digunakake nalika data dianggep "larang" kanggo sawetara alasan. Ing Jawa 8 kita kudu nggunakake antarmuka panyedhiya fungsional kanggo iki.
== 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 + ")");
Mung saiki, muga-muga bisa migunani!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION