JavaRush /Blog Jawa /Random-JV /Ngopi #146. 5 kesalahan sing ditindakake dening 99% pange...

Ngopi #146. 5 kesalahan sing ditindakake dening 99% pangembang Java. Strings ing Jawa - tampilan njero

Diterbitake ing grup

5 kesalahan sing ditindakake dening 99% pangembang Java

Sumber: Sedheng Ing kirim iki, sampeyan bakal sinau babagan kesalahan sing paling umum sing ditindakake dening pangembang Java. Ngopi #146.  5 kesalahan sing ditindakake dening 99% pangembang Java.  Strings ing Jawa - tampilan njero - 1Minangka programmer Jawa, aku ngerti carane ala iku kanggo nglampahi akèh wektu kanggo ndandani kewan omo ing kode. Kadhangkala iki njupuk sawetara jam. Nanging, akeh kesalahan sing katon amarga pangembang ora nggatekake aturan dhasar - yaiku, iki minangka kesalahan tingkat rendah. Dina iki kita bakal nliti sawetara kesalahan coding umum lan banjur nerangake carane ndandani. Muga-muga iki mbantu sampeyan ngindhari masalah ing karya saben dina.

Mbandhingake obyek nggunakake Objects.equals

Aku nganggep sampeyan wis kenal karo metode iki. Akeh pangembang sing kerep nggunakake. Teknik iki, sing dikenalake ing JDK 7, mbantu sampeyan mbandhingake obyek kanthi cepet lan kanthi efektif ngindhari pamriksan null pointer sing ngganggu. Nanging cara iki kadhangkala salah digunakake. Mangkene maksudku:
Long longValue = 123L;
System.out.println(longValue==123); //true
System.out.println(Objects.equals(longValue,123)); //false
Napa ngganti == karo Objects.equals () ngasilake asil sing salah? Iki amarga kompiler == bakal entuk jinis data dhasar sing cocog karo jinis kemasan longValue banjur mbandhingake karo jinis data dhasar kasebut. Iki padha karo compiler kanthi otomatis ngowahi konstanta menyang jinis data perbandingan dhasar. Sawise nggunakake metode Objects.equals () , jinis data dhasar standar saka pancet compiler int . Ing ngisor iki kode sumber kanggo Objects.equals () ngendi a.equals (b) nggunakake Long.equals () lan nemtokake jinis obyek. Iki kedadeyan amarga kompiler nganggep manawa konstanta saka jinis int , mula asil perbandingan kasebut kudu palsu.
public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

  public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }
Ngerti alasane, ndandani kesalahan iku gampang banget. Cukup ngumumake jinis data saka konstanta, kaya Objects.equals(longValue,123L) . Masalah ing ndhuwur ora bakal muncul yen logika ketat. Sing kudu ditindakake yaiku ngetutake aturan pemrograman sing jelas.

Format tanggal sing salah

Ing pangembangan saben dinten, sampeyan asring kudu ngganti tanggal, nanging akeh wong nggunakake format sing salah, sing ndadékaké perkara sing ora dikarepake. Iki contone:
Instant instant = Instant.parse("2021-12-31T00:00:00.00Z");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss")
.withZone(ZoneId.systemDefault());
System.out.println(formatter.format(instant));//2022-12-31 08:00:00
Iki nggunakake format YYYY-MM-dd kanggo ngganti tanggal saka 2021 dadi 2022. Sampeyan ora kudu nglakoni. Kenging punapa? Iki amarga pola Java DateTimeFormatter "YYYY" adhedhasar standar ISO-8601, sing nemtokake taun minangka Kamis saben minggu. Nanging tanggal 31 Desember 2021 tiba ing dina Jumuah, mula program kasebut salah nuduhake 2022. Kanggo ngindhari iki, sampeyan kudu nggunakake format yyyy-MM-dd kanggo ngowahi format tanggal . Kesalahan iki jarang kedadeyan, mung karo tekane taun anyar. Nanging ing perusahaanku nyebabake kegagalan produksi.

Nggunakake ThreadLocal ing ThreadPool

Yen sampeyan nggawe variabel ThreadLocal , thread sing ngakses variabel kasebut bakal nggawe variabel lokal thread. Kanthi cara iki sampeyan bisa nyegah masalah safety thread. Nanging, yen sampeyan nggunakake ThreadLocal ing blumbang thread , sampeyan kudu ati-ati. Kode sampeyan bisa ngasilake asil sing ora dikarepake. Kanggo conto prasaja, kita duwe platform e-commerce lan pangguna kudu ngirim email kanggo konfirmasi tuku produk sing wis rampung.
private ThreadLocal<User> currentUser = ThreadLocal.withInitial(() -> null);

    private ExecutorService executorService = Executors.newFixedThreadPool(4);

    public void executor() {
        executorService.submit(()->{
            User user = currentUser.get();
            Integer userId = user.getId();
            sendEmail(userId);
        });
    }
Yen kita nggunakake ThreadLocal kanggo nyimpen informasi pangguna, kesalahan sing didhelikake bakal katon. Amarga kumpulan benang digunakake, lan benang bisa digunakake maneh, nalika nggunakake ThreadLocal kanggo njupuk informasi pangguna, bisa uga salah nampilake informasi wong liya. Kanggo ngatasi masalah iki, sampeyan kudu nggunakake sesi.

Gunakake HashSet kanggo mbusak data duplikat

Nalika coding, kita asring mbutuhake deduplikasi. Nalika sampeyan mikir babagan deduplikasi, sing paling dhisik dipikirake yaiku nggunakake HashSet . Nanging, panggunaan HashSet sing ora sopan bisa nyebabake deduplikasi gagal.
User user1 = new User();
user1.setUsername("test");

User user2 = new User();
user2.setUsername("test");

List<User> users = Arrays.asList(user1, user2);
HashSet<User> sets = new HashSet<>(users);
System.out.println(sets.size());// the size is 2
Sawetara pembaca sing ati-ati kudu bisa ngira-ngira alasan kegagalan kasebut. HashSet nggunakake kode hash kanggo ngakses tabel hash lan nggunakake metode sing padha kanggo nemtokake manawa obyek padha. Yen obyek sing ditetepake pangguna ora ngilangi metode kode hash lan metode sing padha , mula metode kode hash lan metode sing padha karo obyek induk bakal digunakake kanthi standar. Iki bakal nyebabake HashSet nganggep manawa ana rong obyek sing beda, nyebabake deduplikasi gagal.

Ngilangi benang blumbang "dipangan".

ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(()->{
            //do something
            double result = 10/0;
        });
Kode ndhuwur simulates skenario ngendi pangecualian di buwang ing blumbang thread. Kode bisnis kudu nganggep macem-macem kahanan, mula kemungkinan bakal mbuwang RuntimeException sakperangan alesan . Nanging yen ora ana penanganan khusus ing kene, pangecualian iki bakal "dipangan" dening blumbang benang. Lan sampeyan ora bakal duwe cara kanggo mriksa sabab saka pangecualian. Mulane, paling apik kanggo nyekel pangecualian ing blumbang proses.

Strings ing Jawa - tampilan njero

Sumber: Sedheng Penulis artikel iki mutusake kanggo ndeleng kanthi rinci babagan nggawe, fungsi lan fitur senar ing Jawa. Ngopi #146.  5 kesalahan sing ditindakake dening 99% pangembang Java.  Strings ing Jawa - tampilan njero - 2

cipta

Senar ing Jawa bisa digawe kanthi rong cara: kanthi implisit, minangka string literal, lan kanthi eksplisit, nggunakake tembung kunci anyar . String literals minangka karakter sing dilebokake ing tanda kutip ganda.
String literal   = "Michael Jordan";
String object    = new String("Michael Jordan");
Senajan loro Pranyatan nggawe obyek senar, ana prabédan ing carane loro obyek iki dumunung ing memori numpuk.

Perwakilan internal

Sadurungé, senar disimpen ing wangun char [] , tegesé saben karakter minangka unsur kapisah ing array karakter. Wiwit padha diwakili ing format enkoding karakter UTF-16 , iki tegese saben karakter njupuk rong bita memori. Iki ora bener banget, amarga statistik panggunaan nuduhake manawa obyek senar mung kalebu aksara Latin-1 . Aksara Latin-1 bisa diwakili nggunakake siji bait memori, sing bisa nyuda panggunaan memori kanthi signifikan - nganti 50%. Fitur string internal anyar dileksanakake minangka bagean saka release JDK 9 adhedhasar JEP 254 sing diarani Compact Strings. Ing rilis iki, char[] diganti dadi byte[] lan kolom gendera encoder ditambahake kanggo makili enkoding sing digunakake (Latin-1 utawa UTF-16). Sawise iki, enkoding dumadi adhedhasar isi senar. Yen nilai kasebut mung ngemot karakter Latin-1, mula enkoding Latin-1 digunakake ( kelas StringLatin1 ) utawa enkoding UTF-16 digunakake ( kelas StringUTF16 ).

Alokasi memori

Kaya sing wis kasebut sadurunge, ana prabédan ing cara memori diparengake kanggo obyek kasebut ing tumpukan. Nggunakake tembung kunci anyar sing eksplisit cukup gampang amarga JVM nggawe lan ngalokasi memori kanggo variabel ing tumpukan. Mulane, nggunakake string literal nderek proses disebut interning. String interning yaiku proses nglebokake string menyang blumbang. Iku nggunakake cara kanggo nyimpen mung siji salinan saben nilai string individu, kang kudu immutable. Nilai individu disimpen ing kolam renang String Intern. Kolam iki minangka toko Hashtable sing nyimpen referensi kanggo saben obyek senar sing digawe nggunakake literal lan hash. Sanajan nilai senar ana ing tumpukan, referensi kasebut bisa ditemokake ing blumbang internal. Iki bisa gampang diverifikasi nggunakake eksperimen ing ngisor iki. Ing kene kita duwe rong variabel kanthi nilai sing padha:
String firstName1   = "Michael";
String firstName2   = "Michael";
System.out.println(firstName1 == firstName2);             //true
Sajrone eksekusi kode, nalika JVM ketemu firstName1 , katon munggah nilai senar ing blumbang senar internal Michael . Yen ora bisa nemokake, banjur entri anyar digawe kanggo obyek ing blumbang internal. Nalika eksekusi tekan firstName2 , proses kasebut diulang maneh lan wektu iki nilai kasebut bisa ditemokake ing blumbang adhedhasar variabel firstName1 . Kanthi cara iki, tinimbang duplikat lan nggawe entri anyar, pranala sing padha bali. Mulane, kondisi kesetaraan puas. Ing sisih liya, yen variabel kanthi nilai Michael digawe nggunakake tembung kunci anyar, ora ana interning lan kondisi kesetaraan ora puas.
String firstName3 = new String("Michael");
System.out.println(firstName3 == firstName2);           //false
Interning bisa digunakake karo firstName3 intern () cara , sanajan iki ora biasane disenengi.
firstName3 = firstName3.intern();                      //Interning
System.out.println(firstName3 == firstName2);          //true
Interning uga bisa kedadeyan nalika nggabungake rong literal string nggunakake operator + .
String fullName = "Michael Jordan";
System.out.println(fullName == "Michael " + "Jordan");     //true
Ing kene kita bisa ndeleng manawa ing wektu kompilasi, kompiler nambahake literal lan mbusak operator + saka ekspresi kasebut kanggo mbentuk senar siji kaya ing ngisor iki. Ing runtime, loro fullName lan "ditambahake literal" interned lan kondisi podo wareg.
//After Compilation
System.out.println(fullName == "Michael Jordan");

Podo

Saka eksperimen ing ndhuwur, sampeyan bisa ndeleng manawa mung string literal sing diintern kanthi standar. Nanging, aplikasi Jawa mesthi ora mung duwe string literal, amarga bisa uga nampa string saka macem-macem sumber. Mula, nggunakake operator kesetaraan ora dianjurake lan bisa ngasilake asil sing ora dikarepake. Pengujian kesetaraan mung kudu ditindakake kanthi metode sing padha . Nindakake kesetaraan adhedhasar nilai senar tinimbang alamat memori sing disimpen.
System.out.println(firstName1.equals(firstName2));       //true
System.out.println(firstName3.equals(firstName2));       //true
Ana uga versi rada diowahi saka cara equals disebut equalsIgnoreCase . Bisa uga migunani kanggo tujuan sing ora sensitif huruf cilik.
String firstName4 = "miCHAEL";
System.out.println(firstName4.equalsIgnoreCase(firstName1));  //true

Imutabilitas

String ora bisa diganti, tegese kahanan internal ora bisa diganti sawise digawe. Sampeyan bisa ngganti Nilai saka variabel, nanging ora Nilai saka senar dhewe. Saben cara saka kelas String sing ngurusi manipulasi obyek (umpamane, concat , substring ) ngasilake salinan anyar saka nilai tinimbang nganyari nilai sing wis ana.
String firstName  = "Michael";
String lastName   = "Jordan";
firstName.concat(lastName);

System.out.println(firstName);                       //Michael
System.out.println(lastName);                        //Jordan
Kaya sing sampeyan ngerteni, ora ana owah-owahan ing variabel apa wae: firstName utawa lastName . Metode kelas string ora ngganti negara internal, nggawe salinan asil anyar lan ngasilake asil kaya ing ngisor iki.
firstName = firstName.concat(lastName);

System.out.println(firstName);                      //MichaelJordan
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION