จาวาคอร์
9. อะไรคือความแตกต่างระหว่างการเชื่อมโยงแบบคงที่และไดนามิกใน Java?
ฉันได้ตอบคำถามนี้แล้วในบทความนี้ในคำถามที่ 18 เกี่ยวกับความหลากหลายแบบคงที่และไดนามิก ฉันแนะนำให้คุณอ่าน10. เป็นไปได้ไหมที่จะใช้ตัวแปรส่วนตัวหรือตัวแปรที่ได้รับการป้องกันในอินเทอร์เฟซ?
ไม่คุณไม่สามารถ. เนื่องจากเมื่อคุณประกาศอินเทอร์เฟซ คอมไพเลอร์ Java จะเพิ่ม คีย์เวิร์ดสาธารณะและนามธรรม ก่อนวิธีอินเท อร์เฟซและคีย์เวิร์ดสาธารณะคงที่และสุดท้ายก่อนสมาชิกข้อมูลโดย อัตโนมัติ ที่จริงแล้ว หากคุณเพิ่มprivateหรือprotectedจะเกิดข้อขัดแย้งขึ้น และคอมไพลเลอร์จะบ่นเกี่ยวกับตัวแก้ไขการเข้าถึงพร้อมข้อความ: “ตัวแก้ไข '<selected modifier>' ไม่ได้รับอนุญาตที่นี่” เหตุใดคอมไพเลอร์จึงเพิ่มpublic , staticและFinalตัวแปรในอินเทอร์เฟซ? ลองคิดดู:- สาธารณะ - อินเทอร์เฟซอนุญาตให้ไคลเอนต์โต้ตอบกับวัตถุ หากตัวแปรไม่เป็นแบบสาธารณะ ลูกค้าจะไม่สามารถเข้าถึงได้
- คงที่ - ไม่สามารถสร้างอินเทอร์เฟซได้ (หรือค่อนข้างเป็นวัตถุ) ดังนั้นตัวแปรจึงเป็นแบบคงที่
- สุดท้าย - เนื่องจากอินเทอร์เฟซถูกใช้เพื่อให้ได้นามธรรม 100% ตัวแปรจึงมีรูปแบบสุดท้าย (และจะไม่มีการเปลี่ยนแปลง)
11. Classloader คืออะไรและใช้ทำอะไร?
Classloader - หรือ Class Loader - ให้การโหลดคลาส Java แม่นยำยิ่งขึ้นการโหลดนั้นรับประกันโดยผู้สืบทอด - ตัวโหลดคลาสเฉพาะเพราะ ClassLoaderนั้นเป็นนามธรรม ทุกครั้งที่โหลดไฟล์ .class ตัวอย่างเช่น หลังจากเรียก Constructor หรือเมธอดสแตติกของคลาสที่เกี่ยวข้อง การดำเนินการนี้จะดำเนินการโดยหนึ่งในลูก หลานของคลาส ClassLoader ทายาทมีสามประเภท:-
Bootstrap ClassLoaderเป็นตัวโหลดพื้นฐานที่ใช้งานในระดับ JVM และไม่มีการตอบรับจากสภาพแวดล้อมรันไทม์ เนื่องจากเป็นส่วนหนึ่งของเคอร์เนล JVM และเขียนด้วยโค้ดเนทีฟ ตัวโหลดนี้ทำหน้าที่เป็นพาเรนต์ของอินสแตนซ์ ClassLoader อื่นๆ ทั้งหมด
รับผิดชอบหลักในการโหลดคลาสภายใน JDK ซึ่งมักจะเป็นrt.jarและไลบรารีหลักอื่นๆ ที่อยู่ใน ไดเร็กทอรี $JAVA_HOME/jre/ lib แพลตฟอร์มที่แตกต่างกันอาจมีการใช้งานที่แตกต่างกันของตัวโหลดคลาสนี้ -
Extension Classloaderคือตัวโหลดส่วนขยาย ซึ่งเป็นตัวสืบทอดของคลาสตัวโหลดพื้นฐาน ดูแลการโหลดส่วนขยายของคลาสพื้นฐาน Java มาตรฐาน โหลดจากไดเร็กทอรีส่วนขยาย JDK โดยทั่วไปคือ$JAVA_HOME/lib/extหรือไดเร็กทอรีอื่นๆ ที่กล่าวถึงในคุณสมบัติระบบ java.ext.dirs (ตัวเลือกนี้สามารถใช้เพื่อควบคุมการโหลดส่วนขยาย)
-
System ClassLoaderคือตัวโหลดระบบที่ใช้งานในระดับ JRE ซึ่งดูแลการโหลดคลาสระดับแอปพลิเคชันทั้งหมดลงใน JVM โหลดไฟล์ที่พบในตัวแปรสภาพแวดล้อมคลาส-classpathหรือตัวเลือกบรรทัดคำสั่ง-cp

-
System Classloader พยายามค้นหาคลาสในแคช
-
1.1. หากพบคลาส แสดงว่าการโหลดเสร็จสมบูรณ์
-
1.2. หากไม่พบคลาส การโหลดจะถูกมอบหมายให้กับ Extension Classloader
-
-
Extension Classloader พยายามค้นหาคลาสในแคชของตัวเอง
-
2.1. หากพบคลาสก็จะเสร็จสมบูรณ์
-
2.2. หากไม่พบคลาส การโหลดจะถูกมอบหมายให้กับ Bootstrap Classloader
-
-
Bootstrap Classloader พยายามค้นหาคลาสในแคชของตัวเอง
-
3.1. หากพบคลาส แสดงว่าการโหลดเสร็จสมบูรณ์
-
3.2. หากไม่พบคลาส Bootstrap Classloader พื้นฐานจะพยายามโหลดคลาสนั้น
-
-
หากกำลังโหลด:
-
4.1. สำเร็จ - การโหลดชั้นเรียนเสร็จสมบูรณ์
-
4.2. หากล้มเหลว การควบคุมจะถูกโอนไปยัง Extension Classloader
-
-
5. Extension Classloader พยายามโหลดคลาส และหากกำลังโหลด:
-
5.1. สำเร็จ - การโหลดชั้นเรียนเสร็จสมบูรณ์
-
5.2. หากล้มเหลว การควบคุมจะถูกโอนไปยัง System Classloader
-
-
6. System Classloader พยายามโหลดคลาส และหากกำลังโหลด:
-
6.1. สำเร็จ - การโหลดชั้นเรียนเสร็จสมบูรณ์
-
6.2. ไม่ผ่านสำเร็จ - มีการสร้างข้อยกเว้น - ClassNotFoundException
-
12. พื้นที่ข้อมูลรันไทม์คืออะไร?
Ares ข้อมูลรันไทม์ - พื้นที่ข้อมูลรันไทม์ JVM JVM กำหนดพื้นที่ข้อมูลรันไทม์บางส่วนที่จำเป็นระหว่างการทำงานของโปรแกรม บางส่วนถูกสร้างขึ้นเมื่อ JVM เริ่มทำงาน อื่นๆ เป็นเธรดภายในเครื่องและถูกสร้างขึ้นเมื่อมีการสร้างเธรดเท่านั้น (และถูกทำลายเมื่อเธรดถูกทำลาย) พื้นที่ข้อมูลรันไทม์ JVM มีลักษณะดังนี้:
-
PC Register อยู่ในเครื่องของแต่ละเธรดและมีที่อยู่ของคำสั่ง JVM ที่เธรดกำลังดำเนินการอยู่ในปัจจุบัน
-
JVM Stack คือพื้นที่หน่วยความจำที่ใช้เป็นที่จัดเก็บสำหรับตัวแปรภายในเครื่องและผลลัพธ์ชั่วคราว แต่ละเธรดมีสแต็กแยกเป็นของตัวเอง: ทันทีที่เธรดยุติ สแต็กนี้จะถูกทำลายไปด้วย เป็นที่น่าสังเกตว่าข้อดีของสแต็กเหนือฮีปคือประสิทธิภาพ ในขณะที่ฮีปมีข้อได้เปรียบในระดับการจัดเก็บข้อมูลอย่างแน่นอน
-
Native Method Stack - พื้นที่ข้อมูลต่อเธรดที่เก็บองค์ประกอบข้อมูล คล้ายกับสแต็ก JVM สำหรับการเรียกใช้เมธอดเนทิฟ (ไม่ใช่ Java)
-
ฮีป - ถูกใช้โดยเธรดทั้งหมดเป็นร้านค้าที่มีอ็อบเจ็กต์ ข้อมูลเมตาของคลาส อาร์เรย์ ฯลฯ ซึ่งสร้างขึ้นขณะรันไทม์ พื้นที่นี้ถูกสร้างขึ้นเมื่อ JVM เริ่มทำงานและถูกทำลายเมื่อปิดระบบ
-
พื้นที่วิธีการ - พื้นที่รันไทม์นี้ใช้ร่วมกับเธรดทั้งหมด และถูกสร้างขึ้นเมื่อ JVM เริ่มทำงาน มันจัดเก็บโครงสร้างสำหรับแต่ละคลาส เช่น Runtime Constant Pool, โค้ดสำหรับตัวสร้างและวิธีการ, ข้อมูลวิธีการ ฯลฯ
13. วัตถุที่ไม่เปลี่ยนรูปคืออะไร?
ในส่วนนี้ของบทความในคำถามที่ 14 และ 15 มีคำตอบสำหรับคำถามนี้อยู่แล้ว ดังนั้นลองดูโดยไม่เสียเวลา14. คลาส String มีความพิเศษอย่างไร?
ในช่วงต้นของการวิเคราะห์ เราได้พูดคุยซ้ำแล้วซ้ำเล่าเกี่ยวกับคุณสมบัติบางอย่างของ String (มีส่วนแยกต่างหากสำหรับเรื่องนี้) ตอนนี้เรามาสรุปคุณสมบัติของString :-
เป็นวัตถุที่ได้รับความนิยมมากที่สุดใน Java และใช้เพื่อวัตถุประสงค์ที่หลากหลาย ในแง่ของความถี่ในการใช้งานก็ไม่น้อยหน้าแม้แต่กับประเภทดั้งเดิม
-
วัตถุของคลาสนี้สามารถสร้างได้โดยไม่ต้องใช้คีย์เวิร์ดใหม่ - โดยตรงผ่านเครื่องหมายคำพูดString str = “string”; .
-
สตริงเป็น คลาส ที่ไม่เปลี่ยนรูป : เมื่อสร้างอ็อบเจ็กต์ของคลาสนี้ ข้อมูลจะไม่สามารถเปลี่ยนแปลงได้ (เมื่อคุณเพิ่ม + “สตริงอื่น” ให้กับสตริงบางตัว ดังนั้นคุณจะได้รับสตริงใหม่ที่สาม) ความไม่เปลี่ยนรูปของคลาส String ทำให้เธรดปลอดภัย
-
คลาสStringได้รับการสรุป (มี ตัวแก้ไข ขั้นสุดท้าย ) ดังนั้นจึงไม่สามารถสืบทอดได้
-
สตริงมีพูลสตริงของตัวเองซึ่งเป็นพื้นที่หน่วยความจำในฮีปที่แคชค่าสตริงที่สร้างขึ้น ในส่วนนี้ของซีรีส์ในคำถามที่ 62 ฉันอธิบายพูลสตริง
-
Java มีแอนะล็อกของ Stringซึ่งออกแบบมาเพื่อทำงานกับสตริง - StringBuilderและStringBufferแต่มีความแตกต่างที่ไม่แน่นอน คุณสามารถอ่านเพิ่มเติมเกี่ยวกับพวกเขาได้ในบทความนี้

15. ความแปรปรวนร่วมประเภทคืออะไร?
เพื่อทำความเข้าใจความแปรปรวนร่วม เราจะดูตัวอย่าง สมมติว่าเรามีคลาสสัตว์:public class Animal {
void voice() {
System.out.println("*тишина*");
}
}
และ คลาส Dog บางคลาสก็ขยายออกไป :
public class Dog extends Animal {
@Override
public void voice() {
System.out.println("Гав, гав, гав!!!");
}
}
ดังที่เราจำได้ เราสามารถกำหนดออบเจ็กต์ประเภททายาทให้กับประเภทพาเรนต์ได้อย่างง่ายดาย:
Animal animal = new Dog();
นี่จะไม่มีอะไรมากไปกว่าความหลากหลาย สะดวก คล่องตัวใช่หรือไม่? แล้วรายชื่อสัตว์ล่ะ? เราสามารถให้รายการที่มีวัตถุDog ร่วมกับ Animal ทั่วไปได้ หรือไม่
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs;
ในกรณีนี้ เส้นสำหรับกำหนดรายชื่อสุนัขให้กับรายชื่อสัตว์จะถูกขีดเส้นใต้ด้วยสีแดง เช่น คอมไพเลอร์จะไม่ส่งรหัสนี้ แม้ว่าที่จริงแล้วการมอบหมายนี้ดูเหมือนจะค่อนข้างสมเหตุสมผล (ท้ายที่สุดแล้วเราสามารถกำหนด วัตถุ Dog ให้กับตัวแปรประเภท Animalได้) แต่ก็ไม่สามารถทำได้ เนื่องจากหากได้รับอนุญาตเราจะสามารถใส่ วัตถุ Animal ลงในรายการที่เดิมตั้งใจให้เป็น สุนัข ได้ ในขณะ ที่คิดว่าเรามีเฉพาะสุนัข อยู่ในรายการ จากนั้น ยกตัวอย่าง เราจะใช้ เมธอด get() เพื่อนำ อ็อบเจ็กต์จาก รายการ Dogsโดยคิดว่าเป็นสุนัข และเรียกเมธอดบางอย่างของ อ็อบเจ็กต์ Dogซึ่งAnimalไม่มี และตามที่คุณเข้าใจนี่เป็นไปไม่ได้ - ข้อผิดพลาดจะเกิดขึ้น แต่โชคดีที่คอมไพเลอร์ไม่พลาดข้อผิดพลาดเชิงตรรกะนี้ด้วยการกำหนดรายชื่อผู้สืบทอดให้กับรายชื่อผู้ปกครอง (และในทางกลับกัน) ใน Java คุณสามารถกำหนดวัตถุรายการให้กับรายการตัวแปรที่มีข้อมูลทั่วไปที่ตรงกันเท่านั้น สิ่งนี้เรียกว่าการแปรปรวน หากพวกเขาสามารถทำเช่นนี้ได้ มันจะถูกเรียกและเรียกว่าความแปรปรวนร่วม นั่นคือความแปรปรวนร่วมคือถ้าเราสามารถตั้งค่าวัตถุประเภทArrayList<Dog> ให้เป็นตัวแปร ประเภทList<Animal> ปรากฎว่า Java ไม่รองรับความแปรปรวนร่วมใช่ไหม ไม่ว่ายังไงก็ตาม! แต่สิ่งนี้ทำในลักษณะพิเศษของมันเอง การออกแบบใช้ทำอะไร? ขยายสัตว์ มันถูกวางไว้พร้อมกับตัวแปรทั่วไปที่เราต้องการตั้งค่าออบเจ็กต์รายการ โดยมีตัวแปรทั่วไปของการสืบทอด โครงสร้างทั่วไปนี้หมายความว่าประเภทใดๆ ที่สืบทอดมาจากประเภทAnimal จะทำ (และประเภทAnimalก็ตกอยู่ภายใต้ลักษณะทั่วไปนี้ด้วย) ในทางกลับกันAnimalไม่เพียงแต่เป็นคลาสเท่านั้น แต่ยังเป็นอินเทอร์เฟซด้วย (อย่าหลงกลด้วย คีย์เวิร์ด ขยาย ) เราสามารถทำงานที่ได้รับมอบหมายก่อนหน้านี้ได้ดังนี้: 
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
ด้วยเหตุนี้คุณจะเห็นใน IDE ว่าคอมไพเลอร์จะไม่บ่นเกี่ยวกับโครงสร้างนี้ เรามาตรวจสอบฟังก์ชันการทำงานของการออกแบบนี้กันดีกว่า สมมติว่าเรามีวิธีการที่ให้สัตว์ทุกตัวส่งเสียงเข้ามา:
public static void animalsVoice(List<? extends Animal> animals) {
for (Animal animal : animals) {
animal.voice();
}
}
ให้รายชื่อสุนัขแก่เขา:
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
animalsVoice(dogs);
ในคอนโซลเราจะเห็นผลลัพธ์ต่อไปนี้:
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
animals.add(new Dog());
dogs.add(new Animal());
จริงๆ แล้ว ในสองบรรทัดสุดท้าย คอมไพเลอร์จะเน้นการแทรกวัตถุด้วยสีแดง นี่เป็นเพราะความจริงที่ว่าเราไม่สามารถแน่ใจได้ร้อยเปอร์เซ็นต์ว่ารายการของออบเจ็กต์ประเภทใดที่จะถูกกำหนดให้กับรายการด้วยข้อมูลโดย<? ขยายสัตว์> . 
List<Animal> animals = new ArrayList<>();
List<? super Dog> dogs = animals;
dogs.add(new Dog());
dogs.add(new Dog());
เราสามารถเพิ่มอ็อบเจ็กต์ประเภทDog ลงในรายการได้อย่างปลอดภัยด้วยคำสั่งทั่วไป เพราะไม่ว่าในกรณีใดก็ตาม มันมีวิธีการนำไปใช้ทั้งหมดของบรรพบุรุษของมัน แต่เราจะไม่สามารถเพิ่มวัตถุประเภทAnimalได้ เนื่องจากไม่มีความแน่นอนว่าจะมีวัตถุประเภทนี้อยู่ข้างในและไม่ใช่ ตัวอย่างเช่นDog ท้ายที่สุดแล้ว เราสามารถขอวิธีการของคลาส Dogจากองค์ประกอบของรายการนี้ได้ ซึ่ง Animalจะไม่มี ในกรณีนี้จะเกิดข้อผิดพลาดในการคอมไพล์ นอกจากนี้ หากเราต้องการใช้วิธีก่อนหน้านี้ แต่ด้วยวิธีทั่วไป:
public static void animalsVoice(List<? super Dog> dogs) {
for (Dog dog : dogs) {
dog.voice();
}
}
เราจะได้รับข้อผิดพลาดในการคอมไพล์ในfor loop เนื่องจากเราไม่สามารถแน่ใจได้ว่ารายการที่ส่งคืนนั้นมีอ็อบเจ็กต์ประเภทDogและมีอิสระที่จะใช้วิธีการของมัน หากเราเรียกเมธอด Dogs.get(0)ในรายการนี้ - เราจะได้วัตถุประเภทObject นั่นคือ เพื่อให้ เมธอด AnimalsVoice() ทำงาน อย่างน้อยที่สุดเราจำเป็นต้องเพิ่มการปรับแต่งเล็กๆ น้อยๆ โดยจำกัดข้อมูลประเภทให้แคบลง:
public static void animalsVoice(List<? super Dog> dogs) {
for (Object obj : dogs) {
if (obj instanceof Dog) {
Dog dog = (Dog) obj;
dog.voice();
}
}
}

16. Object class มีวิธีการอย่างไรบ้าง?
ในส่วนนี้ของซีรีส์นี้ ในย่อหน้าที่ 11 ฉันได้ตอบคำถามนี้แล้ว ดังนั้นฉันขอแนะนำให้คุณอ่านหากคุณยังไม่ได้ตอบ นั่นคือจุดที่เราจะสิ้นสุดในวันนี้ พบกันใหม่ในตอนต่อไป!
GO TO FULL VERSION