מטעין כיתתי
הוא משמש לאספקת קוד בתים מהידור ל-JVM, אשר מאוחסן בדרך כלל בקבצים עם הסיומת.class
, אך ניתן להשיגו גם ממקורות אחרים, למשל, הורדה דרך הרשת או שנוצרה על ידי האפליקציה עצמה. על פי מפרט Java SE, על מנת להפעיל קוד ב-JVM, עליך לבצע שלושה שלבים:
-
טעינת bytecode ממשאבים ויצירת מופע של המחלקה
Class
זה כולל חיפוש אחר המחלקה המבוקשת בין אלה שנטענו קודם לכן, השגת קוד בתים לטעינה ובדיקת נכונותו, יצירת מופע של המחלקה
Class
(לעבודה איתה בזמן ריצה) וטעינת מחלקות אב. אם מחלקות וממשקי אב לא נטענו, אזי המחלקה המדוברת נחשבת לא נטענת. -
מחייב (או קישור)
על פי המפרט, שלב זה מחולק לשלושה שלבים נוספים:
- אימות , נבדקת נכונות ה-bytecode שהתקבל.
- הכנה , הקצאת זיכרון RAM לשדות סטטיים ואתחולם עם ערכי ברירת מחדל (במקרה זה, אתחול מפורש, אם קיים, מתרחש כבר בשלב האתחול).
- רזולוציה , רזולוציה של קישורים סמליים של סוגים, שדות ושיטות.
-
אתחול האובייקט שהתקבל
כאן, בניגוד לפסקאות הקודמות, נראה שהכל ברור מה צריך לקרות. זה יהיה, כמובן, מעניין להבין איך זה קורה בדיוק.
- יש לטעון את המחלקה במלואה לפני הקישור.
- מחלקה חייבת להיבדק במלואה ולהכין לפני אתחולה.
- שגיאות ברזולוציית קישור מתרחשות במהלך הפעלת התוכנית, גם אם הן זוהו בשלב הקישור.
סוגי מעמיסי Java
ישנם שלושה מעמיסים סטנדרטיים ב-Java, שכל אחד מהם טוען מחלקה ממיקום ספציפי:-
Bootstrap הוא מטעין בסיסי, הנקרא גם Primordial ClassLoader.
טוען מחלקות JDK סטנדרטיות מארכיון rt.jar
-
Extension ClassLoader – מעמיס הרחבה.
טוען כיתות הרחבות, הממוקמות בספריית jre/lib/ext כברירת מחדל, אך ניתן להגדיר על ידי מאפיין המערכת java.ext.dirs
-
System ClassLoader - מטעין מערכת.
טוען כיתות יישומים המוגדרות במשתנה הסביבה CLASSPATH
מחלקה מופשטת ClassLoader
כל מטעין, למעט הבסיס, הוא צאצא של המעמד המופשטjava.lang.ClassLoader
. לדוגמה, היישום של מטעין ההרחבה הוא המחלקה 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)
. זה יידון ביתר פירוט בפרק "תכנית טעינת כיתות". ולבסוף, אחרון חביב, לאחר טעינת המחלקה, בהתאם לדגל ה- resolve , יוחלט האם לטעון מחלקות באמצעות קישורים סמליים. דוגמה ברורה היא שניתן לקרוא לשלב הרזולוציה בשלב טעינת המחלקה. בהתאם לכך, על ידי הרחבת המחלקה ClassLoader
ועקוף השיטות שלה, הטוען המותאם אישית יכול ליישם את ההיגיון שלו להעברת קוד בתים למחשב הוירטואלי. Java גם תומכת בקונספט של מטעין מחלקות "נוכחי". הטוען הנוכחי הוא זה שטען את המחלקה המבצעת כעת. כל מחלקה יודעת באיזה מטעין נטען, ותוכלו לקבל מידע זה על ידי קריאה שלו String.class.getClassLoader()
. עבור כל מחלקות היישומים, המעמיס ה"נוכחי" הוא בדרך כלל המערכת.
שלושה עקרונות של טעינת כיתות
-
מִשׁלַחַת
הבקשה לטעון את המחלקה מועברת למטעין האב, וניסיון לטעון את המחלקה עצמה מתבצע רק אם טוען האב לא הצליח למצוא ולטעון את המחלקה. גישה זו מאפשרת לך לטעון מחלקות עם המעמיס הקרוב ככל האפשר לבסיס. זה משיג נראות מעמדית מקסימלית. כל מטעין שומר תיעוד של השיעורים שנטענו על ידו, וממקם אותם במטמון שלו. קבוצת המחלקות הללו נקראת היקף.
-
רְאוּת
המעמיס רואה רק את הכיתות "שלו" ואת הכיתות של "ההורה" ואין לו מושג לגבי השיעורים שהועלו על ידי "הילד".
-
ייחודיות
ניתן לטעון מחלקה פעם אחת בלבד. מנגנון האצלה מוודא שהמטעין שיוזם טעינת מחלקה לא יעמיס מחלקה שנטענה בעבר ל-JVM.
ערכת טעינת כיתות
כאשר מתרחשת קריאה לטעינת מחלקה, מחלקה זו מתבצעת בחיפוש במטמון של מחלקות שנטענו כבר של הטוען הנוכחי. אם המחלקה הרצויה לא נטענה בעבר, עקרון האצלה מעביר את השליטה למטעין האב, שנמצא ברמה אחת גבוה יותר בהיררכיה. גם מטעין האב מנסה למצוא את המחלקה הרצויה במטמון שלו. אם המחלקה כבר נטענה והמטעין יודע את מיקומה, אזיClass
יוחזר אובייקט מאותה מחלקה. אם לא, החיפוש יימשך עד שהוא יגיע למטען האתחול הבסיסי. אם למטעין הבסיס אין מידע על המחלקה הנדרשת (כלומר, הוא עדיין לא נטען), ה-bytecode של המחלקה הזו יחפש במיקום המחלקות שהמטעין הנתון יודע עליהן, ואם המחלקה לא יכולה להיטען, הבקרה תחזור למטעין הילד, שינסה לטעון ממקורות המוכרים לו. כפי שצוין לעיל, מיקום המחלקות עבור מטעין הבסיס הוא ספריית rt.jar, עבור טוען ההרחבה - הספרייה עם הרחבות jre/lib/ext, עבור המערכת אחת - CLASSPATH, עבור המשתמש זה יכול להיות משהו שונה . לפיכך, ההתקדמות של מחלקות הטעינה הולכת בכיוון ההפוך - ממטעין השורש אל הנוכחי. כאשר ה-bytecode של המחלקה נמצא, המחלקה נטענת לתוך ה-JVM ומתקבל מופע מהסוג Class
. כפי שאתה יכול לראות בקלות, ערכת הטעינה המתוארת דומה ליישום של השיטה לעיל loadClass(String name)
. להלן ניתן לראות תרשים זה בתרשים.
כמסקנה
בשלבים הראשונים של לימוד שפה, אין צורך מיוחד להבין כיצד מחלקות נטענות ב-Java, אך ידיעת העקרונות הבסיסיים הללו תעזור לך להימנע מייאוש כאשר נתקלים בשגיאות כגוןClassNotFoundException
או NoClassDefFoundError
. ובכן, או לפחות להבין בערך מה שורש הבעיה. לפיכך, יוצא מן הכלל ClassNotFoundException
מתרחש כאשר מחלקה נטענת באופן דינמי במהלך הפעלת התוכנית, כאשר המעמיסים אינם יכולים למצוא את המחלקה הנדרשת לא במטמון או לאורך נתיב המחלקה. אבל השגיאה NoClassDefFoundError
קריטית יותר ומתרחשת כאשר המחלקה הנדרשת הייתה זמינה במהלך ההידור, אך לא הייתה גלויה במהלך הפעלת התוכנית. זה יכול לקרות אם התוכנית שכחה לכלול את הספרייה שבה היא משתמשת. ובכן, עצם הבנת העקרונות של מבנה הכלי בו אתה משתמש בעבודתך (לאו דווקא טבילה ברורה ומפורטת במעמקיו) מוסיפה בהירות מסוימת להבנת התהליכים המתרחשים בתוך מנגנון זה, אשר ב תור, מוביל לשימוש בטוח בכלי זה.
GO TO FULL VERSION