محمل الطبقة
يتم استخدامه لتزويد JVM بالكود الثانوي المترجم، والذي يتم تخزينه عادةً في ملفات ذات الامتداد.class
، ولكن يمكن أيضًا الحصول عليه من مصادر أخرى، على سبيل المثال، تنزيله عبر الشبكة أو إنشاؤه بواسطة التطبيق نفسه. وفقًا لمواصفات Java SE، من أجل تشغيل التعليمات البرمجية في JVM، تحتاج إلى إكمال ثلاث خطوات:
-
تحميل الرمز الثانوي من الموارد وإنشاء مثيل للفئة
Class
يتضمن ذلك البحث عن الفئة المطلوبة من بين تلك التي تم تحميلها مسبقًا، والحصول على الرمز الثانوي للتحميل والتحقق من صحته، وإنشاء مثيل للفئة
Class
(للعمل معها في وقت التشغيل)، وتحميل الفئات الأصلية. إذا لم يتم تحميل الفئات والواجهات الأصلية، فسيتم اعتبار الفئة المعنية غير محملة. -
ملزمة (أو ربط)
ووفقا للمواصفات، تنقسم هذه المرحلة إلى ثلاث مراحل أخرى:
- التحقق ، يتم التحقق من صحة الرمز الثانوي المستلم.
- التحضير ، وتخصيص ذاكرة الوصول العشوائي (RAM) للحقول الثابتة وتهيئتها بالقيم الافتراضية (في هذه الحالة، التهيئة الصريحة، إن وجدت، تحدث بالفعل في مرحلة التهيئة).
- الدقة وحل الروابط الرمزية من الأنواع والمجالات والأساليب.
-
تهيئة الكائن المستلم
هنا، على عكس الفقرات السابقة، يبدو أن كل شيء واضح ما يجب أن يحدث. سيكون من المثير للاهتمام بالطبع أن نفهم بالضبط كيف يحدث هذا.
- يجب أن يتم تحميل الفصل بالكامل قبل ربطه.
- يجب أن يتم اختبار الفصل وإعداده بالكامل قبل تهيئته.
- تحدث أخطاء في تحليل الارتباط أثناء تنفيذ البرنامج، حتى لو تم اكتشافها في مرحلة الارتباط.
أنواع محمل جافا
هناك ثلاثة أدوات تحميل قياسية في Java، كل منها يقوم بتحميل فئة من موقع محدد:-
يعد Bootstrap مُحملًا أساسيًا، يُسمى أيضًا Primordial ClassLoader.
يقوم بتحميل فئات JDK القياسية من أرشيف rt.jar
-
ملحق 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)
. سيتم مناقشة هذا بمزيد من التفصيل في الفصل "مخطط تحميل الفصل". وأخيرًا وليس آخرًا، بعد تحميل الفصل، اعتمادًا على علامة الحل ، سيتم تحديد ما إذا كان سيتم تحميل الفئات عبر روابط رمزية. من الأمثلة الواضحة على ذلك أنه يمكن استدعاء مرحلة الحل أثناء مرحلة تحميل الفصل. وفقًا لذلك، من خلال توسيع الفئة 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