JavaRush /Blog Java /Random-MS /Pengecualian di Jawa: menangkap dan mengendalikan

Pengecualian di Jawa: menangkap dan mengendalikan

Diterbitkan dalam kumpulan
hello! Saya tidak suka memberitahu anda, tetapi sebahagian besar tugas pengaturcara adalah menangani ralat. Dan paling kerap - dengan mereka sendiri. Kebetulan tidak ada orang yang tidak melakukan kesilapan. Dan tidak ada program sedemikian. Sudah tentu, perkara utama apabila bekerja pada ralat adalah untuk memahami puncanya. Dan mungkin terdapat banyak sebab untuk itu dalam program ini. Pada satu ketika, pencipta Java berhadapan dengan soalan: apa yang perlu dilakukan dengan ralat yang sangat berpotensi dalam program ini? Mengelakkan mereka sepenuhnya adalah tidak realistik. Pengaturcara boleh menulis sesuatu yang mustahil untuk dibayangkan :) Ini bermakna bahawa adalah perlu untuk membina ke dalam bahasa mekanisme untuk menangani ralat. Dalam erti kata lain, jika beberapa ralat telah berlaku dalam program, skrip diperlukan untuk kerja selanjutnya. Apakah sebenarnya yang perlu dilakukan oleh program apabila ralat berlaku? Hari ini kita akan berkenalan dengan mekanisme ini. Dan ia dipanggil "Pengecualian " .

Apakah pengecualian di Jawa

Pengecualian ialah beberapa situasi luar biasa dan tidak dirancang yang berlaku semasa pengendalian program. Terdapat banyak contoh pengecualian di Jawa. Contohnya, anda menulis kod yang membaca teks daripada fail dan memaparkan baris pertama ke konsol.
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);
   }
}
Tetapi fail sedemikian tidak wujud! Keputusan program akan menjadi pengecualian - FileNotFoundException. Kesimpulan:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Setiap pengecualian diwakili oleh kelas berasingan di Jawa. Semua kelas pengecualian datang daripada "nenek moyang" biasa - kelas induk Throwable. Nama kelas pengecualian biasanya menggambarkan secara ringkas sebab kejadiannya:
  • FileNotFoundException(fail tidak dijumpai)
  • ArithmeticException(pengecualian semasa menjalankan operasi matematik)
  • ArrayIndexOutOfBoundsException(bilangan sel tatasusunan ditentukan melebihi panjangnya). Contohnya, jika anda cuba memaparkan tatasusunan sel[23] ke konsol untuk tatasusunan dengan panjang 10.
Terdapat hampir 400 kelas sedemikian di Jawa! Mengapa begitu ramai? Tepat untuk menjadikannya lebih mudah untuk pengaturcara bekerja dengan mereka. Bayangkan: anda menulis program, dan apabila ia berjalan, ia melemparkan pengecualian yang kelihatan seperti ini:
Exception in thread "main"
Uh-uh :/ Tiada yang jelas. Apakah jenis kesilapan itu dan dari mana ia datang tidak jelas. Tiada maklumat berguna. Tetapi terima kasih kepada pelbagai kelas, pengaturcara mendapat perkara utama untuk dirinya sendiri - jenis ralat dan kemungkinan penyebabnya, yang terkandung dalam nama kelas. Lagipun, ia adalah perkara yang sama sekali berbeza untuk dilihat dalam konsol:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Ia segera menjadi jelas apa masalahnya dan "ke arah mana untuk digali" untuk menyelesaikan masalah! Pengecualian, seperti mana-mana contoh kelas, adalah objek.

Pengecualian Penangkapan dan Pengendalian

Untuk bekerja dengan pengecualian dalam Java, terdapat blok kod khas: try, catchdan finally. Pengecualian: pemintasan dan pemprosesan - 2Kod di mana pengaturcara menjangkakan pengecualian berlaku diletakkan dalam blok try. Ini tidak bermakna bahawa pengecualian semestinya akan berlaku di lokasi ini. Ini bermakna ia boleh berlaku di sana, dan pengaturcara menyedarinya. Jenis ralat yang anda jangkakan akan diterima diletakkan dalam blok catch("tangkap"). Di sinilah juga semua kod yang perlu dilaksanakan jika pengecualian berlaku diletakkan. Berikut ialah contoh:
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!");
   }
}
Kesimpulan:

