CodeGym /وبلاگ جاوا /Random-FA /مدیریت استثنا در جاوا
John Squirrels
مرحله
San Francisco

مدیریت استثنا در جاوا

در گروه منتشر شد
سلام! امروز نگاهی دقیق تر به مدیریت استثنا در جاوا خواهیم داشت. من از ذکر آن متنفرم، اما بخش بزرگی از کار یک برنامه نویس در برخورد با خطاها است. بیشتر اوقات، مال خودش. معلوم شد که هیچ آدمی نیست که اشتباه نکند. و چنین برنامه هایی نیز وجود ندارد. البته هنگام برخورد با یک خطا، نکته اصلی درک علت آن است. و بسیاری از چیزها می توانند باعث ایجاد اشکال در یک برنامه شوند. در نقطه ای، سازندگان جاوا از خود پرسیدند که با محتمل ترین خطاهای برنامه نویسی چه باید کرد؟ اجتناب کامل از آنها واقع بینانه نیست، برنامه نویسان قادر به نوشتن مطالبی هستند که حتی تصورش را هم نمی کنید. :) بنابراین، باید به زبان مکانیزمی برای کار با خطاها بدهیم. به عبارت دیگر، اگر خطایی در برنامه شما وجود داشته باشد، به نوعی اسکریپت برای کارهای بعدی نیاز دارید. در هنگام بروز خطا یک برنامه دقیقاً چه کاری باید انجام دهد؟ امروز با این مکانیسم آشنا می شویم. به آن " استثنا در جاوا " می گویند.

استثنا چیست؟

استثنا یک موقعیت استثنایی و برنامه ریزی نشده است که در حین اجرای برنامه رخ می دهد. استثناهای زیادی وجود دارد. به عنوان مثال، شما کدی نوشتید که متنی را از یک فایل می خواند و خط اول را نمایش می دهد.
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 عنصر دارد را نمایش دهید، این استثنا رخ می دهد.
در مجموع جاوا تقریباً 400 کلاس از این قبیل دارد! چرا اینقدر زیاد؟ برای اینکه برنامه نویسان کار با آنها را راحت تر کنند. این را تصور کنید: شما یک برنامه می نویسید، و در حین اجرای آن یک استثنا ایجاد می کند که به شکل زیر است:
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. استثناها: گرفتن و جابجایی - 2 کدی که برنامه نویس معتقد است ممکن است یک استثنا رخ دهد در 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-finallytriumvirate رسیدگی به استثنا است. از این نظر متفاوت است که بدون توجه به آنچه در برنامه اتفاق می افتد اجرا می شود.
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();
       }
   }
}
اکنون مطمئن هستیم که از منابع مراقبت خواهیم کرد، صرف نظر از اینکه در هنگام اجرای برنامه چه اتفاقی می افتد. :) این تمام چیزی نیست که باید در مورد استثناها بدانید. مدیریت خطا یک موضوع فوق العاده مهم در برنامه نویسی است. مقالات زیادی به آن اختصاص داده شده است. در درس بعدی، خواهیم فهمید که چه نوع استثنایی وجود دارد و چگونه استثناهای خود را ایجاد کنید. :) بعدا می بینمت!
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION