جافا تشالنجرز # 1: طريقة التحميل الزائد في JVM

جافا تشالنجرز # 1: طريقة التحميل الزائد في JVM


يوم جيد للجميع.


لقد أطلقنا بالفعل السلسلة التالية من الدورة التدريبية "Java Developer" ، ولكن لا يزال لدينا بعض المواد التي نود مشاركتها معك.


مرحبًا بك في سلسلة مقالات Java Challengers ! تركز هذه السلسلة من المقالات على ميزات برمجة Java. تطورهم هو طريقك لتصبح مبرمج جافا مؤهل تأهيلا عاليا.


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


هل أنت مستعد لبدء إتقان المفاهيم الأساسية للبرمجة في جافا؟ ثم دعنا نبدأ بأول لغز لدينا!


توسيع- varargs الملاكمة


مصطلح "طريقة الزائد"

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

ما هي طريقة التحميل الزائد؟


التحميل الزائد للأسلوب هو تقنية برمجة تسمح للمطور في نفس الفئة باستخدام نفس الاسم للأساليب ذات المعلمات المختلفة. في هذه الحالة ، نقول أن الطريقة مثقلة.


تعرض القائمة 1 الطرق ذات المعلمات المختلفة التي تختلف من حيث العدد والنوع والنظام.


الإدراج 1. ثلاثة خيارات لطرق التحميل الزائد.


//   public class Calculator { void calculate(int number1, int number2) { } void calculate(int number1, int number2, int number3) { } } //   public class Calculator { void calculate(int number1, int number2) { } void calculate(double number1, double number2) { } } //   public class Calculator { void calculate(double number1, int number2) { } void calculate(int number1, double number2) { } } 

طريقة التحميل الزائد والأنواع البدائية


في القائمة 1 ، رأيت الأنواع البدائية int و double . دعونا نتطرق لدقيقة ونتذكر الأنواع البدائية في جافا.


الجدول 1. الأنواع البدائية في جافا


اكتبالنطاقالقيمة الافتراضيةالحجمأمثلة حرفية
منطقيصحيحة أو خاطئةخطأ1 بتخطأ حقيقي
بايت-128 ... 12708 بت1 ، -90 ، -128
حرفحرف Unicode أو 0 إلى 65536\ u000016 بت'a'، '\ u0031'، '\ 201'، '\ n'، 4
قصير-32،768 ... 32،767016 بت1 ، 3 ، 720 ، 22000
عدد-2147483648 ... 2 147483 647032 بت-2 ، -1 ، 0 ، 1 ، 9
طويل-9،223،372،036،854،775،808 إلى 9،223،372،036،854،775،807064 بت-4000 لتر ، -900 لتر ، 10 لتر ، 700 لتر
تعويم3.40282347 × 1038 ، 1.40239846 × 10-450.032 بت1.67e200f ، -1.57e-207f ، .9f ، 10.4F
مزدوج1.7976931348623157 × 10308 ، 4.9406564584124654 × 10-3240.064 بت1.e700d ، -123457e ، 37e1d

لماذا يجب علي استخدام طريقة التحميل الزائد؟


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


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


ما ليس الزائد


تذكر أن تغيير اسم متغير ليس عبئًا زائدًا. لا يتم ترجمة التعليمات البرمجية التالية:


 public class Calculator { void calculate(int firstNumber, int secondNumber){} void calculate(int secondNumber, int thirdNumber){} } 

لا يمكنك أيضًا زيادة تحميل الطريقة عن طريق تغيير القيمة المرتجعة في توقيع الطريقة. لا يجمع هذا الرمز أيضًا:


 public class Calculator { double calculate(int number1, int number2){return 0.0;} long calculate(int number1, int number2){return 0;} } 

الزائد منشئ


يمكنك التحميل الزائد للمنشئ بنفس الطريقة مثل الطريقة:


 public class Calculator { private int number1; private int number2; public Calculator(int number1) { this.number1 = number1; } public Calculator(int number1, int number2) { this.number1 = number1; this.number2 = number2; } } 

حل مشكلة طريقة التحميل الزائد


هل أنت مستعد للاختبار الأول؟ دعنا نكتشف!


ابدأ بفحص التعليمات البرمجية التالية بعناية.


قائمة 2. تحدي الحمولة الزائدة


 public class AdvancedOverloadingChallenge3 { static String x = ""; public static void main(String... doYourBest) { executeAction(1); executeAction(1.0); executeAction(Double.valueOf("5")); executeAction(1L); System.out.println(x); } static void executeAction(int ... var) {x += "a"; } static void executeAction(Integer var) {x += "b"; } static void executeAction(Object var) {x += "c"; } static void executeAction(short var) {x += "d"; } static void executeAction(float var) {x += "e"; } static void executeAction(double var) {x += "f"; } } 

جيد. لقد درست الرمز. ماذا ستكون النتيجة؟


  1. قبلت
  2. bfce
  3. efce
  4. AECF

الجواب الصحيح معطى في نهاية المقال.


ماذا حدث الان؟ كيف يقوم JVM بتجميع الطرق المحملة


من أجل فهم ما حدث في القائمة 2 ، تحتاج إلى معرفة بعض الأشياء حول كيفية تجميع JVM للأساليب المحملة بشكل زائد.


بادئ ذي بدء ، JVM كسول بشكل معقول: سيبذل دائمًا أقل جهد لتنفيذ طريقة. وبالتالي ، عندما تفكر في كيفية معالجة JVM للحمل الزائد ، ضع في اعتبارك ثلاث ميزات مهمة للمترجم:


  1. اتساع
  2. التعبئة والتغليف (أوتوبوكسينغ وإلغاء علبته)
  3. وسيطات الطول المتغير (varargs)

إذا لم تصادف هذه التقنيات من قبل ، فستساعدك بعض الأمثلة على فهمها. لاحظ أن JVM يقوم بتشغيلها بالترتيب الذي تم سردها به.


هنا ملحق مثال:


 int primitiveIntNumber = 5; double primitiveDoubleNumber = primitiveIntNumber ; 

هذا هو ترتيب التمديد للأنواع البدائية:


ترتيب التمديد للأنواع البدائية


( ملاحظة المترجم - في JLS ، يوصف تمديد البدائي مع اختلافات كبيرة ، على سبيل المثال ، يمكن توسيعه طويلًا في تعويم أو مزدوج. )


مثال على التعبئة التلقائية:


 int primitiveIntNumber = 7; Integer wrapperIntegerNumber = primitiveIntNumber; 

لاحظ ما يحدث خلف الكواليس عند تجميع الكود:


 Integer wrapperIntegerNumber = Integer.valueOf(primitiveIntNumber); 

فيما يلي مثال لتفريغ:


 Integer wrapperIntegerNumber = 7; int primitiveIntNumber= wrapperIntegerNumber; 

إليك ما يحدث خلف الكواليس عند تجميع هذا الرمز:


 int primitiveIntNumber = wrapperIntegerNumber.intValue(); 

وإليك مثال على طريقة ذات وسيطات متغيرة الطول. لاحظ أن طرق الطول المتغير هي الأخيرة التي يتم تنفيذها.


 execute(int... numbers){} 

ما هي الحجج ذات الطول المتغير؟


وسيطات الطول المتغير ليست سوى مصفوفة من القيم التي تقدمها ثلاث نقاط (...). يمكننا تمرير أكبر عدد int لهذه الطريقة.


على سبيل المثال:


 execute(1,3,4,6,7,8,8,6,4,6,88...); //  ... 

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


ملحق: دراسة حالة


عندما نمرر الرقم 1 مباشرة إلى طريقة executeAction() ، يقوم JVM تلقائيًا بتفسيره على أنه int . هذا هو السبب في عدم تمرير هذا الرقم إلى أسلوب executeAction(short var) .


وبالمثل ، إذا تجاوزنا الرقم 1.0 فسوف يتعرف JVM تلقائيًا على أنه مزدوج.


بالطبع ، يمكن أن يكون الرقم 1.0 أيضًا float ، ولكن يتم تحديد نوع هذه الحرف مسبقًا. لذلك ، في القائمة 2 ، يتم executeAction(double var) طريقة executeAction(double var) .


عندما نستخدم المجمّع Double ، هناك خياران: إما أن يتم فك الرقم إلى نوع بدائي ، أو يمكن توسيعه إلى Object . (تذكر أن كل فئة في Java تمدد فئة Object .) في هذه الحالة ، يختار JVM امتدادًا من النوع Double in Object ، لأنه يتطلب جهدًا أقل من التفريغ.


آخر واحد 1L هو 1L وبما أننا حددنا النوع ، فهو long .


أخطاء التحميل الزائد الشائعة


حتى الآن ، ربما أدركت أن الأمور يمكن أن تكون مربكة مع التحميل الزائد للأسلوب ، لذلك دعونا نلقي نظرة على بعض المشاكل التي من المحتمل أن تواجهها.


AutoBoxing مع الأغلفة


Java هي لغة برمجة مكتوبة بقوة ، وعندما نستخدم الالتفاف التلقائي مع الأغلفة ، هناك بعض الأشياء التي يجب أن نضعها في الاعتبار. أولاً ، لا يتم تجميع التعليمات البرمجية التالية:


 int primitiveIntNumber = 7; Double wrapperNumber = primitiveIntNumber; 

ستعمل ميزة AutoPacking مع النوع double لأنه عندما تقوم بترجمة الشفرة ، فستكون معادلة لما يلي:


 Double number = Double.valueOf(primitiveIntNumber); 

سيتم تجميع هذا الرمز. سيتم توسيع int الأول double ثم تعبأ في Double . ولكن مع التعبئة التلقائية ، لا يوجد ملحق نوع ويتوقع مُنشئ Double.valueof double ، وليس int . في هذه الحالة ، ستعمل التعبئة التلقائية إذا قمنا بتحويل نوع صريح ، على سبيل المثال:


 Double wrapperNumber = (double) primitiveIntNumber; 

تذكر أن Integer لا يمكن أن تكون Long Float ولا يمكن أن تكون Double . لا يوجد ميراث. كل من هذه الأنواع ( Integer ، Long ، Float ، Double ) هو Number Object .


إذا كنت في شك ، فقط تذكر أنه يمكن توسيع أرقام المجمع إلى Number أو Object . (هناك الكثير مما يمكن قوله عن الأغلفة ، لكن دعنا نتركها لمقال آخر.)


الرمز الحرفي


عندما لا نحدد نوع الرقم الحرفي ، سيحسب JVM النوع لنا. إذا استخدمنا مباشرة الرقم 1 في الكود ، فسيقوم JVM بإنشائه int . إذا حاولنا تمرير 1 مباشرة إلى طريقة تقبل short ، فلن يتم تجميعها.


على سبيل المثال:


 class Calculator { public static void main(String... args) { //      // ,   char, short, byte,  JVM    int calculate(1); } void calculate(short number) {} } 

سيتم تطبيق نفس القاعدة عند استخدام الرقم 1.0 . على الرغم من أنها قد تكون float ، إلا أن JVM ستعتبرها double .


 class Calculator { public static void main(String... args) { //      // ,   float,  JVM    double calculate(1.0); } void calculate(float number) {} } 

خطأ شائع آخر هو افتراض أن Double أو أي غلاف آخر أفضل لطريقة double .


والحقيقة هي أن JVM تستغرق جهدًا أقل لتوسيع الغلاف Double إلى Object بدلاً من تفريغه إلى نوع double بدائي.


للتلخيص ، عند استخدامها مباشرة في كود جافا ، سيكون 1 int وسيكون 1.0 double . التمديد هو أسهل طريقة للتنفيذ ، ثم هناك تغليف أو تفريغ وستكون العملية الأخيرة دائمًا طرقًا متغيرة الطول.


مثل حقيقة غريبة. هل تعلم أن النوع char يقبل الأرقام؟


 char anyChar = 127; // ,  ,    

ما تحتاج إلى تذكره حول الحمل الزائد


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


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


ما يجب مراعاته: عند التحميل الزائد للطريقة ، فإن JVM ستبذل أقل جهد ممكن.


فيما يلي ترتيب المسار الأكثر كسلاً للتنفيذ:


  • الأول يتسع.
  • والثاني هو الملاكمة
  • الثالث ، الحجج المتغيرة الطول (varargs)

أشياء يجب مراعاتها: تنشأ المواقف الصعبة عند الإعلان عن الأرقام مباشرة: 1 سيكون عددًا int و 1.0 سيكون double .


تذكر أيضًا أنه يمكنك الإعلان عن هذه الأنواع بشكل صريح باستخدام بناء الجملة 1F أو 1f و 1D أو 1d .


وبهذا ينتهي دور JVM في طريقة التحميل الزائد. من المهم أن نفهم أن JVM كسول بطبيعته ، وسيتبع دائمًا المسار الأكثر كسولًا.


الجواب


الإجابة على القائمة 2 هي الخيار 3. efce.


تعرف على المزيد حول طريقة التحميل الزائد في Java



مقدمة للفئات والكائنات للمبتدئين المطلقين ، بما في ذلك أقسام صغيرة حول الطرق والأسلوب الزائد.



تعرف على المزيد حول سبب أهمية أن تكون Java لغة مكتوبة بقوة وتعرف على أنواع Java البدائية.



تعرف على عيوب وعيوب طريقة التحميل الزائد ، وكذلك كيفية حلها باستخدام أنواع مخصصة وكائنات المعلمات.

Source: https://habr.com/ru/post/ar428307/


All Articles