الاستثناءات المتحقق منها وغير المتحقق منها
الاستثناءات المتحقق منها وغير المتحقق منها
رأيت سابقًا كيف تُتيح لك try وcatch وfinally معالجة الاستثناءات وقت التشغيل. في هذا الدرس ستعرف أن Java تُقسّم جميع الاستثناءات إلى فئتين رئيسيتين — المتحقق منها وغير المتحقق منها — وأن المُترجم (compiler) نفسه يُطبّق قواعد إحداهما. فهم هذا الفرق ضروري لكتابة كود Java يُجمَّع بنجاح ويعالج الأخطاء على المستوى الصحيح.
الفئتان في لمحة سريعة
- الاستثناءات المتحقق منها (Checked) — يعلم المُترجم أنها قد تحدث ويُجبرك على التعامل معها (إما التقاطها أو إعلان أن الدالة ترسلها للمُستدعي عبر
throws). - الاستثناءات غير المتحقق منها (Unchecked) — لا يشترط المُترجم معالجتها. تُمثّل أخطاءً برمجية أو حالات غير متوقعة حقًا.
Exception مباشرةً (لكن ليس من RuntimeException). كل الاستثناءات غير المتحقق منها ترث من RuntimeException (التي ترث بدورها من Exception). فئات Error أيضًا غير متحقق منها، لكنها تُمثّل إخفاقات JVM خارج سيطرتك.
الاستثناءات المتحقق منها — يُطبّقها المُترجم
تُشير الاستثناءات المتحقق منها إلى حالة خارجة عن سيطرة برنامجك لكنها متوقعة — مثل ملف قد لا يوجد، أو اتصال شبكة قد يُرفض، أو قاعدة بيانات قد تكون غير متاحة. ولأن هذه المواقف متوقعة في العالم الحقيقي، تشترط Java أن تُقرّ بها صراحةً.
إذا كانت دالة قد تُطلق استثناءً متحقق منه، يجب عليك تنفيذ أحد الأمرين:
- تغليف الاستدعاء في كتلة
try/catchومعالجة الاستثناء هناك. - الإعلان عن الاستثناء في توقيع الدالة بـ
throws، مُحوّلًا المسؤولية للمُستدعي.
إن لم تفعل أيًّا منهما، لن يُجمَّع البرنامج. إليك مثالًا بسيطًا باستخدام FileNotFoundException، وهو استثناء متحقق منه تُطلقه FileReader:
throws حين لا تعرف الطبقة الحالية ماذا تفعل — ربما طبقة واجهة المستخدم أو طبقة الخدمة الأعلى منها تستطيع الاستجابة بشكل أفضل.
أبرز الاستثناءات المتحقق منها في Java
IOException— قراءة أو كتابة الملفات أو التدفقات (streams) أو مقابس الشبكة.FileNotFoundException— نوع محدد منIOException؛ مسار الملف غير موجود.SQLException— خطأ عند التواصل مع قاعدة البيانات.ParseException— تعذّر تحليل النص كتاريخ أو رقم.ClassNotFoundException— لم تجد JVM فئة بالاسم المحدد وقت التشغيل (تُستخدم في الانعكاس).
الاستثناءات غير المتحقق منها — مفاجآت وقت التشغيل
تُشير الاستثناءات غير المتحقق منها في الغالب إلى خطأ برمجي — مررت null حيث كان كائن مطلوبًا، أو وصلت إلى فهرس مصفوفة غير موجود، أو حاولت القسمة على صفر. لا يُجبرك المُترجم على التقاطها لأنها لا ينبغي أن تحدث أصلًا في كود مكتوب بشكل صحيح.
يمكنك التقاط الاستثناءات غير المتحقق منها إن كان لديك سبب وجيه (مثل معالج في المستوى الأعلى يسجّل الأعطال غير المتوقعة)، لكنك لست مُلزمًا بذلك.
أبرز الاستثناءات غير المتحقق منها (وقت التشغيل)
NullPointerException— استدعاء دالة أو الوصول إلى حقل على مرجعnull.ArrayIndexOutOfBoundsException— الفهرس سالب أو يتجاوز طول المصفوفة.ArithmeticException— عملية حسابية غير قانونية، عادةً القسمة الصحيحة على صفر.NumberFormatException— تعذّر تحليل النص كرقم.IllegalArgumentException— استلمت الدالة وسيطًا غير منطقي.IllegalStateException— الكائن في حالة غير ملائمة للعملية المطلوبة.ClassCastException— تحويل غير صالح بين الأنواع.StackOverflowError— استنفدت العودية اللانهائية كل مساحة المكدس (هذهErrorوليستException).
لماذا يوجد هذا التمييز؟
فلسفة التصميم بسيطة: إذا كانت المشكلة متوقعة وقابلة للتعافي (الشبكة معطلة، الملف مفقود)، فينبغي للغة أن تُجبرك على التخطيط لها. أما إذا كانت المشكلة خطأ برمجيًا (نسيت التحقق من null، استخدمت فهرسًا خاطئًا)، فاللغة تثق بك لكتابة كود صحيح — إذ إن اشتراط كتلة catch في كل مكان سيُضيف ضوضاء دون تحسين الصحة البرمجية.
catch فارغة. هذا يُخفي المشاكل الحقيقية ويجعل التصحيح صعبًا للغاية. على الأقل سجّل الاستثناء أو أعد رميه.
مقارنة جنبًا إلى جنب
الخلاصة
تُمثّل الاستثناءات المتحقق منها إخفاقات خارجية متوقعة — إدخال/إخراج الملفات، قواعد البيانات، الشبكات. يضمن المُترجم إقرارك بها. أما الاستثناءات غير المتحقق منها فتُمثّل أخطاءً برمجية — فهارس خاطئة، إلغاء إشارة null، وسطاء غير صالحة. تُصلح السبب الجذري بدلًا من التقاطها في كل مكان. في الدرس التالي ستتعلم كيف تُطلق كلا النوعين بشكل صريح باستخدام throw وthrows.