مطبات جافا. الجزء 1

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

الغرض من هذا المنشور:
أظهر الأخطاء الأكثر شيوعًا للمبتدئين وبعض الحيل لإصلاحها. من الواضح أن بعض الأخطاء قد تكون معقدة وتحدث لسبب أو لآخر. الغرض من المنشور هو إلى حد ما تحليلها والمساعدة في تحديدها في مرحلة مبكرة. آمل أن يكون هذا المنشور مفيدًا للمبتدئين.

قائمة التحقق من الخطأ:

1. الأخطاء المطبعية. الأخطاء المطبعية المزعجة التي لا تظهر على الفور
2. الاحالة في حالة بدلا من المقارنة
3. الأخطاء المنطقية في الشرط
4. مقارنة سلسلة خاطئة
5. التهيئة غير الصحيحة للمتغيرات من الأنواع البدائية
6. الاستخدام غير السليم للضعف
7. نوع خاطئ من قيمة الإرجاع في المنشئ.
8. القسمة على صفر. POSITIVE_INFINITY
9. عدم مراعاة ترتيب تهيئة الفصل
10. متغير محلي يخفي متغير فئة
11. تجاهل الصب التلقائي في التعبيرات الحسابية
12. لانهائي حلقة مع البايت ، والتي يصعب اكتشافها.
13. يختلف اسم الفئة عن اسم الملف الذي تم تخزينه فيه.
14. الكائنات التي هي عناصر من مجموعة لا يتم تهيئة.
15. وضع عدة فئات في ملف واحد في وقت واحد مع المعدل العام

مطبات جافا


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

بالنسبة للمقال ، كتبت على وجه التحديد رمز العمل مع تعليقات مفصلة. لكتابة مقال مع أمثلة التعليمات البرمجية ، تم استخدام java 8. للاختبار ، يتم وضع java code في مجموعات منفصلة.

مثال: "package underwaterRocks.simple؛"

ما الصعوبات التي يواجهها المبتدئين؟

الأخطاء المطبعية


يحدث أن المبرمجين المبتدئين جعل الأخطاء المطبعية التي يصعب اكتشافها في لمحة.


مثال على الكود:

ملف: "Simple.java"

/*   ;     */ package underwaterRocks.simple; /** * * @author Ar20L80 */ public class Simple { public static void main(String[] args) { int ival = 10; if(ival>0); { System.out.println("     "); } } } 


Explanation : “تشير الفاصلة المنقوطة إلى نهاية العبارة. في هذه الحالة هي نهاية بيان فارغ. هذا خطأ منطقي. قد يكون من الصعب اكتشاف مثل هذا الخطأ.

سوف يعتبر المترجم أن كل شيء صحيح. الحالة إذا (ival> 0) ؛ في هذه الحالة لا معنى له. لأنه يعني: إذا كانت قيمة ival أكبر من الصفر ، فلا تقم بأي شيء وتستمر. "

الاحالة في حالة بدلا من المقارنة


الشرط هو متغير الاحالة.

هذا ليس خطأ ، ولكن يجب تبرير استخدام هذه التقنية.

  boolean myBool = false; if(myBool = true) System.out.println(myBool); 

في هذا الرمز ، إذا كان (myBool = true) يعني: "قم بتعيين المتغير myBool على true ،
إذا كان التعبير صحيحًا ، فاتبع الشرط الذي يلي الأقواس ".

في هذا الرمز ، ستكون الحالة صحيحة دائمًا. و System.out.println (myBool) ؛ سيتم تنفيذها دائمًا ، بغض النظر عن الحالة.

== هي مقارنة للمساواة.
= هو واجب ، يمكنك أن تقول = 10 ؛ مثل: "ولكن تعيين قيمة 10".

الشرط في الأقواس بإرجاع قيمة منطقية.
لا يهم بالترتيب الذي تكتبه. يمكنك مقارنة مثل هذا: (0 == a) أو (5 == a)
إذا نسيت علامة واحدة متساوية ، على سبيل المثال (0 = a) أو (5 = a) ، فسيعلمك المترجم بوجود خطأ. يمكنك تعيين قيمة ، وليس المقارنة.
يمكنك أيضا كتابة في شكل مقروء بعض الفاصل الزمني.
على سبيل المثال: تحتاج إلى الكتابة: أكبر من 5 وأقل من 10.
تكتب مثل هذا: (a> 4 && a <10) ، ولكن بنفس النجاح يمكنك كتابة: (4 <a && a <10) ،
الآن ترى أن الرقم يتراوح بين 4 و 10 ، باستثناء هذه القيم. هذا أكثر وضوحا. يتضح على الفور أنه ، في الفاصل الزمني بين 4 و 10 ، باستثناء هذه القيم.

مثال التعليمات البرمجية (الفاصل الزمني) 3.9 [):
إذا تم تنفيذ (3 <a && a <9) ؛

خطأ منطقي


إذا (شرط) {} إذا (شرط) {} آخر {} - آخر يشير إلى الأقرب إذا.
هذا غالبا ما يكون سبب أخطاء المبتدئين.

مقارنة السلسلة غير صالحة

المبتدئين في كثير من الأحيان استخدام == بدلا من. المساواة في مقارنة السلاسل.

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


النظر في تهيئة المتغيرات من النوع البدائي.

أوليات (بايت ، قصيرة ، كثافة العمليات ، طويلة ، شار ، تطفو ، مزدوجة ، منطقية).

القيم الأولية.

 byte 0 short 0 int 0 long 0L float 0.0f double 0.0d char '\u0000' String (or any object) null boolean false (  jvm) 


ملاحظة:

المتغيرات المحلية مختلفة قليلاً ؛
لا يقوم المحول البرمجي أبداً بتعيين قيمة افتراضية لمتغير محلي غير مهيأ.

إذا لم تتمكن من تهيئة المتغير المحلي الخاص بك حيث تم الإعلان عنه ،
تذكر تعيين قيمة لها قبل محاولة استخدامها.

سيؤدي الوصول إلى متغير محلي غير مهيأ إلى خطأ وقت الترجمة.

تأكيد هذه الملاحظة في الكود:

ملف: "MyInitLocal.java"

 /*         */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ public class MyInitLocal { float classes_f; int classes_gi; public static void main(String[] args) { float f; int i; MyInitLocal myInit = new MyInitLocal(); /*         .*/ System.out.println("myInit.classes_f = " + myInit.classes_f); System.out.println("myInit.classes_gi = " + myInit.classes_gi); // System.out.println("f = " + f); // .     // System.out.println("f = " + i); // .     } } 


نطاقات القيم:

byte ( , 1 , [-128, 127])
short ( , 2 , [-32768, 32767])
int ( , 4 , [-2147483648, 2147483647])
long ( , 8 , [-922372036854775808,922372036854775807])
float ( , 4 )
double ( , 8 )
char ( Unicode, 2 , 16 , [0, 65535])
boolean ( /, int, JVM)

char: نوع بيانات char هو حرف Unicode 16 بت واحد. يحتوي على الحد الأدنى من القيمة "\ u0000" (أو 0) والحد الأقصى لقيمة "\ uffff" (أو 65.535 ضمناً).


توثيق أوراكل >>

دعونا نحاول تهيئة متغير من النوع الطويل بالرقم: 922372036854775807.
لن ينجح شيء بالنسبة لنا. لأنه ، حرفي عدد صحيح من النوع int.
التهيئة الصحيحة مع حرفي طويل: 922372036854775807L؛

مثال على الكود:

ملف: "MyInitLocalLong.java"

 /*    long  */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ public class MyInitLocalLong { public static void main(String[] args) { // long al = 922372036854775807; // integer number too large long bl = 922372036854775807L; //   } } 


ما الذي تبحث عنه عند تهيئة متغير.

نطاق قيم المتغير من هذا النوع. حقيقة تهيئة المتغير بحرف من نوع معين. لالقوالب الصريحة والضمنية. على التوافق النوع.

عند استخدام قذائف من النوع Integer ، يجب الانتباه إلى التعبئة التلقائية والتفريغ التلقائي لهذه الأنواع.

استخدام غير مناسب للضعف


هنا تحتاج إلى توضيح. هذا لا يتعلق بإساءة استخدام النوع المزدوج.
نحن نستخدم بشكل صحيح. فقط النتيجة يمكن أن تفاجئ مبرمج مبتدئ.
ملف: "MinusDouble.java"
 /*   */ package underwaterRocks.tstDouble; /** * * @author vvm */ public class MinusDouble { public static void main(String[] args) { double a = 4.64; double b = 2.64; System.out.println("ab = "+(ab)); } } /*   run: ab = 1.9999999999999996 */ 


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

مقارنة مزدوجة غير صالحة


النظر في نوع مزدوج.

مثال على الكود:

ملف: "MyDouble.java"

 /*    double  - double. */ package underwaterRocks.myDouble; /** * * @author Ar20L80 */ public class MyDouble { public static void main(String[] args) { double dx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1; System.out.println("dx = " + dx); // dx = 0.9999999999999997 System.out.print(" (dx == 1.0):"); System.out.println(dx == 1.0); // false,   1.0   0.9999999999999997 /*   double*/ final double EPSILON = 1E-14; double xx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1; double xy = 1.0; /*  xx c xy */ if (Math.abs(xx - xy) < EPSILON) System.out.println(xx + "    " + xy + " EPSILON = " + EPSILON); } } 

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

منشئ الصف


يطابق مُنشئ الفصل الدراسي اسم الفصل ولا يُرجع أي شيء ، ولا يُبطل.

مثال على الكود:

ملف: "MyConstructor.java"

 /*      ,  void    void -    */ package underwaterRocks.myConstructor; /** * * @author Ar20L80 */ public class MyConstructor { public MyConstructor(){ System.out.println("   void"); } public void MyConstructor(){ System.out.println("  c void"); } public static void main(String[] args) { MyConstructor myconst = new MyConstructor(); myconst.MyConstructor(); //    } } 

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

القسمة على صفر


ما رأيك سيكون نتيجة لتنفيذ هذا الرمز.

ملف: "DivisionByZero.java"

 /* */ package divisionByZero; import static java.lang.Double.POSITIVE_INFINITY; /** * * @author Ar20L80 */ public class DivisionByZero { public static void main(String[] args) { try{ float f = 12.2f; double d = 8098098.8790d; System.out.println(f/0); System.out.println(d/0); System.out.println(POSITIVE_INFINITY == f/0); System.out.println(POSITIVE_INFINITY == d/0); } catch (NumberFormatException ex) { System.out.println("NumberFormatException"); } catch (ArithmeticException ex) { System.out.println("ArithmeticException"); } } } 

تنفيذ الكود سينتج:

 Infinity Infinity true true 

تقسيم نوع عدد صحيح على صفر سيعطي ArithmeticException.

تحدد فئة java.lang.Double فئة POSITIVE_INFINITY الثابتة POSITIVE_INFINITY;

 public static final float POSITIVE_INFINITY = 1.0d / 0.0d; 

يتم تحويله إلى سلسلة تساوي Infinity.

ترتيب التهيئة


ملف: "InitClass.java"

 /*     */ package myInitClass; /** * * @author Ar20L80 */ public class InitClass { InitClass(){ //   System.out.print(""); } { //   System.out.print("3 "); } public static void main(String[] args) { System.out.print("2"); new InitClass(); } static { //    System.out.print("1"); } } 

أولاً ، يتم تنفيذ جميع الكتل الثابتة ، ثم كتل التهيئة ، ثم مُنشئ الفصل.

سيتم عرضه: "123 منشئ"

متغير محلي يخفي متغير فئة
على الرغم من أن IDE الحديثة يمكنها اكتشاف مثل هذا الخطأ بسهولة ، إلا أنني أود أن أعتبر هذا الخطأ بمزيد من التفصيل. لنبدأ بالتعيين المتغير الكلاسيكي في المنشئ. المثال صحيح. لا يوجد خطأ.
 public class MyClass { private int val = 0; public MyClass(int val) { this.val = val; } } 

ومع ذلك ، ماذا يحدث إذا استخدمت هذه التقنية بطريقة وليس في مُنشئ صف؟ في الطريقة المعتادة ، لا ينصح باستخدام هذه التقنية. يتعلق السؤال بالتصميم المناسب للفصل الدراسي.

تفسير بسيط: في طريقة ما ، يكون المتغير الذي يحمل نفس اسم متغير الفئة محليًا للطريقة. يمكنك الوصول إلى متغير الفئة باستخدام this.val. ومع ذلك ، فإن هذا الطعن من الطريقة ، إذا تم تصميم الفئة بشكل غير صحيح ، لن يؤدي إلا إلى آثار جانبية ويمكن أن يقلل من قابلية قراءة الكود.

يتم نوع الصب الحسابي تلقائيا

هذا يمكن أن يسبب أخطاء مزعجة.
 // byte a = 1; // byte b = 1; // byte  = a + b; //  // byte a = (byte) 1; // byte b = (byte) 1; // byte  = a + b; //  


 //     —     . byte a = 1; byte b = 1; byte c = (byte) (a + b); 


 //     —  final // final byte a = 1; // final byte b = 1; // byte c = a + b; //    ,  a  b final 


أحد الحلول الممكنة عند العمل مع سلسلة:
 byte bHundr = Byte.parseByte("100"); //      byte 


ويرد خطأ آخر في التعليمات البرمجية التالية.
 for (byte i = 1; i <= 128; i++) { System.out.println(i); } 

في هذه الحالة ، نحصل على حلقة لانهائية.

التفسير. اكتب البايتة [-128 ، 127]. 128 لم يعد في هذا النطاق. يحدث تجاوز السعة وتكرر الدورة. ضرورة استخدام البايت في هذه الحالة أمر مشكوك فيه. على الرغم من حدوثه في حالات نادرة. التوصية لاستخدام int بدلاً من البايت. توصية أخرى هي عدم استخدام حلقة في الخوارزمية الخاصة بك.

الكائنات التي هي عناصر الصفيف لا يتم تهيئة
 int[] cats = new int[10]; for(int i=0; i<cats.length;i++){ System.out.println("cats " + i + " = " + cats[i]); } 


في هذا المثال ، لدينا مجموعة من عناصر النوع البدائي. ولا يحدث شيء سيء عندما لا نقوم بتهيئتها. سيتم تعيينهم القيمة الافتراضية. في هذه الحالة ، القيمة = 0.

دعونا ننظر في مثال آخر ، ليس مع البدائية في الصفيف ، ولكن مع الكائنات في الصفيف.
 public class ArrInitObj { public static void main(String[] args) { MyObj[] cats = new MyObj[10]; for(int i=0; i<cats.length;i++){ System.out.println("cats " + i + " = " + cats[i]); System.out.println("cats " + i + ".val = " + cats[i].val); //    java.lang.NullPointerException } } } class MyObj{ public int val; } 


يكمن حل هذه المشكلة في تهيئة جميع متغيرات الكائنات قبل استخدامها. يمكن إجراء التهيئة في مُنشئ فئة MyObj.

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

وضع عدة فئات في ملف واحد في وقت واحد مع المعدل العام
الخطأ نادر جدا. سوف IDE تعطيك فورا تحذير.
يجب أن يتطابق اسم الملف مع اسم الفئة العامة.

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

أتمنى أن تستمتعوا بالمقال ووجدته مفيدًا. سأكون سعيدًا بتعليقاتك وتعليقاتك واقتراحاتك ورغباتك. أن تستمر. بدلا من ذلك ، يتبع يلي.

المراجع
إرشادات تصميم شفرة Oracle Java >>>

PS. أصدقائي. ليس لدي أي طريقة لمواصلة النشر دون مساعدتكم. وهذا هو ، لا فرصة مالية. إذا كان المنشور قد ساعدك بالفعل وتريد المتابعة ، فيرجى دعمه. في مكان ما يوجد زر: "ادعم المؤلف".
اتمنى تفهمك شكرا لك شكرا لحبر على الفرصة للنشر.

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

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


All Articles