من الصعب جعل البرنامج مثاليًا، ومن المؤكد أنه سيكون هناك العديد من الانحرافات. على سبيل المثال، يوجد خطأ في البرنامج نفسه، مثل نفاد الورق من الطابعة عند طباعة البرنامج، أو عدم وجود ذاكرة كافية. ومن أجل حل هذه الاستثناءات، علينا أن نعرف سبب حدوث الاستثناء. بالنسبة لبعض الاستثناءات الشائعة، يمكننا أيضًا تقديم خطط استجابة معينة. يتم تنفيذ معالجة الاستثناءات في لغة C ببساطة من خلال قيم إرجاع الوظائف، ولكن غالبًا ما يتم تحديد معنى قيمة الإرجاع من خلال الاصطلاح. يحتاج المبرمجون إلى الاستعلام عن كمية كبيرة من المعلومات قبل أن يتمكنوا من العثور على سبب غامض. غالبًا ما تحتوي اللغات الموجهة للكائنات، مثل C++ وJava وPython، على آليات أكثر تعقيدًا لمعالجة الاستثناءات. تمت مناقشة آلية معالجة الاستثناءات في Java هنا.
معالجة استثناءات جافا
معالجة الاستثناء
جزء كبير من آلية معالجة الاستثناءات في Java يأتي من C++. فهو يسمح للمبرمجين بتخطي المشكلات التي لا يمكن معالجتها مؤقتًا لمواصلة التطوير اللاحق، أو للسماح للبرنامج بمعالجة الاستثناءات بشكل أكثر ذكاءً.
تستخدم Java بعض الكائنات الخاصة لتمثيل الظروف غير الطبيعية، وتسمى هذه الكائنات بكائنات الاستثناء. عند حدوث استثناء، ستقوم Java برمي كائن يمثل الوضع الحالي بناءً على الإعدادات المعدة مسبقًا. إن ما يسمى بالرمي هو طريقة خاصة للعودة. سيتوقف مؤشر الترابط مؤقتًا ويخرج من استدعاءات الطريقة طبقة تلو الأخرى حتى يواجه معالج الاستثناء. يمكن لمعالج الاستثناء التقاط كائن الاستثناء وتحديد الإجراء التالي بناءً على الكائن، مثل:
ذكّر المستخدم بمعالجة الاستثناءات ومتابعة البرنامج للخروج من البرنامج
...
يبدو معالج الاستثناء بهذا الشكل، ويتكون من المحاولة والالتقاط والأخير والكتل التالية. أخيرا غير مطلوب.
حاول { ...;}catch() { ...;}catch() { ...;}أخيرًا { ...;}
يقوم معالج الاستثناء هذا بمراقبة كتلة البرنامج بعد المحاولة. تحتوي أقواس الالتقاط على معلمة تمثل نوع الاستثناء الذي سيتم اكتشافه. سوف يلتقط الصيد النوع المقابل والفئات المشتقة منه. تحتوي كتلة البرنامج بعد المحاولة على العمليات التي سيتم تنفيذها لنوع الاستثناء. قد تؤدي كتلة البرنامج التي تتم مراقبتها بواسطة المحاولة إلى طرح أكثر من نوع واحد من الاستثناءات، لذلك يمكن أن يحتوي معالج الاستثناء على وحدات التقاط متعددة. كتلة البرنامج بعد النهاية هي برنامج يجب تنفيذه بغض النظر عما إذا كان هناك استثناء أم لا.
نحن نضع البرامج التي قد تسوء وتحتاج إلى المراقبة قيد المحاولة، ونصمم حلولًا للتعامل مع الاستثناءات.
ما يلي هو برنامج Java جزئي يستخدم معالجة الاستثناءات. يقوم الجزء التجريبي من البرنامج بقراءة أسطر النص من الملف. أثناء عملية قراءة الملف، قد يحدث IOException:
BufferedReader br = new BufferedReader(new FileReader("file.txt"));try { StringBuilder sb = new StringBuilder(); sb.append("/n"); line = br.readLine(); } سلسلة كل شيء = sb.toString();} Catch(IOException e) { e.printStackTrace();
إذا اكتشفنا كائن فئة IOException e، فيمكننا العمل على الكائن. على سبيل المثال، قم باستدعاء printStackTrace() الخاص بالكائن لطباعة حالة المكدس الحالية. بالإضافة إلى ذلك، قمنا أيضًا بطباعة رسالة "مشكلة الإدخال/الإخراج" إلى المدى المتوسط.
بغض النظر عما إذا كان هناك استثناء أم لا، سيدخل البرنامج في النهاية إلى الكتلة النهائية. نقوم بإغلاق الملف في الكتلة النهائية ومسح الموارد التي يشغلها واصف الملف.
نوع الاستثناء
ترث جميع فئات الاستثناء في Java من فئة Trowable. يمكن رمي كائن من فئة Throwable (رمي).
البرتقالي: غير محدد؛ الأزرق: محدد
يمكن تقسيم الأشياء القابلة للرمي إلى مجموعتين. مجموعة واحدة هي الاستثناءات غير المحددة، وغالبًا ما لا يتم استخدام آليات معالجة الاستثناءات لهذه المجموعة من الاستثناءات، بما في ذلك:
1. تشير فئة الخطأ عادةً إلى الأخطاء والأخطاء الداخلية في Java، مثل استنفاد الموارد. عند حدوث خطأ (ومشتقاته)، لا نستطيع حل الخطأ على مستوى البرمجة، لذا يجب الخروج من البرنامج مباشرة.
2. تحتوي فئة الاستثناء على فئة مشتقة خاصة RuntimeException. سبب RuntimeException (ومشتقاته) هو برنامج Java نفسه، أي بسبب ارتكاب المبرمج لأخطاء أثناء البرمجة. يمكن تجنب RuntimeException تمامًا عن طريق تعديل برنامج Java. على سبيل المثال، تحويل كائن من نوع واحد إلى نوع آخر دون وجود علاقة وراثة هو ClassCastException. وينبغي تجنب مثل هذه الحالات الشاذة، بل ويمكن تجنبها.
يتم فحص الباقي الاستثناءات. تنتج هذه الفئات عن تفاعلات البرمجة مع البيئة التي تسبب أخطاء في البرنامج أثناء وقت التشغيل. على سبيل المثال، عند قراءة ملف، يحدث IOException بسبب خطأ في الملف نفسه. مثال آخر هو أن خادم الشبكة يغير مؤقتًا عنوان URL، مما يتسبب في حدوث MalformedURLException. نظام الملفات وخادم الشبكة خارج بيئة Java ولا يقعان ضمن سيطرة المبرمج. إذا تمكن المبرمجون من توقع الاستثناءات، فيمكنهم استخدام آلية معالجة الاستثناءات لتطوير خطط الاستجابة. على سبيل المثال، عند وجود مشكلة في أحد الملفات، يتم تنبيه مسؤول النظام. مثال آخر هو أنه عندما تكون هناك مشكلة في خادم الشبكة، يتم تذكير المستخدم وينتظر حتى يتعافى خادم الشبكة. تُستخدم آلية معالجة الاستثناءات بشكل أساسي للتعامل مع مثل هذه الاستثناءات.
رمي استثناء
في البرنامج أعلاه، يأتي الاستثناء من استدعائنا إلى Java IO API. يمكننا أيضًا طرح استثناءات في برامجنا الخاصة، مثل فئة البطارية التالية، والتي لها طرق الشحن والاستخدام:
اختبار الطبقة العامة { public static void main(String[] args) { Battery aBattery = new Battery(); aBattery.useBattery(0.5); / public void ChargeBattery(double p) { // power <= 1 if (this.power + p < 1.) { this.power = this.power + p } else { this.power = 1. } } /** * استهلاك البطارية */ public boolean useBattery(double p) {try { test(p) } Catch(Exception e) { System.out. println("catch Exception"); System.out.println(e.getMessage()); p = 0.0; - p; return true; } else { this.power = 0.0; return false; } } /** * استخدام الاختبار */ اختبار الفراغ الخاص (double p) يلقي استثناءً // أنا فقط أرمي، لا أتعامل مع { if ( p < 0) { Exception e = new Exception("يجب أن يكون p موجبًا"); throw e } } قوة مزدوجة خاصة = 0.0;
يشير useBattery() إلى استخدام تشغيل البطارية. هناك معلمة في طريقة useBattery() تشير إلى كمية الكهرباء المستخدمة. نستخدم طريقة الاختبار () لاختبار هذه المعلمة. إذا كانت هذه المعلمة سلبية، فإننا نعتقد أن هناك استثناء ونرميه.
في الاختبار، عند حدوث استثناء (p <0)، نقوم بإنشاء كائن استثناء e واستخدام سلسلة كمعلمة. تحتوي السلسلة على معلومات متعلقة بالاستثناء وهذه المعلمة غير مطلوبة. استخدم الرمي لرمي كائن الاستثناء.
لدينا معالج الاستثناء في useBattery(). نظرًا لأن طريقة test() لا تتعامل مباشرة مع الاستثناء الذي تنشئه، ولكنها تطرح الاستثناء إلى useBattery() العلوي، فنحن بحاجة إلى شرح الاستثناء في تعريف test().
(بافتراض أن معالج الاستثناء غير موجود في useBattery()، ولكن في الأسلوب main() ذي المستوى الأعلى، نحتاج أيضًا إلى إضافة استثناء الرميات إلى تعريف useBattery().)
في الالتقاط، نستخدم طريقة getMessage () لاستخراج المعلومات الموجودة في الاستثناء الخاص بها. نتائج تشغيل البرنامج أعلاه هي كما يلي:
يجب أن يكون صيد الاستثناءp موجبًا
في معالج الاستثناء، سنكتشف أي استثناء لفئة الاستثناء أو مشتقاتها. وهذا لا يساعدنا غالبًا في تحديد المشكلات، خاصة عندما يقوم البرنامج بطرح استثناءات متعددة. يمكننا توفير فئة أكثر تحديدًا لالتقاطها.
استثناء مخصص
يمكننا إنشاء فئات استثناء جديدة من خلال الميراث. عند الوراثة، غالبًا ما نحتاج إلى تجاوز المُنشئ. تحتوي الاستثناءات على مُنشئين، أحدهما بدون معلمات والآخر بمعلمة سلسلة. على سبيل المثال:
class BatteryUsageException يمتد الاستثناء { public BatteryUsageException() {} public BatteryUsageException(String msg) { super(msg }});
يمكننا توفير المزيد من الأساليب والمعلومات المتعلقة بالاستثناء في الفئات المشتقة.
عند تخصيص الاستثناءات، كن حذرًا بشأن الفئة الأساسية التي ترث منها. يجب أن تحتوي الفئة الأكثر تحديدًا على المزيد من معلومات الاستثناء، مثل IOException مقابل الاستثناء.
تلخيص
معالجة الاستثناءات تحل المشكلات، ولكنها تخلق المشكلات أيضًا. في المشاريع الكبيرة، غالبًا ما تؤدي معالجة الاستثناءات المفرطة والمفصلة إلى فوضى البرنامج. معالجة الاستثناءات ليست بسيطة حسب التصميم ويجب استخدامها بحذر.