Ошибка! Файл не найден!
Kami meletakkan kod kami dalam dua blok. Dalam blok pertama kami menjangkakan bahawa ralat "Fail tidak ditemui" mungkin berlaku. Ini adalah blok try. Dalam yang kedua, kami memberitahu program apa yang perlu dilakukan jika ralat berlaku. Selain itu, terdapat jenis ralat tertentu - FileNotFoundException. Jika kita lulus catchsatu lagi kelas pengecualian ke dalam kurungan blok, ia tidak akan ditangkap.
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!");
   }
}
Kesimpulan:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Kod dalam blok catchtidak berfungsi kerana kami "mengkonfigurasi" blok ini untuk memintas ArithmeticException, dan kod dalam blok trymembuang jenis lain - FileNotFoundException. Kami tidak menulis skrip untuk FileNotFoundException, jadi program memaparkan dalam konsol maklumat yang dipaparkan secara lalai untuk FileNotFoundException. Di sini anda perlu memberi perhatian kepada 3 perkara. Pertama. Sebaik sahaja pengecualian berlaku dalam mana-mana baris kod dalam blok cuba, kod selepas itu tidak akan dilaksanakan lagi. Pelaksanaan program akan segera "melompat" ke blok catch. Sebagai contoh:
public static void main(String[] args) {
   try {
       System.out.println("Divide a number 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("Not");
       System.out.println("will");
       System.out.println("done!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Kesimpulan:

Делим число на ноль 
Программа перепрыгнула в блок catch! 
Ошибка! Нельзя делить на ноль! 
Dalam blok trydalam baris kedua, kami cuba membahagikan nombor dengan 0, yang menghasilkan pengecualian ArithmeticException. Selepas ini, baris 6-10 blok trytidak akan dilaksanakan lagi. Seperti yang kami katakan, program itu serta-merta mula melaksanakan blok catch. Kedua. Terdapat beberapa blok catch. Jika kod dalam blok trytidak boleh membuang satu, tetapi beberapa jenis pengecualian, anda boleh menulis blok anda sendiri untuk setiap satu daripadanya 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!");

   }
}
Dalam contoh ini kami menulis dua blok catch. Jika , tryberlaku dalam blok FileNotFoundException, blok pertama akan dilaksanakan catch. Jika berlaku ArithmeticException, yang kedua akan dilaksanakan. Anda boleh menulis sekurang-kurangnya 50 blok catch. Tetapi, sudah tentu, adalah lebih baik untuk tidak menulis kod yang boleh membuang 50 jenis ralat yang berbeza :) Ketiga. Bagaimanakah anda mengetahui pengecualian yang mungkin dibuang oleh kod anda? Sudah tentu, anda boleh meneka beberapa, tetapi mustahil untuk menyimpan segala-galanya di kepala anda. Oleh itu, pengkompil Java mengetahui tentang pengecualian yang paling biasa dan mengetahui dalam situasi yang boleh berlaku. Contohnya, jika anda menulis kod dan pengkompil mengetahui bahawa 2 jenis pengecualian mungkin berlaku semasa operasinya, kod anda tidak akan dikompil sehingga anda mengendalikannya. Kita akan melihat contoh ini di bawah. Sekarang mengenai pengendalian pengecualian. Terdapat 2 cara untuk memprosesnya. Kami telah pun bertemu yang pertama - kaedah ini boleh mengendalikan pengecualian secara bebas dalam blok catch(). Terdapat pilihan kedua - kaedah ini boleh membuang pengecualian ke atas timbunan panggilan. Apakah maksudnya? Sebagai contoh, dalam kelas kami, kami mempunyai kaedah - yang sama printFirstString()- yang membaca fail dan memaparkan baris pertamanya ke konsol:
public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Pada masa ini kod kami tidak menyusun kerana ia mempunyai pengecualian yang tidak dikendalikan. Pada baris 1 anda menunjukkan laluan ke fail. Pengkompil tahu bahawa kod tersebut boleh membawa kepada FileNotFoundException. Pada baris 3 anda membaca teks daripada fail. Dalam proses ini, IOExceptionralat boleh berlaku dengan mudah semasa data input-output (Input-Output). Sekarang pengkompil memberitahu anda, "Kawan, saya tidak akan meluluskan kod ini atau menyusunnya sehingga anda memberitahu saya apa yang perlu saya lakukan jika salah satu daripada pengecualian ini berlaku. Dan ia pasti boleh berlaku berdasarkan kod yang anda tulis!” . Tiada tempat untuk pergi, anda perlu memproses kedua-duanya! Pilihan pemprosesan pertama sudah biasa kepada kami: kami perlu meletakkan kod kami dalam blok trydan menambah dua blok 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("Error while inputting/outputting data from file!");
       e.printStackTrace();
   }
}
Tetapi ini bukan satu-satunya pilihan. Kita boleh mengelak daripada menulis skrip untuk ralat di dalam kaedah, dan hanya membuang pengecualian ke atas. Ini dilakukan menggunakan kata kunci throws, yang ditulis dalam pengisytiharan kaedah:
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Selepas perkataan, throwskami menyenaraikan, dipisahkan dengan koma, semua jenis pengecualian yang boleh dibuang oleh kaedah ini semasa operasi. Mengapa ini dilakukan? Sekarang, jika seseorang dalam program mahu memanggil kaedah printFirstString(), dia perlu melaksanakan pengecualian mengendalikan dirinya sendiri. Sebagai contoh, dalam bahagian lain program, salah seorang rakan sekerja anda menulis kaedah di mana ia memanggil kaedah anda printFirstString():
public static void yourColleagueMethod() {

   //...your colleague's method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Ralat, kod tidak disusun! printFirstString()Kami tidak menulis skrip pengendalian ralat dalam kaedah . Oleh itu, tugas itu terletak di bahu mereka yang akan menggunakan kaedah ini. Iaitu, kaedah yourColleagueMethod()kini menghadapi 2 pilihan yang sama: ia mesti sama ada memproses kedua-dua pengecualian yang "terbang" kepadanya menggunakan try-catch, atau memajukannya lagi.
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   //...the method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Dalam kes kedua, pemprosesan akan jatuh pada bahu kaedah seterusnya pada timbunan - yang akan memanggil yourColleagueMethod(). Itulah sebabnya mekanisme sedemikian dipanggil "melemparkan pengecualian ke atas", atau "melepasi ke atas". Apabila anda membuang pengecualian menggunakan throws, kod tersebut akan disusun. Pada masa ini, penyusun seolah-olah berkata: “Baiklah, okey. Kod anda mengandungi banyak kemungkinan pengecualian, tetapi saya akan menyusunnya juga. Kami akan kembali kepada perbualan ini!” Dan apabila anda memanggil kaedah di suatu tempat dalam program yang tidak mengendalikan pengecualiannya, pengkompil memenuhi janjinya dan mengingatkan anda tentangnya sekali lagi. Akhirnya, kita akan bercakap tentang blok finally(maafkan kata-kata). Ini adalah bahagian terakhir dari triumvirat pengendalian pengecualian try-catch-finally. Keistimewaannya ialah ia dilaksanakan di bawah mana-mana senario operasi program.
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 is the finally block!");
   }
}
Dalam contoh ini, kod di dalam blok finallydilaksanakan dalam kedua-dua kes. Jika kod dalam blok trydilaksanakan sepenuhnya dan tidak membuang pengecualian, blok akan menyala pada penghujungnya finally. Jika kod di dalam tryterganggu dan program melompat ke blok catch, selepas kod di dalamnya dilaksanakan catch, blok itu masih akan dipilih finally. Mengapa ia diperlukan? Tujuan utamanya adalah untuk melaksanakan bahagian kod yang diperlukan; bahagian itu yang mesti dilengkapkan tanpa mengira keadaan. Sebagai contoh, ia sering membebaskan beberapa sumber yang digunakan oleh program. Dalam kod kami, kami membuka aliran untuk membaca maklumat daripada fail dan menghantarnya ke fail BufferedReader. Milik kita readerperlu ditutup dan sumber dibebaskan. Ini mesti dilakukan dalam apa jua keadaan: tidak kira sama ada program berfungsi seperti yang diharapkan atau membuang pengecualian. Ia adalah mudah untuk melakukan ini dalam blok 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 is the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Kini kami benar-benar yakin bahawa kami telah menjaga sumber yang diduduki, tanpa mengira apa yang berlaku semasa program berjalan :) Bukan itu sahaja yang anda perlu tahu tentang pengecualian. Pengendalian ralat adalah topik yang sangat penting dalam pengaturcaraan: lebih daripada satu artikel dikhaskan untuknya. Dalam pelajaran seterusnya kita akan mempelajari jenis pengecualian yang ada dan cara mencipta pengecualian anda sendiri :) Jumpa anda di sana!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION