الأخطاء من نوع Checked مقابل Unchecked
جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 61 من 87.
لقد رأيت أن تسلسل Java الهرمي لـ exceptions ينقسم إلى فروع مختلفة. والتمييز الأساسي الذي تحتاج إلى فهمه هو بين checked وunchecked exceptions. ويؤثر ذلك في كيفية وجوب handle لها في شفرتك.
Checked exceptions هي exceptions التي يُجبرك المترجم على handleها. وهي تمتد إلى Exception directly (وليس من خلال RuntimeException). يجب عليك إما catchها أو declareها باستخدام throws:
// يجب معالجة IOException - فهو مفحوص
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // قد يطلق IOException
}
// أو التقاطه
public void readFileSafe(String path) {
try {
FileReader reader = new FileReader(path);
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}تُمدِّد الاستثناءات غير المُتحقَّق منها RuntimeException. لا ي要求 منك المترجم التعامل معها. وهي تشير عادةً إلى أخطاء برمجية مثل المراجع الفارغة أو الوصول غير الصالح إلى المصفوفات:
// لا يلزم الإعلان عن throws
public int divide(int a, int b) {
return a / b; // قد يرمي ArithmeticException (غير مفحوص)
}
String name = null;
name.length(); // NullPointerException (غير مفحوص)عند إنشاء exceptions مخصصة، اختر الفئة الأصلية بناءً على الطريقة التي تريد من المتصلين بها handle هذه exceptions. استخدم checked exceptions للحالات القابلة للاسترداد التي ينبغي للمتصل توقّعها. استخدم exceptions غير المُتحقَّق منها لأخطاء البرمجة التي لا ينبغي أن تحدث في code صحيح.
التحدي
سهللنَبْنِ نظامًا للتحقق من العمر يوضّح الفرق الأساسي بين الاستثناءات checked وunchecked! ستنشئ استثناءات مخصّصة من كلا النوعين، وسترى كيف يتعامل معها المصرّف بشكل مختلف.
ستنظّم الكود في أربعة ملفات:
InvalidAgeException.java: أنشئ استثناءً checked لفشل التحقق من العمر. يجب أن يمدّد هذا الاستثناءExceptionمباشرةً، ما يعني أن Callers سيُجبرون على التعامل معه. أضف constructor يقبل message ويمرّره إلى parent class.NegativeAgeException.java: أنشئ استثناءً unchecked عندما يقدّم شخص ما عمرًا سالبًا (وهو خطأ برمجي لا ينبغي أن يحدث مع إدخال صحيح). يجب أن يمدّد هذا الاستثناءRuntimeException. أضف constructor يقبل message ويمرّره إلى parent.AgeVerifier.java: أنشئ class يتحقق من الأعمار باستخدام نوعَي exception. أضف طريقتين static:verifyAge(int age)- يجب أن ترمي هذه الطريقةNegativeAgeException(unchecked) مع message"Age cannot be negative: [age]"إذا كان العمر أقل من 0. وإذا كان العمر أقل من 18، فارْمِInvalidAgeException(checked) مع message"Must be 18 or older: [age]". وإلا، اطبعAge [age] verified successfully. وبما أن هذه الطريقة ترمي exception checked، يجب أن تعلن عنه باستخدامthrows InvalidAgeException.verifyAgeUncheckedOnly(int age)- تستخدم هذه الطريقة exceptions من نوع unchecked فقط. إذا كان العمر سالبًا، فارْمِNegativeAgeExceptionبالتنسيق نفسه للرسالة. وإذا كان العمر أقل من 18، فارْمِIllegalArgumentExceptionمع message"Too young: [age]". وإلا، اطبعAge [age] verified (unchecked method). لاحظ أن هذه الطريقة لا تحتاج إلى إعلانthrows!Main.java: اجمع نظام التحقق الخاص بك! ستتلقى input واحدًا: عمرًا (عددًا صحيحًا).أولًا، اطبع
=== Checked Exception Method ===واستدعِverifyAgeباستخدام عمر الإدخال. وبما أنها ترمي exception checked، يجب أن تضع الاستدعاء داخل try-catch. التقطInvalidAgeExceptionواطبعChecked exception caught: [message]. والتقط أيضًاNegativeAgeExceptionواطبعUnchecked exception caught: [message].ثم اطبع سطرًا فارغًا و
=== Unchecked Exception Method ===. استدعِverifyAgeUncheckedOnlyباستخدام العمر نفسه. رغم أن هذه الطريقة قد ترمي exceptions، فلستَ مُجبرًا على التقاطها. ومع ذلك، ضعها داخل try-catch على أي حال للتعامل مع الأخطاء بسلاسة. التقطNegativeAgeExceptionواطبعRuntime exception caught: [message]. والتقطIllegalArgumentExceptionواطبعIllegal argument caught: [message].
ستتلقى input واحدًا: عددًا صحيحًا يمثّل العمر المراد التحقق منه.
لاحظ الفرق الأساسي: الطريقة التي تستخدم exception من نوع checked تتطلب إعلان throws وتجبر Callers على التعامل معه، بينما تُصرَّف الطريقة unchecked بشكل صحيح من دون أي معالجة لـ exception. هذا هو الفرق الجوهري بين exceptions من نوع checked وunchecked في Java!
جرّب بنفسك
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int age = scanner.nextInt();
// TODO: Test the checked exception method
// Print: "=== Checked Exception Method ==="
// قم باستدعاء AgeVerifier.verifyAge(age) في try-catch
// - Catch InvalidAgeException: print "Checked exception caught: [message]"
// - Catch NegativeAgeException: print "Unchecked exception caught: [message]"
// TODO: Print a blank line, then "=== Unchecked Exception Method ==="
// قم باستدعاء AgeVerifier.verifyAgeUncheckedOnly(age) في try-catch
// - Catch NegativeAgeException: print "Runtime exception caught: [message]"
// - Catch IllegalArgumentException: print "Illegal argument caught: [message]"
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص10معالجة الاستثناءات في OOP
التسلسل الهرمي لفئات الاستثناءاتالاستثناءات المخصصةالأخطاء من نوع Checked مقابل Uncheckedنمط Try With Resourcesمراجعة - Validated User2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت