ตัวโหลดคลาส
มันถูกใช้เพื่อจัดหารหัสไบต์ที่คอมไพล์ให้กับ JVM ซึ่งโดยปกติจะถูกเก็บไว้ในไฟล์ที่มีนามสกุล.class
แต่ยังสามารถรับได้จากแหล่งอื่น เช่น ดาวน์โหลดผ่านเครือข่ายหรือสร้างโดยแอปพลิเคชันเอง ตามข้อกำหนดของ Java SE เพื่อให้ได้โค้ดที่รันใน JVM คุณต้องดำเนินการสามขั้นตอนให้เสร็จสิ้น:
-
กำลังโหลด bytecode จากทรัพยากรและสร้างอินสแตนซ์ของคลาส
Class
ซึ่งรวมถึงการค้นหาคลาสที่ร้องขอจากคลาสที่โหลดก่อนหน้านี้ การได้รับ bytecode สำหรับการโหลดและตรวจสอบความถูกต้อง การสร้างอินสแตนซ์ของคลาส
Class
(สำหรับการทำงานกับคลาสในขณะรันไทม์) และการโหลดคลาสพาเรนต์ หากไม่ได้โหลดคลาสพาเรนต์และอินเทอร์เฟซ จะถือว่าคลาสดังกล่าวไม่ได้โหลด -
การผูก (หรือการเชื่อมโยง)
ตามข้อกำหนดขั้นตอนนี้แบ่งออกเป็นสามขั้นตอนเพิ่มเติม:
- การตรวจสอบความถูกต้องของรหัสไบต์ที่ได้รับจะถูกตรวจสอบ
- การเตรียมการ จัดสรร RAM สำหรับฟิลด์คงที่และกำหนดค่าเริ่มต้นด้วยค่าเริ่มต้น (ในกรณีนี้ การกำหนดค่าเริ่มต้นที่ชัดเจน (ถ้ามี) เกิดขึ้นแล้วในขั้นตอนการเริ่มต้น)
- ความละเอียดความละเอียดของลิงก์สัญลักษณ์ประเภท ฟิลด์ และวิธีการ
-
การเริ่มต้นวัตถุที่ได้รับ
ที่นี่ต่างจากย่อหน้าก่อนๆ ทุกอย่างดูชัดเจนว่าจะเกิดอะไรขึ้น แน่นอนว่าเป็นเรื่องน่าสนใจที่จะเข้าใจว่าสิ่งนี้เกิดขึ้นได้อย่างไร
- คลาสจะต้องโหลดเต็มก่อนที่จะลิงก์
- คลาสจะต้องได้รับการทดสอบอย่างเต็มที่และเตรียมพร้อมก่อนที่จะเริ่มต้นได้
- ข้อผิดพลาดในการแก้ไขลิงก์เกิดขึ้นระหว่างการทำงานของโปรแกรม แม้ว่าจะถูกตรวจพบในขั้นตอนการลิงก์ก็ตาม
ประเภทของ Java Loader
มีตัวโหลดมาตรฐานสามตัวใน Java ซึ่งแต่ละตัวโหลดคลาสจากตำแหน่งเฉพาะ:-
Bootstrapเป็นตัวโหลดพื้นฐานหรือที่เรียกว่า Primordial ClassLoader
โหลดคลาส JDK มาตรฐานจากไฟล์เก็บถาวร rt.jar
-
Extension ClassLoader – ตัวโหลดส่วนขยาย
โหลดคลาสส่วนขยายซึ่งอยู่ในไดเร็กทอรี jre/lib/ext ตามค่าเริ่มต้น แต่สามารถตั้งค่าได้โดยคุณสมบัติระบบ java.ext.dirs
-
System ClassLoader – ตัวโหลดระบบ
โหลดคลาสแอปพลิเคชันที่กำหนดในตัวแปรสภาพแวดล้อม CLASSPATH
คลาสนามธรรม ClassLoader
ตัวโหลดแต่ละตัว ยกเว้นตัวฐาน จะสืบทอดมาจากคลาสjava.lang.ClassLoader
abstract ตัวอย่างเช่น การใช้งานตัวโหลดส่วนขยายคือคลาสsun.misc.Launcher$ExtClassLoader
และตัวโหลดระบบsun.misc.Launcher$AppClassLoader
คือ ตัวโหลดพื้นฐานเป็นแบบเนทิฟและการนำไปใช้งานจะรวมอยู่ใน JVM คลาสใดก็ตามที่ขยายออกไปjava.lang.ClassLoader
สามารถจัดเตรียมวิธีการโหลดคลาสด้วยแบล็คแจ็คและคลาสเดียวกันนี้ได้ ในการทำเช่นนี้จำเป็นต้องกำหนดวิธีการที่เกี่ยวข้องใหม่ซึ่งในขณะนี้ฉันสามารถพิจารณาได้เพียงผิวเผินเท่านั้นเพราะ ฉันไม่เข้าใจปัญหานี้โดยละเอียด พวกเขาอยู่ที่นี่:
package java.lang;
public abstract class ClassLoader {
public Class<?> loadClass(String name);
protected Class<?> loadClass(String name, boolean resolve);
protected final Class<?> findLoadedClass(String name);
public final ClassLoader getParent();
protected Class<?> findClass(String name);
protected final void resolveClass(Class<?> c);
}
loadClass(String name)
หนึ่งในวิธีการสาธารณะไม่กี่วิธีซึ่งเป็นจุดเริ่มต้นสำหรับการโหลดคลาส การใช้งานมีจุดประสงค์เพื่อเรียกวิธีการที่ได้รับการป้องกันอื่นloadClass(String name, boolean resolve)
ซึ่งจำเป็นต้องถูกแทนที่ หากคุณดูที่ Javadoc ของวิธีการที่ได้รับการป้องกันนี้ คุณสามารถเข้าใจสิ่งต่อไปนี้: พารามิเตอร์สองตัวถูกจัดเตรียมเป็นอินพุต หนึ่งคือชื่อไบนารีของคลาส (หรือชื่อคลาสแบบเต็ม) ที่ต้องโหลด ชื่อคลาสถูกระบุพร้อมกับรายการแพ็คเกจทั้งหมด พารามิเตอร์ตัวที่สองคือแฟล็กที่กำหนดว่าจำเป็นต้องมีความละเอียดของลิงก์สัญลักษณ์หรือไม่ โดยค่าเริ่มต้นจะเป็นfalseซึ่งหมายความว่ามีการใช้การโหลดคลาสแบบขี้เกียจ นอกจากนี้ ตามเอกสารประกอบในการใช้งานเริ่มต้นของวิธีการนั้น จะมีการเรียกfindLoadedClass(String name)
ซึ่งจะตรวจสอบว่าคลาสถูกโหลดไปก่อนหน้านี้แล้วหรือไม่ และหากเป็นเช่นนั้น จะส่งคืนการอ้างอิงไปยังคลาสนี้ มิฉะนั้น วิธีการโหลดคลาสของตัวโหลดพาเรนต์จะถูกเรียก หากไม่มีตัวโหลดใดสามารถค้นหาคลาสที่โหลดได้ แต่ละคลาสตามลำดับย้อนกลับจะพยายามค้นหาและโหลดคลาสนั้น โดยแทนที่คลาสfindClass(String name)
. ซึ่งจะกล่าวถึงรายละเอียดเพิ่มเติมในบท “แผนการโหลดชั้นเรียน” และสุดท้ายแต่ไม่ท้ายสุด หลังจากโหลดคลาสแล้ว ขึ้นอยู่กับ แฟล็ก การแก้ไขจะมีการตัดสินใจว่าจะโหลดคลาสผ่านลิงก์สัญลักษณ์หรือไม่ ตัวอย่างที่ชัดเจนคือสามารถเรียก ขั้นตอน การแก้ปัญหา ได้ในระหว่างขั้นตอนการโหลดชั้นเรียน ดังนั้น ด้วยการขยายคลาสClassLoader
และการแทนที่เมธอดของมัน ตัวโหลดแบบกำหนดเองจึงสามารถใช้ตรรกะของตัวเองในการส่งไบต์โค้ดไปยังเครื่องเสมือนได้ Java ยังสนับสนุนแนวคิดของตัวโหลดคลาส "ปัจจุบัน" ตัวโหลดปัจจุบันเป็นตัวโหลดคลาสที่กำลังดำเนินการอยู่ แต่ละคลาสรู้ว่าโหลดเดอร์ตัวใด และคุณสามารถรับข้อมูลนี้ได้โดยเรียกคลาสString.class.getClassLoader()
นั้น สำหรับคลาสแอปพลิเคชันทั้งหมด ตัวโหลด "ปัจจุบัน" มักจะเป็นระบบหนึ่ง
หลักการสามประการของการโหลดคลาส
-
การมอบหมาย
คำร้องขอโหลดคลาสจะถูกส่งผ่านไปยังตัวโหลดพาเรนต์ และความพยายามในการโหลดคลาสนั้นจะเกิดขึ้นก็ต่อเมื่อตัวโหลดพาเรนต์ไม่สามารถค้นหาและโหลดคลาสได้ วิธีนี้ช่วยให้คุณสามารถโหลดคลาสด้วยตัวโหลดที่ใกล้กับคลาสพื้นฐานมากที่สุด สิ่งนี้ทำให้มองเห็นชั้นเรียนได้สูงสุด ตัวโหลดแต่ละตัวจะเก็บบันทึกคลาสที่โหลดโดยตัวโหลดโดยวางไว้ในแคช ชุดของคลาสเหล่านี้เรียกว่าขอบเขต
-
ทัศนวิสัย
ตัวโหลดจะเห็นเฉพาะคลาส "ของมัน" และคลาสของ "พาเรนต์" และไม่มีความคิดเกี่ยวกับคลาสที่ "ลูก" ของมันโหลด
-
เอกลักษณ์
โหลดคลาสได้เพียงครั้งเดียวเท่านั้น กลไกการมอบหมายทำให้แน่ใจว่าตัวโหลดที่เริ่มต้นการโหลดคลาสจะไม่โอเวอร์โหลดคลาสที่โหลดลงใน JVM ก่อนหน้านี้
รูปแบบการโหลดชั้นเรียน
เมื่อมีการเรียกให้โหลดคลาสเกิดขึ้น คลาสนี้จะถูกค้นหาในแคชของคลาสที่โหลดไว้แล้วของตัวโหลดปัจจุบัน หากไม่เคยโหลดคลาสที่ต้องการมาก่อน หลักการของการมอบหมายจะโอนการควบคุมไปยังตัวโหลดพาเรนต์ ซึ่งอยู่ในลำดับชั้นที่สูงกว่าหนึ่งระดับ ตัวโหลดพาเรนต์ยังพยายามค้นหาคลาสที่ต้องการในแคช หากคลาสถูกโหลดไปแล้วและตัวโหลดทราบตำแหน่งของคลาสนั้น อ็อบเจ็กต์Class
ของคลาสนั้นจะถูกส่งคืน ถ้าไม่เช่นนั้น การค้นหาจะดำเนินต่อไปจนกว่าจะถึงตัวโหลดบูตพื้นฐาน หากตัวโหลดพื้นฐานไม่มีข้อมูลเกี่ยวกับคลาสที่ต้องการ (นั่นคือ ยังไม่ได้โหลด) รหัสไบต์ของคลาสนี้จะถูกค้นหาในตำแหน่งของคลาสที่ตัวโหลดที่ระบุทราบ และหากคลาสไม่สามารถทำได้ ถูกโหลดแล้ว การควบคุมจะกลับไปยังตัวโหลดย่อย ซึ่งจะพยายามโหลดจากแหล่งที่รู้จัก ดังที่ได้กล่าวไว้ข้างต้น ตำแหน่งของคลาสสำหรับตัวโหลดฐานคือไลบรารี rt.jar สำหรับตัวโหลดส่วนขยาย - ไดเร็กทอรีที่มีส่วนขยาย jre/lib/ext สำหรับระบบหนึ่ง - CLASSPATH สำหรับผู้ใช้หนึ่งอาจเป็นสิ่งที่แตกต่างออกไป . ดังนั้นความคืบหน้าของการโหลดคลาสจะไปในทิศทางตรงกันข้าม - จากตัวโหลดรูทไปจนถึงคลาสปัจจุบัน เมื่อพบรหัสไบต์ของคลาส คลาสจะถูกโหลดลงใน JVM และได้รับอินสแตนซ์ประเภทClass
นั้น อย่างที่คุณเห็นได้ง่าย รูปแบบการโหลดที่อธิบายไว้นั้นคล้ายคลึงกับการใช้งานเมธอดข้างloadClass(String name)
ต้น ด้านล่างคุณสามารถดูไดอะแกรมนี้ในไดอะแกรม
บทสรุป
ในขั้นตอนแรกของการ เรียนรู้ภาษา ไม่จำเป็นต้องเข้าใจวิธีการโหลดคลาสใน Java เป็นพิเศษ แต่การรู้หลักการพื้นฐานเหล่านี้จะช่วยให้คุณหลีกเลี่ยงความสิ้นหวังเมื่อพบข้อผิดพลาด เช่นClassNotFoundException
หรือ NoClassDefFoundError
หรืออย่างน้อยก็คร่าวๆ ก็เข้าใจแล้วว่าต้นตอของปัญหาคืออะไร ดังนั้นข้อยกเว้นClassNotFoundException
จะเกิดขึ้นเมื่อมีการโหลดคลาสแบบไดนามิกระหว่างการทำงานของโปรแกรม เมื่อตัวโหลดไม่พบคลาสที่ต้องการในแคชหรือตามเส้นทางคลาส แต่ข้อผิดพลาดNoClassDefFoundError
นั้นสำคัญกว่าและเกิดขึ้นเมื่อมีคลาสที่ต้องการในระหว่างการคอมไพล์ แต่ไม่สามารถมองเห็นได้ในระหว่างการรันโปรแกรม กรณีนี้อาจเกิดขึ้นได้หากโปรแกรมลืมรวมไลบรารี่ที่ใช้ ความจริงของการทำความเข้าใจหลักการของโครงสร้างของเครื่องมือที่คุณใช้ในงานของคุณ (ไม่จำเป็นต้องเป็นการแช่ลึกที่ชัดเจนและมีรายละเอียด) จะเพิ่มความชัดเจนให้กับความเข้าใจของกระบวนการที่เกิดขึ้นภายในกลไกนี้ ซึ่งใน เทิร์นนำไปสู่การใช้เครื่องมือนี้อย่างมั่นใจ
GO TO FULL VERSION