สวัสดี! แม้แต่เรือที่เร็วที่สุดก็ยังลอยอยู่เหนือคลื่นได้ หากไม่มีเส้นทาง หากคุณกำลังอ่านบทความนี้อยู่ตอนนี้ แสดงว่าคุณมีเป้าหมายอย่างแน่นอน สิ่งสำคัญคืออย่าออกนอกเส้นทาง แต่มุ่งสู่การเป็น Java Developer แทน วันนี้ฉันต้องการทบทวนคำถามสำหรับนักพัฒนา Java ต่อไปเพื่อช่วยเติมเต็มช่องว่างทางทฤษฎีของคุณ สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Developer  ตอนที่ 11 - 1

97. มีกฎใดบ้างที่นำมาใช้เมื่อแทนที่เท่ากับ()?

เมื่อแทนที่เมธอดเท่ากับ () คุณต้องปฏิบัติตามกฎต่อไปนี้:
  • การสะท้อนกลับ — สำหรับค่าใดๆx , x.equals(x)จะต้องคืนค่าจริง เสมอ (โดยที่x != null )

  • สมมาตร — สำหรับค่าใดๆ ที่xและy x.equals(y)จะต้องคืนค่าเป็นจริงก็ต่อเมื่อy.equals(x)ส่งคืนค่าจริงเท่านั้น

  • การผ่านผ่าน — สำหรับค่าใดๆx , yและzถ้าx.equals(y)ส่งคืนtrueและy.equals(z)ส่ง คืน trueด้วยดังนั้นx.equals(z)จะต้องส่งคืนtrue

  • ความสอดคล้อง — สำหรับค่าใดๆxและyการเรียกx.equals(y) ซ้ำ ๆ จะส่งกลับค่าเดียวกันเสมอตราบใดที่ฟิลด์ที่ใช้ในการเปรียบเทียบวัตถุทั้งสองไม่มีการเปลี่ยนแปลงระหว่างการโทรแต่ละครั้ง

  • การเปรียบเทียบค่าว่าง — สำหรับค่าใดๆx การเรียกx.equals(null)จะต้องส่งคืนfalse

98. จะเกิดอะไรขึ้นถ้าคุณไม่แทนที่เท่ากับ() และ hashCode()?

ในกรณีนี้ hashCode() จะส่งคืนตัวเลขที่สร้างขึ้นตามที่อยู่ของเซลล์หน่วยความจำที่จัดเก็บวัตถุนั้น กล่าวอีกนัยหนึ่ง เมื่อ เรียกใช้ เมธอด hashCode() ดั้งเดิม บนอ็อบเจ็กต์สองตัวที่มีฟิลด์เดียวกัน ผลลัพธ์จะแตกต่างออกไป (เนื่องจากพวกมันถูกจัดเก็บไว้ในตำแหน่งหน่วยความจำต่างกัน) วิธีการ เท่ากับ ()ดั้งเดิมจะเปรียบเทียบการอ้างอิง กล่าวคือ เป็นการระบุว่าการอ้างอิงนั้นชี้ไปที่วัตถุเดียวกันหรือไม่ กล่าวอีกนัยหนึ่ง การเปรียบเทียบใช้ ตัวดำเนินการ ==และจะส่งคืนค่าเท็จสำหรับออบเจ็กต์ที่แตกต่างกันเสมอ แม้ว่าฟิลด์จะเหมือนกันก็ตาม trueจะถูกส่งกลับเฉพาะเมื่อเปรียบเทียบการอ้างอิงกับวัตถุเดียวกัน บางครั้งมันก็สมเหตุสมผลที่จะไม่แทนที่วิธีการเหล่านี้ ตัวอย่างเช่น คุณต้องการให้ออบเจ็กต์ทั้งหมดของคลาสใดคลาสหนึ่งไม่ซ้ำกัน การแทนที่วิธีการเหล่านี้อาจทำให้การรับประกันรหัสแฮชเฉพาะที่มีอยู่เสียหายเท่านั้น สิ่งสำคัญคือการเข้าใจความแตกต่างของวิธีการเหล่านี้ ไม่ว่าจะถูกแทนที่หรือไม่ก็ตาม และใช้วิธีการใดก็ตามที่สถานการณ์ต้องการ

99. เหตุใดข้อกำหนดด้านสมมาตรจึงเป็นไปตามข้อกำหนดเฉพาะในกรณีที่ x.equals(y) ส่งคืนค่าจริงเท่านั้น

คำถามนี้ค่อนข้างแปลก ถ้าวัตถุ A เท่ากับวัตถุ B แล้ววัตถุ B ก็เท่ากับวัตถุ A ถ้า B ไม่เท่ากับวัตถุ A แล้วสิ่งที่ตรงกันข้ามจะเป็นไปได้อย่างไร? นี่คือสามัญสำนึก

100. การชนกันของ HashCode คืออะไร? คุณจะจัดการกับมันอย่างไร?

การชนกัน ของ HashCodeเกิดขึ้นเมื่อวัตถุสองชิ้นที่แตกต่างกันมีHashCode เหมือน กัน เป็นไปได้อย่างไร? รหัสแฮชจะถูกแมปกับจำนวนเต็มซึ่งมีช่วงตั้งแต่ -2147483648 ถึง 2147483647 นั่นก็คือ มันสามารถเป็นหนึ่งในจำนวนเต็มที่แตกต่างกันประมาณ 4 พันล้านจำนวน ช่วงนี้มีขนาดใหญ่แต่ไม่สิ้นสุด นั่นหมายความว่ามีสถานการณ์ที่วัตถุสองชิ้นที่แตกต่างกันโดยสิ้นเชิงอาจมีรหัสแฮชเดียวกัน มันไม่น่าเป็นไปได้อย่างมาก แต่ก็เป็นไปได้ ฟังก์ชันแฮชที่ใช้งานไม่ดีสามารถทำให้รหัสแฮชที่เหมือนกันบ่อยขึ้นโดยการส่งคืนตัวเลขในช่วงเล็กๆ ซึ่งจะเป็นการเพิ่มโอกาสที่จะเกิดการชนกัน เพื่อลดการชนกัน คุณต้องมีการนำ เมธอด HashCode ไปใช้อย่างดี ซึ่งจะกระจายค่าอย่างสม่ำเสมอ และลดโอกาสที่จะเกิดค่าซ้ำ

101. จะเกิดอะไรขึ้นหากมูลค่าขององค์ประกอบที่เข้าร่วมในสัญญา hashCode เปลี่ยนแปลง?

หากองค์ประกอบที่เกี่ยวข้องกับการคำนวณรหัสแฮชเปลี่ยนแปลง รหัสแฮชของออบเจ็กต์ก็ควรเปลี่ยน (หากฟังก์ชันแฮชดี) นั่นเป็นเหตุผลที่คุณควรใช้วัตถุที่ไม่เปลี่ยนรูปเป็นคีย์ในHashMapเนื่องจากสถานะภายใน (ฟิลด์) ไม่สามารถเปลี่ยนแปลงได้หลังจากการสร้าง และตามมาด้วยว่ารหัสแฮชของพวกเขาเปลี่ยนไปหลังจากการสร้าง หากคุณใช้ออบเจ็กต์ที่ไม่แน่นอนเป็นคีย์ เมื่อฟิลด์ของออบเจ็กต์เปลี่ยนแปลง รหัสแฮชของมันจะเปลี่ยนไป และคุณอาจสูญเสียคู่คีย์-ค่าที่เกี่ยวข้องในHashMap ท้ายที่สุดแล้ว มันจะถูกจัดเก็บไว้ในที่เก็บข้อมูลที่เกี่ยวข้องกับรหัสแฮชดั้งเดิม แต่หลังจากการเปลี่ยนแปลงออบเจ็กต์ คุณจะค้นหามันในที่เก็บข้อมูลอื่น

102. เขียนเมธอดเท่ากับ() และ hashCode() สำหรับคลาส Student ที่มีฟิลด์ String name และ int age

public class Student {
int age;
String name;

 @Override
 public boolean equals(final Object o) {
   if (this == o) {
     return true;
   }
   if (o == null || this.getClass() != o.getClass()) {
     return false;
   }

   final Student student = (Student) o;

   if (this.age != student.age) {
     return false;
   }
   return this.name != null ? this.name.equals(student.name) : student.name == null;
 }

 @Override
 public int hashCode() {
   int result = this.age;
   result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
   return result;
 }
}
เท่ากับ ():
  • อันดับแรก เราเปรียบเทียบข้อมูลอ้างอิงโดยตรง เพราะถ้าข้อมูลอ้างอิงชี้ไปที่วัตถุเดียวกัน จะมีประโยชน์อะไรในการตรวจสอบความเท่าเทียมกันต่อไป? เรารู้อยู่แล้วว่าผลลัพธ์จะเป็นจริง

  • เราตรวจสอบค่า null และประเภทคลาสเหมือนกันหรือไม่ เนื่องจากหากพารามิเตอร์เป็น null หรือประเภท อื่นออบเจ็กต์จะไม่เท่ากัน และผลลัพธ์จะต้องเป็นfalse

  • เราแปลงพารามิเตอร์เป็นประเภทเดียวกัน (ท้ายที่สุดแล้วหากเป็นออบเจ็กต์ประเภทพาเรนต์)

  • เราเปรียบเทียบฟิลด์ดั้งเดิม (การเปรียบเทียบโดยใช้=!ก็เพียงพอแล้ว) หากไม่เท่ากัน เราจะคืนค่าfalse

  • เราตรวจสอบฟิลด์ที่ไม่ใช่แบบดั้งเดิมเพื่อดูว่าเป็นโมฆะหรือไม่ และใช้ วิธีการ เท่ากับ ( คลาส Stringจะแทนที่วิธีการ ดังนั้นจะดำเนินการเปรียบเทียบอย่างถูกต้อง) หากทั้งสองช่องเป็นค่าว่างหรือเท่ากับคืนค่าเป็น trueเราจะหยุดการตรวจสอบ และเมธอดจะคืนค่าเป็นจริง

hashCode() :
  • เราตั้งค่าเริ่มต้นของรหัสแฮชให้เท่ากับค่าของฟิลด์อายุ ของวัตถุ

  • เราคูณรหัสแฮชปัจจุบันด้วย 31 (เพื่อค่าสเปรดที่มากขึ้น) จากนั้นเพิ่มรหัสแฮชของฟิลด์สตริงที่ไม่ใช่แบบดั้งเดิม (หากไม่เป็นค่าว่าง)

  • เราคืนผลลัพธ์

  • การเอาชนะวิธีการด้วยวิธีนี้หมายความว่าวัตถุที่มีชื่อและ ค่า int เหมือนกัน จะส่งคืนรหัสแฮชเดียวกันเสมอ

103. อะไรคือความแตกต่างระหว่างการใช้ "if (obj instanceof Student)" และ "if (getClass() == obj.getClass())"?

มาดูกันว่าแต่ละนิพจน์ทำอะไรได้บ้าง:
  • instanceofตรวจสอบว่าการอ้างอิงวัตถุทางด้านซ้ายเป็นอินสแตนซ์ประเภททางด้านขวาหรือประเภทย่อยอย่างใดอย่างหนึ่ง

  • "getClass() == ..." ตรวจสอบว่าประเภทเหมือนกันหรือไม่

กล่าวอีกนัยหนึ่งgetClass()ส่งคืนเอกลักษณ์เฉพาะของคลาส แต่instanceofส่งคืนtrueแม้ว่าอ็อบเจ็กต์จะเป็นเพียงชนิดย่อย ซึ่งสามารถทำให้เรามีความยืดหยุ่นมากขึ้นเมื่อใช้ polymorphism ทั้งสองแนวทางมีแนวโน้มที่ดีหากคุณเข้าใจวิธีการทำงานอย่างแม่นยำและนำไปใช้ในตำแหน่งที่ถูกต้อง

104. ให้คำอธิบายสั้น ๆ เกี่ยวกับเมธอด clone()

วิธี การ clone()อยู่ในคลาสObject จุดประสงค์คือเพื่อสร้างและส่งคืนโคลน (สำเนา) ของออบเจ็กต์ปัจจุบัน สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Developer  ตอนที่ 11 - 2หากต้องการใช้วิธีนี้ คุณต้องใช้ อินเทอร์เฟซ Cloneable marker:
Student implements Cloneable
และแทนที่เมธอดclone()เอง:
@Override
protected Object clone() throws CloneNotSupportedException {
 return super.clone();
}
ท้ายที่สุด มันถูกป้องกันใน คลาส Objectกล่าวคือ จะมองเห็นได้เฉพาะใน คลาส Student เท่านั้น และคลาสภายนอกจะไม่สามารถมองเห็นได้

105. คุณต้องคำนึงถึงข้อควรพิจารณาพิเศษใดบ้างเกี่ยวกับเมธอด clone() และตัวแปรอ้างอิงในออบเจ็กต์?

เมื่อโคลนวัตถุ เฉพาะค่าดั้งเดิมและค่าของการอ้างอิงวัตถุเท่านั้นที่จะถูกคัดลอก ซึ่งหมายความว่าหากวัตถุมีช่องที่อ้างอิงถึงวัตถุอื่น เฉพาะการอ้างอิงเท่านั้นที่จะถูกโคลน - วัตถุที่อ้างอิงอื่นนี้จะไม่ถูกโคลน นี่คือสิ่งที่เรียกว่าสำเนาตื้น แล้วถ้าคุณต้องการสำเนาแบบเต็ม โดยที่ทุกอ็อบเจ็กต์ที่ซ้อนกันจะถูกโคลนล่ะ? คุณจะแน่ใจได้อย่างไรว่าสิ่งเหล่านี้ไม่ได้เป็นเพียงสำเนาของการอ้างอิง แต่เป็นสำเนาที่สมบูรณ์ของอ็อบเจ็กต์ที่แตกต่างซึ่งครอบครองที่อยู่หน่วยความจำที่แตกต่างกันในฮีป จริงๆ แล้ว ทุกอย่างค่อนข้างง่าย — สำหรับแต่ละคลาสที่มีการอ้างอิงภายใน คุณจะต้องแทนที่เมธอดclone()และเพิ่ม อินเทอร์เฟซ Cloneable marker เมื่อคุณทำเช่นนี้ การดำเนินการโคลนจะไม่คัดลอกการอ้างอิงไปยังออบเจ็กต์ที่มีอยู่ แต่จะคัดลอกออบเจ็กต์ที่อ้างอิงแทน เนื่องจากตอนนี้มีความสามารถในการคัดลอกด้วยตนเองแล้ว

ข้อยกเว้น

106. อะไรคือความแตกต่างระหว่างข้อผิดพลาดและข้อยกเว้น?

ข้อยกเว้น เช่นเดียวกับข้อผิดพลาด เป็นคลาสย่อยของThrowable อย่างไรก็ตามพวกเขามีความแตกต่างกัน ข้อผิดพลาดบ่งชี้ถึงปัญหาที่เกิดขึ้นส่วนใหญ่เนื่องจากการขาดแคลนทรัพยากรระบบ และแอปพลิเคชันของเราไม่ควรพบปัญหาประเภทนี้ ตัวอย่างของข้อผิดพลาดเหล่านี้ ได้แก่ ระบบล่มและข้อผิดพลาดหน่วยความจำไม่เพียงพอ ข้อผิดพลาดส่วนใหญ่เกิดขึ้นที่รันไทม์ เนื่องจากไม่ได้ทำเครื่องหมายไว้ สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Developer  ตอนที่ 11 - 3ข้อยกเว้นคือปัญหาที่อาจเกิดขึ้นขณะรันไทม์และขณะคอมไพล์ ปัญหาเหล่านี้มักเกิดขึ้นในโค้ดที่เราเขียนในฐานะนักพัฒนา นั่นหมายความว่าข้อยกเว้นเหล่านี้คาดเดาได้ง่ายกว่าและขึ้นอยู่กับเรามากกว่า ในทางตรงกันข้าม ข้อผิดพลาดจะเกิดขึ้นแบบสุ่มมากกว่าและเป็นอิสระจากเรามากกว่า แต่จะขึ้นอยู่กับปัญหาในระบบที่แอปพลิเคชันของเราทำงานอยู่แทน

107. อะไรคือความแตกต่างระหว่างการตรวจสอบ, ไม่ได้ตรวจสอบ, ข้อยกเว้น, การโยนและการโยน?

อย่างที่ผมได้กล่าวไว้ก่อนหน้านี้ข้อยกเว้นคือข้อผิดพลาดรันไทม์หรือเวลาคอมไพล์ที่เกิดขึ้นในโค้ดที่เขียนโดยนักพัฒนา (เนื่องจากสถานการณ์ที่ผิดปกติบางอย่าง) การตรวจสอบคือสิ่งที่เราเรียกว่าข้อยกเว้นที่วิธีการจะต้องจัดการโดยใช้ กลไก try-catch เสมอหรือสร้างใหม่เป็นวิธีการเรียก คำ สำคัญ พ่นใช้ในส่วนหัวของวิธีการเพื่อระบุข้อยกเว้นที่วิธีการอาจโยน กล่าวอีกนัยหนึ่ง มันทำให้เรามีกลไกในการส่งข้อยกเว้นไปยังวิธีการโทร ไม่จำเป็นต้องจัดการข้อยกเว้นที่ไม่ได้ ตรวจสอบ มีแนวโน้มที่จะคาดเดาได้น้อยกว่าและมีโอกาสน้อย ที่กล่าวว่าคุณสามารถจัดการได้ถ้าคุณต้องการ เราใช้การโยนเมื่อทำการโยนข้อยกเว้นด้วยตนเอง ตัวอย่างเช่น:
throw new Exception();

108. ลำดับชั้นข้อยกเว้นคืออะไร?

ลำดับชั้นของข้อยกเว้นนั้นกว้างขวางมาก มีมากเกินไปที่จะอธิบายได้อย่างเพียงพอที่นี่ ดังนั้น เราจะพิจารณาเฉพาะสาขาหลักแทน: สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Developer  ตอนที่ 11 - 4 ที่นี่ ที่ด้านบนสุดของลำดับชั้น เราจะเห็น คลาส Throwableซึ่งเป็นบรรพบุรุษทั่วไปของลำดับชั้นข้อยกเว้น และแบ่งออกเป็น:
  • ข้อผิดพลาด — ปัญหาร้ายแรงและไม่ถูกตรวจสอบ
  • ข้อยกเว้น — ข้อยกเว้นที่สามารถตรวจสอบได้
ข้อยกเว้นแบ่งออกเป็นข้อยกเว้นรันไทม์ที่ไม่ได้ตรวจสอบต่างๆ และข้อยกเว้นที่ได้รับการตรวจสอบต่างๆ

109. อะไรคือข้อยกเว้นที่ถูกตรวจสอบและไม่ถูกตรวจสอบ?

อย่างที่ฉันพูดไปก่อนหน้านี้:
  • ข้อยกเว้น ที่ตรวจสอบแล้วคือข้อยกเว้นที่คุณต้องจัดการด้วยวิธีใดวิธีหนึ่ง นั่นคือคุณต้องจัดการพวกมันใน บล็อก try-catchหรือโยนมันไปที่วิธีการด้านบน เมื่อต้องการทำเช่นนี้ หลังจากแสดงรายการอาร์กิวเมนต์ของวิธีการในลายเซ็นของวิธีการแล้ว ให้ใช้การส่ง <ประเภทข้อยกเว้น>เพื่อระบุว่าวิธีการดังกล่าวสามารถส่งข้อยกเว้นนั้นได้ นี่เป็นเหมือนคำเตือน โดยกำหนดให้วิธีการโทรแจ้งให้ทราบว่าจะต้องรับผิดชอบในการจัดการข้อยกเว้นนั้น

  • ไม่จำเป็นต้องจัดการข้อยกเว้นที่ไม่ได้ ตรวจสอบ เนื่องจากไม่ได้ตรวจสอบ ณ เวลาคอมไพล์ และมักจะคาดเดาไม่ได้มากกว่า ความแตกต่างหลักกับข้อยกเว้นที่ตรวจสอบแล้วคือการจัดการโดยใช้ บล็อก try-catchหรือโดยการโยนใหม่เป็นทางเลือกมากกว่าการบังคับ

101. เขียนตัวอย่างที่คุณใช้ try-catch block เพื่อตรวจจับและจัดการกับข้อยกเว้น

try{                                                 // Start of the try-catch block
 throw new Exception();                             // Manually throw an exception
} catch (Exception e) {                              // Exceptions of this type and its subtypes will be caught
 System.out.println("Oops! Something went wrong =("); // Display the exception
}

102. เขียนตัวอย่างที่คุณจับและจัดการกับข้อยกเว้นที่คุณกำหนดเอง

ขั้นแรก เรามาเขียนคลาสยกเว้นของเราเองที่สืบทอดExceptionและแทนที่ตัวสร้างที่รับข้อความแสดงข้อผิดพลาดเป็นอาร์กิวเมนต์:
public class CustomException extends Exception {

 public CustomException(final String message) {
   super(message);
 }
}
ต่อไป เราจะโยนมันด้วยตัวเองและจับมันเหมือนกับที่เราทำในตัวอย่างสำหรับคำถามก่อนหน้า:
try{
 throw new CustomException("Oops! Something went wrong =(");
} catch (CustomException e) {
 System.out.println(e.getMessage());
}
อีกครั้งหนึ่งเมื่อเรารันโค้ด เราจะได้ผลลัพธ์ดังต่อไปนี้:
อ๊ะ! มีบางอย่างผิดพลาด =(
สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Developer  ตอนที่ 11 - 5นั่นคือทั้งหมดสำหรับวันนี้! เจอกันใหม่ตอนหน้าครับ!
อ่านเพิ่มเติม: