JavaRush /مدونة جافا /Random-AR /الاستثناءات في Java (استثناء Java)

الاستثناءات في Java (استثناء Java)

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

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

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

باختصار حول الكلمات الرئيسية حاول، التقط، أخيرًا، رميات

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

public String input() throws MyException {//warn with throws,
// that the method can throw MyException
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String s = null;
// in the try block we enclose the code in which an exception can occur, in this
// if the compiler tells us that the readLine() method of the class
// BufferedReader may throw an I/O exception
    try {
        s = reader.readLine();
// in the catch block we enclose the code for handling the IOException exception
    } catch (IOException e) {
        System.out.println(e.getMessage());
// close the read stream in the finally block
    } finally {
// when closing the stream, an exception is also possible, for example, if it was not opened, so we “wrap” the code in a try block
        try {
            reader.close();
// write exception handling when closing the read stream
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    if (s.equals("")) {
// we decided that an empty string could disrupt the work of our program in the future, for example, on the result of this method, we need to call the substring(1,2) method, so we are forced to interrupt the program execution with the generation of our exception type MyException using throw
        throw new MyException("String can not be empty!");
    }
    return s;
}

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

دعونا نلقي نظرة على مثال من العالم الحقيقي. تخيل أن هناك قسمًا على الطريق السريع به جسر طوارئ بسعة تحميل محدودة. إذا مرت عبره مركبة تزيد كتلتها عن القدرة الاستيعابية للجسر، فقد تنهار، وقد يصبح الوضع بالنسبة للسائق، بعبارة ملطفة، استثنائيا. ولمنع حدوث ذلك، تقوم خدمة الطرق بتثبيت علامات التحذير على الطريق مسبقًا. سيقوم سائق السيارة، وهو ينظر إلى الإشارة التحذيرية، بمقارنة وزن سيارته مع الوزن المسموح به للقيادة على الجسر. فإذا تجاوز ذلك فإنه يسلك منعطفا. بفضل تصرفات خدمة الطرق، تم منح سائقي الشاحنات، أولا، الفرصة لتغيير طريقهم مقدما، ثانيا، تم تحذيرهم من الخطر على الطريق الرئيسي، وأخيرا، تم تحذيرهم من استحالة استخدام الجسر في ظل ظروف معينة.
Исключения в Java - 2
تعد القدرة على منع الاستثناء وحله في البرنامج حتى يتمكن من الاستمرار أحد أسباب استخدام الاستثناءات في Java. تتيح لك آلية الاستثناء أيضًا حماية الكود الذي تكتبه (واجهة البرمجة) من سوء الاستخدام من قبل المستخدم عن طريق التحقق من صحة (فحص) البيانات الواردة. الآن دعونا نكون شرطة المرور لثانية واحدة. أولاً، يجب أن تعرف الأماكن التي يمكن أن يتعرض فيها سائقو السيارات للمشاكل. ثانيا، تحتاج إلى إعداد وتثبيت العلامات التحذيرية. وأخيرا، تحتاج إلى توفير طرق تحويلة في حالة وجود خطر على الطريق الرئيسي. في Java، تعمل آلية الاستثناء بطريقة مماثلة. في مرحلة تطوير البرنامج، نقوم "بحماية" الأقسام الخطيرة من التعليمات البرمجية من الاستثناءات باستخدام كتلة محاولة{}، وتوفير مسارات "نسخ احتياطي" باستخدام كتلة الصيد{}، وفي الكتلة الأخيرة{} نكتب التعليمات البرمجية التي يتم تنفيذها في برنامج لأي نتيجة. في الحالات التي لا نستطيع فيها توفير "طريق الطوارئ" أو نرغب عمدًا في ترك الاختيار للمستخدم، يجب علينا على الأقل تحذيره من الخطر. لماذا؟ ولكم أن تتخيلوا سخط السائق الذي سيصل إلى جسر الطوارئ الذي لا يمكن عبوره دون أن يواجه علامة تحذير واحدة على طول الطريق! في البرمجة، عند كتابة فئاتنا وأساليبنا، لا يمكننا دائمًا توقع سياق استخدامها من قبل المطورين الآخرين في برامجهم، لذلك لا يمكننا توقع المسار الصحيح بنسبة 100٪ لحل حالة الاستثناء. وفي الوقت نفسه، من الممارسات الجيدة تحذير مستخدمي الكود الخاص بنا من احتمال حدوث استثناء. تسمح لنا آلية الاستثناءات في Java بالقيام بذلك عن طريق استخدام الرميات - الإعلان بشكل أساسي عن السلوك العام لطريقتنا لطرح استثناء، وبالتالي ترك الأمر لمستخدم الطريقة لكتابة التعليمات البرمجية للتعامل مع الاستثناء في Java.

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

عندما لا تخطط للتعامل مع استثناء في طريقتك، ولكنك تريد تحذير مستخدمي الطريقة بشأن حالات الاستثناء المحتملة، استخدم الكلمة الأساسية throws. تعني هذه الكلمة الأساسية في توقيع الطريقة أنه في ظل ظروف معينة، قد تطرح الطريقة استثناءً. يعد هذا التحذير جزءًا من واجهة الطريقة ويمنح المستخدم الحق في تخصيص تنفيذ معالج الاستثناء. بعد الرميات نشير إلى نوع الاستثناء الذي يتم طرحه. عادةً ما يكون هؤلاء من نسل فئة Java Exception . نظرًا لأن Java هي لغة موجهة للكائنات، فإن جميع الاستثناءات في Java هي كائنات.
Исключения в Java - 3

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

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

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

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

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

يتم إنشاء كتل التعليمات البرمجية التي نوفر لها معالجة الاستثناءات في Java في البرنامج باستخدام بنيات حاول{}catch، حاول{}catch{}أخيرًا، حاول{}أخيرًا{}.
Исключения в Java - 4
عند ظهور استثناء في كتلة المحاولة، يتم البحث عن معالج الاستثناء في كتلة الالتقاط التالية. إذا كان الالتقاط يحتوي على معالج لهذا النوع من الاستثناءات، فسيتم تمرير التحكم إليه. إذا لم يكن الأمر كذلك، يبحث JVM عن معالج لهذا النوع من الاستثناءات في سلسلة استدعاءات الطريقة حتى يتم العثور على أداة الصيد المناسبة. بعد تنفيذ كتلة الالتقاط، يتم تمرير التحكم إلى كتلة اختيارية أخيرًا . إذا لم يتم العثور على كتلة التقاط مناسبة، فسيقوم JVM بإيقاف تنفيذ البرنامج ويعرض مجموعة من استدعاءات الطريقة - تتبع المكدس ، بعد تنفيذ رمز الكتلة النهائي مسبقًا، إذا كان موجودًا. مثال لمعالجة الاستثناء:
public class Print {

     void print(String s) {
        if (s == null) {
            throw new NullPointerException("Exception: s is null!");
        }
        System.out.println("Inside method print: " + 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 was processed. Program continues");
            }
            finally {
                System.out.println("Inside bloсk finally");
            }
            System.out.println("Go program....");
            System.out.println("-----------------");
        }

    }
    }
نتائج الطريقة الرئيسية :
Inside method print: first step
Inside bloсk finally
Go program....
-----------------
Exception: s is null!
Exception was processed. Program continues
Inside bloсk finally
Go program....
-----------------
Inside method print: second step
Inside bloсk finally
Go program....
-----------------
تُستخدم الكتلة finallyعادةً لإغلاق التدفقات المفتوحة في كتلة المحاولة أو لتحرير الموارد. ومع ذلك، عند كتابة البرنامج، ليس من الممكن دائما تتبع إغلاق جميع الموارد. لتسهيل حياتنا، قدم لنا مطورو Java بنية try-with-resourcesتغلق الموارد المفتوحة تلقائيًا في كتلة المحاولة. يمكن إعادة كتابة مثالنا الأول على النحو التالي 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 ("String can not 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("String can not 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