Exception یا استثنا
بررسی خطا
در دنیای بی عیب، کاربران هرگز داده ها را به شکل غلط وارد نخواهند کرد، فایلهایی که آن ها باز می کنند همیشه وجود دارد و کد ها هرگز باگ(اشکال) ندارند. اکنون وقتش است که به مکانیزم زبان برنامه نویسی جاوا رجوع کنیم تا به دنیای حقیقی داده های غلط و کد های دارای باگ، رسیدگی کنیم.مواجه شدن با خطا ناخوشایند است. اگر یک کاربر همه ی کارهایی را که در طی یک برنامه انجام داد را از دست دهد(به خاطر اشتباه برنامه نویسی یا رویداد خارجی) کاربر ممکن است برای همیشه از برنامه اش زده می شود. حد اقل شما باید:
1- آن خطا را به کاربر اطلاع دهید
2- همه ی کارها را save کنید
3- به کاربر اجازه دهید تا به خوبی از برنامه خارج شود
در شرایط استثنا، مانند داده های ورودی غلط که ممکن است برنامه crash(یک برنامه زمانى دچار این حالت مى شود که خروجى هاى ان نادرست بوده و یا به علت اشتباهات منطقى و گرامرى قابل اجرا شدن نباشد) شود، جاوا از یک غلط یابی به نام اداره کردن استثنا(exception handling)، استفاده می کند. استثنا در جاوا شبیه c++ و دلفی است.
ماموریت اداره کننده ی استثنا، انتقال کنترل از جایی که خطا اتفاق افتاده به اداره کننده ی خطا(error handler) که می تواند این وضعیت را رسیدگی کند، می باشد. برای اداره کردن شرایط استثنا در برنامه، شما باید علت خطا یا مشکلی که اتفاق می افتد را بدانید. چه مشکلاتی را باید بررسی کرد؟
1- خطاهای ورودی کاربر
2- خطا های تجهیزات
3- محدودیتهای فیزیکی
4- خطا های کد
Exceptions
وقتی یک خطا تشخیص داده می شود، یک استثنا پیش می آید. استثنا کدی است که اجرا را فورا متوقف می کند و کنترل به بخش catch مربوط به همان بلوک try انتقال پیدا می کند. بلوک try ممکن است در فانکشن جاری باشد(اونی که سبب خطا می شود) یا در بعضی از فانکشن هایی که در فانکشن جاری فراخوانی می شوند قرار گیرند(یعنی اگر فانکشن جاری برای اداره ی استثنامهیا نباشد، به زنجیر فراخوانی پاس داده می شود) اگر هیچ فانکشن فعال جاری برای catch استثنا مهیا نباشد، یک پیام خطا چاپ می شود و برنامه متوقف می شود.استثنا می تواند توکار یا داخلی، باشد یعنی در یکی از کتابخانه های استاندارد جاوا تعریف شده باشد و یا توسط کاربر تعریف شود. در زیر تعدادی از نمونه های استثنای داخلی آورده شده است:
ArithmeticException (مثلا تقسیم بر صفر)
ClassCastException (مثلا تبدیل شی string به integer)
IndexOutOfBoundsException
NullPointerException
FileNotFoundException (مثلا باز کردن فایلی که وجود ندارد)
استثناهای checked و unchecked
1- روش اول: استثنا باید با استفاده از عبارت throws ، در هدر متد تعریف شود. در این روش مدیریت خطا وجود ندارد و زمانی که خطایی بوجود می آید فقط پیغام خطا چاپ می شود.
زمانیکه می دانیم در متدی استثنا رخ می دهد اما نمی خواهیم try/catch را بنویسیم، باید جلوی آن، throws Exception را بنویسیم یعنی آن را پرتاب می کنیم. سپس در متد فراخواننده ی این متد، آن را پیاده سازی کنیم. مثال زیر را در نظر بگیرید:
فرضا در کلاسی به نام example متدی به نام test داریم و نمی خواهیم درآن try/catch را پیاده سازی کنیم. اگر try/catch را در main پیاده سازی نکنیم، زمانی که می خواهیم در main متد test را فراخوانی کنیم، زمان اجرا کامپایلر پیغام خطا می دهد. بنابراین باید در متد main آن را در try/catchقرار دهیم.
2- روش دوم: کدی که ممکن است سبب استثنا شود، باید داخل بلوک try با یک عبارت catch برای استثنا قرار گیرد. (درقسمت try دستورات اصلی را می نویسید، در صورتی که این دستورات باعث ایجاد خطا شوند قسمت catch اجرا می شود) :
چگونه یک استثنا catch می شود:
• هر بخش catch نوع یک استثنا را مشخص می کند و نامی را برای آن در نظر می کیرد(مشابه هدر یک فانکشن نوع و نام یک پارامتر را تعیین می کند). استثنا های جاوا اشیا هستند، بنابراین دستورات در یک عبارت catch، می توانند به شی استثنای پیش آمده، با استفاده از نام اختصاص یافته ی آن، رجوع کند.
• عبارت Finally اختیاری است.
• به طور کلی، در آنجا می تواند یک catch یا بیشتر قرار گیرد. اگر یک عبارت Finally وجود داشته باشد، انجا می تواند عبارت catch وجود نداشته باشد.
• e که catch به عنوان پارامتر ورودی می گیرد(یا id-1 دربالا) می تواند هر چیزی باشد. مثلا می توانیم x یا y یا هر چیز دیگری بجای آن بنویسیم و در واقع آبجکتی از کلاس exception است و JVM این آبجکت را به آن پاس می دهد. مثلا اگر در قسمت catch ، در دستور system.out.println این را قرار دهیم، هنگام چاپ، کلاسی که مسئول این استثنا است و همچنین نوع خطا را درخروجی نشان می هد. مثلا هنگامی که می خواهیم 1 را بر 0 تقسیم کنیم، استثنا رخ می دهد.
e.PrintStackTrace();
این نوع نمی خواهد خطا را مدیریت کند و فقط می خواهد بداند که این خطا از کجا ناشی شده و بعد از اینکه به آن پی بردید می تواید آن را پاک کنید و بجای آن همان System.out.println را در قسمت بلوکcatch بنویسید. این نوع ذکر شده تا اگر در کدی با این دستور مواجه شده اید، کارآن را بدانید.
نکات:
• برنامه باید اطمینان پیدا کند که قبل از استفاده از args[0] ، یک خط دستور آرگومان وجود دارد
• همچنین، باید اطمینان پیدا کند تا بلوک try در loopقرار گرفته ، برای اینکه اگر فایل پیدا نشد، کاربر بتواند درخواست کند تا فایل جدیدی را وارد کند و بتواند فایل جدیدی را باز کند.
• اگر کاربر برنامه را با نام فایل بی اعتبار اجرا کرد، پیام "file foo not found" چاپ شود و برنامه متوقف شود.
• بالا ترین ساختار در مبحث استثنا ها کلاس exception می باشدو این کلاس پدر همه ی کلاسهای دیگر است.
• در catch می توان دوباره try/catch نوشت.
• اگر هیچ بلوک try وجود نداشته باشد و برنامه با نام نامعتبر اجرا شود، پیام پیچیده تری ممکن است چاپ شود مثل:
at java.io.FileInputStream ...
at ...
at Test.main ...
عبارت Finally
وقتی دی کد یک استثنا اتفاق می افتد، پردازش باقی مانده ی کد در متد متوقف می شود و از آن خارج می شود. اگر متد منابع محلی در دست داشته باشد این مسئله ای است، که فقط این متد از آن اطلاع دارد و آن منبع باید آزاد شود. جاوا راه حلی برای این مورد ارائه کرده است و آن عبارت finally می باشد. در اینجا ما به شما نشان خواهیم داد که چگونه یک فایل را در جاوا ببندیم. اگر شما بخواهید برنامه نویسی پایگاه داده انجام دهید، نیاز دارید از یک چنین تکنیک برای بستن کانکشن های پایگاه داده استفاده کنید. این مهم است که هنگام وقوع استثنا، تمام کانکشن های پایگاه داده را ببندید.خواه استثنا اتفاق بیافتدخواه نیافتد، کد درون عبارت finally اجرا می شود. در مثال زیر، همه ی حالت ها را بررسی کرده است:
2- حالت دوم: اگردر کد استثنا اتفاق بیافتد، که در بلوک catch آن را می فهمیم(در مثال ما یک IOException می باشد). برای این مورد برنامه همه ی کد های بلوک try را اجرا می کند، و به نقطه ی وقوع استثنا می رسد. و از باقی مانده ی کد در بلوک try پرش می کند. سپس برنامه کد درون catch مربوط به آن و بعد از آن، کد درون بلوک finally را اجرا می کند. اگردر بلوک catch یک استثنا اتفاق نیافتد، برنامه ابتدا خط بعد از بلوک finally را اجرا می کند. در این سناریو، اجرا از خطوط 1،3،4،5،6 عبور می کند.
3- حالت سوم: در کد استثنائی رخ دهد که در هیچ بلوک catch ای گرفته نشود. در اینجا برنامه تمام کد های درون بلوک try را اجرا می کند تا استثنا رخ دهد. سپس از باقی مانده ی کد بلوک try پرش می کند. و کد درون بلوک finally را اجرا می کند و استثنا به فراخواننده ی این متد بر می گرداننده می شود. اجرا فقط از نقاط 1،5 عبور می کند.
شما می توانید از بلوک finally، بدون بلوک catch استفاده کنید. مانند زیر:
ما شدیدا پیشنهاد می کنیم که از حالت try/catch و try/finally استفاده کنید. این شما را کمتر گیج می کند:
Try خارجی هم وظیفه ی جداگانه ی دیگری دارد: این اطمینان را می دهد که خطاها گزارش داده شده اند. نه فقط این مسئله، بلکه خطاهای درون بلوک finally را گزارش می دهد.
وقتی یک بلوک finally شامل دستور return باشد، نتایج غیر منتظره می تواند برگرداند. فرض کنید با دستور return از وسط بلوک try خارج می شوید. قبل از خارج شدن متد، بلوک finally اجرا می شود. اگر بلوک finally هم شامل دستور return را داشته باشد، مفدار return اصلی اول ماسک می کند یا می پوشاند. مثال زیر را در نظر بگیرید:
طبقه بندی استثنا:
در زبان برنامه نویسی جاوا، یک شی استثنا همیشه یک نمونه از یک کلاس مشتق شده از Throwable می باشد. اگر آن استثنا هایی را که در جاوا ساخته شده اند، مناسب با نیاز شما نباشد، می توانید کلاسهای استثنا خودتان را بسازید.در تصویر زیر سلسله مراتب استثنا را در جاوا مشاهده می کنید:
1. بیشتر استثنا ها unchecked هستند.
2. IOExceptions از دسته ی checked هستند.
3. استثنا های تعریف شده ی کاربر باید معمولا checked باشند، بنابراین آنها باید زیر کلاس Exception. باشند.
قانون عمومی این است: یک RuntimeException برای این اتفاق می افتد که شما یک خطای برنامه نویسی انجام دهید.
استثنا هایی که از RuntimeException ارث بری می کنند شامل زیر می باشند:
1- تبدیل نادرست
2- دسترسی خارج از محدوده به آرایه
3- دسترسی به اشاره گر null
استثنا هایی که از IOtimeException ارث بری می کنند شامل زیر می باشند:
1- خواندن بعد از انتهای فایل
2- باز کردن فایلی که وجود ندارد
3- جستجوی یک شی کلاس برای یک رشته که یک کلاس موجود برای آن مشخص نشده
انواع استثنا ها
تعداد انواع Exception های ممکن می تواند بیشمار باشد. که چند تا را نام می بریم:
1- NullPointerException: استثنا اشاره به تهی
کلاسی تحت عنوان NullPointerException ایجاد می کنیم. سپس در متد main این کلاس یک شی از روی کلاس string تحت عنوان str می سازیم و مقدار اولیه آن را برابر null قرار می دهیم. سپس با استفاده از متد length در دستور System.out.println(s.length()); می خواهیم تعداد کاراکترهای شی ساخته شده از روی کلاس string را شمارش کنیم.
برنامه به صورت زیر می باشد:
2- NumberFormatException:
شی ای تحت عنوان str از روی کلاس string ایجاد می کنیم و مقدار اولیه ی آن برابر rasekhoon قرار می دهیم. سپس متغییری از نوع int با نام i ایجاد می کنیم و مقدار آن را برابر با متد parselnt() که به کلاس integer ضمیمه شده قرار می دهیم. شی ایجاد شده از روی کلاس string را به عنوان پارامتر متد parselnt در نظر می گیریم (برای تبدیل متغییر ها از کلاس ها و متد های آنها استفاده می کنیم).
برنامه به صورت زیر می باشد:
3- ArrayIndexOutOfBoundsException :
یک آرایه از نوع int به نام x به طول 15 ایجاد می کنیم. سپس می خواهیم در خانه ی 16 آن مقدار 270 را قرار دهیم.
کدهای این برنامه به صورت زیر می باشد:
شما می توانید با تست ایندکس آرایه در برابر محدوده ی آرایه از ArrayIndexOutOfBoundsException اجتناب کنید .
ایجاد exception سفارشی
برای این کار ابتدا کلاسی به نام Myexp می سازیم که کلاس Exception را به ارث می برد و برای آن یک سازنده تعریف می کنیم که این سازنده یک ورودی به نام e از نوع exception می گیرد. و پیغام درون سازنده ("Myexp....") زمانی بوجود می آید که در سیستم خطای استثنا بوجود آید. ما یک سازنده ی دیگر هم برای آن تعریف کردیم که پارامتر ورودی نمی گیرد و پیغام آن به صورت "Myexp .... rasekhooooooon " می باشد. به صورت زیر کد ها را می نویسیم:اگر از کدهای بالا اجرا بگیریم، و چون کلاس Myexp() با پارامتر ورودی e می باشد، سازنده ی دومی را (در کلاس سازنده ی سفارشی) فراخوانی می کند و خروجی به صورت زیر می شود:
در اینجا بخش exception را به پایان می بریم.
استفاده از مطالب این مقاله با ذکر منبع راسخون بلامانع است.
/ج