JavaRush /Java Blog /Random-TL /Coffee break #56. Isang Mabilis na Gabay sa Pinakamahuhus...

Coffee break #56. Isang Mabilis na Gabay sa Pinakamahuhusay na Kasanayan sa Java

Nai-publish sa grupo
Pinagmulan: DZone Ang gabay na ito ay kinabibilangan ng pinakamahusay na mga kasanayan sa Java at mga sanggunian upang mapabuti ang pagiging madaling mabasa at pagiging maaasahan ng iyong code. May malaking responsibilidad ang mga developer na gumawa ng mga tamang desisyon araw-araw, at ang pinakamagandang bagay na makakatulong sa kanila na gumawa ng mga tamang desisyon ay ang karanasan. At kahit na hindi lahat sa kanila ay may malawak na karanasan sa pagbuo ng software, lahat ay maaaring gumamit ng karanasan ng iba. Naghanda ako ng ilang rekomendasyon para sa iyo na nakuha ko mula sa aking karanasan sa Java. Umaasa ako na tulungan ka nilang mapabuti ang pagiging madaling mabasa at pagiging maaasahan ng iyong Java code.Coffee break #56.  Isang Mabilis na Gabay sa Pinakamahuhusay na Kasanayan sa Java - 1

Mga Prinsipyo sa Programming

Huwag magsulat ng code na gumagana lang . Sikaping magsulat ng code na mapanatili —hindi lamang sa iyo, ngunit ng sinumang iba pa na maaaring magtrabaho sa software sa hinaharap. Ang isang developer ay gumugugol ng 80% ng kanyang oras sa pagbabasa ng code, at 20% sa pagsusulat at pagsubok ng code. Kaya, tumuon sa pagsulat ng nababasang code. Hindi dapat kailangan ng iyong code ng mga komento para maunawaan ng sinuman kung ano ang ginagawa nito. Upang magsulat ng magandang code, maraming mga prinsipyo ng programming na magagamit natin bilang mga patnubay. Sa ibaba ay ililista ko ang pinakamahalaga.
  • • KISS – Ang ibig sabihin ay “Keep It Simple, Stupid.” Maaari mong mapansin na ang mga developer sa simula ng kanilang paglalakbay ay sumusubok na magpatupad ng mga kumplikado at hindi maliwanag na disenyo.
  • • DRY - "Huwag Ulitin ang Iyong Sarili." Subukang iwasan ang anumang mga duplicate, sa halip ay ilagay ang mga ito sa isang bahagi ng system o pamamaraan.
  • YAGNI - "Hindi Mo Ito Kailangan." Kung bigla mong sisimulan ang pagtatanong sa iyong sarili, "Paano ang pagdaragdag ng higit pa (mga tampok, code, atbp.)?", Malamang na kailangan mong pag-isipan kung talagang sulit na idagdag ang mga ito.
  • Malinis na code sa halip na smart code - Sa madaling salita, iwanan ang iyong ego sa pintuan at kalimutan ang tungkol sa pagsulat ng matalinong code. Gusto mo ng malinis na code, hindi ng smart code.
  • Iwasan ang Premature Optimization - Ang problema sa premature optimization ay hindi mo alam kung saan ang mga bottleneck sa programa hanggang sa lumitaw ang mga ito.
  • Isang responsibilidad - Ang bawat klase o module sa isang programa ay dapat lamang magmalasakit sa pagbibigay ng isang piraso ng isang partikular na paggana.
  • Komposisyon kaysa sa pagpapatupad na mana - Ang mga bagay na may kumplikadong gawi ay dapat maglaman ng mga pagkakataon ng mga bagay na may indibidwal na gawi, sa halip na magmana ng klase at magdagdag ng mga bagong gawi.
  • Ang Object gymnastics ay mga pagsasanay sa programming na idinisenyo bilang isang set ng 9 na panuntunan .
  • Mabilis na mabigo, mabilis na huminto - Nangangahulugan ang prinsipyong ito na ihinto ang kasalukuyang operasyon kapag may nangyaring hindi inaasahang error. Ang pagsunod sa prinsipyong ito ay humahantong sa mas matatag na operasyon.

Mga package

  1. Unahin ang pagbubuo ng mga pakete ayon sa paksa sa halip na sa teknikal na antas.
  2. Paboran ang mga layout na nagpo-promote ng encapsulation at pagtatago ng impormasyon upang maprotektahan laban sa maling paggamit sa halip na mag-organisa ng mga klase para sa mga teknikal na dahilan.
  3. Tratuhin ang mga pakete na parang mayroon silang hindi nababagong API - huwag ilantad ang mga panloob na mekanismo (mga klase) na inilaan lamang para sa panloob na pagproseso.
  4. Huwag ilantad ang mga klase na nilayon na gamitin lamang sa loob ng package.

Mga klase

Static

  1. Huwag payagan ang paglikha ng isang static na klase. Palaging lumikha ng isang pribadong tagabuo.
  2. Ang mga static na klase ay dapat manatiling hindi nababago, huwag payagan ang subclassing o multi-threaded na mga klase.
  3. Ang mga static na klase ay dapat protektahan mula sa mga pagbabago sa oryentasyon at dapat ibigay bilang mga kagamitan tulad ng pag-filter ng listahan.

Mana

  1. Piliin ang komposisyon kaysa mana.
  2. Huwag magtakda ng mga protektadong field. Sa halip, tumukoy ng ligtas na paraan ng pag-access.
  3. Kung ang isang variable ng klase ay maaaring markahan bilang final , gawin ito.
  4. Kung hindi inaasahan ang mana, gawin ang klase na pinal .
  5. Markahan ang isang paraan bilang pinal kung hindi inaasahan na ang mga subclass ay papayagang i-override ito.
  6. Kung hindi kinakailangan ang isang constructor, huwag gumawa ng default na constructor nang walang lohika ng pagpapatupad. Awtomatikong magbibigay ang Java ng default na constructor kung hindi tinukoy ang isa.

Mga interface

  1. Huwag gamitin ang interface ng mga constants pattern dahil pinapayagan nito ang mga klase na ipatupad at dumumi ang API. Gumamit na lang ng static na klase. Ito ay may karagdagang benepisyo ng pagpapahintulot sa iyong gumawa ng mas kumplikadong pagsisimula ng bagay sa isang static na bloke (tulad ng pag-populate ng isang koleksyon).
  2. Iwasan ang sobrang paggamit ng interface .
  3. Ang pagkakaroon ng isa at isang klase lamang na nagpapatupad ng isang interface ay malamang na humantong sa labis na paggamit ng mga interface at mas makakasama kaysa sa mabuti.
  4. Ang "Programa para sa interface, hindi ang pagpapatupad" ay hindi nangangahulugan na dapat mong i-bundle ang bawat isa sa iyong mga klase ng domain na may higit pa o hindi gaanong kaparehong interface, sa paggawa nito ay nilalabag mo ang YAGNI .
  5. Palaging panatilihing maliit at partikular ang mga interface upang malaman lamang ng mga kliyente ang tungkol sa mga pamamaraan na kinaiinteresan nila. Tingnan ang ISP mula sa SOLID.

Mga finalizer

  1. Ang object na #finalize() ay dapat gamitin nang matalino at bilang isang paraan lamang ng pagprotekta laban sa mga pagkabigo kapag naglilinis ng mga mapagkukunan (tulad ng pagsasara ng isang file). Palaging magbigay ng tahasang paraan ng paglilinis (tulad ng close() ).
  2. Sa isang inheritance hierarchy, palaging tawagan ang parent's finalize() sa isang try block . Ang paglilinis ng klase ay dapat na nasa wakas na bloke .
  3. Kung hindi tinawag ang isang tahasang paraan ng paglilinis at isinara ng finalizer ang mga mapagkukunan, i-log ang error na ito.
  4. Kung hindi available ang isang logger, gamitin ang exception handler ng thread (na nagtatapos sa pagpasa ng karaniwang error na nakukuha sa mga log).

Pangkalahatang tuntunin

Mga pahayag

Ang isang assertion, kadalasan sa anyo ng isang precondition check, ay nagpapatupad ng isang "fail fast, stop fast" na kontrata. Dapat itong gamitin nang malawakan upang matukoy ang mga error sa programming nang mas malapit sa dahilan hangga't maaari. Kondisyon ng bagay:
  • • Ang isang bagay ay hindi dapat gawin o ilagay sa isang hindi wastong estado.
  • • Sa mga konstruktor at pamamaraan, palaging ilarawan at ipatupad ang kontrata gamit ang mga pagsubok.
  • • Ang Java keyword assert ay dapat na iwasan dahil maaari itong i-disable at kadalasan ay isang malutong na konstruksyon.
  • • Gamitin ang Assertions utility class para maiwasan ang verbose if-else na mga kundisyon para sa precondition checks.

Generics

Ang isang buong, lubhang detalyadong paliwanag ay makukuha sa Java Generics FAQ . Nasa ibaba ang mga karaniwang sitwasyon na dapat malaman ng mga developer.
  1. Hangga't maaari, mas mainam na gumamit ng uri ng inference kaysa ibalik ang base class/interface:

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

  2. Kung hindi awtomatikong matukoy ang uri, i-inline ito.

    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. Mga wildcard:

    Gumamit ng pinahabang wildcard kapag nakakakuha ka lang ng mga value mula sa isang structure, gumamit ng super wildcard kapag naglalagay ka lang ng mga value sa isang structure, at huwag gumamit ng wildcard kapag pareho mong ginagawa.

    1. Gustung-gusto ng lahat ang PECS ! ( Producer-extends, Consumer-super )
    2. Gamitin ang Foo para sa producer na si T.
    3. Gamitin ang Foo para sa consumer na si T.

Mga singleton

Ang isang singleton ay hindi dapat isulat sa klasikong istilo ng pattern ng disenyo , na maayos sa C++ ngunit hindi naaangkop sa Java. Kahit na ito ay wastong thread-safe, hindi kailanman ipatupad ang sumusunod (ito ay magiging isang bottleneck sa pagganap!):
public final class MySingleton {
  private static MySingleton instance;
  private MySingleton() {
    // singleton
  }
  public static synchronized MySingleton getInstance() {
    if (instance == null) {
      instance = new MySingleton();
    }
    return instance;
  }
}
Kung ang tamad na pagsisimula ay talagang ninanais, kung gayon ang kumbinasyon ng dalawang pamamaraang ito ay gagana.
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: Bilang default, ang isang bean ay nakarehistro sa singleton scope, na nangangahulugan na isang instance lang ang gagawin ng container at konektado sa lahat ng mga consumer. Nagbibigay ito ng parehong semantika bilang isang regular na singleton, nang walang anumang pagganap o umiiral na mga limitasyon.

Mga pagbubukod

  1. Gumamit ng mga may check na exception para sa mga naitatama na kundisyon at runtime exception para sa mga error sa programming. Halimbawa: pagkuha ng integer mula sa isang string.

    Masama: Pinapalawak ng NumberFormatException ang RuntimeException, kaya nilayon itong magpahiwatig ng mga error sa programming.

  2. Huwag gawin ang sumusunod:

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

    Tamang Paggamit:

    // 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. Kailangan mong pangasiwaan ang mga pagbubukod sa tamang lugar, sa tamang lugar sa antas ng domain.

    MALING PARAAN - Hindi alam ng layer ng data object kung ano ang gagawin kapag may nangyaring exception sa 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
            }
        }}
    

    INIREREKOMENDADONG PARAAN - Ang layer ng data ay dapat na muling ihagis ang exception at ipasa ang responsibilidad para sa paghawak ng exception o hindi sa tamang layer.

    === 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. Ang mga pagbubukod sa pangkalahatan ay HINDI dapat naka-log sa oras na maibigay ang mga ito, ngunit sa oras na aktwal na naproseso ang mga ito. Ang mga pagbubukod sa pag-log, kapag sila ay itinapon o muling itinapon, ay may posibilidad na punan ang mga log file ng ingay. Tandaan din na ang exception stack trace ay nagtatala pa rin kung saan itinapon ang exception.

  5. Suportahan ang paggamit ng mga karaniwang pagbubukod.

  6. Gumamit ng mga pagbubukod sa halip na ibalik ang mga code.

Katumbas at HashCode

Mayroong ilang mga isyu na dapat isaalang-alang kapag nagsusulat ng wastong object at hash code equivalence method. Upang gawing mas madaling gamitin, gamitin ang java.util.Objects' equals at 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());
 }
}

Pamamahala ng mapagkukunan

Mga paraan upang ligtas na mailabas ang mga mapagkukunan: Tinitiyak ng pahayag ng pagsubok sa mga mapagkukunan na ang bawat mapagkukunan ay sarado sa dulo ng pahayag. Ang anumang bagay na nagpapatupad ng java.lang.AutoCloseable, na kinabibilangan ng lahat ng bagay na nagpapatupad ng java.io.Closeable , ay maaaring gamitin bilang isang mapagkukunan.
private doSomething() {
try (BufferedReader br = new BufferedReader(new FileReader(path)))
 try {
   // business logic
 }
}

Gumamit ng Shutdown Hooks

Gumamit ng shutdown hook na tinatawag kapag maganda ang pagsara ng JVM. (Ngunit hindi nito kakayanin ang mga biglaang pagkaantala, tulad ng dahil sa pagkawala ng kuryente) Ito ay isang inirerekomendang alternatibo sa halip na magdeklara ng finalize() na paraan na tatakbo lang kung ang System.runFinalizersOnExit() ay totoo (default ay 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();
   }
 }
}
Payagan ang mga mapagkukunan na maging kumpleto (pati na rin ang renewable) sa pamamagitan ng pamamahagi ng mga ito sa pagitan ng mga server. (Ito ay magbibigay-daan sa pagbawi mula sa isang biglaang pagkagambala tulad ng pagkawala ng kuryente.) Tingnan ang halimbawang code sa itaas na gumagamit ng ExpiringGeneralLock (isang lock na karaniwan sa lahat ng system).

Petsa-Oras

Ipinakilala ng Java 8 ang bagong Date-Time API sa java.time package. Ipinakilala ng Java 8 ang isang bagong Date-Time API upang matugunan ang mga sumusunod na pagkukulang ng lumang Date-Time API: non-threading, hindi magandang disenyo, kumplikadong paghawak ng time zone, atbp.

Paralelismo

Pangkalahatang tuntunin

  1. Mag-ingat sa mga sumusunod na library, na hindi ligtas sa thread. Palaging i-synchronize sa mga bagay kung ginagamit ang mga ito ng maraming thread.
  2. Petsa ( hindi nababago ) - Gamitin ang bagong Date-Time API, na ligtas sa thread.
  3. SimpleDateFormat - Gamitin ang bagong Date-Time API, na ligtas sa thread.
  4. Mas gusto na gumamit ng java.util.concurrent.atomic na mga klase sa halip na gawing pabagu-bago ang mga variable .
  5. Ang pag-uugali ng mga klase ng atom ay mas malinaw sa karaniwang developer, samantalang ang pabagu-bago ng isip ay nangangailangan ng pag-unawa sa modelo ng memorya ng Java.
  6. Binabalot ng mga klase ng atomic ang mga pabagu-bagong variable sa isang mas maginhawang interface.
  7. Unawain ang mga kaso ng paggamit kung saan naaangkop ang pabagu-bago ng isip . (tingnan ang artikulo )
  8. Gamitin ang Callable kapag ang isang naka-check na pagbubukod ay kinakailangan ngunit walang uri ng pagbabalik. Dahil hindi ma-instantiate ang Void, ipinapahayag nito ang layunin at maaaring ligtas na ibalik ang null .

Batis

  1. java.lang.Thread ay dapat na hindi na ginagamit. Bagama't hindi ito opisyal na kaso, sa halos lahat ng kaso ang java.util.concurrent package ay nagbibigay ng mas malinaw na solusyon sa problema.
  2. Ang pagpapalawak ng java.lang.Thread ay itinuturing na masamang kasanayan - sa halip ay ipatupad ang Runnable at lumikha ng bagong thread na may isang instance sa constructor (composition rule over inheritance).
  3. Mas gusto ang mga executor at thread kapag kailangan ang parallel processing.
  4. Laging inirerekomenda na tukuyin ang iyong sariling custom na thread factory upang pamahalaan ang configuration ng mga nilikhang thread ( higit pang mga detalye dito ).
  5. Gumamit ng DaemonThreadFactory sa Executors para sa mga hindi kritikal na thread para maisara kaagad ang thread pool kapag nag-shut down ang server ( higit pang mga detalye dito ).
this.executor = Executors.newCachedThreadPool((Runnable runnable) -> {
   Thread thread = Executors.defaultThreadFactory().newThread(runnable);
   thread.setDaemon(true);
   return thread;
});
  1. Ang pag-synchronize ng Java ay hindi na masyadong mabagal (55–110 ns). Huwag itong iwasan sa pamamagitan ng paggamit ng mga trick tulad ng pag-double check sa pag-lock .
  2. Mas gusto ang pag-synchronize sa isang panloob na bagay kaysa sa isang klase, dahil ang mga user ay maaaring mag-synchronize sa iyong klase/instance.
  3. Palaging i-sync ang maraming bagay sa parehong pagkakasunud-sunod upang maiwasan ang mga deadlock.
  4. Ang pag-synchronize sa isang klase ay hindi likas na hinaharangan ang pag-access sa mga panloob na bagay nito. Palaging gamitin ang parehong mga lock kapag nag-a-access ng isang mapagkukunan.
  5. Tandaan na ang naka-synchronize na keyword ay hindi itinuturing na bahagi ng lagda ng pamamaraan at samakatuwid ay hindi mamamana.
  6. Iwasan ang labis na pag-synchronize, maaari itong humantong sa hindi magandang pagganap at deadlock. Gamitin ang naka-synchronize na keyword nang mahigpit para sa bahagi ng code na nangangailangan ng synchronization.

Mga koleksyon

  1. Gumamit ng mga parallel na koleksyon ng Java-5 sa multi-threaded code hangga't maaari. Ang mga ito ay ligtas at may mahusay na mga katangian.
  2. Kung kinakailangan, gamitin ang CopyOnWriteArrayList sa halip na synchronizedList.
  3. Gamitin ang Collections.unmodifiable list(...) o kopyahin ang koleksyon kapag tinatanggap ito bilang parameter sa new ArrayList(list) . Iwasang baguhin ang mga lokal na koleksyon mula sa labas ng iyong klase.
  4. Palaging magbalik ng kopya ng iyong koleksyon, pag-iwas sa pagbabago ng iyong listahan sa labas gamit ang bagong ArrayList (listahan) .
  5. Ang bawat koleksyon ay dapat na nakabalot sa isang hiwalay na klase, kaya ngayon ang gawi na nauugnay sa koleksyon ay may tahanan (hal. mga paraan ng pag-filter, paglalapat ng panuntunan sa bawat elemento).

Miscellaneous

  1. Pumili ng mga lambda sa mga hindi kilalang klase.
  2. Pumili ng mga sanggunian ng pamamaraan sa halip na mga lambdas.
  3. Gumamit ng mga enum sa halip na mga int constant.
  4. Iwasang gumamit ng float at double kung kailangan ng mga tumpak na sagot, sa halip ay gumamit ng BigDecimal gaya ng Money.
  5. Pumili ng mga primitive na uri kaysa sa mga naka-box na primitive.
  6. Dapat mong iwasan ang paggamit ng mga magic number sa iyong code. Gumamit ng mga constants.
  7. Huwag ibalik ang Null. Makipag-ugnayan sa iyong method client gamit ang `Opsyonal`. Pareho para sa mga koleksyon - ibalik ang mga walang laman na array o koleksyon, hindi null.
  8. Iwasan ang paggawa ng mga hindi kinakailangang bagay, muling gamitin ang mga bagay, at iwasan ang hindi kinakailangang paglilinis ng GC.

Tamad na pagsisimula

Ang lazy initialization ay isang performance optimization. Ginagamit ito kapag ang data ay itinuturing na "mahal" para sa ilang kadahilanan. Sa Java 8 kailangan nating gamitin ang functional na interface ng provider para dito.
== 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 + ")");
Iyon lang sa ngayon, sana nakatulong ito!
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION