ในการเขียนโปรแกรม Java ความรู้บางอย่างไม่สามารถเรียนรู้ได้ผ่านข้อกำหนดภาษาหรือเอกสาร API มาตรฐานเท่านั้น ในบทความนี้ฉันจะพยายามรวบรวมสำนวนที่ใช้กันมากที่สุดโดยเฉพาะอย่างยิ่งบทความที่ยากที่จะเดา
ฉันใส่รหัสทั้งหมดในบทความนี้ในที่สาธารณะ คุณสามารถคัดลอกและแก้ไขตัวอย่างโค้ดใด ๆ ตามการตั้งค่าของคุณโดยไม่มีข้อมูลรับรองใด ๆ
ใช้เท่ากับ ()
บุคคลในชั้นเรียน {ชื่อสตริง; int วันเกิดปี; ไบต์ [] ดิบ; บูลีนสาธารณะเท่ากับ (Object obj) {ถ้า (! OBJ Instanceof Person) คืนค่าเท็จ; บุคคลอื่น = (บุคคล) obj; return name.equals (อื่น ๆ name) && birthdayyear == อื่น ๆ birthyear && array.equals (raw, other.raw); } public int hashCode () {... }} พารามิเตอร์จะต้องเป็นวัตถุประเภทไม่ใช่ของคลาสอุปกรณ์ต่อพ่วง
foo.equals (null) จะต้องส่งคืนเท็จและไม่สามารถโยน nullpointerexception (โปรดทราบว่าอินสแตนซ์ null ของคลาสใด ๆ จะส่งกลับเท็จเสมอดังนั้นรหัสด้านบนสามารถเรียกใช้ได้)
การเปรียบเทียบโดเมนประเภทพื้นฐาน (ตัวอย่างเช่น int) ใช้ == และการเปรียบเทียบโดเมนอาร์เรย์ประเภทพื้นฐานถูกใช้โดย array.equals ()
เมื่อการเขียนทับเท่ากับ () อย่าลืมเขียนทับ HashCode () ดังนั้นจึงสอดคล้องกับค่าเท่ากับ ()
การอ้างอิง: java.lang.object.equals (วัตถุ)
ใช้ HashCode ()
บุคคลในชั้นเรียน {String A; วัตถุ B; ไบต์ C; int [] d; public int hashCode () {return a.hashCode () + b.HashCode () + c + array.hashCode (d); } บูลีนสาธารณะเท่ากับ (Object O) {... }} เมื่อวัตถุ x และ y มี x.equals (y) == จริงคุณต้องตรวจสอบให้แน่ใจว่า x.hashcode () == y.hashcode ()
ตามข้อเสนอผกผันถ้า x.hashCode ()! = y.hashCode () ดังนั้น x.equals (y) == เท็จจะต้องเป็นจริง
คุณไม่จำเป็นต้องรับประกันว่า x.hashCode ()! = y.hashCode () เมื่อ x.equals (y) == FALSE อย่างไรก็ตามสิ่งนี้จะช่วยปรับปรุงประสิทธิภาพของตารางแฮชหากคุณสามารถทำให้นานที่สุด
การดำเนินการตามกฎหมายที่ง่ายที่สุดของ HashCode () คือการส่งคืน 0; แม้ว่าการใช้งานนี้จะถูกต้อง แต่สิ่งนี้จะทำให้โครงสร้างข้อมูลเช่น HashMap ทำงานช้ามาก
ใช้งาน comparTo ()
บุคคลในชั้นเรียนใช้ความเปรียบเทียบ <person> {String FirstName; สตริงนามสกุล; int วันเกิด; // เปรียบเทียบโดย FirstName, Break Ties โดย LastName, ในที่สุดก็ทำลายความสัมพันธ์โดยวันเกิด public int compereto (บุคคลอื่น ๆ ) {ถ้า (firstName.Compareto (อื่น ๆ . อย่างอื่นถ้า (lastName.Compareto (อื่น ๆ . lastName)! = 0) return.Compareto (อื่น ๆ . lastName); อื่นถ้า (วันเกิด <อื่น ๆ . birthdate) return -1; อย่างอื่นถ้า (วันเกิด> อื่น ๆ กลับมา) กลับ 1; กลับมาอีก 0; - ใช้เวอร์ชันทั่วไปเสมอเทียบเท่าแทนประเภทดั้งเดิมเทียบเท่ากัน เพราะสิ่งนี้สามารถบันทึกปริมาณรหัสและลดความยุ่งยากที่ไม่จำเป็น
เพียงแค่ใส่ใจเกี่ยวกับสัญญาณ (ลบ/ศูนย์/บวก) ที่ส่งคืนผลลัพธ์ขนาดของพวกเขาไม่สำคัญ
comparator.compare () การใช้งานคล้ายกับสิ่งนี้
ใช้โคลน ()
ค่าคลาสใช้ cloneable {string abc; double foo; int [] บาร์; วันที่จ้าง; ค่าสาธารณะ clone () {ลอง {ค่าผลลัพธ์ = (ค่า) super.clone (); result.bars = result.bars.clone (); result.hired = result.hired.clone (); ผลการกลับมา; } catch (clonenotsupportedException e) {// เป็นไปไม่ได้โยน assertionError ใหม่ (e); - ใช้ super.clone () เพื่อให้คลาสวัตถุรับผิดชอบในการสร้างวัตถุใหม่
โดเมนประเภทพื้นฐานได้รับการคัดลอกอย่างถูกต้อง อีกครั้งเราไม่จำเป็นต้องโคลนประเภทที่ไม่เปลี่ยนรูปเช่น String และ BigInteger
ดำเนินการคัดลอกอย่างลึกซึ้งของฟิลด์ประเภทที่ไม่ได้ใช้งานทั้งหมดด้วยตนเอง (วัตถุและอาร์เรย์)
คลาส cloneable ถูกนำมาใช้และวิธีการ clone () ไม่ควรโยน clonenotsupportedexception ดังนั้นคุณต้องจับข้อยกเว้นนี้และเพิกเฉยต่อมันหรือห่อด้วยข้อยกเว้นที่ไม่ได้ตรวจสอบ
มันก็โอเคและถูกกฎหมายในการใช้วิธี clone () ด้วยตนเองโดยไม่ต้องใช้วิธี Object.clone ()
ใช้ StringBuilder หรือ StringBuffer
// เข้าร่วม (["A", "B", "C"]) -> "สตริง A และ B และ C" เข้าร่วม (รายการ <String> strs) {StringBuilder sb = new StringBuilder (); บูลีนแรก = true; สำหรับ (String s: strs) {ถ้า (ครั้งแรก) แรก = false; else sb.append ("และ"); SB.Append (S); } return sb.toString ();} อย่าใช้การเชื่อมต่อสตริงซ้ำแบบนี้: s += รายการเพราะประสิทธิภาพเวลาของมันคือ o (n^2)
เมื่อใช้ StringBuilder หรือ StringBuffer คุณสามารถใช้วิธีการผนวก () เพื่อเพิ่มข้อความและใช้วิธี TOSTRING () เพื่อรับข้อความทั้งหมดที่เชื่อมต่อ
ลำดับความสำคัญจะมอบให้กับ StringBuilder เนื่องจากเร็วกว่า วิธีการทั้งหมดของ StringBuffer นั้นถูกซิงโครไนซ์และคุณมักจะไม่จำเป็นต้องใช้วิธีการซิงโครไนซ์
สร้างจำนวนเต็มสุ่มในช่วง
Random Rand = new Random (); // ระหว่าง 1 ถึง 6 รวมถึง INTINC DICEROLL () {return Rand.Nextint (6) + 1;} ใช้วิธี Java API เสมอเพื่อสร้างหมายเลขสุ่มในช่วงของจำนวนเต็ม
อย่าพยายามใช้ Math.Abs (rand.nextint ()) %n สำหรับการใช้งานที่ไม่แน่นอนเหล่านี้เนื่องจากผลลัพธ์ของมันมีอคติ นอกจากนี้ค่าผลลัพธ์อาจเป็นลบเช่นเมื่อ Rand.Nextint () == Integer.min_value
ใช้ iterator.remove ()
Void Filter (รายการ <String> รายการ) {สำหรับ (iterator <string> iter = list.iterator (); iter.hasnext ();) {string item = iter.next (); ถ้า (... ) iter.remove (); -วิธีการลบ () ทำหน้าที่ในรายการที่ส่งคืนเมื่อเร็ว ๆ นี้ของวิธีถัดไป () แต่ละรายการสามารถใช้วิธีการลบ () หนึ่งครั้งเท่านั้น
คืนสตริง
String Reverse (String S) {ส่งคืน StringBuilder ใหม่ (S) .Reverse (). ToString ();}วิธีนี้ควรเพิ่มในไลบรารีมาตรฐาน Java
เริ่มเธรด
สามตัวอย่างต่อไปนี้ทำสิ่งเดียวกันในรูปแบบที่แตกต่างกัน
วิธีใช้งาน Runnnable:
เป็นโมฆะ startAthread0 () {เธรดใหม่ (ใหม่ myrunnable ()). start ();} คลาส myrunnable onplunable runnable {public void run () {... }}}วิธีการสืบทอดเธรด:
เป็นโมฆะ startathread1 () {ใหม่ mythread (). start ();} คลาส mythread ขยายเธรด {public void run () {... }}วิธีการสืบทอดเธรดโดยไม่ระบุชื่อ:
เป็นโมฆะ startathread2 () {เธรดใหม่ () {โมฆะสาธารณะเรียกใช้ () {... }} .start ();}อย่าเรียกวิธีการเรียกใช้ () โดยตรง เมธอด thread.start () ถูกเรียกเสมอซึ่งจะสร้างเธรดใหม่และทำให้เธรดที่สร้างขึ้นใหม่เพื่อเรียกใช้ Run ()
ใช้การลองครั้งสุดท้าย
ตัวอย่างสตรีม I/O:
เป็นโมฆะ Writestuff () พ่น IOException {OutputStream Out = ใหม่ fileOutputStream (... ); ลอง {out.write (... ); } ในที่สุด {out.close (); -ตัวอย่างล็อค:
โมฆะ dowithlock (ล็อคล็อค) {lock.acquire (); ลอง {... } ในที่สุด {lock.release (); - หากคำสั่งก่อนการลองล้มเหลวในการรันและมีการโยนข้อยกเว้นแล้วบล็อกคำสั่งสุดท้ายจะไม่ถูกเรียกใช้งาน แต่ไม่ว่าในตัวอย่างนี้จะเกิดอะไรขึ้นไม่จำเป็นต้องกังวลเกี่ยวกับการเปิดตัวทรัพยากร
หากคำสั่งในบล็อกคำสั่งลองโยนข้อยกเว้นการทำงานของโปรแกรมจะข้ามไปยังบล็อกคำสั่งสุดท้ายเพื่อเรียกใช้คำสั่งให้ได้มากที่สุดเท่าที่จะเป็นไปได้จากนั้นกระโดดออกจากวิธีนี้ (เว้นแต่ว่าวิธีนี้จะมีบล็อกคำสั่งต่อพ่วงอื่นในที่สุด)
อ่านข้อมูลไบต์จากสตรีมอินพุต
inputStream in = (... ); ลอง {ในขณะที่ (จริง) {int b = in.read (); if (b == -1) break; (... กระบวนการ b ... )}} ในที่สุด {in.close ();}วิธีการอ่าน () ส่งคืนจำนวนไบต์ถัดไปที่อ่านจากสตรีม (0 ถึง 255 รวมถึง 0 และ 255) หรือส่งคืน -1 เมื่อถึงจุดสิ้นสุดของสตรีม
อ่านข้อมูลบล็อกจากสตรีมอินพุต
inputStream in = (... ); ลอง {byte [] buf = byte ใหม่ [100]; ในขณะที่ (จริง) {int n = in.read (buf); if (n == -1) break; (... กระบวนการ buf ด้วยออฟเซ็ต = 0 และความยาว = n ... )}} ในที่สุด {in.close ();}โปรดจำไว้ว่าวิธีการอ่าน () ไม่จำเป็นต้องเติมเต็ม BUF ทั้งหมดดังนั้นคุณต้องพิจารณาความยาวของผลตอบแทนในตรรกะการประมวลผล
อ่านข้อความจากไฟล์
bufferedReader in = new bufferedReader (ใหม่ inputStreamReader (ใหม่ fileInputStream (... ), "UTF-8")); ลอง {ในขณะที่ (จริง) {สตริงบรรทัด = in.readline (); if (line == null) break; (... บรรทัดกระบวนการ ... )}} ในที่สุด {in.close ();} การสร้างวัตถุ bufferedReader ดูเหมือนจะเป็นสีสันมาก นี่เป็นเพราะ Java ถือว่าไบต์และตัวละครเป็นสองแนวคิดที่แตกต่างกัน (ซึ่งแตกต่างจาก C)
คุณสามารถใช้อินพุทสตรีมทุกประเภทแทน FileInputStream เช่นซ็อกเก็ต
bufferedReader.readline () ส่งคืนค่า NULL เมื่อถึงจุดสิ้นสุดของสตรีม
หากต้องการอ่านหนึ่งอักขระในแต่ละครั้งให้ใช้วิธี reader.read ()
คุณสามารถใช้การเข้ารหัสอักขระอื่น ๆ โดยไม่ต้องใช้ UTF-8 แต่จะไม่ทำเช่นนั้น
เขียนข้อความไปยังไฟล์
PrintWriter Out = ใหม่ PrintWriter (ใหม่ OutputStreamWriter (ใหม่ FileOutputStream (... ), "UTF-8")); ลอง {out.print ("สวัสดี"); Out.print (42); out.println ("World!");} ในที่สุด {out.close ();} การสร้างวัตถุ Printwriter ดูเหมือนจะเป็นคำอื่นมาก นี่เป็นเพราะ Java ถือว่าไบต์และตัวละครเป็นสองแนวคิดที่แตกต่างกัน (ซึ่งแตกต่างจาก C)
เช่นเดียวกับ System.out คุณสามารถใช้ print () และ println () เพื่อพิมพ์ค่าหลายประเภท
คุณสามารถใช้การเข้ารหัสอักขระอื่น ๆ โดยไม่ต้องใช้ UTF-8 แต่จะไม่ทำเช่นนั้น
มูลค่าการตรวจสอบการป้องกัน
int factorial (int n) {ถ้า (n <0) โยน unlegalargumentException ใหม่ ("undefined"); อย่างอื่นถ้า (n> = 13) โยน arithmeticexception ใหม่ ("ผลลัพธ์ล้น"); อื่นถ้า (n == 0) ส่งคืน 1; else return n * factorial (n - 1);} อย่าคิดว่าค่าอินพุตนั้นเป็นบวกเล็กพอ ฯลฯ เพื่อตรวจจับเงื่อนไขเหล่านี้อย่างชัดเจน
ฟังก์ชั่นที่ออกแบบมาอย่างดีควรจะสามารถดำเนินการได้อย่างถูกต้องสำหรับค่าอินพุตที่เป็นไปได้ทั้งหมด ตรวจสอบให้แน่ใจว่าสถานการณ์ทั้งหมดถูกนำมาพิจารณาและไม่มีผลลัพธ์ที่ผิด (เช่นล้น)
วัตถุทดสอบป้องกัน
int findIndex (รายการ <string> รายการเป้าหมายสตริง) {ถ้า (list == null || target == null) โยน nullpointerexception ใหม่ (); -อย่าคิดว่าพารามิเตอร์วัตถุจะไม่เป็นโมฆะ เพื่อตรวจจับเงื่อนไขนี้อย่างชัดเจน
ดัชนีอาร์เรย์ตรวจจับเชิงป้องกัน
เป็นโมฆะ frob (byte [] b, ดัชนี int) {ถ้า (b == null) โยน nullpointerexception ใหม่ (); if (index <0 || index> = b.length) โยน indexoutofboundsexception ใหม่ (); -อย่าคิดว่าดัชนีอาร์เรย์ที่กำหนดจะไม่ข้ามขอบเขต เพื่อตรวจจับอย่างชัดเจน
ช่วงเวลาอาร์เรย์ตรวจจับเชิงป้องกัน
เป็นโมฆะ frob (byte [] b, int ปิด, int len) {ถ้า (b == null) โยน nullpointerexception ใหม่ (); if (ปิด <0 || ปิด> b.length || len <0 || b.length - ปิด <len) โยน indexoutofboundsexception ใหม่ (); -อย่าคิดว่าช่วงเวลาอาร์เรย์ที่กำหนด (ตัวอย่างเช่นการเริ่มต้นจากปิดการอ่านองค์ประกอบของเลน) จะไม่เกินขอบเขต เพื่อตรวจจับอย่างชัดเจน
เติมองค์ประกอบอาร์เรย์
ใช้ลูป:
// เติมแต่ละองค์ประกอบของอาร์เรย์ 'a' ด้วย 123Byte [] a = (... ); สำหรับ (int i = 0; i <a.length; i ++) a [i] = 123;
(พิเศษ) วิธีการใช้ไลบรารีมาตรฐาน:
array.fill (a, (ไบต์) 123);
คัดลอกองค์ประกอบอาร์เรย์ในช่วง
ใช้ลูป:
// คัดลอก 8 องค์ประกอบจากอาร์เรย์ 'A' เริ่มต้นที่ออฟเซ็ต 3 // ถึงอาร์เรย์ 'B' เริ่มต้นที่ออฟเซ็ต 6, // สมมติว่า 'A' และ 'B' เป็นอาร์เรย์ที่แตกต่างกัน [] a = (... ); byte [] b = (... ); สำหรับ (int i = 0; i <8;
(พิเศษ) วิธีการใช้ไลบรารีมาตรฐาน:
System.arraycopy (a, 3, b, 6, 8);
ปรับขนาดอาร์เรย์
ใช้ลูป (ปรับขนาดขึ้น):
// ทำอาร์เรย์ 'a' ใหญ่ถึง newlenbyte [] a = (... ); byte [] b = ไบต์ใหม่ [newlen]; สำหรับ (int i = 0; i <a.length; i ++) // ขึ้นไปตามความยาวของ b [i] = a [i]; a = b;
ใช้ลูป (ลดขนาด):
// สร้างอาร์เรย์ 'a' เล็กกว่า newlenbyte [] a = (... ); byte [] b = byte ใหม่ [newlen]; สำหรับ (int i = 0; i <b.length; i ++) // เพิ่มความยาวของ b b [i] = a [i]; a = b;
(พิเศษ) วิธีการใช้ไลบรารีมาตรฐาน:
a = arrays.copyof (a, newlen);
บรรจุ 4 ไบต์ลงใน INT
int packbigendian (byte [] b) {return (b [0] & 0xff) << 24 | (b [1] & 0xff) << 16 | (b [2] & 0xff) << 8 | (b [3] & 0xff) << 0;} int packlittleendian (byte [] b) {return (b [0] & 0xff) << 0 | (b [1] & 0xff) << 8 | (b [2] & 0xff) << 16 | (b [3] & 0xff) << 24;}สลายตัวเป็น 4 ไบต์
Byte [] unpackbigendian (int x) {ส่งคืน byte ใหม่ [] {(byte) (x >>> 24), (byte) (x >>> 16), (byte) (x >>> 8), (byte) (x >>> 0)};} byte [] >>> 8), (ไบต์) (x >>> 16), (ไบต์) (x >>> 24)};}ใช้ตัวดำเนินการที่ไม่ได้ลงนามขวา (>>>) เสมอเพื่อห่อบิตอย่าใช้ตัวดำเนินการเลขคณิตขวา (>>)