JavaRush /مدونة جافا /Random-AR /تحليل الأسئلة والأجوبة من المقابلات لمطور جافا. الجزء 15

تحليل الأسئلة والأجوبة من المقابلات لمطور جافا. الجزء 15

نشرت في المجموعة
مرحبا مرحبا! ما مقدار ما يحتاج مطور Java إلى معرفته؟ يمكنك أن تجادل لفترة طويلة حول هذه المسألة، ولكن الحقيقة هي أنه في المقابلة سوف تكون مدفوعة بالكامل بالنظرية. حتى في مجالات المعرفة التي لن تتاح لك الفرصة لاستخدامها في عملك. تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 1حسنًا، إذا كنت مبتدئًا، فسيتم أخذ معرفتك النظرية على محمل الجد. نظرًا لعدم وجود خبرة وإنجازات عظيمة حتى الآن، يبقى فقط التحقق من قوة قاعدة المعرفة. سنواصل اليوم تعزيز هذه القاعدة من خلال فحص أسئلة المقابلة الأكثر شيوعًا لمطوري Java. لنطير!

جافا كور

9. ما الفرق بين الربط الثابت والديناميكي في Java؟

لقد سبق أن أجبت على هذا السؤال في هذا المقال في السؤال رقم 18 حول تعدد الأشكال الساكن والديناميكي، أنصحك بقراءته.

10. هل من الممكن استخدام المتغيرات الخاصة أو المحمية في الواجهة؟

لا لا يمكنك. لأنه عندما تعلن عن واجهة، يقوم مترجم Java تلقائيًا بإضافة الكلمات الأساسية العامة والمجردة قبل أساليب الواجهة والكلمات الأساسية العامة والثابتة والنهائية قبل أعضاء البيانات. في الواقع، إذا أضفت خاصًا أو محميًا ، فسينشأ تعارض، وسيشكو المترجم من معدّل الوصول بالرسالة: "المعدِّل '<selected modifier>' غير مسموح به هنا." لماذا يضيف المترجم عمومًا وثابتًا ونهائيًا المتغيرات في الواجهة؟ دعونا معرفة ذلك:
  • عام - تسمح الواجهة للعميل بالتفاعل مع الكائن. إذا لم تكن المتغيرات عامة، فلن يتمكن العملاء من الوصول إليها.
  • ثابت - لا يمكن إنشاء الواجهات (أو بالأحرى كائناتها)، وبالتالي فإن المتغير ثابت.
  • نهائي - نظرًا لاستخدام الواجهة لتحقيق التجريد بنسبة 100%، فإن المتغير له شكله النهائي (ولن يتم تغييره).

11. ما هو Classloader وما هو استخدامه؟

يوفر Classloader - أو Class Loader - إمكانية تحميل فئات Java. بتعبير أدق، يتم ضمان التحميل من قبل أحفاده - محمل فئة معينة، لأن ClassLoader نفسه مجرد. في كل مرة يتم فيها تحميل ملف .class، على سبيل المثال، بعد استدعاء مُنشئ أو طريقة ثابتة للفئة المقابلة، يتم تنفيذ هذا الإجراء بواسطة أحد أحفاد فئة ClassLoader . هناك ثلاثة أنواع من الورثة:
  1. يعد Bootstrap ClassLoader مُحملًا أساسيًا، ويتم تنفيذه على مستوى JVM ولا يحتوي على أي تعليقات من بيئة وقت التشغيل، نظرًا لأنه جزء من JVM kernel ومكتوب بالكود الأصلي. يعمل هذا المُحمل باعتباره الأصل لجميع مثيلات ClassLoader الأخرى.

    المسؤول بشكل أساسي عن تحميل فئات JDK الداخلية، عادةً rt.jar والمكتبات الأساسية الأخرى الموجودة في الدليل $JAVA_HOME/jre/lib . قد يكون للأنظمة الأساسية المختلفة تطبيقات مختلفة لمحمل الفئة هذا.

  2. ملحق Classloader هو مُحمل ملحق، وهو سليل فئة المُحمل الأساسي. يعتني بتحميل امتداد فئات Java الأساسية القياسية. يتم تحميله من دليل ملحقات JDK، عادةً $JAVA_HOME/lib/ext أو أي دليل آخر مذكور في خاصية النظام java.ext.dirs (يمكن استخدام هذا الخيار للتحكم في تحميل الملحقات).

  3. System ClassLoader هو مُحمل نظام يتم تنفيذه على مستوى JRE ويهتم بتحميل جميع فئات مستوى التطبيق في JVM. يقوم بتحميل الملفات الموجودة في خيار سطر الأوامر -classpath أو -cp لمتغير بيئة الفئة .

تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 2تعد أدوات تحميل الفئة جزءًا من وقت تشغيل Java. في اللحظة التي يطلب فيها JVM فئة ما، يحاول مُحمل الفئة العثور على الفئة وتحميل تعريف الفئة في وقت التشغيل باستخدام الاسم المؤهل بالكامل للفئة. الطريقة java.lang.ClassLoader.loadClass() هي المسؤولة عن تحميل تعريف الفئة في وقت التشغيل. يحاول تحميل فئة بناءً على اسمها الكامل. إذا لم يتم تحميل الفئة بعد، فسيتم تفويض الطلب إلى محمل الفئة الأصل. تحدث هذه العملية بشكل متكرر وتبدو كما يلي:
  1. يحاول System Classloader العثور على الفئة في ذاكرة التخزين المؤقت الخاصة به.

    • 1.1. إذا تم العثور على الفئة، فسيتم إكمال التحميل بنجاح.

    • 1.2. إذا لم يتم العثور على الفئة، فسيتم تفويض التحميل إلى ملحق Classloader.

  2. يحاول ملحق Classloader العثور على الفئة في ذاكرة التخزين المؤقت الخاصة به.

    • 2.1. إذا تم العثور على الفصل، فسيتم إكماله بنجاح.

    • 2.2. إذا لم يتم العثور على الفئة، فسيتم تفويض التحميل إلى Bootstrap Classloader.

  3. يحاول Bootstrap Classloader العثور على الفصل في ذاكرة التخزين المؤقت الخاصة به.

    • 3.1. إذا تم العثور على الفئة، فسيتم إكمال التحميل بنجاح.

    • 3.2. إذا لم يتم العثور على الفئة، فسيحاول Bootstrap Classloader الأساسي تحميلها.

  4. في حالة التحميل:

    • 4.1. ناجح - اكتمل تحميل الفصل.

    • 4.2. إذا فشل، يتم نقل التحكم إلى ملحق Classloader.

  5. 5. يحاول ملحق Classloader تحميل الفصل، وفي حالة التحميل:

    • 5.1. ناجح - اكتمل تحميل الفصل.

    • 5.2. إذا لم ينجح الأمر، فسيتم نقل التحكم إلى System Classloader.

  6. 6. يحاول System Classloader تحميل الفصل، وفي حالة التحميل:

    • 6.1. ناجح - اكتمل تحميل الفصل.

    • 6.2. لم يتم المرور بنجاح - تم إنشاء استثناء - ClassNotFoundException.

موضوع محمل الفصل هو موضوع واسع ولا ينبغي إهماله. وللتعرف عليه بمزيد من التفصيل أنصحك بقراءة هذا المقال ولن نطيل فيه ونمضي قدمًا.

12. ما هي مناطق بيانات وقت التشغيل؟

آريس بيانات وقت التشغيل - مناطق بيانات وقت تشغيل JVM. يحدد JVM بعض مناطق بيانات وقت التشغيل المطلوبة أثناء تنفيذ البرنامج. يتم إنشاء بعضها عند بدء تشغيل JVM. والبعض الآخر يكون مؤشر ترابط محلي ويتم إنشاؤه فقط عند إنشاء مؤشر الترابط (ويتم إتلافه عند إتلاف مؤشر الترابط). تبدو مناطق بيانات وقت تشغيل JVM كما يلي: تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 3
  • يعد تسجيل الكمبيوتر محليًا لكل مؤشر ترابط ويحتوي على عنوان تعليمات JVM التي ينفذها مؤشر الترابط حاليًا.

  • JVM Stack هي منطقة ذاكرة تُستخدم كمخزن للمتغيرات المحلية والنتائج المؤقتة. يحتوي كل خيط على مكدس منفصل خاص به: بمجرد انتهاء الخيط، يتم إتلاف هذه المكدس أيضًا. تجدر الإشارة إلى أن ميزة المكدس على الكومة هي الأداء، في حين أن الكومة لديها بالتأكيد ميزة في حجم التخزين.

  • مكدس الطريقة الأصلية - منطقة بيانات لكل مؤشر ترابط تقوم بتخزين عناصر البيانات، على غرار مكدس JVM، لتنفيذ الأساليب الأصلية (غير Java).

  • الكومة - تستخدمها جميع سلاسل الرسائل كمخزن يحتوي على كائنات وبيانات تعريف الفئة والمصفوفات وما إلى ذلك، والتي يتم إنشاؤها في وقت التشغيل. يتم إنشاء هذه المنطقة عند بدء تشغيل JVM ويتم تدميرها عند إيقاف تشغيله.

  • منطقة الطريقة - منطقة وقت التشغيل هذه مشتركة بين جميع سلاسل الرسائل ويتم إنشاؤها عند بدء تشغيل JVM. يقوم بتخزين الهياكل الخاصة بكل فئة، مثل Runtime Constant Pool، والتعليمات البرمجية للمنشئين والأساليب، وبيانات الطريقة، وما إلى ذلك.

13. ما هو الشيء غير القابل للتغيير؟

في هذا الجزء من المقال، في السؤالين 14 و15، هناك بالفعل إجابة لهذا السؤال، فألقِ نظرة دون إضاعة وقتك.

14. ما الذي يميز فئة String؟

في وقت سابق من التحليل، تحدثنا مرارًا وتكرارًا عن ميزات معينة للسلسلة (كان هناك قسم منفصل لهذا). الآن دعونا نلخص ميزات السلسلة :
  1. إنه الكائن الأكثر شيوعًا في Java ويستخدم لمجموعة متنوعة من الأغراض. من حيث تكرار الاستخدام، فهو ليس أقل شأنا حتى من الأنواع البدائية.

  2. يمكن إنشاء كائن من هذه الفئة دون استخدام الكلمة الأساسية الجديدة - مباشرة من خلال علامات الاقتباس String str = "string"; .

  3. السلسلة هي فئة غير قابلة للتغيير : عند إنشاء كائن من هذه الفئة، لا يمكن تغيير بياناته (عند إضافة + "سلسلة أخرى" إلى سلسلة معينة، ونتيجة لذلك، ستحصل على سلسلة ثالثة جديدة). إن ثبات فئة السلسلة يجعلها آمنة للخيط.

  4. تم الانتهاء من فئة السلسلة (تحتوي على المعدل النهائي )، لذلك لا يمكن توريثها.

  5. تحتوي السلسلة على مجموعة سلاسل خاصة بها، وهي منطقة من الذاكرة في الكومة تقوم بتخزين قيم السلسلة التي تنشئها مؤقتًا. في هذا الجزء من السلسلة ، في السؤال 62، قمت بوصف تجمع السلاسل.

  6. لدى Java نظائرها من String ، المصممة أيضًا للعمل مع السلاسل - StringBuilder و StringBuffer ، ولكن مع اختلاف أنها قابلة للتغيير. يمكنك قراءة المزيد عنهم في هذا المقال .

تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 4

15. ما هو نوع التغاير؟

لفهم التغاير المشترك، سننظر إلى مثال. لنفترض أن لدينا فئة من الحيوانات:
public class Animal {
 void voice() {
   System.out.println("*тишина*");
 }
}
وبعض فئات الكلاب توسعها :
public class Dog extends Animal {

 @Override
 public void voice() {
   System.out.println("Гав, гав, гав!!!");
 }
}
كما نتذكر، يمكننا بسهولة تعيين كائنات من النوع الوريث إلى النوع الأصلي:
Animal animal = new Dog();
وهذا لن يكون أكثر من تعدد الأشكال. مريحة ومرنة أليس كذلك؟ حسنًا، ماذا عن قائمة الحيوانات؟ هل يمكننا تقديم قائمة تحتوي على حيوان عام، وقائمة تحتوي على كائنات كلب ؟
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs;
في هذه الحالة، سيتم وضع خط باللون الأحمر لتخصيص قائمة الكلاب لقائمة الحيوانات، أي. لن يقوم المترجم بتمرير هذا الرمز. على الرغم من أن هذا التعيين يبدو منطقيًا تمامًا (بعد كل شيء، يمكننا تعيين كائن Dog لمتغير من النوع Animal )، إلا أنه لا يمكن القيام به. هذا لأنه إذا سمح بذلك، فسنكون قادرين على وضع كائن حيواني في القائمة التي كان من المفترض في الأصل أن تكون كلبًا ، بينما نعتقد أنه لدينا كلاب فقط في القائمة . وبعد ذلك، على سبيل المثال، سنستخدم التابع get() ‎ لأخذ كائن من قائمة الكلاب ، معتقدًا أنه كلب، ونستدعي طريقة ما لكائن Dog عليه، وهو ما لا يحتوي عليه Animal . وكما تفهم، فمن المستحيل - سيحدث خطأ. لكن لحسن الحظ، لا يفوّت المترجم هذا الخطأ المنطقي عند تعيين قائمة المتحدرين إلى قائمة الآباء (والعكس صحيح). في Java، يمكنك فقط تعيين كائنات القائمة لقائمة المتغيرات ذات الأسماء العامة المطابقة. وهذا ما يسمى الثبات. إذا تمكنوا من القيام بذلك، فسوف يسمى ذلك ويسمى التغاير. أي أن التباين هو إذا تمكنا من تعيين كائن من النوع ArrayList<Dog> إلى متغير من النوع List<Animal> . اتضح أن التباين غير مدعوم في جافا؟ لا يهم كيف هو! ولكن يتم ذلك بطريقته الخاصة. ما هو التصميم المستخدم ل ؟ يمتد الحيوان . يتم وضعه مع عام للمتغير الذي نريد تعيين كائن القائمة عليه، مع عام من السليل. هذا البناء العام يعني أن أي نوع ينحدر من نوع الحيوان سيفعله (ويقع نوع الحيوان أيضًا ضمن هذا التعميم). في المقابل، لا يمكن أن يكون Animal فئة فحسب، بل واجهة أيضًا (لا تنخدع بالكلمة الأساسية الممتدة ). يمكننا تنفيذ مهمتنا السابقة مثل هذا: تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 5
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);
في وحدة التحكم سنرى الإخراج التالي:
ويف ويف ويف!!! ويف ويف ويف!!! ويف ويف ويف!!!
وهذا يعني أن هذا النهج في التغاير يعمل بنجاح. اسمحوا لي أن أشير إلى أن هذا النوع العام مدرج في القائمة ؟ يمتد Animal لا يمكننا إدراج بيانات جديدة من أي نوع: لا نوع Dog ولا حتى نوع Animal :
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
animals.add(new Dog());
dogs.add(new Animal());
في الواقع، في السطرين الأخيرين سوف يقوم المترجم بتسليط الضوء على إدراج الكائنات باللون الأحمر. ويرجع ذلك إلى حقيقة أننا لا نستطيع التأكد بنسبة مائة بالمائة من قائمة الكائنات من أي نوع سيتم تخصيصها للقائمة التي تحتوي على بيانات بواسطة <? يمتد الحيوان> . تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 6أود أيضًا أن أتحدث عن التباين ، نظرًا لأن هذا المفهوم عادةً ما يتوافق دائمًا مع التباين، وكقاعدة عامة يتم سؤالهم عنهما معًا. هذا المفهوم هو إلى حد ما عكس التغاير المشترك، حيث يستخدم هذا البناء نوع الوريث. لنفترض أننا نريد قائمة يمكن تخصيص قائمة بها كائنات الكتابة التي ليست أسلافًا لكائن Dog . ومع ذلك، فإننا لا نعرف مقدمًا ما هي الأنواع المحددة التي ستكون عليها. في هذه الحالة، بناء النموذج ؟ السوبر دوج ، الذي تناسبه جميع الأنواع - أسلاف فئة الكلاب :
List<Animal> animals = new ArrayList<>();
List<? super Dog> dogs = animals;
dogs.add(new Dog());
dogs.add(new Dog());
يمكننا إضافة كائنات من النوع Dog بأمان إلى القائمة بمثل هذا النوع العام ، لأنه على أي حال يحتوي على جميع الأساليب المطبقة لأي من أسلافه. لكننا لن نتمكن من إضافة كائن من النوع حيوان ، لأنه ليس من المؤكد أنه سيكون هناك كائنات من هذا النوع بالداخل، وليس كلبًا على سبيل المثال . بعد كل شيء، يمكننا أن نطلب من أحد عناصر هذه القائمة طريقة لفئة Dog ، والتي لن يكون لدى Animal . في هذه الحالة، سيحدث خطأ في التجميع. وأيضاً إذا أردنا تطبيق الطريقة السابقة ولكن بهذا العام:
public static void animalsVoice(List<? super Dog> dogs) {
 for (Dog dog : dogs) {
   dog.voice();
 }
}
سنحصل على خطأ في الترجمة في حلقة for ، حيث لا يمكننا التأكد من أن القائمة التي تم إرجاعها تحتوي على كائنات من النوع Dog وأننا أحرار في استخدام أساليبها. إذا قمنا باستدعاء طريقة dog.get(0) في هذه القائمة . - سوف نحصل على كائن من النوع Object . أي أنه لكي تعمل طريقة AnimalVoice() ‎، نحتاج على الأقل إلى إضافة معالجات صغيرة مع تضييق نوع البيانات:
public static void animalsVoice(List<? super Dog> dogs) {
 for (Object obj : dogs) {
   if (obj instanceof Dog) {
     Dog dog = (Dog) obj;
     dog.voice();
   }
 }
}
تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 7

16. كيف توجد طرق في فئة الكائن؟

في هذا الجزء من السلسلة، في الفقرة 11، سبق أن أجبت على هذا السؤال، لذا أنصحك بشدة بقراءته إذا لم تكن قد فعلت ذلك بعد. هذا هو المكان الذي سننتهي فيه لهذا اليوم. نراكم في الجزء التالي! تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 15 - 8
مواد أخرى في السلسلة:
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION