آموزش جاوا (فصل ۱۴)

کنترل کردن Exception ها در زبان برنامه نویسی جاوا


در این آموزش با مفهومی تحت عنوان Exception در زبان برنامه نویسی جاوا آشنا خواهیم شد. به طور کلی در زبان جاوا به منظور مواجهه با Error ها از چیزی تحت عنوان Exception استفاده می کنیم. معادل فارسی واژه Exception برابر است با “استثناء” و در زبان جاوا به “رویدادی گفته می شود که در پروسه اجرای یک برنامه یا اپلیکیشن بوجود می آید و از اجرای طبیعی برنامه جلوگیری به عمل می آورد”.

فرض کنیم متدی داریم که این وظیفه را دارا است تا کاری انجام دهد. حال در حین اجرای دستورات داخل این متد یک Error روی می دهد. در شرایطی اینچنینی، کاری که این متد انجام می دهد این است که شیئی تحت عنوان Exception Object می سازد که حاوی اطلاعاتی پیرامون نوع Error و همچنین زمانیکه این Error در برنامه یا اپلیکیشن رخ داده است می باشد و سپس این شیئ را تحویل سیستم می دهد. از این مرحله به بعد سیستم سعی می کند تا راه کاری برای رفع این Error بیابد. اگر سیستم بتواند دستور یا به طور کلی کدی را بیابد که بتواند این Error را رفع کند، آن دستور یا کد که اصطلاحاً Exception Handler نام دارد به رفع مشکل برنامه ما خواهد پرداخت و در غیر این صورت برنامه Crash خواهد کرد.

به طور کلی در زبان برنامه نویسی جاوا وقتی این احتمال وجود داشته باشد که ممکن است با یک Exception مواجه شویم، بایستی کد خود را داخل دستوری تحت عنوان try بنویسیم که در این صورت اگر Error هم داخل برنامه یا اپلیکیشن ما وجود داشته باشد برنامه به هیچ وجه Crash نخواهد کرد.

برای روش شدن این مطلب پروژه تحت عنوان 54th Session در محیط اکلیپس ایجاد کرده و کلاسی تحت عنوان ExceptionsInJava ایجاد می کنیم (به خاطر داشته باشیم که در حین ساخت این کلاس گزینه public static void main را تیک بزنیم):

public class ExceptionsInJava {
    public static void main(String[] args) {

    }
}

همانطور که ملاحظه می شود پس از حذف کامنت ها کد ما به صورت بالا خواهد بود. برای درک بهتر روابط مابین بخش های مختلف Exception ها کد فوق را به صورت زیر تکمیل می کنیم:

public class ExceptionsInJava {
    public static void main(String[] args) {
        try{
            // کدی که می خواهیم اجرا شود
            }catch(){
            //کدی که در صورت بروز مشکل می خواهیم اجرا شود
            }finally{
            //کدی که در پایان اجرای مراحل فوق می خواهیم اجرا شود
        }
    }
}

همانطور که در کد فوق می بینیم مابین دو {} مرتبط با دستور try بخشی از برنامه خود را می نویسیم که می خواهیم اجرا شود. حال این بخش از کد ما ممکن است که برنامه را با Error یی مواجه سازد. از این رو داخل دو {} مرتبط با دستور catch کدی را می نویسیم که در صورت بروز هر گونه مشکلی اجرا شود. در نهایت داخل دو {} مرتبط با دستور finally کدی را می نویسیم که در انتهای برنامه قصد داریم اجرا شود. حال اگر کدی که داخل دستور try است هیچ گونه مشکلی ایجاد نکرد، برنامه ما مستقیم به سراغ کدی خواهد دارد که داخل دستور finally قرار دارد و اگر هم کدی که داخل دستور try بود مشکلی ایجاد کرد، برنامه ابتدا دستور داخل catch را اجرا خواهد نمود سپس به سراغ اجرای دستور داخل finally خواهد رفت.

اگر توجه کرده باشیم خواهیم دید که مقابل دستور catch دو پرانتز قرار دارد. کاری که این دو پرانتز انجام می دهند این است که می بایست داخل آنها نوع Exception یی که قصد داریم سیستم تشخیص دهد را بنویسیم. به عبارت دیگر چیزی که می بایست داخل پرانتزها نوشت نام یک کلاس از پیش تعریف شده در API زبان جاوا است که مرتبط با Exception ها می باشد به علاوه نام شیئی که برای آن کلاس در نظر می گیریم (در ادامه آموزش به خوبی به توضیح این مسئله خواهیم پرداخت).

اکنون برای آنکه به طور عملی با کارکرد Exception ها آشنا شویم کد فوق را به صورت زیر تکمیل می کنیم:

public class ExceptionsInJava {
    public static void main(String[] args) {
        System.out.println("This is my first output!");
        int numberOne = 10;
        int numberTwo = 0;
        int result = numberOne / numberTwo;
        System.out.println(result);
        System.out.println("This is my final output!");
    }
}

همانطور که در کد بالا مشاهده می کنیم در ابتدا با استفاده از دستور System.out.println عبارت This is my first output! به معنی “این اولین خروجی من است!” را در پنجره Console به نمایش در خواهیم آورد. سپس اقدام به تعریف چند متغیر از جنس int نموده که این وظیفه را دارا هستند که اعدادی از جنس عدد صحیح را در خود ذخیره سازند. متغیر اول ما numberOne به معنی “عدد شماره یک” است که مقدار اولیه آن معادل با 10 است. متغیر دوم ما numberTwo به معنی “عدد شماره دو” است که مقدار اولیه آن معادل با صفر است. متغیر سوم ما result به معنی “نتیجه” نام دارد که عددی به عنوان Value آن در نظر نگرفته ایم بلکه Value آن را حاصل تقسیم مقدار متغیر numberOne بر numberTwo قرار داده ایم (به منظور آشنایی بیشتر با اعمال ریاضیاتی در جاوا به آموزش هشتم مراجعه نمایید).

همانطور که منطق ریاضی حکم می کند ما نمی توانیم عددی همچون ده را بر صفر تقسیم کنیم. علیرغم اینکه از این نکته اطلاع داریم می خواهیم ببینم که عکس العمل ماشین مجازی جاوا یا همان JVM چیست. حال یک بار دیگر دستور System.out.println را نوشته و این بار با قرار دادن نام متغیر result داخل پرانتز این دستور، از سیستم می خواهیم که پس از به نمایش در آوردن عبارت This is my first output! اقدام به نمایش مقدار متغیر result نماید. در نهایت یک بار دیگر دستور System.out.println را نوشته و این بار عبارت This is my final output! به معنی “این خروجی پایانی من است!” را می خواهیم در پنجره Console به نمایش در آوریم.

پس از اجرای برنامه خروجی زیر مشاهده خواهد شد:

همانطور که در اجرای بالا می بینیم، اولین دستوری که نوشته بودیم بدون هیچ مشکلی اجرا شده و عبارت This is my first output! در پنجره Console به نمایش در آمده است. سیستم پس از اجرای اولین دستور به سراغ دستور دوم خواهد رفت و از آنجا که در دستور دوم از برنامه خود خواسته ایم که عدد ده را به عدد صفر تقسیم کند و این چنین عملی خارج از منطق ریاضیاتی است، از این رو برنامه ما اصطلاحاً Crash کرده و در پنجره Console همانطور که در تصویر فوق مشخص است یک Exception از نوع کلاس ArithmeticException در برنامه رخ داده است. واژه Arithmetic به معنی “محاسباتی، ریاضیاتی و …” است و همانطور که از نام این کلاس پیدا است نوع Exception بوجود آمده در ارتباط با اعمال ریاضیاتی است.

حال از آنجا که برنامه ما در حین اجرای دومین دستور خود Crash کرد بنابراین از اجرای دستور سوم که همان به نمایش در آوردن عبارت This is my final output! است نیز ناتوان خواهد بود.

در آموزش آتی خواهیم دید که به چه شکل با استفاده از دستورات try, catch, finally پروژه ای که در این آموزش نوشتیم و Crash کرد را باز نویسی کرده و از Crash کردن آن جلوگیری خواهیم کرد.

آشنایی با دستورات try و catch و finally در زبان برنامه نویسی جاوا


در آموزش گذشته پروژه ای طراحی کردیم که در آن یک ArithmeticException وجود داشت بنابراین برنامه ما Crash کرد.

اکنون ببینیم اگر همین برنامه را به استفاده از دستور ات try, catch, finally بنویسیم چه اتفاقی خواهد افتاد:

public class ExceptionsInJava {
    public static void main(String[] args) {
        System.out.println("This is my first output!");
        try {
            int numberOne = 10;
            int numberTwo = 0;
            int result = numberOne / numberTwo;
            System.out.println(result);
            } catch (ArithmeticException myProblem) {
            System.out.println(myProblem);
            } finally {
            System.out.println("This is my final output!");
        }
    }
}

همانطور که می بینیم ابتدا دستوری که قرار است عبارت This is my first output! را در پنجره Console به نمایش در آورد را نوشته ایم. سپس دستوری که در کد قبل مشکل زا بود را داخل try نوشته ایم. بلافاصله پس از دستور try دستور catch آمده است که این وظیفه را دارا است تا اگر در دستور try مشکلی وجود داشت آن مشکل را به کاربر نشان دهد و از Crash کردن برنامه جلوگیری به عمل آورد. همانطور که ملاحظه می شود پارامتری که برای دستور catch در نظر گرفته شده است کلاسی تحت عنوان ArithmeticException است و نامی هم که برای شیئ ساخته شده از روی این کلاس در نظر گرفته ایم myProblem به معنی “مشکل من” است (لازم به ذکر است اکثر برنامه نویسان دنیا برای نام کلاس های مرتبط با Exception نام e را در نظر می گیرند که حرف اول Exception می باشد اما به منظور جلوگیری از سردرگمی برنامه نویسان مبتدی در این آموزش نامی گویاتر همچون myProblem در نظر گرفته شده است).

سپس با استفاده از دستور System.out.println و قرار دادن نام شیئ ساخته شده از روی کلاس ArithmeticException داخل این دستور از برنامه خود می خواهیم که چنانچه مشکلی در دستور try بود آن را با استفاده از این دستور نمایش دهد. در خاتمه در دستور finally دستوری که قصد داریم در پایان برنامه اجرا شود را می نویسیم که این دستور همان به نمایش در آوردن عبارت This is my final output! است.

مجدد برنامه خود را اجرا می کنیم:

می بینیم که دستور اول که مسئول به نمایش در آوردن عبارت This is my first output! بود به نمایش در آمده است. حال نوبت به اجرای دستور داخل try می رسد و از آنجا که دستور داخل try یک دستور غیر منطقی است برنامه مستقیماً وارد دستور catch می شود و همانطور که می بینیم نوع Exception نوشته شده است و مقابل آن عبارت / by zero آمده است. چنانچه بخواهیم این عبارت را به زبان فارسی ترجمه کنیم بایستی گفت ” یک مشکل محاسباتی بوجود آمده است که علت آن هم تقسیم شدن عددی بر صفر است”. در نهایت برنامه علیرغم اینکه دارای یک مشکل است اما به هیچ وجه Crash نکرده و ادامه پیدا می کند تا جاییکه دستور به نمایش در آوردن عبارت This is my final output! هم اجرا خواهد شد.

در صورتیکه بخواهیم اجرای برنامه خود را به صورت یک نمودار نشان دهیم، می توانیم نموداری همانند نمودار زیر در نظر بگیریم:

همانطور که در نمودار فوق مشخص است در صورت وجود یک دستور نادرست از لحاظ ریاضیاتی مثل تقسیم کردن عدد ده بر عدد صفر یک Exception Object ایجاد می شود که به ماشین مجازی جاوا یا JVM فرستاده می شود. حال دو اتفاق ممکن است روی دهد: اگر این Exception اصطلاحاً Handle شود یا رفع شود برنامه به کار خود ادامه خواهد داد و مابقی دستورات اجرا خواهند شد (مورد سمت راست) و اگر هم که این Exception اصطلاحاً Handle نشود برنامه متوقف خواهد شد، در پنجره Console نوع Exception به نمایش در خواهد آمد و مابقی دستورات داخل برنامه به هیچ وجه اجرا نخواهند شد.

به نظر می رسد پس از اجرای برنامه خود در مرحله دوم کاملاً به اهمیت استفاده از دستورات try, catch, finally بیشتر پی ببریم. همانطور که دیدیم برنامه بدون آنکه Crash کند اجرا شده و در جایی از کد هم که مشکلی وجود داشت آن مشکل به اطلاع کاربر رسید.

اکنون فرض کنیم که در صورت وجود یک Exception در برنامه خود نمی خواهیم که نوع Exception و دلیل آن در پنجره Console به نمایش در آید بلکه قصد داریم چنانچه برنامه مشکلی داشت جمله ای را به نمایش در آوریم. برای این منظور کد خود را به صورت زیر ویرایش می کنیم:

public class ExceptionsInJava {
    public static void main(String[] args) {
        System.out.println("This is my first output!");
        try {
            int numberOne = 10;
            int numberTwo = 0;
            int result = numberOne / numberTwo;
            System.out.println(result);
            } catch (ArithmeticException myProblem) {
            System.out.println("Hey Boy! You cannot divide 10 by 0 ...");
            } finally {
            System.out.println("This is my final output!");
        }
    }
}

می بینیم که داخل دستور System.out.println یی که داخل catch قرار گرفته است به جای نوشتن نام شیئ ساخته شده از روی کلاس ArithmeticException عبارت Hey Boy! You cannot divide 10 by 0… به معنی “هی پسر! تو نمی توانی عدد ده رو بر صفر تقسیم کنی …” را نوشته ایم. یک بار دیگر برنامه خود را اجرا می کنیم:

می بینیم که برنامه ما پس از اجرا با مشاهده کردن مشکلی در کد مرتبط با try عبارتی را نمایش خواهد داد که در دستور catch نوشته ایم. حال فرض کنیم که به هیچ وجه نمی خواهیم برنامه ما پس از مشاهده مشکلی در کد مرتبط با try چیزی در پنجره Console نمایش دهد. برای این منظور کد فوق را به صورت زیر بازنویسی می کنیم:

public class ExceptionsInJava {
    public static void main(String[] args) {
        System.out.println("This is my first output!");
        try {
            int numberOne = 10;
            int numberTwo = 0;
            int result = numberOne / numberTwo;
            System.out.println(result);
            } catch (ArithmeticException myProblem) {
            } finally {
            System.out.println("This is my final output!");
        }
    }
}

در واقع کاری که در کد بالا انجام داده ایم این است که دستوری که داخل {} مرتبط با catch بود را از برنامه حذف کرده ایم. با اینکار به برنامه خود دستور می دهیم که چنانچه مشکلی در دستور try بود آن مشکل را در دستور catch بررسی کند اما به هیچ وجه چیزی مرتبط با دستور catch در پنجره Console به نمایش در نیاورد. حال مجدد برنامه خود را اجرا می کنیم:

همانطور که ملاحظه می شود علیرغم اینکه برنامه ما داری یک Exception است اما برنامه Crash نکرده و چیزی هم حاکی از آنکه برنامه داری مشکلی است به اطلاع کاربر نخواهد رسید.

آشنایی با انواع Exception ها در زبان جاوا


به طور کلی Exception ها را می توان به دو گروه اصلی Checked و Unchecked دسته بندی کرد. در واقع منظور از Checked Exception ها Exception هایی هستند که در حین Compile-time رخ می دهند که از آن جمله می توان به IOException و SQLException اشاره کرد. در واقع این دست از Error ها مشکلاتی هستند که به Syntax برنامه مرتبط هستند. مثلاً اگر به جای نوشتن int عبارت INT را بنویسیم، از آنجا که یک مشکل Syntax یی داریم بایستی انتظار مشکلی مرتبط با Compile time داشته باشیم. به طور کلی این دسته از Exception ها “حتماً” می بایست اصطلاحاً Handle شده یا مد نظر قرار داده شوند.

منظور از Unchecked Exception ها نوعی از Exception ها است که در حین Runtime رخ می دهندکه از آن جمله می توان به ArithmeticException و NullPointerException اشاره نمود. در واقع در این دست از مشکلات برنامه بدون هیچ مشکلی Compile می شود اما این در حالی است که در حین اجرای برنامه کاربر نتایج غیر قابل انتظاری مشاهده خواهد کرد و حتی ممکن است برنامه Crash هم بکند.

در حقیقت منظور از Compile-time این است که زمانیکه ما یک برنامه در زبان برنامه نویسی جاوا می نویسیم، کدهای ما می بایست به زبان ماشین تبدیل شوند و زمانیکه کدها به زبان ماشین تبدیل می شوند Compile-time نامیده می شود. حال پس از آنکه کدها به زمان ماشین تبدیل شدند و اصطلاحاً برنامه Compile شد، کاربر می تواند برنامه را اجرا کند و به زمانیکه کاربر یک برنامه را اجرا می کند Runtime گفته می شود.

پس از آنکه با مفاهیم Compile-time و Run-time در زبان برنامه نویسی جاوا آشنا شدیم در این بخش از آموزش به منظور بررسی دیگر انواع Exception ها کلاس های دیگری به پروژه خود اضافه می کنیم:

public class NullPointerException {
    public static void main(String[] args) {
        String s = null;
        System.out.println(s.length());
    }
}

همانطور که در کد فوق می بینیم کلاسی تحت عنوان NullPointerException ایجاد کرده ایم. سپس در متد main این کلاس یک شیئ از روی کلاس String تحت عنوان s ساخته ایم و مقدار اولیه آن را برابر با null قرار داده ایم. حال در دستور System.out.println با استفاده از متد length قصد داریم تعداد کاراکتر های شیئ ساخته شده از روی کلاس String را شمارش کنیم. برای همین منظور داخل پرانتز مرتبط با متد println نام شیئ ساخته شده از روی کلاس String را نوشته سپس یک نقطه قرار داده و متد length را به آن ضمیمه می کنیم. اکنون می توانیم برنامه خود را اجرا کنیم:

همانطور که در تصویر فوق می بینیم پس از اجرای برنامه با یک NullPointerException مواجه می شویم. علت مواجهه با چنین Exception یی این است که برای Object های ایجاد شده از روی کلاس String می بایست مقداری همچون یک عبارت یا یک کلمه در نظر گرفت که در این صورت اگر از متد length هم استفاده کنیم این متد تعداد کاراکترهای مرتبط با شیئ ساخته شده از روی کلاس String را خواهد شمارد اما از آنجا که در مثال فوق مقدار اولیه این کلاس را برابر با null قرار داده ایم حال اگر بخواهیم متد length را به شیئی ضمیمه کنیم که مقدار اولیه آن null است، برنامه ما با Exception یی از جنس NullPointerException رو به رو خواهد شد. چنانچه بخواهیم عبارت NullPointerException را به صورت تحت الفظی ترجمه کنیم می توانیم معادل “مشکلی که به خاطر اشاره به چیزی که تهی است ایجاد شده است” را در نظر بگیریم.

حال کلاس دیگری تحت عنوان NumberFormatException ایجاد می کنیم که از طریق آن Exception یی با نام NumberFormatException را مورد بررسی قرار خواهیم داد:

public class NumberFormatException {
    public static void main(String[] args) {
        String s = "Hello";
        int i = Integer.parseInt(s);
    }
}

همانطور که در کد فوق مشخص است شیئی تحت عنوان s از روی کلاس String ایجاد کرده و مقدار اولیه آن را برابر با Hello قرار می دهیم. سپس یک متغیر از جنس int تحت عنوان i ایجاد می کنیم و مقدار آن را برابر با متدی تحت عنوان parseInt که به کلاسی تحت عنوان Integer ضمیمه شده است قرار داده و شیئ ساخته شده از روی کلاس String را به عنوان پارامتر متد parseInt در نظر می گیریم.

همانطور که در آموزش های گذشته توضیح داده شد، به منظور تبدیل متغیرها به یکدیگر می توان از کلاس ها و متدهای مرتبط با آنها استفاده کرد. حال برنامه را اجرا می کنیم:

در واقع علت بروز چنین Exception یی این است که با توجه به مقدار در نظر گرفته شده برای این متغیر که یک رشته (Hello) است، به هیچ وجه نمی توانیم این شیئ که از جنس کلاس String است را به متغیری از جنس int تبدیل کنیم.

Exception دیگری که می خواهیم مورد بررسی قرار دهیم ArrayIndexOutOfBoundsException نام دارد. برای این منظور کلاسی با همین نام در پروژه خود ایجاد می کنیم و آن را به صورت زیر تکمیل می کنیم:

public class ArrayIndexOutOfBoundsException {
    public static void main(String[] args) {
        int[] numbers = new int[10];
        numbers[11] = 100;
    }
}

همانطور که در کد فوق می بینیم یک Array از جنس int ایجاد کرده ایم که numbers نام دارد. همانطور که مشخص است این Array قرار است 10 گزینه را در خود جای دهد. پس از تعریف این Array در خط دوم می بینیم که Array یی با شماره 11 را مد نظر قرار داده ایم و مقدار آن را برابر با عدد 100 در نظر گرفته ایم. حال برنامه را اجرا می کنیم:

همانطور که در تصویر فوق می بینیم برنامه ما با یک Exception از جنس ArrayIndexOutOfBoundsException رو به رو می شود و علت هم آن است که ما در Array خود فقط ده گزینه داریم اما در ادامه برنامه گزینه شماره یازده را هدف قرار داده ایم و از آنجا که این گزینه خارج از محدوده تعریف شده برای این Array است با چنین Exception یی رو به رو خواهیم شد.

مطالب مرتبط

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *