
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เราจะหยุดการตรวจสอบ และเมธอดจะคืนค่าเป็นจริง
-
เราตั้งค่าเริ่มต้นของรหัสแฮชให้เท่ากับค่าของฟิลด์อายุ ของวัตถุ
-
เราคูณรหัสแฮชปัจจุบันด้วย 31 (เพื่อค่าสเปรดที่มากขึ้น) จากนั้นเพิ่มรหัสแฮชของฟิลด์สตริงที่ไม่ใช่แบบดั้งเดิม (หากไม่เป็นค่าว่าง)
-
เราคืนผลลัพธ์
-
การเอาชนะวิธีการด้วยวิธีนี้หมายความว่าวัตถุที่มีชื่อและ ค่า int เหมือนกัน จะส่งคืนรหัสแฮชเดียวกันเสมอ
103. อะไรคือความแตกต่างระหว่างการใช้ "if (obj instanceof Student)" และ "if (getClass() == obj.getClass())"?
มาดูกันว่าแต่ละนิพจน์ทำอะไรได้บ้าง:-
instanceofตรวจสอบว่าการอ้างอิงวัตถุทางด้านซ้ายเป็นอินสแตนซ์ประเภททางด้านขวาหรือประเภทย่อยอย่างใดอย่างหนึ่ง
-
"getClass() == ..." ตรวจสอบว่าประเภทเหมือนกันหรือไม่
104. ให้คำอธิบายสั้น ๆ เกี่ยวกับเมธอด clone()
วิธี การ clone()อยู่ในคลาสObject จุดประสงค์คือเพื่อสร้างและส่งคืนโคลน (สำเนา) ของออบเจ็กต์ปัจจุบัน
Student implements Cloneable
และแทนที่เมธอดclone()เอง:
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
ท้ายที่สุด มันถูกป้องกันใน คลาส Objectกล่าวคือ จะมองเห็นได้เฉพาะใน คลาส Student เท่านั้น และคลาสภายนอกจะไม่สามารถมองเห็นได้
105. คุณต้องคำนึงถึงข้อควรพิจารณาพิเศษใดบ้างเกี่ยวกับเมธอด clone() และตัวแปรอ้างอิงในออบเจ็กต์?
เมื่อโคลนวัตถุ เฉพาะค่าดั้งเดิมและค่าของการอ้างอิงวัตถุเท่านั้นที่จะถูกคัดลอก ซึ่งหมายความว่าหากวัตถุมีช่องที่อ้างอิงถึงวัตถุอื่น เฉพาะการอ้างอิงเท่านั้นที่จะถูกโคลน - วัตถุที่อ้างอิงอื่นนี้จะไม่ถูกโคลน นี่คือสิ่งที่เรียกว่าสำเนาตื้น แล้วถ้าคุณต้องการสำเนาแบบเต็ม โดยที่ทุกอ็อบเจ็กต์ที่ซ้อนกันจะถูกโคลนล่ะ? คุณจะแน่ใจได้อย่างไรว่าสิ่งเหล่านี้ไม่ได้เป็นเพียงสำเนาของการอ้างอิง แต่เป็นสำเนาที่สมบูรณ์ของอ็อบเจ็กต์ที่แตกต่างซึ่งครอบครองที่อยู่หน่วยความจำที่แตกต่างกันในฮีป จริงๆ แล้ว ทุกอย่างค่อนข้างง่าย — สำหรับแต่ละคลาสที่มีการอ้างอิงภายใน คุณจะต้องแทนที่เมธอดclone()และเพิ่ม อินเทอร์เฟซ Cloneable marker เมื่อคุณทำเช่นนี้ การดำเนินการโคลนจะไม่คัดลอกการอ้างอิงไปยังออบเจ็กต์ที่มีอยู่ แต่จะคัดลอกออบเจ็กต์ที่อ้างอิงแทน เนื่องจากตอนนี้มีความสามารถในการคัดลอกด้วยตนเองแล้วข้อยกเว้น
106. อะไรคือความแตกต่างระหว่างข้อผิดพลาดและข้อยกเว้น?
ข้อยกเว้น เช่นเดียวกับข้อผิดพลาด เป็นคลาสย่อยของThrowable อย่างไรก็ตามพวกเขามีความแตกต่างกัน ข้อผิดพลาดบ่งชี้ถึงปัญหาที่เกิดขึ้นส่วนใหญ่เนื่องจากการขาดแคลนทรัพยากรระบบ และแอปพลิเคชันของเราไม่ควรพบปัญหาประเภทนี้ ตัวอย่างของข้อผิดพลาดเหล่านี้ ได้แก่ ระบบล่มและข้อผิดพลาดหน่วยความจำไม่เพียงพอ ข้อผิดพลาดส่วนใหญ่เกิดขึ้นที่รันไทม์ เนื่องจากไม่ได้ทำเครื่องหมายไว้
107. อะไรคือความแตกต่างระหว่างการตรวจสอบ, ไม่ได้ตรวจสอบ, ข้อยกเว้น, การโยนและการโยน?
อย่างที่ผมได้กล่าวไว้ก่อนหน้านี้ข้อยกเว้นคือข้อผิดพลาดรันไทม์หรือเวลาคอมไพล์ที่เกิดขึ้นในโค้ดที่เขียนโดยนักพัฒนา (เนื่องจากสถานการณ์ที่ผิดปกติบางอย่าง) การตรวจสอบคือสิ่งที่เราเรียกว่าข้อยกเว้นที่วิธีการจะต้องจัดการโดยใช้ กลไก try-catch เสมอหรือสร้างใหม่เป็นวิธีการเรียก คำ สำคัญ พ่นใช้ในส่วนหัวของวิธีการเพื่อระบุข้อยกเว้นที่วิธีการอาจโยน กล่าวอีกนัยหนึ่ง มันทำให้เรามีกลไกในการส่งข้อยกเว้นไปยังวิธีการโทร ไม่จำเป็นต้องจัดการข้อยกเว้นที่ไม่ได้ ตรวจสอบ มีแนวโน้มที่จะคาดเดาได้น้อยกว่าและมีโอกาสน้อย ที่กล่าวว่าคุณสามารถจัดการได้ถ้าคุณต้องการ เราใช้การโยนเมื่อทำการโยนข้อยกเว้นด้วยตนเอง ตัวอย่างเช่น:throw new Exception();
108. ลำดับชั้นข้อยกเว้นคืออะไร?
ลำดับชั้นของข้อยกเว้นนั้นกว้างขวางมาก มีมากเกินไปที่จะอธิบายได้อย่างเพียงพอที่นี่ ดังนั้น เราจะพิจารณาเฉพาะสาขาหลักแทน:
- ข้อผิดพลาด — ปัญหาร้ายแรงและไม่ถูกตรวจสอบ
- ข้อยกเว้น — ข้อยกเว้นที่สามารถตรวจสอบได้
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());
}
อีกครั้งหนึ่งเมื่อเรารันโค้ด เราจะได้ผลลัพธ์ดังต่อไปนี้:

GO TO FULL VERSION