الفصل النهائي
عندما يتم تعريف الفئة على أنها فئة نهائية ، فهذا يعني أنه لا يمكن أن يتم مورث الفصل بواسطة فئات أخرى ، أي أنه لا يمكن استخدامه بعد تمديده. وإلا ستحصل على خطأ أثناء التجميع.
حزمة com.iderzheng.finalkeyword ؛ الفئة النهائية العامة النهائية {} // error: لا يمكن أن يرث من chinalsclass packageclass يمتد النهائيات {} تدعم Java تعريف الفصل على أنه نهائي ، والذي يبدو أنه ينتهك المبادئ الأساسية للبرمجة الموجهة للكائنات. ومع ذلك ، من ناحية أخرى ، يضمن الفئة المرفقة أيضًا أن تكون جميع طرق الفصل ثابتة ولن يكون هناك تجاوزات فئة فرعية يتم تحميلها ديناميكيًا. هذا يوفر المزيد من الاحتمالات لتحسين المترجم. أفضل مثال هو سلسلة ، وهي الفئة النهائية. يمكن لمجمول Java تشغيل ثوابت السلسلة مباشرة (تلك الواردة في عروض أسعار مزدوجة) إلى كائنات سلسلة ، وفي الوقت نفسه قم بتحسين تشغيل التشغيل + المشغل مباشرة إلى ثوابت جديدة ، لأن التعديل النهائي يضمن عدم وجود فئات فرعية تُرجع قيمًا مختلفة لعمليات الربط.
بالنسبة لجميع تعريفات الفئات المختلفة - يمكن تعديل فئات المستوى الأعلى (العالمي أو الحزمة المرئية) ، ويمكن تعديل فئات متداخلة (فئات متداخلة داخلية أو ثابتة) مع النهائي. ومع ذلك ، بشكل عام ، يتم استخدام النهائي في الغالب لتعديل الفئات المعرّفة على أنها عامة ، لأنه بالنسبة للصفوف غير العارضة ، فإن معدلات الوصول قد تقيد وضوحها ، ومن الصعب بالفعل أن ترث هذه الفئات ، لذلك ليست هناك حاجة لإضافة طبقة من القيود النهائية.
وذكر أيضًا أنه على الرغم من أنه لا يمكن مورث الفصول المجهولة ، إلا أنها لا تقتصر على المترجم النهائي.
استيراد java.lang.reflect.modifier ؛ الفئة العامة الرئيسية {public static void main (string [] args) {RunNable Anonymous = new RunNable () {Override public void run () {}} ؛ System.out.println (modifier.isfinal (Anonymous.getClass (). getModiFiers ())) ؛ }}الإخراج:
خطأ شنيع
الطريقة النهائية
يرتبط ارتباطًا وثيقًا بمفهوم الميراث هو تعدد الأشكال ، والذي يتضمن الفرق بين مفاهيم التجاوز والإخفاء (للراحة ، يسمى ما يلي مجتمعة "إعادة الكتابة"). ومع ذلك ، على عكس تعريف الطريقة في C ++ ما إذا كانت الكلمة الرئيسية الافتراضية قد تمت إضافة إلى الفئة الفرعية ، ما إذا كانت طريقة توقيع الفئة الفرعية مكتوبة أو مخفية ، في Java ، تستخدم الفئات الفرعية نفس توقيع الطريقة للكتابة على طريقة الفئة الأصل ، والتي ستشكل طريقة فئة مخفية (طريقة ثابتة) ، في حين أن طرق الكائن (طريقة غير طبيعية) فقط فوق الكتابة. نظرًا لأن Java يسمح بالوصول المباشر إلى طرق الفصل من خلال الكائنات ، فإن Java لا تسمح بطرق الفصل وأساليب الكائن أن يكون لها نفس التوقيع في نفس الفئة.
تحدد الفئة النهائية أنه لا يمكن مورث الفئة بأكملها ، وهذا يعني أيضًا أنه لا يمكن تغطية جميع الطرق في الفصل وإخفائها بواسطة الفئات الفرعية. عندما لا يتم تعديل الفصل بواسطة النهائي ، لا يزال من الممكن تعديل بعض الطرق باستخدام النهائي لمنع إعادة كتابة هذه الطرق بواسطة الفئات الفرعية.
وبالمثل ، فإن مثل هذا التصميم يدمر تعدد الأشكال الموجهة للكائنات ، لكن الطريقة النهائية يمكن أن تضمن حتمية تنفيذها ، وبالتالي ضمان استقرار مكالمات الطريقة. في بعض التصميمات الإطارية ، غالبًا ما يُنظر إلى بعض الأساليب المنفذة للفئات التجريدية على النهائي ، لأن بعض رمز السائق في الإطار ستعتمد على هذه الأساليب لتحقيق الأهداف المحددة ، لذلك لا توجد فئات فرعية تغطيها.
يوضح المثال التالي دور التعديل النهائي في أنواع مختلفة من الطرق:
Package com.iderzheng.other ؛ الفئة العامة FinalMethods {public static void publicstaticmethod () {} public void publicfinisthod () {} public static final finalstaticfinalmethod () {} final void stitedmethod (} {} protative foid foid staticfinalmethod () void staticfinalmethod () {} private static void privatestaticfinalmethod () {} private final void privatefinalmethod () {}} package com.iderzheng.finkyword ؛ استيراد com.iderzheng.other.finalmethods ؛ تمتد أساليب الفئة العامة على FinalMethods {public static void publicstaticmethod () {} // error: لا يمكن تجاوز public publicfinalfinalmethod () {} // error: لا يمكن تجاوز publicstatin static publicfinal static final {}. void publicstaticfinalmethod () {} // error: لا يمكن تجاوز الفراغ النهائي المحمي المحمي FinalMethod () {} // error: لا يمكن أن يتجاوز الفراغ النهائي المحمي المحمي staticfinalmethod () {} finalmethod finalmethod () {} {} private void privatefinalmethod () {}} بادئ ذي بدء ، لاحظ أنه في المثال أعلاه ، يتم تعريف methods النهائية والأساليب تحت حزم مختلفة. بالنسبة لأول أول StaticMethod ، نجحت الفئة الفرعية في إعادة كتابة الطريقة الثابتة للفئة الأصل ، ولكن لأنها طريقة ثابتة ، فإن ما يحدث هو في الواقع "مخفي". على وجه التحديد ، ستنفيذ Methods.PublicStaticMethod () التنفيذ في فئة الأساليب. عند استدعاء FinalMethods.PublicStaticMethod () ، لن يحدث التنفيذ مع التحميل متعدد الأشكال للكاسورة الفرعية ، ولكنه سيستخدم مباشرة تنفيذ Methods. لذلك ، عند استخدام الفئات الفرعية للوصول إلى الأساليب ، يتم إخفاء رؤية الأساليب الموقعة من فئة الأصل.
بالنسبة للطريقة العالمية PublicFinalMethod ، كما هو موضح في طريقة التعديل النهائي ، يُمنع الفئة الفرعية من الكتابة فوقها ، وسيتم طرح استثناء في وقت الترجمة. ومع ذلك ، فإن اسم الطريقة هو نفسه في الفئة الفرعية ، ولكن يحتوي على معلمة ، مثل: PublicFinalMethod (السلسلة X) على ما يرام ، لأن هذا هو توقيع الطريقة المتزامنة.
في Intellij ، تُظهر IDE تحذيرًا لـ PublicStaticFinalMethod: طريقة "ثابتة" معلنة "نهائية". يبدو أنه لا لزوم له ، ولكن من المثال ، يمكن ملاحظة أن النهائي يحظر أيضًا تعريفات الفئة الفرعية من الطرق الثابتة لإخفائها. في التطور الفعلي ، فإن سلوك تعريف نفس الأساليب الثابتة للطبقة الفرعية والفئات الوالدين أمر مرغوب فيه للغاية ، لأن الأساليب المخفية تتطلب من المطورين الانتباه إلى استخدام أسماء فصول مختلفة لتحديد تأثيرات مختلفة ، والتي من السهل التسبب في أخطاء. علاوة على ذلك ، في الفصل ، يمكنك استدعاء طرق ثابتة مباشرة دون استخدام اسم الفصل. عندما يرث المطور مرة أخرى ، قد لا يلاحظ الوجود الخفي. بشكل افتراضي ، عند استخدام طريقة الفئة الأصل ، سيجد أنها ليست النتيجة المتوقعة. لذلك ، يجب أن تكون الأساليب الثابتة نهائية بشكل افتراضي ولا ينبغي إخفاؤها ، لذلك يعتقد IDE أنه تعديل غير ضروري.
إن التعديل المحمي والتعديل العام في الفئة الأصل مرئية للكاسورة الفرعية ، وبالتالي فإن حالة التعديل النهائي للطرق المحمية هي نفس حالة الأساليب العامة. تجدر الإشارة إلى أنه في التطور الفعلي ، نادراً ما يتم تعريف الأساليب الثابتة المحمية لأن هذه الأساليب عملية للغاية.
بالنسبة لطريقة حزمة الفئة الأصل ، فإن الفئات الفرعية تحت حزم مختلفة غير مرئية. تم تخصيص الطريقة الخاصة ويمكن فقط الوصول إلى الفئة الأم. لذلك يسمح المترجم للفئات الفرعية بتحديد نفس الطريقة. لكن هذا لا يشكل تجاوزًا أو يخفي ، لأن فئة الوالدين قد أخفوا هذه الأساليب من خلال المعدلات ، التي لا تسببها إعادة كتابة الفئات الفرعية. بالطبع ، إذا كانت الفئة الفرعية والفئة الأم في نفس الحزمة ، فسيكون الموقف هو نفسه الجمهور السابق والمحمي.
لماذا الطريقة النهائية فعالة؟
ستستخدم الطريقة النهائية الآلية المدمجة لتحسين مضمّنة أثناء التجميع. يشير التحسين المضمّن إلى: استدعاء استبدال رمز الدالة مباشرة أثناء التجميع ، بدلاً من استدعاء وظائف في وقت التشغيل. يحتاج INLINE إلى معرفة الوظيفة التي يجب استخدامها في النهاية عند التجميع. من الواضح ، لا يمكن استخدامه بدون نهائي. يمكن إعادة كتابة الطرق غير النهائية في الفئات الفرعية. بسبب تعدد الأشكال المحتمل ، لا يمكن للمترجم تحديد النوع الحقيقي للكائن لاستدعاء الطريقة في المستقبل أثناء مرحلة التجميع ، ولا يمكنه تحديد طريقة الاتصال.
المتغير النهائي
ببساطة ، يمكن للمتغير النهائي في Java فقط ويجب تهيئته مرة واحدة ، ثم يرتبط المتغير بالقيمة. ومع ذلك ، لا تحتاج هذه المهمة بالضرورة إلى تهيئة على الفور عند تحديد المتغير. تدعم Java أيضًا نتائج مختلفة للمتغيرات النهائية من خلال البيانات الشرطية ، ولكن لا يمكن تعيين المتغير إلا مرة واحدة في أي حال.
ومع ذلك ، فإن المتغير النهائي لـ Java ليس ثابتًا مطلقًا ، لأن متغيرات كائن Java هي قيم مرجعية فقط ، لذلك يعني النهائي فقط أنه لا يمكن تغيير المرجع ، ولا يزال من الممكن تعديل محتوى الكائن. بالمقارنة مع مؤشرات C/C ++ ، فهو يشبه نوع Const المتغير من النوع Const *.
يمكن تقسيم متغيرات Java إلى فئتين: المتغيرات المحلية (المتغير المحلي) ومتغيرات عضو الفئة (حقل الفئة). فيما يلي رمز لتقديم موقف التهيئة بشكل منفصل.
المتغير المحلي
تشير المتغيرات المحلية بشكل رئيسي إلى المتغيرات المحددة في الأساليب. سوف تختفي ويصبحون لا يمكن الوصول إليهم بعد الطريقة. هناك حالة خاصة يمكن تقسيمها إلى: معلمات الوظيفة. في هذه الحالة ، يرتبط تهيئةها بالمعلمات التي تم تمريرها عند استدعاء الوظيفة.
بالنسبة للمتغيرات المحلية الأخرى ، يتم تعريفها في الطريقة ، ويمكن تهيئة قيمها بشكل مشروط:
طريقة السلسلة العامة (Final Boolean FinalParam) {// error: قد لا يتم تعيين المعلمة النهائية FinalParam // finalparam = true ؛ الكائن النهائي finallocal = finalparam؟ كائن جديد (): فارغة ؛ النهائي int Finalvar ؛ if (finallocal! = null) {FinalVar = 21 ؛ } آخر {FinalVar = 7 ؛ } // خطأ: قد تم بالفعل تعيين متغير FinalVar // FinalVar = 80 ؛ سلسلة نهائية. Switch (FinalVar) {Case 21: FinalRet = "Me" ؛ استراحة؛ الحالة 7: FinalRet = "She" ؛ استراحة؛ الافتراضي: FinalRet = null ؛ } return FinalRet ؛} من المثال أعلاه ، يمكن ملاحظة أنه لا يمكن تعيين معلمات الوظيفة المعدلة حسب النهائي قيمة جديدة ، ولكن يمكن تعيين متغيرات محلية نهائية أخرى قيمة في عبارة مشروطة. هذا يوفر أيضًا مرونة معينة للنهائي.
بالطبع ، يجب أن تحتوي جميع الشروط في البيان الشرطي على مهام للمتغيرات المحلية النهائية ، وإلا فسوف تحصل على خطأ في عدم تهيئة المتغير.
طريقة السلسلة العامة (الكائن النهائي FinalParam) {Final int FinalVar ؛ if (finalparam! = null) {finalVar = 21 ؛ } السلسلة النهائية FinalRet ؛ // خطأ: قد لا يتم تهيئة Finalvar المتغير (FinalVar) {Case 21: FinalRet = "Me" ؛ استراحة؛ الحالة 7: FinalRet = "She" ؛ استراحة؛ } // error: قد لا يتم تهيئة FinalRet المتغير FinalRet ؛} من الناحية النظرية ، ليست المتغيرات المحلية ضرورية ليتم تعريفها على أنها نهائية ، ويجب أن تكون طريقة التصميم المعقولة قادرة على الحفاظ على المتغيرات المحلية جيدًا. إنه فقط عند استخدام وظائف مجهولة لإجراء عمليات الإغلاق في أساليب Java ، تتطلب Java أن يتم تعريف المتغير المحلي المشار إليه على أنه نهائي:
طريقة التشغيل العامة (سلسلة السلسلة) {int integer = 12 ؛ إرجاع جديد RunNable () {Override public void run () {// error: يجب إعلانه النهائي system.out.println (string) ؛ // خطأ: يحتاج إلى إعلان النظام النهائي. }} ؛}حقل الفصل
يمكن بالفعل تقسيم متغيرات أعضاء الفصل إلى نوعين: ثابت وغير منتظم. بالنسبة لمتغيرات أعضاء الفئة الثابتة ، نظرًا لأنها مرتبطة بالفصول ، بالإضافة إلى تهيئة مباشرة في وقت التعريف ، يمكن أيضًا وضعها في كتلة ثابتة ، ويمكن استخدام الأخير تنفيذ بيانات أكثر تعقيدًا:
حزمة com.iderzheng.finalkeyword ؛ استيراد java.util.hashset ؛ استيراد java.util.linkedhashset ؛ استيراد java.util.set ؛ الفئة العامة staticfinalfields {static final int static_final_init_inline = 7 ؛ مجموعة نهائية ثابتة <integer> static_final_init_static_block ؛ / ** كتلة ثابتة **/ static {if (System.CurrentTimeMillis () ٪ 2 == 0) {static_final_init_static_block = new hashset <> () ؛ } آخر {static_final_init_static_block = new LinkedHashSet <> () ؛ } static_final_init_static_block.add (7) ؛ static_final_init_static_block.add (21) ؛ }}هناك أيضًا كتل غير منتظمة في Java يمكنها تهيئة متغيرات الأعضاء غير القتالية ، ولكن بالنسبة لهذه المتغيرات ، غالبًا ما يتم وضعها في مُنشئ التهيئة. بالطبع ، من الضروري التأكد من تهيئة كل متغير نهائي مرة واحدة في المنشئ. إذا تم استدعاء مُنشئين آخرين من خلال هذا () ، فلن يتم تخصيص هذه المتغيرات النهائية في المنشئ.
حزمة com.iderzheng.finalkeyword ؛ الفئة العامة Finalfields {Final Long Final_init_inline = System.CurrentTimeMillis () ؛ نهائي طويل Final_init_block ؛ Final Final_init_constructor ؛ / ** الكتلة الأولية **/ {final_init_block = system.nanotime () ؛ } finalfields () {this (217) ؛ } FinalFields (Boolean Bool) {Final_init_constructor = 721 ؛ } FinalFields (long init) {final_init_constructor = init ؛ }}عندما يتم استخدام النهائي لتعديل الفئات (الفئة) والأساليب (الطريقة) ، فإنه يؤثر بشكل أساسي على الميراث الموجهة للكائنات. بدون الميراث ، لن يكون هناك اعتماد على رمز الفئة الفرعية على الفئة الأصل. لذلك ، عند تعديل الكود أثناء الصيانة ، لا يحتاج إلى النظر في ما إذا كان سيتم تدمير تنفيذ الفئة الفرعية ، مما يجعله أكثر ملاءمة. عند استخدامه على متغير ، يضمن Java عدم تعديل القيمة المتغيرة. إذا كان التصميم الإضافي يضمن عدم تعديل أعضاء الفصل ، فيمكن تحويل المتغير بأكمله إلى ثابت ، وهو مفيد للغاية للبرمجة متعددة الخيوط. لذلك ، فإن النهائي له تأثير جيد للغاية على صيانة الكود.