Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الأخطاء من نوع Checked مقابل Unchecked

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 61 من 87.

لقد رأيت أن تسلسل استثناءات Java الهرمي ينقسم إلى فروع مختلفة. التمييز الأساسي الذي تحتاج إلى فهمه هو بين الاستثناءات المفحوصة (checked) وغير المفحوصة (unchecked)—وهذا يؤثر على كيفية تعاملك معها في الكود الخاص بك.

الاستثناءات المفحوصة (Checked exceptions) هي استثناءات يجبرك المترجم (compiler) على التعامل معها. وهي ترث من Exception مباشرة (ليس من خلال RuntimeException). يجب عليك إما التقاطها (catch) أو التصريح عنها باستخدام throws:

// يجب التعامل مع IOException - إنه استثناء مفحوص (checked)
public void readFile(String path) throws IOException {
    FileReader reader = new FileReader(path);  // قد يرمي IOException
}

// أو قم بالتقاطه (catch)
public void readFileSafe(String path) {
    try {
        FileReader reader = new FileReader(path);
    } catch (IOException e) {
        System.out.println("File error: " + e.getMessage());
    }
}

الاستثناءات غير المفحوصة (Unchecked exceptions) ترث من RuntimeException. لا يلزمك المترجم (compiler) بمعالجتها—فهي تشير عادةً إلى أخطاء برمجية مثل المراجع الفارغة (null references) أو الوصول غير الصالح للمصفوفة:

// لا حاجة لتصريح throws
public int divide(int a, int b) {
    return a / b;  // قد يرمي ArithmeticException (غير مفحوص)
}

String name = null;
name.length();  // NullPointerException (غير مفحوص)

عند إنشاء استثناءات مخصصة، اختر الفئة الأب بناءً على الطريقة التي تريد من المستدعين التعامل بها معها. استخدم الاستثناءات المفحوصة (checked exceptions) للحالات القابلة للإصلاح التي يجب على المستدعي توقعها. استخدم الاستثناءات غير المفحوصة (unchecked exceptions) لأخطاء البرمجة التي لا ينبغي أن تحدث في الكود الصحيح.

challenge icon

التحدي

سهل

لنقم ببناء نظام للتحقق من العمر يوضح الفرق الجوهري بين الاستثناءات المفحوصة (checked) وغير المفحوصة (unchecked)! ستقوم بإنشاء استثناءات مخصصة من كلا النوعين وترى كيف يتعامل المترجم (compiler) معها بشكل مختلف.

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • InvalidAgeException.java: قم بإنشاء استثناء مفحوص (checked exception) لفشل التحقق من العمر. يجب أن يرث هذا الاستثناء من Exception مباشرة، مما يعني أن المستدعين سيُجبرون على التعامل معه. قم بتضمين منشئ (constructor) يقبل رسالة ويمررها إلى الفئة الأب.
  • NegativeAgeException.java: قم بإنشاء استثناء غير مفحوص (unchecked exception) للحالات التي يقدم فيها شخص ما عمراً سالباً (خطأ برمجي لا ينبغي أن يحدث مع المدخلات الصحيحة). يجب أن يرث هذا من RuntimeException. قم بتضمين منشئ يقبل رسالة ويمررها إلى الأب.
  • AgeVerifier.java: قم بإنشاء فئة تتحقق من الأعمار باستخدام كلا نوعي الاستثناءات. قم بتضمين طريقتين (static methods):

    verifyAge(int age) - يجب أن تقوم هذه الطريقة برمي NegativeAgeException (غير مفحوص) مع الرسالة "Age cannot be negative: " + age إذا كان العمر أقل من 0. وإذا كان العمر أقل من 18، قم برمي InvalidAgeException (مفحوص) مع الرسالة "Must be 18 or older: " + age. خلاف ذلك، اطبع "Age " + age + " verified successfully". بما أن هذه الطريقة ترمي استثناءً مفحوصاً، يجب عليك التصريح عنها باستخدام throws InvalidAgeException.

    verifyAgeUncheckedOnly(int age) - تستخدم هذه الطريقة الاستثناءات غير المفحوصة فقط. إذا كان العمر سالباً، قم برمي NegativeAgeException بنفس تنسيق الرسالة. إذا كان العمر أقل من 18، قم برمي IllegalArgumentException مع الرسالة "Too young: " + age. خلاف ذلك، اطبع "Age " + age + " verified (unchecked method)". لاحظ أن هذه الطريقة لا تحتاج إلى تصريح throws!

  • Main.java: اجمع نظام التحقق الخاص بك معاً! ستتلقى مدخلاً واحداً: العمر (عدد صحيح).

    أولاً، اطبع "=== Checked Exception Method ===" واستدعِ verifyAge مع العمر المدخل. بما أنها ترمي استثناءً مفحوصاً، يجب عليك تغليفها في try-catch. التقط InvalidAgeException واطبع "Checked exception caught: " + e.getMessage(). التقط أيضاً NegativeAgeException واطبع "Unchecked exception caught: " + e.getMessage().

    ثم اطبع سطراً فارغاً و "=== Unchecked Exception Method ===". استدعِ verifyAgeUncheckedOnly بنفس العمر. على الرغم من أن هذه الطريقة يمكن أن ترمي استثناءات، إلا أنك لست مجبراً على التقاطها. ومع ذلك، قم بتغليفها في try-catch على أي حال للتعامل مع الأخطاء بلباقة. التقط NegativeAgeException واطبع "Runtime exception caught: " + e.getMessage(). التقط IllegalArgumentException واطبع "Illegal argument caught: " + e.getMessage().

ستتلقى مدخلاً واحداً: عدد صحيح يمثل العمر المراد التحقق منه.

لاحظ الفرق الرئيسي: الطريقة التي تحتوي على الاستثناء المفحوص تتطلب تصريح throws وتجبر المستدعين على التعامل معه، بينما يتم تجميع (compile) الطريقة غير المفحوصة بشكل جيد دون أي معالجة للاستثناءات. هذا هو التمييز الأساسي بين الاستثناءات المفحوصة وغير المفحوصة في Java!

ورقة مرجعية

تنقسم استثناءات جافا (Java exceptions) إلى استثناءات مفحوصة (checked) وغير مفحوصة (unchecked)، وهو ما يحدد كيفية فرض المترجم (compiler) للتعامل معها.

الاستثناءات المفحوصة (Checked exceptions) ترث من Exception مباشرة (وليس من خلال RuntimeException). يجبرك المترجم إما على التقاطها أو التصريح عنها باستخدام throws:

// يجب التصريح باستخدام throws
public void readFile(String path) throws IOException {
    FileReader reader = new FileReader(path);
}

// أو التقاطه
public void readFileSafe(String path) {
    try {
        FileReader reader = new FileReader(path);
    } catch (IOException e) {
        System.out.println("File error: " + e.getMessage());
    }
}

الاستثناءات غير المفحوصة (Unchecked exceptions) ترث من RuntimeException. لا يتطلب المترجم منك التعامل معها. وعادة ما تشير إلى أخطاء برمجية:

// لا حاجة للتصريح بـ throws
public int divide(int a, int b) {
    return a / b;  // قد يرمي ArithmeticException (غير مفحوص)
}

String name = null;
name.length();  // NullPointerException (غير مفحوص)

عند إنشاء استثناءات مخصصة، اختر الفئة الأب بناءً على كيفية تعامل المستدعين معها:

  • استخدم الاستثناءات المفحوصة (ترث من Exception) للحالات القابلة للاسترداد التي يجب على المستدعي توقعها
  • استخدم الاستثناءات غير المفحوصة (ترث من RuntimeException) للأخطاء البرمجية التي لا ينبغي أن تحدث في الكود الصحيح

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int age = scanner.nextInt();
        
        // TODO: اختبار طريقة الاستثناء المفحوص (checked exception)
        // طباعة: "=== Checked Exception Method ==="
        // استدعاء AgeVerifier.verifyAge(age) داخل try-catch
        // - Catch InvalidAgeException: طباعة "Checked exception caught: [message]"
        // - Catch NegativeAgeException: طباعة "Unchecked exception caught: [message]"
        
        
        // TODO: طباعة سطر فارغ، ثم "=== Unchecked Exception Method ==="
        // استدعاء AgeVerifier.verifyAgeUncheckedOnly(age) داخل try-catch
        // - Catch NegativeAgeException: طباعة "Runtime exception caught: [message]"
        // - Catch IllegalArgumentException: طباعة "Illegal argument caught: [message]"
        
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming