CodeGym /مدونة جافا /Random-AR /استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java. ...
John Squirrels
مستوى
San Francisco

استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java. الجزء 11

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

97. هل تنطبق أي قواعد عند تجاوز يساوي ()؟

عند تجاوز طريقة يساوي () يجب عليك الالتزام بالقواعد التالية:
  • الانعكاسية — لأي قيمة x ، يجب أن تُرجع x.equals(x) دائمًا القيمة true (حيث x != null ).

  • التناظر - لأي قيم x و y ، يجب أن تُرجع x.equals(y) صحيحًا فقط إذا أعادت y.equals(x) صحيحًا .

  • العبورية - لأي قيم x و y و z ، إذا كانت x.equals(y) تُرجع true و y.equals(z) تُرجع true أيضًا ، فيجب أن تُرجع x.equals(z) true .

  • الاتساق - بالنسبة لأي قيمتين x و y ، فإن استدعاء x.equals(y) بشكل متكرر سيعيد دائمًا نفس القيمة طالما أن الحقول المستخدمة لمقارنة الكائنين لم تتغير بين كل استدعاء.

  • مقارنة فارغة - لأي قيمة x ، يجب أن يؤدي استدعاء x.equals(null) إلى إرجاع false .

98. ماذا يحدث إذا لم تقم بتجاوز يساوي () و hashCode ()؟

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

99. لماذا لا يتم استيفاء شرط التناظر إلا إذا كانت قيمة x.equals(y) صحيحة؟

هذا السؤال غريب بعض الشيء. إذا كان الكائن A يساوي الكائن B، فإن الكائن B يساوي الكائن A. وإذا كان B لا يساوي الكائن A، فكيف يمكن أن يكون العكس ممكنًا؟ هذا هو المنطق السليم.

100. ما هو تصادم HashCode؟ كيف تتعامل مع ذلك؟

يحدث تصادم HashCode عندما يكون لكائنين مختلفين نفس HashCode . كيف يعقل ذلك؟ حسنًا، يتم تعيين رمز التجزئة إلى عدد صحيح، والذي يتراوح نطاقه من -2147483648 إلى 2147483647. أي أنه يمكن أن يكون واحدًا من حوالي 4 مليارات عدد صحيح مختلف. هذا النطاق ضخم ولكنه ليس لانهائيًا. وهذا يعني أن هناك مواقف قد يكون فيها لكائنين مختلفين تمامًا نفس رمز التجزئة. إنه أمر مستبعد للغاية، ولكنه ممكن. يمكن لوظيفة التجزئة التي يتم تنفيذها بشكل سيء أن تجعل رموز التجزئة المتطابقة أكثر تكرارًا عن طريق إرجاع الأرقام في نطاق صغير، وبالتالي زيادة فرصة الاصطدامات. لتقليل التصادمات، يجب أن يكون لديك تنفيذ جيد لطريقة HashCode التي تنشر القيم بشكل موحد وتقلل من فرصة القيم المتكررة.

101. ماذا يحدث إذا تغيرت قيمة العنصر المشارك في عقد hashCode؟

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

102. اكتب أساليب يساوي () و hashCode () لفصل الطالب الذي يحتوي على حقول اسم السلسلة وعمر int.

public class Student {
int age;
String name;

 @Override
 public boolean equals(final Object o) {
   if (this == o) {
     return true;
   }
   if (o == null || this.getClass() != o.getClass()) {
     return false;
   }

   final Student student = (Student) o;

   if (this.age != student.age) {
     return false;
   }
   return this.name != null ? this.name.equals(student.name) : student.name == null;
 }

 @Override
 public int hashCode() {
   int result = this.age;
   result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
   return result;
 }
}
يساوي ():
  • أولاً، نقوم بمقارنة المراجع مباشرة، لأنه إذا كانت المراجع تشير إلى نفس الكائن، فما الفائدة من الاستمرار في التحقق من المساواة؟ ونحن نعلم بالفعل أن النتيجة ستكون صحيحة .

  • نتحقق من وجود null وما إذا كانت أنواع الفئات متماثلة لأنه إذا كانت المعلمة null أو من نوع آخر، فلا يمكن أن تكون الكائنات متساوية، ويجب أن تكون النتيجة false .

  • لقد قمنا بتحويل المعلمة إلى نفس النوع (وبعد كل شيء، ماذا لو كان كائنًا من النوع الأصلي).

  • نقوم بمقارنة الحقول البدائية (المقارنة باستخدام =! ستكون كافية). إذا لم يكونوا متساوين، نعود كاذبة .

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

رمز التجزئة () :
  • قمنا بتعيين القيمة الأولية لرمز التجزئة مساوية لقيمة حقل عمر الكائن .

  • نقوم بضرب رمز التجزئة الحالي في 31 (لتوزيع أكبر للقيم) ثم نضيف رمز التجزئة لحقل السلسلة غير البدائي (إذا لم يكن فارغًا).

  • نعيد النتيجة.

  • تجاوز الطريقة بهذه الطريقة يعني أن الكائنات التي لها نفس الاسم وقيم int ستُرجع دائمًا نفس رمز التجزئة.

103. ما الفرق بين استخدام "if (obj مثيل الطالب)" و"if (getClass() == obj.getClass())"؟

دعونا نلقي نظرة على ما يفعله كل تعبير:
  • يتحقق المثيل مما إذا كان مرجع الكائن الموجود على الجانب الأيسر هو مثيل للنوع الموجود على الجانب الأيمن أو أحد أنواعه الفرعية.

  • "getClass() == ..." يتحقق مما إذا كانت الأنواع متماثلة.

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

104. أعط وصفًا موجزًا ​​لطريقة الاستنساخ ().

تنتمي طريقة clone () إلى فئة الكائن . والغرض منه هو إنشاء وإرجاع نسخة (نسخة) من الكائن الحالي. استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java.  الجزء 11 - 2لاستخدام هذه الطريقة، تحتاج إلى تنفيذ واجهة العلامة القابلة للاستنساخ :
Student implements Cloneable
وتجاوز طريقة clone() نفسها:
@Override
protected Object clone() throws CloneNotSupportedException {
 return super.clone();
}
بعد كل شيء، فهو محمي في فئة الكائن ، أي أنه سيكون مرئيًا فقط داخل فئة الطالب ولن يكون مرئيًا للفئات الخارجية.

105. ما هي الاعتبارات الخاصة التي يجب أن تضعها في الاعتبار فيما يتعلق بطريقة clone() والمتغيرات المرجعية في الكائن؟

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

الاستثناءات

106. ما الفرق بين الخطأ والاستثناء؟

الاستثناءات، وكذلك الأخطاء، هي فئات فرعية من Throwable . ومع ذلك، لديهم اختلافاتهم. يشير الخطأ إلى مشكلة تحدث بشكل أساسي بسبب نقص موارد النظام. ويجب ألا يرى تطبيقنا هذه الأنواع من المشكلات. تتضمن أمثلة هذه الأخطاء تعطل النظام وخطأ نفاد الذاكرة. تحدث الأخطاء غالبًا في وقت التشغيل، نظرًا لعدم تحديدها. استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java.  الجزء 11 - 3الاستثناءات هي المشاكل التي يمكن أن تحدث في وقت التشغيل وفي وقت الترجمة. تنشأ هذه المشكلات عادةً في الكود الذي نكتبه كمطورين. وهذا يعني أن هذه الاستثناءات أكثر قابلية للتنبؤ بها وأكثر اعتمادًا علينا. وعلى النقيض من ذلك، فإن الأخطاء أكثر عشوائية وأكثر استقلالية عنا. وبدلاً من ذلك، فهي تعتمد على مشاكل في النظام الذي يعمل عليه تطبيقنا.

107. ما الفرق بين المحدد، وغير المحدد، والاستثناء، والرمي، والرميات؟

كما قلت سابقًا، الاستثناء هو خطأ في وقت التشغيل أو وقت الترجمة يحدث في التعليمات البرمجية المكتوبة بواسطة المطور (بسبب بعض المواقف غير الطبيعية). المحدد هو ما نسميه الاستثناءات التي يجب أن تتعامل معها الطريقة دائمًا باستخدام آلية محاولة الالتقاط أو إعادة الرمي إلى طريقة الاستدعاء. يتم استخدام الكلمة الأساسية throws في رأس الطريقة للإشارة إلى الاستثناءات التي قد تطرحها الطريقة. بمعنى آخر، يوفر لنا آلية لطرح استثناءات لأسلوب الاستدعاء. لا يلزم معالجة الاستثناءات التي لم يتم التحقق منها. تميل إلى أن تكون أقل قابلية للتنبؤ بها وأقل احتمالًا. ومع ذلك، يمكنك التعامل معهم إذا كنت تريد ذلك. نستخدم الرمي عند طرح استثناء يدويًا، على سبيل المثال:
throw new Exception();

108. ما هو التسلسل الهرمي للاستثناء؟

التسلسل الهرمي للاستثناء واسع جدًا. هناك الكثير مما يمكن وصفه بشكل مناسب هنا. لذلك، بدلاً من ذلك، سننظر فقط في فروعها الرئيسية: استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java.  الجزء 11 - 4 هنا، في أعلى التسلسل الهرمي، نرى فئة Throwable ، وهي السلف العام للتسلسل الهرمي للاستثناء وتنقسم بدورها إلى:
  • الأخطاء - المشاكل الحرجة التي لم يتم التحقق منها.
  • الاستثناءات - الاستثناءات التي يمكن التحقق منها.
تنقسم الاستثناءات إلى استثناءات مختلفة لوقت التشغيل لم يتم التحقق منها واستثناءات متعددة محددة.

109. ما هي الاستثناءات المحددة وغير المحددة؟

كما قلت من قبل:
  • الاستثناءات المحددة هي استثناءات يجب عليك التعامل معها بطريقة ما. أي أنه يجب عليك إما التعامل معها في كتلة محاولة الالتقاط أو رميها بالطريقة المذكورة أعلاه. للقيام بذلك، بعد إدراج وسيطات الطريقة في توقيع الطريقة، استخدم throws <نوع الاستثناء> للإشارة إلى أن الطريقة يمكنها طرح هذا الاستثناء. هذا يشبه إلى حد ما تحذيرًا، حيث ينبه طريقة الاستدعاء إلى أنها يجب أن تتحمل مسؤولية معالجة هذا الاستثناء.

  • لا يلزم معالجة الاستثناءات التي لم يتم التحقق منها، حيث لا يتم التحقق منها في وقت الترجمة وعادة ما تكون غير قابلة للتنبؤ بها. يتمثل الاختلاف الرئيسي بينهما مع الاستثناءات المحددة في أن التعامل معها باستخدام كتلة محاولة الالتقاط أو عن طريق إعادة الرمي هو أمر اختياري وليس إلزاميًا.

101. اكتب مثالاً حيث تستخدم كتلة محاولة الالتقاط لالتقاط الاستثناء والتعامل معه.

try{                                                 // Start of the try-catch block
 throw new Exception();                             // Manually throw an exception
} catch (Exception e) {                              // Exceptions of this type and its subtypes will be caught
 System.out.println("Oops! Something went wrong =("); // Display the exception
}

102. اكتب مثالاً يمكنك من خلاله التقاط الاستثناءات المخصصة الخاصة بك والتعامل معها.

أولاً، دعنا نكتب فئة الاستثناء الخاصة بنا والتي ترث الاستثناء وتتجاوز منشئه الذي يأخذ رسالة خطأ كوسيطة:
public class CustomException extends Exception {

 public CustomException(final String message) {
   super(message);
 }
}
بعد ذلك، سنقوم برمي واحدة يدويًا والتقاطها تمامًا كما فعلنا في مثال السؤال السابق:
try{
 throw new CustomException("Oops! Something went wrong =(");
} catch (CustomException e) {
 System.out.println(e.getMessage());
}
مرة أخرى، عندما نقوم بتشغيل الكود الخاص بنا، نحصل على الإخراج التالي:
أُووبس! حدث خطأ ما =(
استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java.  الجزء 11 - 5حسنا، هذا كل شيء لهذا اليوم! نراكم في الجزء التالي!
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION