سلام! امروز نگاهی دقیق تر به مدیریت استثنا در جاوا خواهیم داشت. من از ذکر آن متنفرم، اما بخش بزرگی از کار یک برنامه نویس در برخورد با خطاها است. بیشتر اوقات، مال خودش. معلوم شد که هیچ آدمی نیست که اشتباه نکند. و چنین برنامه هایی نیز وجود ندارد. البته هنگام برخورد با یک خطا، نکته اصلی درک علت آن است. و بسیاری از چیزها می توانند باعث ایجاد اشکال در یک برنامه شوند. در نقطه ای، سازندگان جاوا از خود پرسیدند که با محتمل ترین خطاهای برنامه نویسی چه باید کرد؟ اجتناب کامل از آنها واقع بینانه نیست، برنامه نویسان قادر به نوشتن مطالبی هستند که حتی تصورش را هم نمی کنید. :) بنابراین، باید به زبان مکانیزمی برای کار با خطاها بدهیم. به عبارت دیگر، اگر خطایی در برنامه شما وجود داشته باشد، به نوعی اسکریپت برای کارهای بعدی نیاز دارید. در هنگام بروز خطا یک برنامه دقیقاً چه کاری باید انجام دهد؟ امروز با این مکانیسم آشنا می شویم. به آن " استثنا در جاوا
" می گویند.
کدی که برنامه نویس معتقد است ممکن است یک استثنا رخ دهد در
استثنا چیست؟
استثنا یک موقعیت استثنایی و برنامه ریزی نشده است که در حین اجرای برنامه رخ می دهد. استثناهای زیادی وجود دارد. به عنوان مثال، شما کدی نوشتید که متنی را از یک فایل می خواند و خط اول را نمایش می دهد.public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
}
}
اما اگر چنین فایلی وجود نداشته باشد چه می شود! برنامه یک استثنا ایجاد می کند: FileNotFoundException
. خروجی: استثنا در رشته "اصلی" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (سیستم نمی تواند مسیر مشخص شده را پیدا کند) در جاوا، هر استثنا با یک کلاس جداگانه نمایش داده می شود. همه این طبقات استثنایی از یک "جد" مشترک - Throwable
کلاس والد - سرچشمه می گیرند. نام یک کلاس استثنا معمولاً به طور خلاصه نشان می دهد که چرا این استثنا رخ داده است:
FileNotFoundException
(فایل پیدا نشد)ArithmeticException
(یک استثنا در حین انجام یک عملیات ریاضی رخ داده است)ArrayIndexOutOfBoundsException
(شاخص فراتر از مرزهای آرایه است). به عنوان مثال، اگر بخواهید موقعیت 23 آرایه ای را که فقط 10 عنصر دارد را نمایش دهید، این استثنا رخ می دهد.
Exception in thread "main"
اوهههه :/ این خیلی کمکی نمی کند. مشخص نیست این خطا به چه معناست یا از کجا آمده است. هیچ اطلاعات مفیدی در اینجا وجود ندارد. اما تنوع زیاد کلاس های استثنا در جاوا به برنامه نویس مهم ترین چیز را می دهد: نوع خطا و علت احتمالی آن (در نام کلاس تعبیه شده است). دیدنش چیز دیگری است
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
بلافاصله مشخص می شود که مشکل از کجا می تواند باشد و برای حل مشکل از کجا باید کند و کاو را شروع کرد! استثناها، مانند نمونه های هر کلاس، اشیا هستند.
گرفتن و رسیدگی به استثناها در جاوا
جاوا بلوک های کد خاصی برای کار با استثناها دارد:try
, catch
و finally
. 
try
بلوک قرار می گیرد. این بدان معنا نیست که استثنایی در اینجا رخ خواهد داد. یعنی ممکن است در اینجا رخ دهد و برنامه نویس از این امکان آگاه است. نوع خطایی که انتظار دارید رخ دهد در catch
بلوک قرار می گیرد. این همچنین شامل تمام کدهایی است که در صورت بروز استثنا باید اجرا شوند. در اینجا یک مثال است:
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error! File not found!");
}
}
خروجی: خطا! فایل پیدا نشد! کد خود را در دو بلوک قرار می دهیم. در بلوک اول، پیشبینی میکنیم که ممکن است خطای «فایل یافت نشد» رخ دهد. این try
بلوک است. در مرحله دوم به برنامه می گوییم که در صورت بروز خطا چه کاری انجام دهد. و نوع خطای خاص: FileNotFoundException
. اگر یک کلاس استثنای متفاوت را در پرانتز بلوک قرار دهیم catch
، FileNotFoundException
گرفتار نخواهد شد.
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (ArithmeticException e) {
System.out.println("Error! File not found!");
}
}
خروجی: استثنا در رشته "اصلی" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (سیستم نمی تواند مسیر مشخص شده را پیدا کند) کد موجود در catch
بلوک اجرا نشد، زیرا ما "پیکربندی" کردیم. این بلوک برای گرفتن ArithmeticException
، و کد موجود در try
بلوک یک نوع متفاوت پرتاب کرد: FileNotFoundException
. ما هیچ کدی برای مدیریت ننوشتیم FileNotFoundException
، بنابراین برنامه اطلاعات پیش فرض را برای FileNotFoundException
. در اینجا باید به سه نکته توجه کنید. شماره یک. هنگامی که یک استثنا در برخی از خط های try
بلوک رخ می دهد، کد زیر اجرا نمی شود. اجرای برنامه بلافاصله به catch
بلوک "پرش" می کند. مثلا:
public static void main(String[] args) {
try {
System.out.println("Divide by zero");
System.out.println(366/0);// This line of code will throw an exception
System.out.println("This");
System.out.println("code");
System.out.println("will not");
System.out.println("be");
System.out.println("executed!");
} catch (ArithmeticException e) {
System.out.println("The program jumped to the catch block!");
System.out.println("Error! You can't divide by zero!");
}
}
خروجی: تقسیم بر صفر برنامه به بلوک catch پرید! خطا! شما نمی توانید بر صفر تقسیم کنید! در خط دوم بلوک try
، سعی می کنیم بر 0 تقسیم کنیم که نتیجه آن یک ArithmeticException
. در نتیجه، خطوط 3-10 بلوک try
اجرا نمی شود. همانطور که گفتیم برنامه بلافاصله شروع به اجرای catch
بلوک می کند. شماره دو. می تواند چندین catch
بلوک وجود داشته باشد. اگر کد موجود در try
بلوک ممکن است نه یک، بلکه چندین نوع مختلف استثنا را ایجاد کند، می توانید catch
برای هر یک از آنها یک بلوک بنویسید.
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
System.out.println(366/0);
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error! File not found!");
} catch (ArithmeticException e) {
System.out.println("Error! Division by 0!");
}
}
در این مثال دو بلوک نوشته ایم catch
. اگر a FileNotFoundException
در try
بلوک رخ دهد، اولین catch
بلوک اجرا خواهد شد. اگر an ArithmeticException
رخ دهد، بلوک دوم اجرا خواهد شد. catch
اگر بخواهید می توانید 50 بلوک بنویسید . البته، بهتر است کدی ننویسید که بتواند 50 نوع استثنا ایجاد کند. :) سوم. چگونه میدانید کد شما ممکن است چه استثناهایی ایجاد کند؟ خوب، ممکن است بتوانید برخی از آنها را حدس بزنید، اما غیرممکن است که همه چیز را در ذهن خود نگه دارید. بنابراین کامپایلر جاوا رایج ترین استثناها و موقعیت هایی را که ممکن است رخ دهند را می شناسد. برای مثال، اگر کدی بنویسید که کامپایلر بداند ممکن است دو نوع استثنا ایجاد کند، کد شما تا زمانی که آنها را مدیریت نکنید، کامپایل نخواهد شد. نمونه هایی از این را در زیر می بینیم.
حالا چند کلمه در مورد Exception Handling
2 راه برای مدیریت استثناها در جاوا وجود دارد. ما قبلاً با اولین مورد مواجه شده ایم: متد می تواند خود استثنا را در یکcatch()
بلوک مدیریت کند. یک گزینه دوم وجود دارد: این روش میتواند استثنا را دوباره در پشته تماس پرتاب کند. معنی آن چیست؟ به عنوان مثال، ما یک کلاس با روش مشابه داریم printFirstString()
که یک فایل را می خواند و اولین خط آن را نمایش می دهد:
public static void printFirstString(String filePath) {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
در حال حاضر، کد ما کامپایل نمی شود، زیرا دارای استثنائات کنترل نشده است. در خط 1 مسیر فایل را مشخص می کنید. کامپایلر می داند که چنین کدی می تواند به راحتی یک FileNotFoundException
. در خط 3، متن فایل را می خوانید. این فرآیند به راحتی می تواند منجر به IOException
(خطای ورودی/خروجی) شود. حالا کامپایلر به شما میگوید: "رفیق، من این کد را تایید نمیکنم و آن را کامپایل نمیکنم تا زمانی که به من بگویید اگر یکی از این استثناها رخ داد باید چه کار کنم. و مطمئناً ممکن است بر اساس کدی که شما نوشتید اتفاق بیفتد. !" هیچ راهی برای دور زدن آن وجود ندارد: شما باید هر دو را مدیریت کنید! ما قبلاً در مورد اولین روش مدیریت استثنا می دانیم: باید کد خود را در یک try
بلوک قرار داده و دو catch
بلوک اضافه کنیم:
public static void printFirstString(String filePath) {
try {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error, file not found!");
e.printStackTrace();
} catch (IOException e) {
System.out.println("File input/output error!");
e.printStackTrace();
}
}
اما این تنها گزینه نیست. به سادگی میتوانیم به جای نوشتن کد مدیریت خطا در متد، استثنا را بالاتر بیاندازیم.
جاوا کنترل کلمات کلیدی
این کار با استفاده از کلمه کلیدیthrows
در اعلان متد انجام می شود:
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
بعد از کلمه کلیدی throws
، فهرستی از همه انواع استثناهایی که ممکن است متد ایجاد کند، با کاما جدا شده است. چرا؟ حال، اگر کسی بخواهد printFirstString()
متد را در برنامه فراخوانی کند، او (نه شما) باید مدیریت استثنا را پیاده سازی کند. به عنوان مثال، فرض کنید در جای دیگری از برنامه یکی از همکاران شما متدی نوشته است که printFirstString()
متد شما را فراخوانی می کند:
public static void yourColleagueMethod() {
// Your colleague's method does something
//...and then calls your printFirstString() method with the file it needs
printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
خطا می گیریم! این کد کامپایل نمی شود! ما در متد کد کنترل استثنایی ننوشتیم printFirstString()
. در نتیجه اکنون این وظیفه بر دوش کسانی است که از این روش استفاده می کنند. به عبارت دیگر، methodWrittenByYourColleague()
روش اکنون همان 2 گزینه را دارد: یا باید از یک try-catch
بلوک برای رسیدگی به هر دو استثنا استفاده کند یا آنها را دوباره پرتاب کند.
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
// The method does something
//...and then calls your printFirstString() method with the file it needs
printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
در حالت دوم، روش بعدی در پشته فراخوانی - همان چیزی که فراخوانی می کند methodWrittenByYourColleague()
- باید استثناها را مدیریت کند. به همین دلیل است که ما به این می گوییم "پرتاب یا عبور از استثنا". اگر با استفاده از کلمه کلیدی استثناها را به سمت بالا پرتاب کنید throws
، کد شما کامپایل می شود. در این مرحله، کامپایلر به نظر میرسد که میگوید، "خوب، باشه. کد شما حاوی یکسری استثناهای بالقوه است، اما من آن را کامپایل میکنم. اما ما به این گفتگو برمیگردیم!" و هنگامی که شما هر متدی را که دارای استثناهای کنترل نشده است فراخوانی می کنید، کامپایلر به قول خود عمل می کند و دوباره آنها را به شما یادآوری می کند. در نهایت، ما در مورد finally
بلوک صحبت خواهیم کرد (با عرض پوزش برای جناس). این آخرین بخش از try-catch-finally
triumvirate رسیدگی به استثنا است. از این نظر متفاوت است که بدون توجه به آنچه در برنامه اتفاق می افتد اجرا می شود.
public static void main(String[] args) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
System.out.println("Error! File not found!");
e.printStackTrace();
} finally {
System.out.println ("And here's the finally block!");
}
}
در این مثال کد داخل finally
بلوک در هر دو حالت اجرا خواهد شد. اگر کد موجود در try
بلوک به طور کامل بدون پرتاب هیچ استثنایی اجرا شود، finally
بلوک در پایان اجرا خواهد شد. اگر کد داخل try
بلوک با یک استثنا قطع شود و برنامه به catch
بلوک بپرد، finally
بلاک همچنان بعد از کد داخل catch
بلوک اجرا می شود.
چرا این لازم است؟
هدف اصلی آن اجرای کد اجباری است: کدی که باید بدون توجه به شرایط اجرا شود. به عنوان مثال، اغلب برخی از منابع مورد استفاده برنامه را آزاد می کند. در کد ما یک جریان برای خواندن اطلاعات از فایل و ارسال آن بهBufferedReader
شی باز می کنیم. ما باید خواننده خود را ببندیم و منابع را آزاد کنیم. این باید بدون توجه به هر کاری انجام شود - وقتی برنامه همانطور که باید کار می کند، و زمانی که یک استثنا ایجاد می کند. بلوک finally
مکان بسیار مناسبی برای انجام این کار است:
public static void main(String[] args) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
String firstString = reader.readLine();
System.out.println(firstString);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
System.out.println ("And here's the finally block!");
if (reader != null) {
reader.close();
}
}
}
اکنون مطمئن هستیم که از منابع مراقبت خواهیم کرد، صرف نظر از اینکه در هنگام اجرای برنامه چه اتفاقی می افتد. :) این تمام چیزی نیست که باید در مورد استثناها بدانید. مدیریت خطا یک موضوع فوق العاده مهم در برنامه نویسی است. مقالات زیادی به آن اختصاص داده شده است. در درس بعدی، خواهیم فهمید که چه نوع استثنایی وجود دارد و چگونه استثناهای خود را ایجاد کنید. :) بعدا می بینمت!
GO TO FULL VERSION