JavaRush /مدونة جافا /Random-AR /أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية. الجزء 1
Roman Beekeeper
مستوى

أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية. الجزء 1

نشرت في المجموعة
مرحبًا بالجميع، سيداتي وسادتي مهندسي البرمجيات! دعونا نتحدث عن أسئلة المقابلة. حول ما تحتاج إلى الاستعداد له وما تحتاج إلى معرفته. وهذا سبب ممتاز لتكرار هذه النقاط أو دراستها من الصفر. أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 1لدي مجموعة واسعة إلى حد ما من الأسئلة المتداولة حول OOP، و Java Syntax، والاستثناءات في Java، والمجموعات وتعدد مؤشرات الترابط، والتي سأقسمها إلى عدة أجزاء من أجل الراحة. مهم:سنتحدث فقط عن إصدارات Java حتى الإصدار 8. ولن يتم أخذ جميع الابتكارات من 9 و10 و11 و12 و13 في الاعتبار هنا. نرحب بأي أفكار/تعليقات حول كيفية تحسين الإجابات . قراءة سعيدة، دعونا نذهب!

مقابلة جافا: أسئلة OOP

1. ما هي الميزات التي تمتلكها جافا؟

إجابة:
  1. مفاهيم OOP:

    1. اتجاه الكائن
    2. ميراث؛
    3. التغليف؛
    4. تعدد الأشكال؛
    5. التجريد.
  2. عبر الأنظمة الأساسية: يمكن تشغيل برنامج Java على أي نظام أساسي دون أي تعديلات. الشيء الوحيد الذي تحتاجه هو تثبيت JVM (جهاز جافا الظاهري).

  3. الأداء العالي: يتيح JIT (مترجم Just In Time) الأداء العالي. يقوم JIT بتحويل الرمز الثانوي إلى رمز الجهاز ثم يبدأ JVM في التنفيذ.

  4. تعدد مؤشرات الترابط: مؤشر ترابط التنفيذ المعروف باسم Thread. يقوم JVM بإنشاء مؤشر ترابط يسمى main thread. يمكن للمبرمج إنشاء سلاسل رسائل متعددة عن طريق الوراثة من فئة Thread أو عن طريق تنفيذ واجهة Runnable.

2. ما هو الميراث؟

الميراث يعني أن فئة واحدة يمكن أن ترث (" تمتد ") فئة أخرى. بهذه الطريقة يمكنك إعادة استخدام التعليمات البرمجية من الفئة التي ورثتها. تُعرف الفئة الموجودة باسم superclass، وتُعرف الفئة التي يتم إنشاؤها باسم subclass. ويقولون أيضا parentو child.
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
أين Animalهو parentو Dog- child.

3. ما هو التغليف؟

غالبًا ما يُطرح هذا السؤال أثناء مقابلات مطوري Java. يقوم التغليف بإخفاء التنفيذ باستخدام معدّلات الوصول، وذلك باستخدام الحروف والمحددات. يتم ذلك من أجل إغلاق الوصول للاستخدام الخارجي في تلك الأماكن التي يراها المطورون ضرورية. مثال يمكن الوصول إليه من الحياة هو السيارة. ليس لدينا إمكانية الوصول المباشر إلى تشغيل المحرك. بالنسبة لنا، المهمة هي وضع المفتاح في الإشعال وتشغيل المحرك. وما هي العمليات التي ستتم تحت الغطاء، ليس من شأننا. علاوة على ذلك، فإن تدخلنا في هذا النشاط يمكن أن يؤدي إلى موقف غير متوقع، حيث يمكننا كسر السيارة وإيذاء أنفسنا. بالضبط نفس الشيء يحدث في البرمجة. موصوفة بشكل جيد على ويكيبيديا . يوجد أيضًا مقال حول التغليف في JavaRush .

4. ما هو تعدد الأشكال؟

تعدد الأشكال هو قدرة البرنامج على استخدام الكائنات بنفس الواجهة دون معلومات حول النوع المحدد لذلك الكائن. كما يقولون، واجهة واحدة - العديد من التطبيقات. باستخدام تعدد الأشكال، يمكنك دمج واستخدام أنواع مختلفة من الكائنات بناءً على سلوكها المشترك. على سبيل المثال، لدينا فئة من الحيوانات، والتي لديها اثنين من أحفاد - الكلب والقط. لدى فئة الحيوانات العامة سلوك مشترك للجميع - إصدار صوت. في الحالة عندما نحتاج إلى تجميع كل أحفاد فئة الحيوان وتنفيذ طريقة "إصدار صوت"، فإننا نستخدم إمكانيات تعدد الأشكال. وهذا ما سيبدو عليه:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
لذا فإن تعدد الأشكال يساعدنا. علاوة على ذلك، ينطبق هذا أيضًا على الطرق متعددة الأشكال (المحملة بشكل زائد). ممارسة استخدام تعدد الأشكال

أسئلة المقابلة - بناء جملة جافا

5. ما هو المنشئ في جافا؟

الخصائص التالية صالحة:
  1. عند إنشاء كائن جديد، يستخدم البرنامج المنشئ المناسب للقيام بذلك.
  2. المنشئ يشبه الطريقة. وتكمن خصوصيتها في عدم وجود عنصر عائد (بما في ذلك الفراغ)، واسمها هو نفس اسم الفئة.
  3. إذا لم تتم كتابة أي مُنشئ بشكل صريح، فسيتم إنشاء مُنشئ فارغ تلقائيًا.
  4. يمكن تجاوز المنشئ.
  5. إذا تم إنشاء مُنشئ بمعلمات، ولكن هناك حاجة إليه أيضًا بدون معلمات، فيجب كتابته بشكل منفصل، لأنه لا يتم إنشاؤه تلقائيًا.

6. ما الفئتان اللتان لا ترثان من الكائن؟

لا تنخدع بالاستفزازات، فلا توجد مثل هذه الفئات: جميع الفئات مباشرة أو من خلال الأسلاف موروثة من فئة الكائن!

7. ما هو المتغير المحلي؟

سؤال شائع آخر أثناء مقابلة مطور Java. المتغير المحلي هو متغير يتم تعريفه داخل الطريقة ويظل موجودًا حتى لحظة تنفيذ الطريقة. بمجرد انتهاء التنفيذ، سيتوقف المتغير المحلي عن الوجود. فيما يلي برنامج يستخدم المتغير المحلي helloMessage في الطريقة الرئيسية ():
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8. ما هو المثيل المتغير؟

متغير المثيل هو متغير يتم تعريفه داخل فئة، وهو موجود حتى لحظة وجود الكائن. مثال على ذلك هو فئة Bee، التي تحتوي على متغيرين nectarCapacity وmaxNectarCapacity:
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. ما هي معدّلات الوصول؟

معدّلات الوصول هي أداة تسمح لك بتخصيص الوصول إلى الفئات والأساليب والمتغيرات. توجد المعدلات التالية، مرتبة حسب زيادة الوصول:
  1. private- تستخدم للطرق والحقول والمنشئين. مستوى الوصول هو فقط الفئة التي تم الإعلان عنها.
  2. package-private(default)- يمكن استخدامها للفصول الدراسية. الوصول فقط في حزمة محددة يتم فيها الإعلان عن فئة أو طريقة أو متغير أو مُنشئ.
  3. protected- نفس الوصول مثل package-private+ لتلك الفئات التي ترث من فئة مع المعدل protected.
  4. public- تستخدم أيضا للفصول الدراسية. الوصول الكامل في جميع أنحاء التطبيق بأكمله.
  5. أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 2

10. ما هي الأساليب المهيمنة؟

يحدث تجاوز الطريقة عندما يريد الطفل تغيير سلوك الفصل الأصلي. إذا كنت تريد تنفيذ ما هو موجود في الطريقة الأصلية، فيمكنك استخدام بناء مثل super.methodName() في الطريقة الفرعية، والتي ستقوم بعمل الطريقة الأصلية، وبعد ذلك فقط تضيف المنطق. المتطلبات الواجب توافرها:
  • يجب أن يكون توقيع الطريقة هو نفسه؛
  • يجب أن تكون قيمة الإرجاع هي نفسها.

11. ما هو توقيع الطريقة؟

أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 3توقيع الطريقة عبارة عن مجموعة من اسم الطريقة والوسائط التي تقبلها الطريقة. يعد توقيع الطريقة معرفًا فريدًا للطريقة عند التحميل الزائد للطرق.

12. ما هو أسلوب التحميل الزائد؟

التحميل الزائد للطريقة هو خاصية تعدد الأشكال حيث من خلال تغيير توقيع الطريقة، يمكنك إنشاء طرق مختلفة لنفس الإجراءات:
  • نفس اسم الطريقة؛
  • حجج مختلفة؛
  • قد يكون هناك نوع إرجاع مختلف.
على سبيل المثال، يمكن تحميل نفس add()الأمر ArrayListبشكل زائد على النحو التالي وسيتم تنفيذ عملية الإضافة بطريقة مختلفة، اعتمادًا على الوسيطات الواردة:
  • add(Object o)- ببساطة يضيف كائن؛
  • add(int index, Object o)— إضافة كائن إلى فهرس محدد؛
  • add(Collection<Object> c)— إضافة قائمة بالكائنات؛
  • add(int index, Collection<Object> c)— إضافة قائمة بالكائنات، بدءًا من فهرس معين.

13. ما هي الواجهة؟

لا يتم تنفيذ الوراثة المتعددة في Java، لذا للتغلب على هذه المشكلة، تمت إضافة الواجهات كما نعرفها؛) لفترة طويلة، كانت الواجهات تحتوي فقط على طرق دون تنفيذها. كجزء من هذه الإجابة، سنتحدث عنها. على سبيل المثال:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
بعض الفروق الدقيقة تتبع من هذا:
  • جميع الأساليب في الواجهة عامة ومجردة؛
  • جميع المتغيرات نهائية عامة ثابتة؛
  • الطبقات لا ترثها (تمتد)، ولكنها تنفذها (التطبيقات). علاوة على ذلك، يمكنك تنفيذ أي عدد تريده من الواجهات.
  • يجب أن توفر الفئات التي تنفذ واجهة ما تطبيقات لجميع الأساليب التي تحتوي عليها الواجهة.
مثله:
public class Cat implements Animal {
   public void makeSound() {
       // method implementation
   }

   public void eat() {
       // implementation
   }

   public void sleep() {
       // implementation
   }
}

14. ما هي الطريقة الافتراضية في الواجهة؟

الآن دعونا نتحدث عن الأساليب الافتراضية. لماذا ولمن؟ تمت إضافة هذه الأساليب لجعل كل شيء "لك ولنا". ما الذي أتحدث عنه؟ نعم، من ناحية، كان من الضروري إضافة وظائف جديدة: Lambdas، Stream API، من ناحية أخرى، كان من الضروري ترك ما تشتهر به Java - التوافق مع الإصدارات السابقة. للقيام بذلك، كان من الضروري تقديم حلول جاهزة للواجهات. هذه هي الطريقة التي جاءت إلينا الأساليب الافتراضية. أي أن الطريقة الافتراضية هي طريقة مطبقة في الواجهة التي تحتوي على الكلمة الأساسية default. على سبيل المثال، الطريقة المعروفة stream()في Collection. التحقق من ذلك، هذه الواجهة ليست بسيطة كما تبدو؛). أو أيضًا طريقة معروفة بنفس القدر forEach()من Iterable. كما أنها لم تكن موجودة حتى تمت إضافة الطرق الافتراضية. بالمناسبة، يمكنك أيضًا القراءة عن هذا على JavaRush .

15. كيف إذن ترث طريقتين افتراضيتين متطابقتين؟

بناءً على الإجابة السابقة حول ماهية الطريقة الافتراضية، يمكنك طرح سؤال آخر. إذا كان بإمكانك تنفيذ الأساليب في الواجهات، فمن الناحية النظرية يمكنك تنفيذ واجهتين بنفس الطريقة، وكيف تفعل ذلك؟ هناك واجهتان مختلفتان بنفس الطريقة:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
وهناك فئة تنفذ هاتين الواجهتين. لتجنب عدم اليقين وتجميع التعليمات البرمجية، نحتاج إلى تجاوز الطريقة الموجودة foo()في الفصل C، ويمكننا ببساطة استدعاء طريقة foo()أي من الواجهات الموجودة فيه - Aأو B. ولكن كيف يتم اختيار طريقة واجهة معينة Аأو В؟ هناك هيكل مثل هذا لهذا A.super.foo():
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
أو:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
وبالتالي، ستستخدم طريقة foo()الفصل إما الطريقة الافتراضية من الواجهة أو الطريقة من الواجهة . Cfoo()Afoo()B

16. ما هي الأساليب والطبقات المجردة؟

تحتوي Java على كلمة محجوزة abstractتُستخدم للإشارة إلى الفئات والأساليب المجردة. أولا، بعض التعريفات. الطريقة المجردة هي طريقة يتم إنشاؤها بدون تطبيق باستخدام كلمة أساسية abstractفي فئة مجردة. أي أن هذه طريقة كما في الواجهة، فقط مع إضافة كلمة رئيسية، على سبيل المثال:
public abstract void foo();
الطبقة المجردة هي فئة تحتوي أيضًا على abstractالكلمة:
public abstract class A {

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

17. ما الفرق بين String وString Builder وString Buffer؟

يتم تخزين القيم Stringفي تجمع سلسلة ثابتة. بمجرد إنشاء صف، سوف يظهر في هذا التجمع. ولن يكون من الممكن حذفه. على سبيل المثال:
String name = "book";
... سيشير المتغير إلى تجمع السلاسل تجمع السلاسل الثابت أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 4 إذا قمت بتعيين اسم المتغير إلى قيمة مختلفة، فستحصل على ما يلي:
name = "pen";
تجمع سلسلة ثابت أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 5لذلك ستبقى هاتان القيمتان هناك. المخزن المؤقت للسلسلة:
  • يتم تخزين القيم Stringعلى المكدس. إذا تم تغيير القيمة، فسيتم استبدال القيمة الجديدة بالقيمة القديمة؛
  • String Bufferمتزامنة وبالتالي آمنة للخيط؛
  • نظرًا لسلامة الخيط، فإن سرعة التشغيل تترك الكثير مما هو مرغوب فيه.
مثال:
StringBuffer name = "book";
أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 6بمجرد تغيير قيمة الاسم، تتغير القيمة الموجودة في المكدس: أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 7StringBuilder تمامًا مثل StringBuffer، ولكنها ليست آمنة لمؤشر الترابط. ولذلك، فمن الواضح أن سرعته أعلى مما كانت عليه في StringBuffer.

18. ما الفرق بين الفئة المجردة والواجهة؟

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

19. لماذا يستغرق الوصول إلى عنصر في المصفوفة O(1)؟

هذا السؤال هو حرفيا من المقابلة الأخيرة. كما تعلمت لاحقا، يتم طرح هذا السؤال لمعرفة كيف يفكر الشخص. من الواضح أن هناك القليل من المعنى العملي في هذه المعرفة: فمجرد معرفة هذه الحقيقة يكفي. أولاً، نحتاج إلى توضيح أن O(1) هو تعيين للتعقيد الزمني للخوارزمية عندما تتم العملية في وقت ثابت. أي أن هذا التصنيف هو أسرع تنفيذ. للإجابة على هذا السؤال، علينا أن نفهم ما نعرفه عن المصفوفات؟ لإنشاء مصفوفة intيجب أن نكتب ما يلي:
int[] intArray = new int[100];
يمكن استخلاص عدة استنتاجات من هذا التسجيل:
  1. عند إنشاء مصفوفة، يكون نوعها معروفًا، وإذا كان النوع معروفًا، فمن الواضح حجم كل خلية في المصفوفة.
  2. ومن المعروف حجم المصفوفة.
ويترتب على ذلك: لفهم الخلية التي تريد الكتابة إليها، ما عليك سوى حساب منطقة الذاكرة التي تريد الكتابة إليها. بالنسبة للسيارة، لا يمكن أن يكون الأمر أسهل. يحتوي الجهاز على بداية الذاكرة المخصصة وعدد من العناصر وحجم خلية واحدة. ومن هذا يتضح أن مساحة التسجيل ستكون مساوية لموقع بداية المصفوفة + حجم الخلية مضروباً في حجمها.

كيف يمكنك الحصول على O(1) في الوصول إلى الكائنات في ArrayList؟

هذا السؤال يتبع مباشرة السؤال السابق. صحيح أنه عندما نعمل مع مصفوفة وتكون هناك عناصر أولية، فإننا نعرف مسبقًا حجم هذا النوع عند إنشائه. ولكن ماذا لو كان هناك مخطط مثل الذي في الصورة: أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 1 - 8ونريد إنشاء مجموعة تحتوي على عناصر من النوع A، وإضافة تطبيقات مختلفة - B، C، D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
في هذه الحالة، كيف يمكنك فهم حجم كل خلية، لأن كل كائن سيكون مختلفًا وقد يحتوي على حقول إضافية مختلفة (أو سيكون مختلفًا تمامًا). ما يجب القيام به؟ وهنا يطرح السؤال بطريقة تثير الحيرة والارتباك. نحن نعلم أنه في الواقع، لا تقوم المجموعة بتخزين الكائنات، ولكنها ترتبط فقط بهذه الكائنات. وجميع الروابط لها نفس الحجم، وهذا معروف. إذن، عد المساحة هنا يعمل بنفس الطريقة المتبعة في السؤال السابق.

21. التشغيل التلقائي والفتح

الخلفية التاريخية: يعد autoboxing وautounboxing أحد الابتكارات الرئيسية لـ JDK 5. Autoboxing هي عملية التحويل التلقائي من نوع بدائي إلى فئة الغلاف المناسبة. إلغاء الملاكمة التلقائي - يقوم بعكس الملاكمة التلقائية تمامًا - يحول فئة المجمع إلى فئة بدائية. ولكن إذا كانت هناك قيمة مجمعة null، فسيتم طرح استثناء أثناء التفريغ NullPointerException.

مطابقة البدائية - المجمع

بدائية الطبقة المجمعة
منطقية منطقية
كثافة العمليات عدد صحيح
بايت بايت
شار شخصية
يطفو يطفو
طويل طويل
قصير قصير
مزدوج مزدوج

تحدث التعبئة التلقائية:

  • عند تعيين مرجع بدائي لفئة المجمع:

    قبل جافا 5:

    //manual packaging or how it was BEFORE Java 5.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // and so on to other types
    }
    
    после Java 5:
    //automatic packaging or how it became in Java 5.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // and so on to other types
    }
  • عند تمرير عنصر بدائي كوسيطة إلى طريقة تتوقع غلافًا:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

يحدث التفريغ التلقائي:

  • عندما نقوم بتعيين متغير بدائي لفئة المجمع:

    //before Java 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    //and after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • في الحالات التي تحتوي على عمليات حسابية. إنها تنطبق فقط على الأنواع البدائية، ولهذا عليك القيام بالتفريغ إلى الأنواع البدائية.

    // Before Java 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // for comparison it was necessary to do this:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • عند تمريرها إلى غلاف بطريقة تقبل البدائية المقابلة:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22. ما هي الكلمة الرئيسية النهائية وأين يمكن استخدامها؟

يمكن استخدام الكلمة الأساسية finalللمتغيرات والأساليب والفئات.
  1. لا يمكن إعادة تعيين المتغير النهائي إلى كائن آخر.
  2. الطبقة النهائية عقيمة)) لا يمكن أن يكون لها ورثة.
  3. لا يمكن تجاوز الطريقة النهائية على سلف.
لقد غطينا الجزء العلوي، والآن دعونا نناقشه بمزيد من التفصيل.

المتغيرات النهائية

توفر لنا Java طريقتين لإنشاء متغير وتعيين بعض القيمة له:
  1. يمكنك الإعلان عن متغير وتهيئته لاحقًا.
  2. يمكنك الإعلان عن متغير وتعيينه على الفور.
مثال باستخدام المتغير النهائي لهذه الحالات:
public class FinalExample {

   //final static variable, which is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final is a variable that is not initialized, but will only work if
   //initialize this in the constructor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // final field Config.creationTime cannot be assigned
//    finalExample.creationTime = 1L;
   }
}

هل يمكن اعتبار المتغير النهائي ثابتا؟

وبما أننا لن نتمكن من تعيين قيمة جديدة للمتغير النهائي، فيبدو أن هذه متغيرات ثابتة. ولكن هذا فقط للوهلة الأولى. إذا كان نوع البيانات الذي يشير إليه المتغير هو immutableنعم، فهو ثابت. لكن إذا كان نوع البيانات mutableقابلاً للتغيير، فباستخدام الأساليب والمتغيرات سيكون من الممكن تغيير قيمة الكائن الذي finalيشير إليه المتغير، وفي هذه الحالة لا يمكن تسميته ثابتًا. لذلك، يوضح المثال أن بعض المتغيرات النهائية هي في الواقع ثوابت، ولكن بعضها ليس كذلك، ويمكن تغييرها.
public class FinalExample {

   //immutable final variables:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // mutable filter variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

المتغيرات النهائية المحلية

عندما finalيتم إنشاء متغير داخل طريقة، فإنه يسمى local finalمتغير:
public class FinalExample {

   public static void main(String[] args) {
       // This is how you can
       final int minAgeForDriveCar = 18;

       // or you can do it this way, in the foreach loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
يمكننا استخدام الكلمة الأساسية finalفي حلقة ممتدة forلأنه بعد إكمال تكرار الحلقة، forيتم إنشاء متغير جديد في كل مرة. لكن هذا لا ينطبق على حلقة for العادية، لذا فإن الكود أدناه سوف يلقي خطأ في وقت الترجمة.
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

الطبقة النهائية

لا يمكنك تمديد فئة تم تعريفها كـ final. ببساطة، لا يمكن لأي فئة أن ترث من هذه الطبقة. من الأمثلة الرائعة finalعلى الفصل الدراسي في JDK String. الخطوة الأولى لإنشاء فئة غير قابلة للتغيير هي وضع علامة عليها finalبحيث لا يمكن توسيعها:
public final class FinalExample {
}

// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}

الطرق النهائية

عندما يتم وضع علامة نهائية على طريقة ما، فإنها تسمى طريقة نهائية (منطقية، أليس كذلك؟). لا يمكن تجاوز الطريقة النهائية في فئة سليل. بالمناسبة، الطرق الموجودة في فئة الكائن - wait() و notify() - نهائية، لذلك ليس لدينا الفرصة لتجاوزها.
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // compile error here
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

كيف وأين يتم استخدام Final في Java

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

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

متقلب

Mutable هي كائنات يمكن تغيير حالاتها ومتغيراتها بعد إنشائها. على سبيل المثال، فئات مثل StringBuilder وStringBuffer. مثال:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // this setter can change the name field
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("first address");
       System.out.println(obj.getAddress());

       // update the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

غير قابل للتغيير

غير قابل للتغيير هي الكائنات التي لا يمكن تغيير حالاتها ومتغيراتها بعد إنشاء الكائن. لماذا لا يكون مفتاحًا رائعًا لـ HashMap، أليس كذلك؟) على سبيل المثال، String وInteger وDouble وما إلى ذلك. مثال:
// make this class final so no one can change it
public final class ImmutableExample {

   private String address;

   ImmutableExample (String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   //remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Therefore, do not change this field in any way, so this is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24. كيف تكتب فئة غير قابلة للتغيير؟

بعد أن تعرف ما هي الكائنات القابلة للتغيير وغير القابلة للتغيير، فإن السؤال التالي طبيعي - كيف تكتبه؟ لكتابة فئة غير قابلة للتغيير، عليك اتباع خطوات بسيطة:
  • جعل الفصل النهائي.
  • جعل كافة الحقول خاصة وإنشاء الحروف فقط لهم. ليست هناك حاجة إلى المستوطنين بالطبع.
  • اجعل كافة الحقول القابلة للتغيير نهائية بحيث لا يمكن تعيين القيمة إلا مرة واحدة.
  • تهيئة جميع الحقول من خلال المُنشئ، وإجراء نسخة عميقة (أي نسخ الكائن نفسه، ومتغيراته، ومتغيرات المتغيرات، وما إلى ذلك)
  • استنساخ الكائنات المتغيرة القابلة للتغيير في الحروف لإرجاع نسخ من القيم فقط وليس مراجع للكائنات الفعلية.
مثال:
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {

   private final int age;

   private final String name;

   private final HashMap<String, String> addresses;

   public int getAge() {
       return age;
   }


   public String getName() {
       return name;
   }

   /**
    * Clone the object before returning it.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * In the constructor, deep copy the mutable objects.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println("Performing a deep copy in the constructor");
       this.age = age;
       this.name = name;
       HashMap<String, String> temporaryMap = new HashMap<>();
       String key;
       Iterator<String> iterator = addresses.keySet().iterator();
       while (iterator.hasNext()) {
           key = iterator.next();
           temporaryMap.put(key, addresses.get(key));
       }
       this.addresses = temporaryMap;
   }
}
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION