الكلمة الإنجليزية غير الطبيعية هي استثناء ، والترجمة الحرفية تعني "حادث ، استثناء" ، مما يعني المواقف غير الطبيعية. في الواقع ، الاستثناءات هي أخطاء في البرامج بشكل أساسي ، بما في ذلك أخطاء منطق البرنامج وأخطاء النظام.
مقدمة
الجميع على دراية بالتعامل مع استثناء Java ، بشكل عام ، هناك نقطتان:
1. رمي الاستثناء: رمي الاستثناء
class simpleException {public void a () rems {refl infer new requestion () ؛ } ؛}2. استثناءات Catch:
الفئة العامة myException {public static void main (string [] args) {myException e = new MyException () ؛ SimpleException se = new SimpleException () ؛ حاول {se.a () ؛ } catch (استثناء e1) {e1.printstacktrace () ؛ }}} class SimpleException {public void a () rems {refl new requestion () ؛ } ؛}ستناقش هذه المقالة بعض التفاصيل حول هذا الأساس المتعمق.
فئة استثناء مخصصة
توفر لنا لغة Java العديد من فئات الاستثناءات ، ولكن في بعض الأحيان لا يزال يتعين علينا تخصيص فئات الاستثناء لراحة رمز الكتابة:
class simpleException يمتد الاستثناء {} ؛
بعد الخلق ، يمكننا استخدام Try Catch لالتقاطه:
الفئة العامة myException {public static void main (string [] args) {myException e = new MyException () ؛ حاول {ea () ؛ } catch (SimpleException E1) {e1.printStackTrace () ؛ }} public void a () يلقي SimpleException {رمي New SimpleException () ؛ }} class SimpleException يمتد الاستثناء {} ؛نحدد الطريقة A () في MyException ، ودعها ترمي استثناءًا بسيطًا ، ثم نسمي هذه الطريقة في Main () ونلقي هذا الاستثناء باستخدام Try Catch:
SimpleException في myException.a (myException.java:15) في myexception.main (myexception.java:8) في sun.reflect.nativemethodaccessorimpl.invoke0 (الطريقة الأصلية) في Sun.Reflect.NativeMetCorcessorImpl.invoke (nativemitactorimpl.javafa:57. sun.reflect.delegatingMethodAccessorImpl.invoke (devatingMethodAccessorImpl.Java:43) في java.lang.reflect.method.invoke (method.java:606) في com.intellij.rt.execution.application.appmain.main
تعتمد النتيجة بعد التجميع والتنفيذ بشكل رئيسي على الأسطر الثلاثة الأولى. فيما يلي بعض النقاط التي يجب شرحها:
1. حدد نوع الاستثناء: (مواصفات الاستثناء)
عندما نحتاج إلى إلقاء استثناء بطريقة ما ، نستخدم رمي ونضيف مثيلًا لفئة الاستثناء. سيقوم البرنامج بإلقاء الاستثناء المقابل لبرنامج العميل (البرنامج الذي يدعو هذا الرمز) ويخرج هنا (أي ما يعادل العودة). لاحظ أيضًا أنه يجب علينا تحديد نوع الاستثناء عند تحديد هذه الطريقة. على سبيل المثال ، سوف يرمي الرمز التالي استثناء بسيط
الفراغ العام A () يلقي SimpleException
2. رمي استثناءات متعددة:
public void a () يلقي SimpleException ، aexception ، bexception {رمي New SimpleException () ؛ }يمكن فصل فصول الاستثناء المختلفة عن طريق الفواصل. في هذه الحالة ، لا يتعين علينا رمي كل مستثمر استثناء () ، ولكن يجب أن يلتقط رمز العميل كل فئة استثناء:
الفئة العامة myException {public static void main (string [] args) {myException e = new MyException () ؛ حاول {ea () ؛ } catch (SimpleException E1) {e1.printStackTrace () ؛ } catch (bexception e1) {e1.printStackTrace () ؛ } catch (aException e1) {e1.printStackTrace () ؛ }} public void a () يلقي SimpleException ، aexception ، bexception {throw new SimpleException () ؛ }} class SimpleException يمتد الاستثناء {} ؛ الفئة Aexception يمتد الاستثناء {} class bexception يمتد الاستثناء {} ثلاثة تتبع المكدس
سواء أكان ذلك إلقاء الاستثناءات أو الاستثناءات والتعامل مع الاستثناءات ، فإن هدفنا هو كتابة برنامج أكثر قوة ، والذي يعتمد إلى حد كبير على معلومات الاستثناء التي توفرها آلية استثناء Java ، وناقلها هو تتبع المكدس.
في الكود السابق ، نستخدم مباشرة PrintStackTrace () لطباعة معلومات الاستثناء. في الواقع ، يمكننا أيضًا استخدام طريقة getStackTrace () للحصول على مجموعة من StackTraceElement. إذا كان لديك فكرة في متناول اليد ، يمكنك أولاً البحث عن فئة StackTraceElement ، ويمكنك أن تجد أنها تنفذ الواجهة قابلة للتسلسل ، ثم إلقاء نظرة على وصفها في الفصل:
/** * عنصر في تتبع المكدس ، كما تم إرجاعه بواسطة {link * throwable#getStackTrace ()}. يمثل كل عنصر إطار مكدس واحد. * جميع إطارات المكدس باستثناء الإطارات الموجودة في الجزء العلوي من المكدس تمثل * استدعاء الطريقة. يمثل الإطار الموجود في الجزء العلوي من المكدس نقطة التنفيذ * التي تم فيها إنشاء تتبع المكدس. عادة ، * هذه هي النقطة التي تم فيها إنشاء رمي المقابلة لتتبع المكدس *. * * since 1.4 * Author Josh Bloch */من الواضح أن كل مثيل في هذه الفئة هو عنصر في تتبع المكدس ، ويمثل إطار المكدس ، ويتم إرجاع تتبع المكدس بواسطة طريقة getStackTrace (). حاولت ترجمة ما يلي عدة مرات ، لكنني شعرت أنه لم يكن جيدًا ، لذلك يمكنني فقط كتابة الرمز مباشرة لشرحه:
الفئة العامة myException {public static void main (string [] args) {myException e = new MyException () ؛ ea () ؛ public void a () {try {throw new issecivery () ؛ } catch (استثناء e) {StackTraceElement [] ste = e.getStackTrace () ؛ system.out.println (ste.length) ؛ }}}نحدد الطريقة A ، دعها ترمي استثناءً من استثناء أثناء التقاطها ، ثم نحصل على صفيف StackTraceElement من خلال طريقة getStackTrace () وطبع طول المصفوفة:
7
انتهت العملية برمز الخروج 0
قمنا بتغيير الكود قليلاً وتوقفنا عن التقاط الاستثناءات في. نعيد تعريف الطريقة ب حتى يمسك الاستثناءات أثناء استدعاء:
الفئة العامة myException {public static void main (string [] args) {myException e = new MyException () ؛ eb () ؛ } public void b () {try {a () ؛ } catch (استثناء e) {StackTraceElement [] ste = e.getStackTrace () ؛ system.out.println (ste.length) ؛ }} public void a () يلقي استثناء {رمي استثناء جديد () ؛ }}النتائج كما يلي:
8
انتهت العملية برمز الخروج 0
لا تقلق ، دعنا نلقي نظرة على شيء مثير للاهتمام:
الفئة العامة myException {public static void main (string [] args) {myException issection = new MyException () ؛ جرب {استثناء. c () ؛ } catch (استثناء e) {StackTraceElement [] ste = e.getStackTrace () ؛ system.out.println (ste.length) ؛ System.out.println ("-------------------------------------------------------------------") ؛ لـ (StackTraceElement S: E.GetStackTrace ()) {system.out.println (s.getClassName ()+": method"+s.getMethodName ()+"at line"+s.getlinenumber ()) ؛ } System.out.println ("---------------------------------------------------------------------- }} public void c () يلقي الاستثناء {try {a () ؛ } catch (استثناء e) {throw e ؛ }} public void a () يلقي استثناء {رمي استثناء جديد () ؛ }}ها هي النتائج:
8 --------------------------------------------------------------- MyException: الطريقة A at Line43MyException: الطريقة C في line39myexception: الطريقة الرئيسية في line9sun.reflect.nativemethodaccormpl: method invoke0 at line-2sun.reflect.nativemetaccessorimpl استدعاء في line43java.lang.reflect.method: طريقة استدعاء في line606com.intellij.rt.execution.application.appmain: الطريقة الرئيسية في السطر 144 --------------------------------------------------------------
وهذا يعني أن getStackTrace () إرجاع مكدس ، والذي يحتوي على بعض المعلومات الأساسية من المتصل (Main ()) إلى استثناء الرمي الأولي (A ()). في الكود أعلاه ، نلتقط الاستثناء عند استدعاء الطريقة A في طريقة C ورميها مرة أخرى من خلال الرميات. يمكن للطريقة التي استدعاء طريقة C أن تلتقط الاستثناء والتعامل معها ، أو يمكنك اختيار الاستمرار في رمي للسماح للمتصلين ذات المستوى الأعلى (بالقرب من أسفل المكدس) التعامل معها. على الرغم من أن Rethrow مريحة للغاية ، إلا أن هناك بعض المشاكل. دعونا نلقي نظرة على الكود التالي:
الفئة العامة myException {public static void main (string [] args) {myException issection = new MyException () ؛ جرب {استثناء. c () ؛ } catch (استثناء e) {E.PrintStackTrace (System.out) ؛ }} public void c () يلقي الاستثناء {try {a () ؛ } catch (استثناء e) {throw e ؛ }} public void a () يلقي استثناء {رمي استثناء جديد ("استثناء من A ()") ؛ }} java.lang.exception: استثناء من A () في myException.a (myException.java:40) في myexception.c (myexception.java:30) في myexception.main (myexception.java:21)نقوم بإعادة إدخال e في C وطباعته باستخدام E.PrintStackTrace () بشكل رئيسي. يمكنك أن ترى أن تتبع المكدس المطبوع لا يزال ينتمي إلى. إذا أردنا تحويل تتبع المكدس إلى C ، فيمكننا كتابته مثل هذا:
الفئة العامة myException {public static void main (string [] args) {myException issection = new MyException () ؛ جرب {استثناء. c () ؛ } catch (استثناء e) {E.PrintStackTrace (System.out) ؛ }} public void c () يلقي الاستثناء {try {a () ؛ } catch (استثناء e) {// throw e ؛ رمي (استثناء) efillinstacktrace () ؛ }} public void a () يلقي استثناء {رمي استثناء جديد ("استثناء من A ()") ؛ }} java.lang.exception: استثناء من A () في myException.c (myexception.java:22) في myException.main (myexception.java:10) أربعة استثناء السلاسل
لنلقي نظرة على سيناريو:
الفئة العامة testException {public static void main (string [] args) {testException testException = new testexception () ؛ حاول {testException.c () ؛ } catch (cexception e) {E.PrintStackTrace () ؛ }} public void a () يرمي AEXCESTION {aexception aexception = new aException ("هذا استثناء") ؛ رمي aexception. } public void b () remrows bexception {try {a () ؛ } catch (aexception e) {رمي bexception جديد ("هذا هو B استثناء") ؛ }} public void c () يلقي cexception {try {b () ؛ } catch (bexception e) {رمي cexception جديد ("هذا هو استثناء c") ؛ }}} class axception يمتد الاستثناء {public aexception (string msg) {super (msg) ؛ }} class bexception يمتد الاستثناء {public bexception (string msg) {super (msg) ؛ }} class cexception يمتد الاستثناء {public cexception (string msg) {super (msg) ؛ }}يتم إنشاء ثلاث فئات استثناء من الفئات AException و Bexception و Cexception ، ثم يتم إلقاء AException في A () ، يتم اكتشاف AException في B () ويتم إلقاء bexception ، وأخيراً يتم اكتشاف bexception في C () ويتم إلقاء cexception ، ويتم طباعة النتيجة على النحو التالي:
Cexception: هذا هو استثناء في testexception.c (testexception.java:31) في testexception.main (testexception.java:8)
حسنًا ، نرى فقط معلومات Cexception ، وقد فقدت AEXCESTION و BEXCESTION ، وستظهر وظيفة سلسلة الاستثناءات ، انظر الرمز:
الفئة العامة testException {public static void main (string [] args) {testException testException = new testexception () ؛ حاول {testException.c () ؛ } catch (cexception e) {E.PrintStackTrace () ؛ }} public void a () يرمي AEXCESTION {aexception aexception = new aException ("هذا استثناء") ؛ رمي aexception. } public void b () remrows bexception {try {a () ؛ } catch (aexception e) {// رمي bexception جديد ("هذا هو B استثناء") ؛ bexception bexception = new bexception ("هذا هو B استثناء") ؛ bexception.initcause (e) ؛ رمي bexception. }} public void c () يلقي cexception {try {b () ؛ } catch (bexception e) {// رمي cexception جديد ("هذا هو استثناء c") ؛ cexception cexception = cexception جديد ("هذا هو استثناء C") ؛ cexception.initcause (e) ؛ رمي cexception. }}} class axception يمتد الاستثناء {public aexception (string msg) {super (msg) ؛ }} class bexception يمتد الاستثناء {public bexception (string msg) {super (msg) ؛ }} class cexception يمتد الاستثناء {public cexception (string msg) {super (msg) ؛ }}نستخدم طريقة initcause () لتوصيل معلومات الاستثناء ، والنتيجة هي كما يلي:
Cexception: هذا هو استثناء C في testexception.c (testexception.java:35) في testexception.main (testexception.java:8) في sun.reflect.nativemethodaccessorimpl.invoke0 (nativeMpl.javapl.java: ats. sun.reflect.delegatingMethodAccessorImpl.invoke (devatingMethodAccessorImpl.java:43) في java.lang.reflect.method.invoke استثناء في testexception.b (testexception.java:24) في testexception.c (testexception.java:32) ... 6 morecaused بواسطة: aexception: هذا استثناء في testexception.a (testexception.java:15) في testexception.java.java:21) ... 7.
الخمسة بوستسكريبت
في الواقع ، لا يزال هناك العديد من الأشياء التي يجب مناقشتها حول معالجة استثناءات Java ، ولكن نظرًا لأن لدي خبرة محدودة ، لا أستطيع أن أفهمها بعمق ، والأكثر استخدامًا هي
جرب {...} catch (استثناء e) {...} أخيرًا {// الكود الذي سيتم تنفيذه بغض النظر عما إذا كان سيتم القبض على الاستثناء أو معالجته ، مثل إغلاق عمليات IO}ولكن بغض النظر عن ماذا ، لا يزال يتعين علينا أن نشكر جافا لتزويدنا بآلية الاستثناء. إنه مثل الشيخ ، ويوجهنا من وقت لآخر ، ويجعلنا أقل مللًا عند الترميز :)