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



معالجة الاستثناءات في c++ (12)

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

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

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

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

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

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

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


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


ليس هناك نقطة في اصطياد خطأ .

يتم استخدام الأخطاء للإشارة إلى حدوث خطأ ما في التطبيق ، ويجب إعادة تشغيله.

على سبيل المثال ، هناك خطأ شائع واحد

java.lang.OutOfMemoryError

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

انظر هذه الإجابة الأخرى لفهم المزيد عن الأنواع الثلاثة من الاستثناءات .


بشكل عام ، قد يكون من المنطقي محاولة اكتشاف الأخطاء إذا تم الإبلاغ عنها بشكل صحيح.

ومع ذلك ، أعتقد أن هناك حالات يكون من المناسب فيها اكتشاف خطأ وعدم الإبلاغ عنه. أنا أشير إلى UnsatisfiedLinkError. في JAI ، تستخدم المكتبة بعض المكتبات الأصلية لتنفيذ معظم المشغلين لأسباب تتعلق بالأداء ، ولكن إذا فشلت المكتبة في التحميل (لا توجد ، تنسيق خاطئ ، نظام أساسي غير مدعوم) ستظل المكتبة تعمل لأنها ستعود إلى وضع java فقط .


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


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

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

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

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

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

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

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

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


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

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

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

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

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

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

عادةً عند البرمجة ، يجب فقط التقاط استثناء محدد (مثل IOException ). في الكثير من البرامج يمكنك مشاهدة toplevel جدا

try {
    ...
} catch(Exception e) {
    ...
}

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

كل شيء مشتق من Error شيء سيئ للغاية ، لا يمكنك فعل أي شيء ضده. السؤال هو ، إذا كان من المنطقي للقبض على OutOfMemoryError أو VirtualMachineError . (إنه خطأ في JavaVM نفسه ، ربما لا يمكنك حتى عرض مربع رسالة أو إرسال بريد إلكتروني ثم)

ربما لا يجب أن تكون فئة مشتقة من Error ، يجب أن تشتق من Exception أو RuntimeException .


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

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

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

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

لا Throwable أو Error ، ويجب أن لا تصطاد Exception عام فقط. Error s بشكل عام الأشياء التي لا يمكن استرداد معظم البرامج المعقولة. إذا كنت تعرف ما يحدث ، فقد تتمكن من الاسترداد من خطأ محدد ، ولكن في هذه الحالة ، يجب أن تصطاد ذلك الخطأ الوحيد فقط وليس كل الأخطاء بشكل عام.

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

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


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


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


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

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

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





java