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

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

نشرت في المجموعة
الألعاب النارية! العالم يتحرك باستمرار ونحن نتحرك باستمرار. في السابق، لكي تصبح مطور Java، كان يكفي معرفة القليل من بناء جملة Java، والباقي سيأتي. مع مرور الوقت، ارتفع مستوى المعرفة المطلوبة لتصبح مطور Java بشكل ملحوظ، وكذلك المنافسة، التي تستمر في دفع الحد الأدنى من المعرفة المطلوبة إلى الأعلى. تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 14 - 1إذا كنت تريد حقًا أن تصبح مطورًا، فعليك أن تعتبر ذلك أمرًا مفروغًا منه وأن تستعد جيدًا للتميز بين المبتدئين مثلك، وما سنفعله اليوم هو أننا سنستمر في تحليل أكثر من 250 سؤالًا . لقد تناولنا في المقالات السابقة جميع أسئلة المستوى المبتدئ، واليوم سنتناول أسئلة المستوى المتوسط. على الرغم من أنني لاحظت أن هذه ليست أسئلة للمستوى المتوسط ​​بنسبة 100%، إلا أنه يمكنك مواجهة معظمها في مقابلة المستوى المبتدئ، لأنه في مثل هذه المقابلات يتم إجراء فحص تفصيلي لقاعدتك النظرية، بينما بالنسبة للطالب المتوسط وتتركز الأسئلة أكثر على التحقيق في تجربته. تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 14 - 2ولكن، دون مزيد من اللغط، دعونا نبدأ.

وسط

شائعة

1. ما هي مزايا وعيوب OOP بالمقارنة مع البرمجة الإجرائية/الوظيفية؟

هذا السؤال كان موجودا في تحليل الأسئلة لجونيور، وبناء على ذلك أجبت عليه بالفعل. ابحث عن هذا السؤال وإجابته في هذا الجزء من المقال السؤالين رقم 16 و17.

2. كيف يختلف التجميع عن التكوين؟

في OOP، هناك عدة أنواع من التفاعل بين الأشياء، متحدة تحت المفهوم العام "علاقة Has-A". تشير هذه العلاقة إلى أن أحد الكائنات هو أحد مكونات كائن آخر. في الوقت نفسه، هناك نوعان فرعيان من هذه العلاقة: التركيب - كائن واحد يخلق كائنًا آخر ويعتمد عمر كائن آخر على عمر الخالق. التجميع - يتلقى الكائن رابطًا (مؤشرًا) لكائن آخر أثناء عملية الإنشاء (في هذه الحالة، لا يعتمد عمر الكائن الآخر على عمر المنشئ). لفهم أفضل، دعونا نلقي نظرة على مثال محدد. لدينا فئة معينة من السيارات - Car والتي بدورها تحتوي على حقول داخلية من النوع - Engine وقائمة الركاب - List<Passenger> ، ولها أيضًا طريقة لبدء الحركة - startMoving() :
public class Car {

 private Engine engine;
 private List<Passenger> passengers;

 public Car(final List<Passenger> passengers) {
   this.engine = new Engine();
   this.passengers = passengers;
 }

 public void addPassenger(Passenger passenger) {
   passengers.add(passenger);
 }

 public void removePassengerByIndex(Long index) {
   passengers.remove(index);
 }

 public void startMoving() {
   engine.start();
   System.out.println("Машина начала своё движение");
   for (Passenger passenger : passengers) {
     System.out.println("В машине есть пассажир - " + passenger.getName());
   }
 }
}
في هذه الحالة، التركيب هو الاتصال بين السيارة والمحرك ، حيث أن أداء السيارة يعتمد بشكل مباشر على وجود كائن المحرك، لأنه إذا كان المحرك = null ، فسنتلقى NullPointerException . وفي المقابل، لا يمكن للمحرك أن يوجد بدون آلة (لماذا نحتاج إلى محرك بدون آلة؟) ولا يمكن أن ينتمي إلى عدة آلات في وقت واحد. هذا يعني أنه إذا قمنا بحذف كائن السيارة ، فلن يكون هناك المزيد من الإشارات إلى كائن المحرك ، وسيتم حذفه قريبًا بواسطة جامع البيانات المهملة . كما ترون، هذه العلاقة صارمة للغاية (قوية). التجميع هو العلاقة بين السيارة والراكب ، حيث أن أداء السيارة لا يعتمد بأي حال من الأحوال على الأشياء من نوع الراكب وعددها. يمكنهم إما مغادرة السيارة - RemovePassengerByIndex(Long Index) أو إدخال أخرى جديدة - AddPassenger(Passenger الركاب) ، على الرغم من ذلك، ستستمر السيارة في العمل بشكل صحيح. في المقابل، يمكن أن توجد كائنات الركاب بدون كائن السيارة . كما تفهم، هذا اتصال أضعف بكثير مما نراه في التكوين. تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 14 - 3ولكن هذا ليس كل شيء، فالكائن المرتبط عن طريق التجميع بكائن آخر يمكن أن يكون له أيضًا اتصال معين بكائنات أخرى في نفس النقطة الزمنية. على سبيل المثال، أنت، كطالب Java، مسجل في دورات اللغة الإنجليزية وOOP واللوغاريتمات في نفس الوقت، ولكن في نفس الوقت لست جزءًا ضروريًا منها، والتي بدونها يكون الأداء الطبيعي مستحيلًا (مثل معلم).

3. ما هي أنماط GoF التي استخدمتها عمليًا؟ أعط أمثلة.

لقد أجبت على هذا السؤال من قبل، لذا سأترك رابط التحليل ، راجع السؤال الأول. لقد عثرت أيضًا على مقالة رائعة حول أنماط التصميم، والتي أوصي بشدة بالاحتفاظ بها في متناول اليد.

4. ما هو الكائن الوكيل؟ أعط أمثلة

الوكيل هو نمط تصميم هيكلي يسمح لك باستبدال كائنات بديلة خاصة، أو بمعنى آخر، كائنات الوكيل، بدلاً من الكائنات الحقيقية. تعترض كائنات الوكيل هذه الاستدعاءات إلى الكائن الأصلي، مما يسمح بإدراج بعض المنطق قبل أو بعد تمرير الاستدعاء إلى الكائن الأصلي. تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 14 - 4أمثلة على استخدام كائن الوكيل:
  • كوكيل بعيد - يُستخدم عندما نحتاج إلى كائن بعيد (كائن في مساحة عنوان مختلفة) يحتاج إلى تمثيله محليًا. في هذه الحالة، سيتولى الوكيل إنشاء الاتصال والتشفير وفك التشفير وما إلى ذلك، بينما سيستخدمه العميل كما لو كان الكائن الأصلي الموجود في المساحة المحلية.

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

  • كوكيل أمان - يُستخدم عندما تحتاج إلى التحكم في الوصول إلى بعض الكائنات بناءً على حقوق العميل. أي أنه إذا حاول عميل لديه حقوق وصول مفقودة الوصول إلى الكائن الأصلي، فسيعترضه الوكيل ولن يسمح به.

دعونا نلقي نظرة على مثال للوكيل الظاهري: لدينا بعض واجهة المعالج:
public interface Processor {
 void process();
}
يستخدم تنفيذه الكثير من الموارد، ولكن في نفس الوقت لا يجوز استخدامه في كل مرة يتم فيها تشغيل التطبيق:
public class HiperDifficultProcessor implements Processor {
 @Override
 public void process() {
   // некоторый сверхсложная обработка данных
 }
}
فئة الوكيل:
public class HiperDifficultProcessorProxy implements Processor {
private HiperDifficultProcessor processor;

 @Override
 public void process() {
   if (processor == null) {
     processor = new HiperDifficultProcessor();
   }
   processor.process();
 }
}
دعونا تشغيله بشكل رئيسي :
Processor processor = new HiperDifficultProcessorProxy();
// тут тяжеловсеного оригинального an object, ещё не сущетсвует
// но при этом есть an object, который его представляет и у которого можно вызывать его методы
processor.process(); // лишь теперь, an object оригинал был создан
ألاحظ أن العديد من الأطر تستخدم الوكلاء، وبالنسبة للربيع ، فهذا هو النمط الرئيسي (يتم خياطة الربيع معه من الداخل والخارج). اقرأ المزيد عن هذا النمط هنا . تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 14 - 5

5. ما هي الابتكارات التي تم الإعلان عنها في Java 8؟

الابتكارات التي جلبتها Java 8 هي كما يلي:
  • تمت إضافة واجهات وظيفية، اقرأ عن نوع الوحش الموجود هنا .

  • تعبيرات Lambda، التي ترتبط ارتباطًا وثيقًا بالواجهات الوظيفية، اقرأ المزيد عن استخدامها هنا .

  • تمت إضافة Stream API للمعالجة المريحة لمجموعات البيانات، اقرأ المزيد هنا .

  • تمت إضافة روابط إلى الأساليب .

  • تمت إضافة طريقة forEach() إلى الواجهة القابلة للتكرار .

  • تمت إضافة واجهة برمجة تطبيقات التاريخ والوقت الجديدة في حزمة java.time ، والتحليل التفصيلي هنا .

  • تحسين واجهة برمجة التطبيقات المتزامنة .

  • إضافة فئة مجمعة اختيارية ، والتي يتم استخدامها للتعامل مع القيم الخالية بشكل صحيح، يمكنك العثور على مقالة ممتازة حول هذا الموضوع هنا .

  • إضافة قدرة الواجهات على استخدام الأساليب الثابتة والافتراضية (والتي، في جوهرها، تجعل Java أقرب إلى الوراثة المتعددة)، مزيد من التفاصيل هنا .

  • تمت إضافة طرق جديدة إلى فئة Collection(removeIf(), Spliterator()) .

  • تحسينات طفيفة على جافا كور.

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

6. ما هو التماسك العالي والاقتران المنخفض؟ أعط أمثلة.

التماسك العالي أو التماسك العالي هو المفهوم عندما تحتوي فئة معينة على عناصر ترتبط ارتباطًا وثيقًا ببعضها البعض ويتم دمجها لغرضها. على سبيل المثال، يجب أن تمثل كافة الأساليب الموجودة في فئة المستخدم سلوك المستخدم. يكون الفصل ذو تماسك منخفض إذا كان يحتوي على عناصر غير مرتبطة. على سبيل المثال، فئة المستخدم التي تحتوي على طريقة التحقق من صحة عنوان البريد الإلكتروني:
public class User {
private String name;
private String email;

 public String getName() {
   return this.name;
 }

 public void setName(final String name) {
   this.name = name;
 }

 public String getEmail() {
   return this.email;
 }

 public void setEmail(final String email) {
   this.email = email;
 }

 public boolean isValidEmail() {
   // некоторая логика валидации емейла
 }
}
قد تكون فئة المستخدم مسؤولة عن تخزين عنوان البريد الإلكتروني للمستخدم، ولكن ليس عن التحقق من صحته أو إرسال البريد الإلكتروني. لذلك، من أجل تحقيق تماسك عالٍ، نقوم بنقل طريقة التحقق من الصحة إلى فئة فائدة منفصلة:
public class EmailUtil {
 public static boolean isValidEmail(String email) {
   // некоторая логика валидации емейла
 }
}
ونستخدمه حسب الحاجة (على سبيل المثال، قبل حفظ المستخدم). الاقتران المنخفض أو الاقتران المنخفض هو مفهوم يصف الترابط المنخفض بين وحدات البرامج. في الأساس، الاعتماد المتبادل هو كيف أن تغيير أحدهما يتطلب تغيير الآخر. هناك فئتان لهما اقتران قوي (أو اقتران محكم) إذا كانا مرتبطين ارتباطًا وثيقًا. على سبيل المثال، هناك فئتان ملموستان تخزنان المراجع لبعضهما البعض وتستدعيان أساليب بعضهما البعض. من السهل تطوير وصيانة الفصول المترابطة بشكل فضفاض. وبما أنها مستقلة عن بعضها البعض، فيمكن تطويرها واختبارها بالتوازي. علاوة على ذلك، يمكن تغييرها وتحديثها دون التأثير على بعضها البعض. دعونا نلقي نظرة على مثال للفئات المقترنة بقوة. لدينا بعض فئة الطلاب:
public class Student {
 private Long id;
 private String name;
 private List<Lesson> lesson;
}
والذي يحتوي على قائمة الدروس:
public class Lesson {
 private Long id;
 private String name;
 private List<Student> students;
}
يحتوي كل درس على رابط لحضور الطلاب. قبضة قوية بشكل لا يصدق، ألا تعتقد ذلك؟ كيف يمكنك تقليله؟ أولاً، دعونا نتأكد من أن الطلاب ليس لديهم قائمة بالمواد الدراسية، بل قائمة بمعرفاتهم:
public class Student {
 private Long id;
 private String name;
 private List<Long> lessonIds;
}
ثانيًا، ليس من الضروري أن يعرف الفصل الدراسي عن جميع الطلاب، فلنحذف قائمتهم تمامًا:
public class Lesson {
 private Long id;
 private String name;
}
لذلك أصبح الأمر أسهل بكثير، وأصبح الاتصال أضعف بكثير، ألا تعتقد ذلك؟ تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 14 - 7

عفوًا

7. كيف يمكنك تنفيذ الوراثة المتعددة في جافا؟

الوراثة المتعددة هي سمة من سمات المفهوم الموجه للكائنات حيث يمكن للفئة أن ترث خصائص من أكثر من فئة رئيسية واحدة. تنشأ المشكلة عندما تكون هناك طرق بنفس التوقيع في كل من الفئة الفائقة والفئة الفرعية. عند استدعاء طريقة ما، لا يمكن للمترجم تحديد طريقة الفئة التي يجب استدعاؤها، وحتى عند استدعاء طريقة الفئة التي لها الأسبقية. لذلك، جافا لا تدعم الوراثة المتعددة! ولكن هناك نوع من الثغرة، والتي سنتحدث عنها بعد ذلك. كما ذكرت سابقًا، مع إصدار Java 8، تمت إضافة القدرة على استخدام الأساليب الافتراضية إلى الواجهات . إذا لم تتجاوز الفئة التي تنفذ الواجهة هذه الطريقة، فسيتم استخدام هذا التنفيذ الافتراضي (ليس من الضروري تجاوز الطريقة الافتراضية، مثل تنفيذ طريقة مجردة). في هذه الحالة، من الممكن تنفيذ واجهات مختلفة في فئة واحدة واستخدام أساليبها الافتراضية. لنلقي نظرة على مثال. لدينا بعض واجهات النشرة الإعلانية، باستخدام طريقة fly() الافتراضية :
public interface Flyer {
 default void fly() {
   System.out.println("Я лечу!!!");
 }
}
واجهة Walker، باستخدام طريقة walk() الافتراضية :
public interface Walker {
 default void walk() {
   System.out.println("Я хожу!!!");
 }
}
واجهة السباح باستخدام طريقة swim() :
public interface Swimmer {
 default void swim() {
   System.out.println("Я плыву!!!");
 }
}
حسنًا، دعونا الآن ننفذ كل هذا في فئة بط واحدة:
public class Duck implements Flyer, Swimmer, Walker {
}
ودعونا ننفذ جميع أساليب البط لدينا:
Duck donald = new Duck();
donald.walk();
donald.fly();
donald.swim();
في وحدة التحكم سوف نتلقى:
انا اذهب!!! أنا أطير!!! انا اسبح!!!
وهذا يعني أننا قد صورنا الميراث المتعدد بشكل صحيح، على الرغم من أن هذا ليس هو الحال. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 14 - 8وألاحظ أيضًا أنه إذا قام الفصل بتنفيذ واجهات ذات طرق افتراضية لها نفس أسماء الطرق ونفس الوسائط في هذه الطرق، فسيبدأ المترجم في الشكوى من عدم التوافق، لأنه لا يفهم الطريقة التي يجب استخدامها حقًا. هناك عدة طرق للخروج:
  • إعادة تسمية الأساليب في الواجهات بحيث تختلف عن بعضها البعض.
  • تجاوز هذه الأساليب المثيرة للجدل في فئة التنفيذ.
  • وراثة من الفصل الذي ينفذ هذه الأساليب المثيرة للجدل (ثم سيستخدم فصلك تطبيقه بالضبط).

8. ما الفرق بين الطرق النهائية والأخيرة والنهائية ()؟

Final هي كلمة أساسية تُستخدم لوضع قيد على فئة أو طريقة أو متغير، وهو معنى القيد:
  • بالنسبة للمتغير - بعد التهيئة الأولية، لا يمكن إعادة تعريف المتغير.
  • بالنسبة للطريقة، لا يمكن تجاوز الطريقة في فئة فرعية (الفئة اللاحقة).
  • بالنسبة للفئة - لا يمكن وراثة الفئة.
أخيرًا هي كلمة أساسية تسبق كتلة التعليمات البرمجية، وتُستخدم عند التعامل مع الاستثناءات، جنبًا إلى جنب مع كتلة محاولة ، ومعها (أو بالتبادل) مع كتلة التقاط. يتم تنفيذ التعليمات البرمجية الموجودة في هذه الكتلة في أي حال، بغض النظر عما إذا تم طرح استثناء أم لا. في هذا الجزء من المقالة، في السؤال 104، تتم مناقشة المواقف الاستثنائية التي لن يتم فيها تنفيذ هذه الكتلة. Finalize() هي طريقة من فئة الكائن ، يتم استدعاؤها قبل حذف كل كائن بواسطة أداة تجميع البيانات المهملة، وسيتم استدعاء هذه الطريقة (الأخيرة)، وتستخدم لتنظيف الموارد المشغولة. لمزيد من المعلومات حول أساليب فئة الكائن التي يرثها كل كائن، راجع السؤال رقم 11 في هذا الجزء من المقالة. حسنًا، هذا هو المكان الذي سننتهي فيه اليوم. نراكم في الجزء التالي! Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 14 - 9
مواد أخرى في السلسلة:
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION