Rumah > Java > javaTutorial > Ralat Java paling menarik dalam 4

Ralat Java paling menarik dalam 4

Mary-Kate Olsen
Lepaskan: 2025-01-01 09:19:09
asal
608 orang telah melayarinya

Pada tahun 2024, kami telah menganalisis pelbagai projek, berkongsi penemuan kami di blog kami. Kini malam Tahun Baru—masanya untuk menceritakan kisah perayaan! Kami telah mengumpulkan ralat Java paling menarik yang dikesan dalam projek sumber terbuka dan kini membawakannya kepada anda!

Top most intriguing Java errors in 4

kata pengantar

Kami telah lama menegakkan tradisi menyiarkan pepijat paling menarik yang dikesan dengan PVS-Studio, tetapi tidak ada bahagian atas berkaitan Java sejak 2020! Kali ini, saya telah cuba menghidupkan semula rubrik vintaj. Saya harap anda mempunyai selimut yang selesa dan secawan teh hangat di tangan kerana saya telah memilih lebih daripada 10 pepijat yang menghiburkan untuk anda. Begini cara kedudukan mereka:

  • pendapat peribadi saya;
  • latar belakang pepijat yang menarik;
  • kepelbagaian, kredibiliti dan bukan perkara remeh.

Bersedia untuk 10 cerita menghiburkan dengan kebijaksanaan pengaturcaraan mereka sendiri—Malam Tahun Baru akan tiba tidak lama lagi :)

tempat ke-10. Kembali ke masa hadapan

Di tempat kesepuluh, coretan kod pertama menyambut kami dengan tangan terbuka.

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Saya tidak dapat membantu tetapi memasukkannya ke bahagian atas Ambang Tahun Baharu kerana pepijat dalam kod ini membolehkan kita melakukan perjalanan ke tahun hadapan dengan lebih cepat :) Teka dari mana pepijat itu berasal?

Mari kita lihat hujah yang diserahkan kepada pembina SimpleDateFormat. Adakah ia kelihatan sah? Jika kita melepasi hampir mana-mana tarikh, seperti tarikh menulis artikel ini (10/12/2024), kod tersebut akan mengembalikan nilai yang betul, 20241210.

Namun, jika kita melepasi 29/12/2024, ia akan kembali 20251229 sebaliknya, sekali gus bijak beralih ke tahun baharu lebih awal. Btw, perjalanan ke masa lalu juga boleh dilakukan.

Ini berlaku kerana aksara Y dalam hujah kepada SimpleDateFormat mewakili tahun berdasarkan nombor minggu. Ringkasnya, seminggu dianggap sebagai yang pertama apabila ia termasuk sekurang-kurangnya empat hari tahun baru. Jadi, jika minggu kita bermula pada hari Ahad, kita boleh memasuki tahun baharu tiga hari lebih awal.

Untuk membetulkannya, cuma gantikan huruf besar Y dengan huruf kecil y. Ingin mengetahui lebih lanjut? Kami telah mendedikasikan keseluruhan siaran untuk topik ini.

Berikut ialah amaran PVS-Studio untuk ralat ini:

V6122 Penggunaan corak 'Y' (tahun minggu) telah dikesan: ia mungkin bertujuan untuk menggunakan 'y' (tahun). SkeinParameters.java 246

Disebabkan oleh nombor minggu yang khusus, jadi ujian bukanlah rakan terbaik untuk mencari ralat ini. Jadi mengapa pepijat topikal sedemikian datang di tempat terakhir? Sebabnya ialah amaran itu bukan dari versi sebenar Bouncy Castle tetapi dari pangkalan ujian kami. Sumber lama masih kekal di sana, dan pepijat ini telah diperbaiki untuk masa yang lama. Ini adalah salute dari masa lalu, hanya perjalanan masa lagi :)

tempat ke-9. "Nampak tak berkesan"

Di tempat kesembilan, kami mendapat amaran daripada analisis GeoServer:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Berikut ialah amaran PVS-Studio:

V6027 Pembolehubah 'newValue', 'oldValue' dimulakan melalui panggilan ke fungsi yang sama. Ia mungkin ralat atau kod yang tidak dioptimumkan. DataAccessRuleDAOTest.java 110, DataAccessRuleDAOTest.java 111

Apa yang menarik tentang ralat sedemikian? Biar saya dedahkan apa yang tersembunyi di sebalik empat titik:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Terdapat ulasan bahawa kod itu tidak berfungsi atas sebab tertentu. Sejujurnya, ia membuatkan saya tergelak ketika pertama kali melihatnya.

Komen itu agak samar-samar. Kemungkinan besar ujian telah ditulis sedemikian dengan sengaja untuk mengelakkan kegagalan semasa perbandingan sedang menurun. Walau bagaimanapun, kod tersebut telah berada dalam keadaan ini selama lebih sedekad, yang menimbulkan beberapa persoalan. Kekaburan itulah sebabnya saya tidak meletakkannya lebih tinggi.

tempat ke-8. Ditembak di kaki

Jika kita tidak boleh memanggil pepijat dari JBullet sebagai tembakan di kaki, saya tidak tahu yang mana yang boleh kita panggil begitu. Berikut ialah ralat daripada artikel:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Saya rasa kita tidak memerlukan amaran PVS-Studio pun untuk melihat di mana ralatnya. Bagaimanapun, untuk berjaga-jaga, ini dia:

V6026 Nilai ini telah diberikan kepada pembolehubah 'proxy1'. HashedOverlappingPairCache.java 233

Ya, ia adalah ralat yang memalukan mudah. Kesederhanaan itu menjadikannya lebih kelakar, walaupun. Namun begitu, ia mempunyai kisah tersendiri.

Pustaka JBullet ialah port daripada pustaka bullet C/C, dan terdapat fungsi serupa di sana:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Mudah untuk melihat bahawa kod ini ditulis dengan betul. Berdasarkan git blame, ia pada asalnya ditulis dengan betul. Ternyata ralat berlaku apabila kod dialihkan dari satu bahasa ke bahasa lain.

Atas sifat bersahajanya yang mengagumkan ditambah dengan sejarah yang kaya, saya memberikan amaran ini dengan tempat kelapan. Saya harap anda menikmati bahawa pepijat tembakan di kaki ternyata berkaitan dengan C.

tempat ke-7. Malah ahli matematik yang hebat melakukan kesilapan

Diakui, amaran seterusnya menghangatkan hati saya kerana pelbagai sebab. Berikut ialah coretan kod daripada semakan GeoGebra:

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Cuba cari sendiri ralatnya! Untuk mengelakkan anda daripada mengintip, saya menyembunyikan amaran dan penjelasan dalam spoiler.

Jawapannya
Mula-mula, mari kita lihat amaran PVS-Studio:

V6107 Pemalar 0.7071067811865 sedang digunakan. Nilai yang terhasil mungkin tidak tepat. Pertimbangkan untuk menggunakan Math.sqrt(0.5). DrawAngle.java 303

Pada hakikatnya, 0.7071067811865 bukanlah nombor ajaib—ia hanyalah hasil bulat untuk mengambil punca kuasa dua 0.5. Tetapi betapa kritikalnya kehilangan ketepatan ini? GeoGebra ialah perisian yang disesuaikan untuk ahli matematik dan ketepatan tambahan nampaknya tidak menyakitkan di sana.

Mengapa saya sangat menyukai pepijat ini?

Pertama, di persidangan, saya sering meminta peserta mencari ralat yang sama dalam serpihan kod lain. Menonton mereka dengan teliti menganalisis kod apabila pepijat disembunyikan dalam pemalar sentiasa menghiburkan.

Kedua, ini ialah peraturan diagnostik pertama saya yang dilaksanakan untuk penganalisis Java. Itulah sebabnya saya tidak dapat menahan memasukkannya ke bahagian atas—walaupun dengan semua kesedaran berat sebelah—saya meletakkannya di tempat ketujuh :)

tempat ke-6. Corak ini tidak berfungsi

Amaran berikut, yang saya ambil dari artikel pertama berdasarkan semakan DBeaver, mungkin tidak serta-merta menarik perhatian kita. Berikut ialah serpihan kod:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Berikut ialah perkara yang dikesan oleh penganalisis PVS-Studio:

V6082 Penguncian semak dua kali tidak selamat. Medan harus diisytiharkan sebagai tidak menentu. TaskImpl.java 59, TaskImpl.java317

Walaupun tiada apa-apa yang istimewa tentang amaran ini, saya masih mendapati ia sangat menarik. Intinya ialah corak Penguncian Dwi-Semak yang digunakan tidak berfungsi. Apa muslihatnya? Ini relevan 20 tahun yang lalu :)

Jika anda ingin membaca lebih lanjut mengenai topik tersebut, saya cadangkan membaca artikel penuh. Tetapi buat masa ini, izinkan saya memberikan ringkasan ringkas kepada anda.

Corak Penguncian Dwi-Semak digunakan untuk melaksanakan pengamulaan tertunda dalam persekitaran berbilang benang. Sebelum semakan "berat", yang "ringan" dilaksanakan tanpa blok penyegerakan. Sumber dicipta hanya apabila kedua-dua cek lulus.

Walau bagaimanapun, dalam pendekatan ini, penciptaan objek adalah bukan atom dan pemproses serta pengkompil boleh menukar tertib operasi. Oleh itu, benang lain mungkin secara tidak sengaja menerima objek yang dibuat separa dan mula bekerja dengannya, yang boleh membawa kepada tingkah laku yang salah. Ralat ini mungkin jarang berlaku, jadi penyahpepijatan akan menjadi cabaran besar bagi pembangun.

Ini adalah kelainan: corak ini tidak berfungsi sehingga JDK 5. Bermula dengan JDK 5, kata kunci yang tidak menentu telah diperkenalkan untuk menyelesaikan potensi isu operasi penyusunan semula berkat prinsip berlaku-sebelum. Penganalisis memberi amaran kepada kami bahawa kami harus menambah kata kunci ini.

Walau bagaimanapun, adalah lebih baik untuk mengelakkan corak ini. Prestasi perkakasan dan JVM telah berkembang jauh sejak itu, dan operasi yang disegerakkan tidak begitu perlahan lagi. Walau bagaimanapun, pelaksanaan corak DCL secara tidak betul kekal sebagai perangkap biasa, dengan kemungkinan akibat yang serius yang diterangkan di atas. Ia mengesahkan fakta bahawa penganalisis kami masih menemui ralat cuai sedemikian dalam projek lama.

tempat ke-5. Pengoptimuman mikro

Tempat kelima mengambil satu lagi amaran DBeaver yang telah kami dedikasikan artikel. Jom tengok:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Berikut ialah penjelasan:

V6030 Kaedah yang terletak di sebelah kanan operator '&' akan dipanggil tanpa mengira nilai operan kiri. Mungkin, lebih baik menggunakan '&&'. ExasolTableColumnManager.java 79, DB2TableColumnManager.java 77

Pemaju telah mencampuradukkan logik && dengan bitwise &. Mereka mempunyai tingkah laku yang berbeza: syarat dalam ungkapan tidak ditamatkan selepas bitwise DAN. penilaian litar pintas tidak berfungsi untuk bitwise DAN. Jadi, walaupun exasolTableBase != null akan mengembalikan false, urutan pelaksanaan akan mencapai exasolTableBase.getClass() dan membawa kepada NPE.

Baiklah, itu hanya kesilapan menaip, mari kita teruskan, ya? DBeaver mempunyai banyak amaran sedemikian. banyak. Banyak yang agak tidak berbahaya, tetapi untuk pembaca yang ingin tahu, saya telah meninggalkan beberapa contoh di bawah:

Menggunakan operasi bitwise yang tidak membawa kepada ralat
ExasolSecurityPolicy.java:
public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

ExasolConnectionManager.java:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

ExasolDataSource.java:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Selepas menggali lebih dalam, pasukan saya telah mengandaikan bahawa pembangun mungkin cuba untuk mengoptimumkan prestasi mikro. Untuk gambar penuh, anda boleh menyemak artikel kami—di sini saya memperkenalkan ringkasan.

Intinya ialah operasi bitwise tidak bergantung pada ramalan cawangan, yang berpotensi membenarkan pelaksanaan yang lebih pantas berbanding dengan operasi logik.

Apa yang mengejutkan kami, penanda aras buatan sendiri menyokong tuntutan ini:

Top most intriguing Java errors in 4

Carta menggambarkan masa yang diperlukan untuk setiap jenis operasi. Jika kami mempercayainya, operasi bitwise nampaknya 40% lebih pantas daripada operasi logik.

Mengapa saya membangkitkan topik ini? Untuk menyerlahkan kos potensi pengoptimuman mikro.

Mula-mula, pembangun mencipta ramalan cawangan atas sebab tertentu—terlalu mahal untuk meninggalkannya. Oleh itu, penanda aras mungkin berfungsi lebih pantas kerana nilai mempunyai taburan normal, yang tidak mungkin diperhatikan dalam kes sebenar.

Kedua, meninggalkan mekanisme penilaian litar pintas boleh membawa kepada kos yang jauh lebih tinggi. Jika kita melihat contoh ketiga daripada spoiler di atas, kita dapat melihat bahawa bukan yang terpantas mengandungi operasi akan dilaksanakan sepanjang masa dan bukannya berhenti dengan segera.

Ketiga, kami memberikan carte blanche untuk kesilapan sedemikian dari permulaan bab.

Secara keseluruhannya, saya dapati kisah peringatan tentang harga pengoptimuman mikro cukup menarik untuk membuka lima teratas kami.

tempat ke-4. Ujian tidak akan jatuh jika ia tidak berjaya

Ujian automatik sering dianggap sebagai perlindungan utama terhadap pelbagai ralat. Walau bagaimanapun, setiap kali, saya terdorong untuk bertanya: "Siapakah yang menguji ujian itu sendiri?" Satu lagi amaran daripada semakan GeoServer menunjukkan ini sekali lagi. Berikut ialah serpihan kod:

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Amaran PVS-Studio:

V6060 Rujukan 'e' telah digunakan sebelum ia disahkan terhadap nol. ResourceAccessManagerWCSTest.java 186, ResourceAccessManagerWCSTest.java 193

Pada pandangan pertama, amaran ini mungkin tidak kelihatan seperti penganalisis yang paling menarik kerana V6060 sering dikeluarkan untuk kod berlebihan. Namun, saya berjanji bahawa saya akan memilih pencalonan berdasarkan rayuan mereka. Jadi, kes ini jauh lebih menarik daripada yang kelihatan.

Pada mulanya, logik ujian mungkin kelihatan salah kerana pembolehubah e diperoleh daripada operator tangkapan dan kekal tidak berubah lagi, jadi ia tidak akan menjadi batal. Kita hanya boleh membuat suntingan sesat dan mengalih keluar cabang keadaan if(e == nul) sebagai tidak boleh dicapai. Namun, itu akan menjadi sangat salah. Sudahkah anda mengetahui tangkapannya?

Kuncinya terletak pada satu lagi pembolehubah dalam kod yang mengandungi objek pengecualian, ia adalah se. Ia mempunyai nilai yang berubah dalam badan gelung. Jadi, kita boleh meneka dengan mudah bahawa dalam keadaan itu, perlu ada pembolehubah se bukannya e.

Ralat ini akan menyebabkan cawangan kemudian tidak akan dilaksanakan, jadi kami tidak akan tahu bahawa tiada pengecualian. Lebih buruk lagi, agak sukar untuk melihat ralat sedemikian pada semakan kod kerana nama pembolehubah adalah serupa.

Ada dua hikmah yang boleh dipetik daripada kisah ini:

  1. Namakan pembolehubah dengan jelas, walaupun dalam ujian. Jika tidak, lebih mudah untuk melakukan kesilapan sedemikian;
  2. Ujian tidak mencukupi untuk menjamin kualiti projek kerana ia juga boleh mengandungi ralat. Oleh itu, ia meninggalkan celah untuk pepijat dalam apl.

Untuk menyampaikan pengajaran yang begitu berharga, saya memberikan amaran ini dengan tempat keempat.

tempat ke-3. Selamat menyahpepijat, kawan-kawan

Tiga pemenang teratas tergolong dalam amaran daripada cek NetBeans. Coretan kod sebelumnya agak padat, jadi mari lihat yang panjang:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Untuk kali terakhir, cuba cari pepijat itu sendiri—saya akan tunggu...

Top most intriguing Java errors in 4

Mencari?

Bagus! Mereka yang menemui ralat hanya dalam ungkapan iDesc.neighbor != null || iDesc.index == iDesc.index, saya minta maaf untuk mengatakan—anda kalah :)

Memang ada isu, tetapi ia tidak begitu menarik untuk yang teratas. Ya, terdapat dua kesilapan di sini, saya telah menipu anda sedikit. Tetapi apakah cuti tanpa sedikit kerosakan? :)

Penganalisis telah mengesan ralat dalam ungkapan i^i di sini dan mengeluarkan amaran berikut:

V6001 Terdapat subungkapan 'i' yang sama di sebelah kiri dan di sebelah kanan pengendali '^'. LayoutFeeder.java 3897

Operasi XOR tidak masuk akal kerana OR eksklusif dengan dua nilai yang sama akan sentiasa sifar. Untuk penyegaran pantas, berikut ialah jadual kebenaran untuk XOR:

a b a^b
0 0 0
0 1 1
1 0 1
1 1 0

Dalam erti kata lain, operasi adalah benar hanya apabila operan berbeza. Kami akan mempunyai semua bit yang sama, kerana nilainya adalah sama.

Mengapa saya sangat menyukai pepijat ini? Terdapat operasi i^1, yang kelihatan hampir sama dengan i^i. Oleh itu, adalah sangat mudah untuk terlepas ralat ini pada semakan kod, kerana kami telah melihat i^1 yang betul di atas.

Saya tidak tahu tentang anda, tetapi ia mengingatkan saya kepada yang terkenal:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jika tidak, sukar untuk menerangkan cara ia masuk ke dalam kod—melainkan, kami mengetepikan versi yang membosankan dengan kesilapan menaip yang mudah. Jika anda berjaya mengesan pepijat itu, tepuk belakang diri anda—atau kongsi kemahiran detektif anda dalam ulasan :)

tempat ke-2. Apabila corak gagal

Saya telah menunjukkan ralat dari artikel DBeaver pertama dan ketiga, melangkau yang kedua. Saya tetap diperbetulkan—yang berikut hanyalah daripada artikel kedua.

Penganalisis PVS-Studio tidak menyukainya isBinaryContents dipanggil daripada pembina kelas TextWithOpen, yang ditindih dalam subkelas:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jadi apa? Ia telah ditindih-walaupun bukan masalah besar. Nampaknya seperti bau kod, tiada yang kritikal. Sekurang-kurangnya, itulah yang saya fikirkan. Saya telah mendedikasikan artikel untuk perjuangan saya dengan pepijat ini.

TextWithOpen mempunyai banyak subkelas, dan salah satunya ialah TextWithOpenFile. Di sana, kaedah itu sebenarnya diganti, dan bukannya palsu, ia mengembalikan medan yang tiada kelas super:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Meragukan lagi? Apakah rupa pembina kelas ini?

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Perasan tak? Medan binari dimulakan selepas pembina superclass dipanggil. Walau bagaimanapun, terdapat panggilan ke kaedah isBinaryContents, yang merujuk medan subkelas!

Top most intriguing Java errors in 4

Berikut ialah amaran PVS-Studio:

V6052 Memanggil kaedah 'isBinaryContents' yang ditindih dalam pembina kelas induk 'TextWithOpen' boleh menyebabkan penggunaan data yang tidak dimulakan. Periksa medan: binari. TextWithOpenFile.java(77), TextWithOpen.java 59

Ia adalah gambar yang agak menghiburkan. Pada pandangan pertama, pembangun nampaknya mengikuti amalan terbaik: elakkan spageti kod, yang mustahil untuk dikekalkan, dan cuba melaksanakan OOP kanonik melalui corak kaedah templat. Tetapi apabila melaksanakan corak yang begitu mudah, kita boleh membuat kesilapan, itulah yang berlaku. Pada pendapat saya, keindahan (palsu) dalam kesederhanaan adalah tempat kedua yang kukuh.

tempat pertama. Satu ralat mengimbangi yang lain

Bersukacitalah! Tempat pertama di atas pentas! Persaingan memang sengit, tetapi pilihan terpaksa dibuat. Selepas banyak pertimbangan, saya telah memutuskan untuk mengambil amaran daripada semakan NetBeans. Izinkan saya memperkenalkan coretan kod akhir:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Saya yakin bahawa adalah mustahil untuk mengesan pepijat sedemikian sepintas lalu—melainkan, sudah tentu, anda sendiri yang melakukan kesilapan ini. Saya tidak akan membuat anda menunggu—ini amaran PVS-Studio:

Kapasiti penimbal V6009 ditetapkan kepada '47' menggunakan nilai aksara. Kemungkinan besar, simbol '/' sepatutnya diletakkan dalam penimbal. IgnoreUnignoreCommand.java 107

Sebenarnya, ralat adalah sangat asas: pembina StringBuilder tidak mempunyai beban berlebihan yang menerima char. Apakah pembina dipanggil kemudian? Pembangun nampaknya berpendapat bahawa beban yang menerima String akan dipanggil, dan kemudian nilai awal StringBuilder ialah garis miring ini.

Walau bagaimanapun, penukaran jenis tersirat berlaku, dan pembina jenis yang menerima int dipanggil. Dalam kes kami, ia mewakili saiz awal StringBuilder. Meluluskan char sebagai hujah tidak menjejaskan apa-apa kerana ia tidak akan disertakan dalam rentetan akhir. Jika saiz awal melebihi, ia hanya akan meningkat dengan sendirinya tanpa menyebabkan pengecualian atau kesan sampingan lain.

Tetapi tunggu, saya menyebut dua kesilapan, bukan? Di manakah yang kedua, dan bagaimana ia disambungkan? Untuk melihat perkara ini, kita perlu membaca ke dalam badan kaedah dan menyedari apa yang dilakukan oleh kod ini.

Ia menjana laluan mutlak ke fail atau direktori. Berdasarkan kod, laluan yang terhasil sepatutnya kelihatan seperti ini:

  • untuk fail: /folder1/file
  • untuk direktori: /folder1/folder/.

Kod itu kelihatan agak betul. Itu masalahnya. Kod ini benar-benar berfungsi sebagaimana mestinya :) Tetapi jika kita membetulkan ralat dengan menggantikan aksara dengan rentetan, kita akan mendapat ini dan bukannya hasil yang betul:

  • /folder1/file/;
  • /folder1/folder//

Dalam erti kata lain, kita akan mendapat garis miring tambahan di hujung rentetan. Ia akan berada di penghujung kerana kod di atas menambah teks baharu pada permulaan baris setiap kali.

Oleh itu, ralat kedua ialah garis miring ini diserahkan kepada pembina sebagai hujah sama sekali. Walau bagaimanapun, saya tidak akan memandang rendah ralat sedemikian kerana jika seseorang memutuskan untuk menggantikan aksara dengan rentetan tanpa menyemak, mungkin terdapat masalah.

Begitulah cara tempat pertama di bahagian atas ralat pergi ke kod yang berfungsi dengan betul. Keajaiban Tahun Baru, apa yang anda jangkakan? :)

Kesimpulan

Saya harap anda seronok membaca cerita pepijat saya. Jika mana-mana cerita tertentu menonjol kepada anda—atau jika anda mempunyai cadangan untuk pelarasan pada kedudukan—sila kongsikan pendapat anda dalam ulasan, saya akan mengingatinya untuk kali seterusnya :)

Jika anda berminat dengan bahasa lain, saya menjemput anda untuk menyemak pepijat C# teratas untuk 2024 di sini—nantikan lagu teratas baharu!

Semua ralat ini telah dikesan dengan penganalisis PVS-Studio, dan versi terkini (7.34) baru sahaja dikeluarkan! Anda boleh mencubanya melalui pautan ini.

Untuk menantikan artikel baharu tentang kualiti kod, kami menjemput anda untuk melanggan:

  • PVS-Studio X (Twitter);
  • cernaan artikel bulanan kami;

Selamat Tahun Baru!

Atas ialah kandungan terperinci Ralat Java paling menarik dalam 4. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan