java - شرح - معالجة الاستثناءات في c++




لماذا تلتقط الاستثناءات في جافا ، عندما يمكنك التقاط Throwables؟ (10)

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

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

لذا ، لماذا تريد التقاط الاستثناءات ، عندما يمكنك التقاط Throwables ؟


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

الاستيلاء على java.lang.Exception عدوانية يمكن أن يؤدي إلى بعض الأخطاء الخطيرة في التطبيقات - لأن استثناءات غير متوقعة لا فقاعات أبدا ، لا يتم القبض أبدا أثناء التطوير / الاختبار ، إلخ.

أفضل الممارسات: الصيد فقط

  1. الاستثناءات التي يمكنك التعامل معها
  2. الاستثناءات اللازمة للقبض

الكثير من الإجابات الأخرى تبحث في الأمور بشكل ضيق للغاية.

كما يقولون ، إذا كنت تكتب رمز التطبيق ، يجب عليك عدم التقاط Throwable. لا يمكنك فعل أي شيء حيال ذلك ، لذا فإن السماح بالنظام المحيط (JVM أو إطار العمل) للتعامل مع هذه المشكلات هو الأفضل.

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


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


سأذهب إلى طريق مختلف قليلاً عن الآخرين.

هناك العديد من الحالات التي تريد التقاطها Throwable (أساسا لتسجيل / الإبلاغ عن شيء ما حدث الشر).

ومع ذلك ، تحتاج إلى توخي الحذر وإعادة إعادة أي شيء لا يمكنك التعامل معه.

هذا ينطبق بشكل خاص على ThreadDeath.

إذا سبق لك أن التقطت Throwable ، فاحرص على إجراء ما يلي:

try {
    ...
} catch (SomeExceptionYouCanDoSomethingWith e) {
    // handle it
} catch (ThreadDeath t) {
    throw t;
} catch (Throwable t) {
    // log & rethrow
}

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


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

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

ومع ذلك ، هناك بضع حالات ركنية حيث تكون آمنة و / أو مرغوبة للقبض على أخطاء ، أو على الأقل بعض الفئات الفرعية:

  • هناك حالات تريد فيها حقاً إيقاف المسار العادي للتدفق: على سبيل المثال ، إذا كنت في Servlet ، قد لا ترغب في أن يقوم معالج الاستثناء الافتراضي الخاص بعام Servlet بالإعلان للعالم بأن لديك OutOfMemoryError ، لا يمكنك التعافي منه.
  • من حين لآخر ، سيتم طرح خطأ في الحالات التي يتعذر فيها استرداد JVM من سبب الخطأ. على سبيل المثال ، إذا حدث OutOfMemoryError أثناء محاولة تخصيص صفيف ، في Hotspot على الأقل ، يبدو أنه يمكنك استرداده بأمان من هذا. (هناك بالطبع حالات أخرى حيث يمكن طرح OutOfMemoryError في مكان غير آمن ومحاولة المحراث.)

لذا ، فإن النقطة الأساسية هي: إذا قمت بالقبض على Thowsable / Error بدلاً من Exception ، فيجب أن تكون حالة واضحة المعالم حيث تعرف أنك "تقوم بشيء مميز" .

تحرير: ربما هذا واضح ، ولكن نسيت أن أقول أنه في الممارسة العملية ، قد لا JVM في الواقع استدعاء شرط الصيد على خطأ. لقد رأيت بالتأكيد بريق Hotspot بريق فوق محاولات للقبض على بعض OutOfMemoryErrors و NoClassDefFoundError.


لماذا لا نمسك بهم جميعا؟ ثم سجلهم ، على الأقل أنت تعلم أن لديك خطأ. حتى أفضل الصيد Throwable / s من استثناء / s فقط.


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

مخطط مرجعي سريع:

  • رمي - أبدا التقاط هذا
  • خطأ - يشير إلى خطأ VM - لا يمسك بهذا
  • RuntimeException - أشار إلى خطأ مبرمج - لا يمسك بهذا
  • استثناء - أبدا التقاط هذا

السبب الذي يجب أن لا يصادف استثناء هو أنه يمسك كل من الفئات الفرعية ، بما في ذلك RuntimeException.

والسبب الذي لا ينبغي أن يمسك به Throwable هو أنه يمسك بكل الفئات الفرعية ، بما في ذلك Error and Exception.

هناك استثناءات (لا يقصد التورية) إلى "القواعد" المذكورة أعلاه:

  • الرمز الذي تعمل به (من طرف ثالث) يرمي رمي أو استثناء
  • تقوم بتشغيل تعليمات برمجية غير موثوقة قد تتسبب في تعطل البرنامج إذا كان هو استثناء.

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


من وثائق واجهة برمجة تطبيقات جافا:

Exception الطبقة والفئات الفرعية لها هي شكل من أشكال Throwable التي تشير إلى الشروط التي قد يرغب التطبيق المعقول في التقاطها.

Error هو فئة فرعية من Throwable تشير إلى مشكلات خطيرة لا يجب أن يحاول تطبيق معقول التقاطها.

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


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


public void run() {
   try {
       ...
   }
   catch(Throwable t) {
       threadCompletionError = t;
   }
}

أنا لست متأكدا حقا إذا كان هذا هو النهج الأفضل ، لكنه يعمل. وكنت أواجه خطأ "ClassNotFound" الذي تثيره JVM ، وهو خطأ وليس استثناء. إذا سمحت بطرح الاستثناء ، فأنا لست متأكدًا من كيفية التقاطه في مؤشر الترابط المتصل (ربما هناك طريقة ولكن لا أعرف عن ذلك - حتى الآن).

أما بالنسبة إلى طريقة ThreadDeath ، فلا تتصل بـ "الأسلوب Thread.stop ()". استدعاء Thread.interrupt ويكون مؤشر الترابط الخاص بك للتحقق مما إذا كان مقاطعة من قبل شخص ما.





java