CodeGym /مدونة جافا /Random-AR /الاستثناءات في جافا
John Squirrels
مستوى
San Francisco

الاستثناءات في جافا

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

ما هو استثناء جافا؟

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

بضع كلمات حول الكلمات الرئيسية

تعتمد معالجة الاستثناءات في Java على استخدام الكلمات الأساسية التالية في البرنامج:
  • محاولة - تحدد كتلة من التعليمات البرمجية حيث يمكن أن يحدث استثناء؛
  • Catch - يحدد كتلة من التعليمات البرمجية حيث تتم معالجة الاستثناءات؛
  • أخيرًا - يحدد كتلة اختيارية من التعليمات البرمجية التي، إذا كانت موجودة، سيتم تنفيذها بغض النظر عن نتائج كتلة المحاولة.
يتم استخدام هذه الكلمات الرئيسية لإنشاء بنيات خاصة في الكود: حاول{}catch ، حاول{}catch{}أخيرًا ، حاول{}أخيرًا{} .
  • رمي - يستخدم لرفع الاستثناء؛
  • الرميات - تُستخدم في توقيع الطريقة للتحذير من أن الطريقة قد تطرح استثناءً.
مثال على استخدام الكلمات الأساسية في برنامج Java:

// This method reads a string from the keyboard

public String input() throws MyException { // Use throws to warn 
// that the method may throw a MyException
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String s = null;
// We use a try block to wrap code that might create an exception. In this case,
// the compiler tells us that the readLine() method in the 
// BufferedReader class might throw an I/O exception
    try {
        s = reader.readLine();
// We use a catch block to wrap the code that handles an IOException  
    } catch (IOException e) {
        System.out.println(e.getMessage());
// We close the read stream in the finally block
    } finally {
// An exception might occur when we close the stream if, for example, the stream was not open, so we wrap the code in a try block
        try {
            reader.close();
// Handle exceptions when closing the read stream
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    if (s.equals("")) {
// We've decided that an empty string will prevent our program from working properly. For example, we use the result of this method to call the substring(1, 2) method. Accordingly, we have to interrupt the program by using throw to generate our own MyException exception type.
        throw new MyException("The string cannot be empty!");
    }
    return s;
}

لماذا نحتاج إلى استثناءات؟

دعونا نلقي نظرة على مثال من العالم الحقيقي. تخيل أن جزءًا من الطريق السريع به جسر صغير ذو سعة وزن محدودة. إذا مرت فوقه سيارة أثقل من الحد المسموح به للجسر، فقد ينهار. سيصبح الوضع بالنسبة للسائق، بعبارة ملطفة، استثنائيا. ولتجنب ذلك، تقوم إدارة النقل بتثبيت علامات تحذيرية على الطريق قبل حدوث أي خطأ. عند رؤية علامة التحذير، يقوم السائق بمقارنة وزن سيارته مع الحد الأقصى لوزن الجسر. إذا كانت السيارة ثقيلة جدًا، يسلك السائق طريقًا التفافيًا. أولاً، أتاحت إدارة النقل لسائقي الشاحنات تغيير مسارهم إذا لزم الأمر، وثانيًا، حذرت السائقين من المخاطر على الطريق الرئيسي، وثالثًا، حذرت السائقين من ضرورة عدم استخدام الجسر في ظل ظروف معينة. الاستثناءات في جافا - 2تعد القدرة على منع وحل المواقف الاستثنائية في البرنامج، مما يسمح له بمواصلة التشغيل، أحد أسباب استخدام الاستثناءات في Java. تتيح لك آلية الاستثناء أيضًا حماية التعليمات البرمجية (API) الخاصة بك من الاستخدام غير السليم عن طريق التحقق من صحة (فحص) أي مدخلات. الآن تخيل أنك قسم النقل لثانية واحدة. أولاً، عليك أن تعرف الأماكن التي يمكن أن يتوقع فيها سائقو السيارات حدوث مشكلات. ثانيًا، تحتاج إلى إنشاء علامات تحذيرية وتثبيتها. وأخيرًا، تحتاج إلى توفير تحويلات إذا ظهرت مشاكل على الطريق الرئيسي. في Java، تعمل آلية الاستثناء بطريقة مماثلة. أثناء التطوير، نستخدم كتلة المحاولة لبناء "حواجز استثناء" حول الأقسام الخطيرة من التعليمات البرمجية، ونوفر "مسارات احتياطية" باستخدام كتلة الالتقاط {} ، ونكتب التعليمات البرمجية التي يجب تشغيلها بغض النظر عن ما هو موجود في كتلة أخيرًا{} . إذا لم نتمكن من توفير "طريق احتياطي" أو أردنا منح المستخدم الحق في الاختيار، فيجب علينا على الأقل تحذيره من الخطر. لماذا؟ ولكم أن تتخيلوا سخط السائق الذي، دون أن يرى أي إشارة تحذيرية، يصل إلى جسر صغير لا يستطيع عبوره! في البرمجة، عند كتابة فئاتنا وأساليبنا، لا يمكننا دائمًا التنبؤ بكيفية استخدامها من قبل المطورين الآخرين. ونتيجة لذلك، لا يمكننا التنبؤ بالطريقة الصحيحة بنسبة 100% لحل الوضع الاستثنائي. ومع ذلك، من الجيد تحذير الآخرين بشأن احتمال حدوث مواقف استثنائية. تتيح لنا آلية الاستثناء في Java القيام بذلك باستخدام الكلمة الأساسية throws - وهو في الأساس إعلان بأن السلوك العام لطريقتنا يتضمن طرح استثناء. وبالتالي، فإن أي شخص يستخدم هذه الطريقة يعرف أنه يجب عليه كتابة تعليمات برمجية للتعامل مع الاستثناءات.

تحذير الآخرين من "مشكلة"

إذا كنت لا تخطط للتعامل مع الاستثناءات في طريقتك، ولكنك تريد تحذير الآخرين من احتمال حدوث استثناءات، فاستخدم الكلمة الأساسية throws . تعني هذه الكلمة الأساسية الموجودة في توقيع الطريقة أنه في ظل ظروف معينة، قد تقوم الطريقة بطرح استثناء. يعد هذا التحذير جزءًا من واجهة الطريقة ويتيح لمستخدميه تنفيذ منطق معالجة الاستثناءات الخاص بهم. بعد الرميات، نحدد أنواع الاستثناءات التي تم طرحها. تنحدر هذه عادةً من فئة استثناء Java . نظرًا لأن Java هي لغة موجهة للكائنات، فإن جميع الاستثناءات هي كائنات في Java. الاستثناءات في جافا - 3

التسلسل الهرمي للاستثناء

عند حدوث خطأ أثناء تشغيل برنامج ما، يقوم JVM بإنشاء كائن من النوع المناسب من التسلسل الهرمي لاستثناءات Java - مجموعة من الاستثناءات المحتملة التي تنحدر من سلف مشترك - فئة Throwable . يمكننا تقسيم حالات التشغيل الاستثنائية إلى مجموعتين:
  1. الحالات التي لا يمكن للبرنامج التعافي منها ومواصلة التشغيل العادي.
  2. الحالات التي يكون فيها التعافي ممكنًا.
تتضمن المجموعة الأولى حالات تتضمن استثناءً ينحدر من فئة الخطأ . هذه هي الأخطاء التي تحدث بسبب خلل في JVM، أو تجاوز سعة الذاكرة، أو فشل النظام. تشير عادةً إلى مشكلات خطيرة لا يمكن حلها بواسطة البرنامج. في Java، لا يتم التحقق من إمكانية وجود مثل هذه الاستثناءات بواسطة المترجم، لذلك تُعرف باسم الاستثناءات غير المحددة. تتضمن هذه المجموعة أيضًا RuntimeExceptions، وهي استثناءات تنحدر من فئة Exception ويتم إنشاؤها بواسطة JVM في وقت التشغيل. غالبًا ما يكون سببها أخطاء في البرمجة. لا يتم تحديد هذه الاستثناءات أيضًا (غير محددة) في وقت الترجمة، لذلك لا يُطلب منك كتابة تعليمات برمجية للتعامل معها. تتضمن المجموعة الثانية مواقف استثنائية يمكن توقعها عند كتابة البرنامج (وبالتالي يجب عليك كتابة التعليمات البرمجية للتعامل معها). تسمى هذه الاستثناءات بالاستثناءات المحددة. عندما يتعلق الأمر بالاستثناءات، فإن معظم عمل مطور Java يتعامل مع مثل هذه المواقف.

إنشاء استثناء

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

معالجة الاستثناء

في Java، نقوم بإنشاء كتل تعليمات برمجية حيث نتوقع الحاجة إلى معالجة الاستثناءات باستخدام بنيات محاولة{}catch و try{}catch{}أخيرًا ومحاولة {}أخيرًا{} . الاستثناءات في جافا - 4عندما يتم طرح استثناء في كتلة المحاولة ، يبحث JVM عن معالج الاستثناء المناسب في كتلة الالتقاط التالية . إذا كانت كتلة الالتقاط تحتوي على معالج الاستثناء المطلوب، فسيتم تمرير التحكم إليها. إذا لم يكن الأمر كذلك، فسيقوم JVM بالبحث في سلسلة كتل الالتقاط حتى يتم العثور على المعالج المناسب. بعد تنفيذ كتلة الالتقاط ، يتم نقل التحكم إلى الكتلة الاختيارية النهائية . إذا لم يتم العثور على كتلة التقاط مناسبة ، فسيقوم JVM بإيقاف البرنامج وعرض تتبع المكدس (المكدس الحالي لاستدعاءات الطريقة)، بعد تنفيذ الكتلة النهائية أولاً إذا كانت موجودة. مثال على معالجة الاستثناء:

public class Print {

     void print(String s) {
        if (s == null) {
            throw new NullPointerException("Exception: s is null!");
        }
        System.out.println("Inside print method: " + s);
    }

    public static void main(String[] args) {
        Print print = new Print();
        List list= Arrays.asList("first step", null, "second step");

        for (String s : list) {
            try {
                print.print(s);
            }
            catch (NullPointerException e) {
                System.out.println(e.getMessage());
                System.out.println("Exception handled. The program will continue");
            }
            finally {
                System.out.println("Inside finally block");
            }
            System.out.println("The program is running...");
            System.out.println("-----------------");
        }

    }
    }
فيما يلي نتائج الطريقة الرئيسية :

Inside print method: first step
Inside finally block
The program is running...
-----------------
Exception: s is null!
Exception handled. The program will continue
Inside finally block
The program is running...
-----------------
Inside print method: second step
Inside finally block
The program is running...
-----------------
يتم استخدام أخيرًا عادةً لإغلاق أي تدفقات وتحرير أي موارد مفتوحة/مخصصة في كتلة المحاولة . ومع ذلك، عند كتابة برنامج، ليس من الممكن دائمًا تتبع إغلاق جميع الموارد. لتسهيل حياتنا، يقدم مطورو Java بنية المحاولة باستخدام الموارد ، والتي تغلق تلقائيًا أي موارد مفتوحة في كتلة المحاولة . يمكن إعادة كتابة المثال الأول باستخدام Try-with-resources :

public String input() throws MyException {
    String s = null;
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
        s = reader.readLine();
   } catch (IOException e) {
       System.out.println(e.getMessage());
   }
    if (s.equals("")) {
        throw new MyException ("The string cannot be empty!");
    }
    return s;
}
بفضل إمكانيات Java المقدمة في الإصدار 7، يمكننا أيضًا دمج التقاط الاستثناءات غير المتجانسة في كتلة واحدة، مما يجعل التعليمات البرمجية أكثر إحكاما وقابلية للقراءة. مثال:

public String input() {
    String s = null;
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
        s = reader.readLine();
        if (s.equals("")) {
            throw new MyException("The string cannot be empty!");
        }
    } catch (IOException | MyException e) {
        System.out.println(e.getMessage());
    }
    return s;
}

الخط السفلي

يتيح لك استخدام الاستثناءات في Java جعل برامجك أكثر قوة عن طريق إنشاء "مسارات احتياطية"، واستخدام كتل الالتقاط لفصل الكود الرئيسي عن كود معالجة الاستثناء، واستخدام الرميات لتحويل مسؤولية معالجة الاستثناء إلى من يستخدم طريقتك .
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION