در زندگی روزمره، گاهی اوقات موقعیت هایی پیش می آید که برای آن برنامه ریزی نکرده بودیم. به عنوان مثال، شما صبح برای سر کار بیدار می شوید، به دنبال شارژر برای تلفن خود می گردید، اما شارژر وجود ندارد. برای شستن صورت خود به حمام می روید - آب قطع شده است. سوار ماشین شدم و روشن نشد. اما یک فرد می تواند به راحتی با چنین موقعیت های پیش بینی نشده کنار بیاید. در این مقاله سعی خواهیم کرد نحوه برخورد برنامه های جاوا با آنها را دریابیم.
استثنائات جاوا چیست؟
در دنیای برنامه نویسی به بروز خطاها و موقعیت های غیرمنتظره در حین اجرای برنامه استثنا گفته می شود. در یک برنامه، استثناها می توانند در نتیجه اقدامات نادرست کاربر، عدم وجود منبع لازم روی دیسک یا از دست دادن اتصال به سرور از طریق شبکه رخ دهند. استثناها در حین اجرای برنامه نیز می تواند ناشی از خطاهای برنامه نویسی یا استفاده نادرست از API باشد. برخلاف دنیای ما، برنامه باید به وضوح بداند در چنین شرایطی چه کاری انجام دهد. جاوا مکانیزم استثنایی برای این منظور فراهم می کند.
به طور خلاصه در مورد کلمات کلیدی try، catch، در نهایت، پرتاب می کند
مدیریت استثنا در جاوا بر اساس استفاده از کلمات کلیدی زیر در برنامه است:
- try - یک بلوک از کد را تعریف می کند که در آن یک استثنا می تواند رخ دهد.
- catch - بلوک کدی را که در آن استثنا مورد استفاده قرار می گیرد، تعریف می کند.
- در نهایت – یک بلوک از کد را تعریف می کند که اختیاری است، اما در صورت وجود، بدون توجه به نتایج بلوک try، به هر حال اجرا می شود.
این کلمات کلیدی برای ایجاد ساختارهای پردازشی ویژه در کد برنامه استفاده میشوند: سعی کنید{}catch, try{}catch{}در نهایت، تلاش{}در نهایت{}.
- پرتاب - برای مطرح کردن یک استثنا استفاده می شود.
- پرتاب - در امضای متدها برای هشدار دادن به اینکه یک روش ممکن است استثنا ایجاد کند استفاده می شود.
مثالی از استفاده از کلمات کلیدی در برنامه جاوا:
public String input() throws MyException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
s = reader.readLine();
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
reader.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
if (s.equals("")) {
throw new MyException("String can not be empty!");
}
return s;
}
چرا به مکانیزم استثنا نیاز داریم؟
بیایید به یک مثال دنیای واقعی نگاه کنیم. تصور کنید که بخشی در بزرگراه با یک پل اضطراری با ظرفیت بار محدود وجود دارد. اگر وسیله نقلیه ای با جرم بیش از ظرفیت حمل پل از روی آن عبور کند، ممکن است سقوط کند و وضعیت برای راننده، به بیان ملایم، استثنایی شود. برای جلوگیری از این اتفاق، راهداری از قبل علائم هشدار دهنده را در جاده نصب می کند. راننده ماشین با نگاه کردن به علامت هشدار، وزن ماشین خود را با وزن مجاز برای رانندگی روی پل مقایسه می کند. اگر از آن فراتر رود، او مسیر را منحرف می کند. با اقدامات سرویس راه، به رانندگان کامیون اولاً این فرصت داده شد تا از قبل مسیر خود را تغییر دهند، ثانیاً در مورد خطر در مسیر اصلی به آنها هشدار داده شد و در نهایت در مورد عدم امکان استفاده به آنها هشدار داده شد. پل تحت شرایط خاص
امکان جلوگیری و رفع استثنا در یک برنامه به طوری که بتواند ادامه یابد یکی از دلایل استفاده از استثناها در جاوا است. مکانیسم استثنا همچنین به شما این امکان را می دهد تا با اعتبارسنجی (بررسی) داده های دریافتی، از کدی که می نویسید (رابط برنامه نویسی) در برابر سوء استفاده توسط کاربر محافظت کنید. حالا بیایید یک لحظه پلیس راهنمایی و رانندگی باشیم. ابتدا باید مکان هایی را بشناسید که رانندگان ممکن است دچار مشکل شوند. در مرحله دوم، شما باید علائم هشدار دهنده را آماده و نصب کنید. در نهایت باید مسیرهای انحرافی را در صورت خطر در مسیر اصلی ارائه دهید. در جاوا، مکانیسم استثنا به روشی مشابه کار می کند. در مرحله توسعه برنامه، با استفاده از بلوک try{} از بخشهای خطرناک کد از استثناها محافظت میکنیم، مسیرهای «پشتیبان» را با استفاده از بلوک catch{} ارائه میکنیم و در بلوک نهایی{} کدی را مینویسیم که در بلوک اجرا میشود. برنامه ای برای هر نتیجه ای در مواردی که نمیتوانیم «مسیر اضطراری» ارائه کنیم یا عمداً میخواهیم انتخاب را به کاربر واگذار کنیم، حداقل باید او را از خطر آگاه کنیم. چرا؟ فقط خشم راننده ای را تصور کنید که به یک پل اضطراری می رسد که نمی توان بدون برخورد با یک علامت هشدار در طول مسیر از آن عبور کرد! در برنامه نویسی، هنگام نوشتن کلاس ها و متدهای خود، ما همیشه نمی توانیم زمینه استفاده از آنها توسط سایر توسعه دهندگان را در برنامه های خود پیش بینی کنیم، بنابراین نمی توانیم مسیر 100% صحیح را برای حل وضعیت استثنا پیش بینی کنیم. در عین حال، تمرین خوبی است که به کاربران کد خود در مورد امکان استثنا هشدار دهیم. مکانیسم استثنای جاوا به ما این امکان را میدهد که این کار را با استفاده از پرتاب انجام دهیم - اساساً رفتار کلی روش خود را برای ایجاد یک استثنا اعلام میکنیم، در نتیجه نوشتن کد برای مدیریت استثنا در جاوا را به کاربر متد واگذار میکنیم.
هشدار درباره "مشکل"
هنگامی که قصد ندارید استثنایی را در روش خود مدیریت کنید، اما می خواهید به کاربران این روش در مورد موقعیت های استثنایی احتمالی هشدار دهید، از کلمه کلیدی throws استفاده کنید. این کلمه کلیدی در امضای متد به این معنی است که تحت شرایط خاص متد ممکن است یک استثنا ایجاد کند. این هشدار بخشی از رابط متد است و به کاربر این حق را می دهد که پیاده سازی کنترل کننده استثنا را سفارشی کند. پس از پرتاب ها، نوع استثنای پرتاب شده را نشان می دهیم. اینها معمولاً از نوادگان کلاس Java
Exception هستند . از آنجایی که جاوا یک زبان شی گرا است، همه استثناها در جاوا شی هستند.
سلسله مراتب استثنایی جاوا
هنگامی که خطایی در حین اجرای برنامه رخ می دهد، زمان اجرا JVM یک شی از نوع مورد نیاز را از سلسله مراتب استثنایی جاوا ایجاد می کند - مجموعه ای از استثناهای ممکن که از یک "جد" مشترک به ارث برده شده است - کلاس Throwable. موقعیت های استثنایی که در یک برنامه اتفاق می افتد را می توان به دو گروه تقسیم کرد:
- شرایطی که در آن بازیابی عملکرد عادی برنامه غیرممکن است
- بازیابی امکان پذیر است.
گروه اول شامل موقعیت هایی است که در آن استثنائات به ارث رسیده از کلاس
Error رخ می دهد . اینها خطاهایی هستند که در حین اجرای برنامه در نتیجه خرابی JVM، سرریز حافظه یا خرابی سیستم رخ می دهند. آنها معمولاً مشکلات جدی را نشان می دهند که با استفاده از نرم افزار قابل رفع نیستند. این نوع استثنا در جاوا در مرحله کامپایل به عنوان بدون علامت طبقه بندی می شود. این گروه همچنین شامل RuntimeException - استثناها، وارثان کلاس
Exception است که توسط JVM در حین اجرای برنامه تولید می شود. آنها اغلب به دلیل خطاهای برنامه نویسی ایجاد می شوند. این استثناها نیز در زمان کامپایل علامت نمیشوند، بنابراین نوشتن کد برای مدیریت آنها ضروری نیست. گروه دوم شامل موقعیتهای استثنایی است که در مرحله نوشتن برنامه پیشبینی میشود و برای آنها کد پردازش باید نوشته شود. چنین استثناهایی بررسی می شوند. بخش عمده ای از کار یک توسعه دهنده جاوا هنگام برخورد با استثناها، رسیدگی به چنین موقعیت هایی است.
ایجاد یک استثنا
در طول اجرای برنامه، یک استثنا توسط JVM یا به صورت دستی با استفاده از دستور
throw ایجاد می شود . این یک شی استثنا در حافظه ایجاد می کند و اجرای کد برنامه اصلی را قطع می کند در حالی که کنترل کننده استثناء JVM سعی می کند راهی برای رسیدگی به استثنا پیدا کند.
رسیدگی به استثنا
ایجاد بلوکهای کدی که برای آنها مدیریت استثنا در جاوا ارائه میکنیم، در برنامه با استفاده از ساختارهای try{}catch, try{}catch{}finally, try{}finally{} انجام میشود.
هنگامی که یک استثنا در یک بلوک try مطرح می شود، کنترل کننده استثنا در بلوک catch زیر جستجو می شود. اگر catch حاوی یک کنترل کننده برای این نوع استثنا باشد، کنترل به آن منتقل می شود. در غیر این صورت، JVM به دنبال یک کنترل کننده برای آن نوع استثنا در زنجیره فراخوانی متد می گردد تا زمانی که یک catch مناسب پیدا شود. پس از اجرای بلوک catch، کنترل به بلوک
نهایی اختیاری منتقل می شود . اگر بلوک catch مناسب پیدا نشد، JVM اجرای برنامه را متوقف میکند و پشتهای از فراخوانیهای متد را نمایش میدهد -
stack trace ، در صورتی که قبلاً کد بلوک نهایی را اجرا کرده باشد. مثال رسیدگی به استثنا:
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
معمولاً برای بستن جریان های باز شده در بلوک try یا برای آزاد کردن منابع استفاده می شود. با این حال، هنگام نوشتن یک برنامه، همیشه نمی توان بسته شدن همه منابع را پیگیری کرد. برای آسانتر کردن زندگیمان، توسعهدهندگان جاوا ساختاری را به ما پیشنهاد کردند
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;
}
به لطف قابلیتهای جاوا، با شروع نسخه 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;
}
نتایج
استفاده از استثناها در جاوا به ما این امکان را می دهد که تحمل خطای برنامه را از طریق استفاده از مسیرهای "پشتیبان گیری" افزایش دهیم، منطق کد اصلی را از کد رسیدگی به استثنا از طریق استفاده از بلوک های catch جدا کنیم، و همچنین به ما این فرصت را می دهد که تفویض اختیار کنیم. مدیریت استثنا به کاربر کد ما با استفاده از پرتاب.
GO TO FULL VERSION