CodeGym /Blog Java /rawak /Pengecualian: menangkap dan mengendalikan
John Squirrels
Tahap
San Francisco

Pengecualian: menangkap dan mengendalikan

Diterbitkan dalam kumpulan
Hai! Saya tidak suka menyebutnya, tetapi sebahagian besar kerja pengaturcara adalah menangani ralat. Selalunya, miliknya sendiri. Ternyata tidak ada orang yang tidak melakukan kesilapan. Dan tidak ada program sedemikian. Pengecualian: menangkap dan mengendalikan - 1 Sudah tentu, apabila berhadapan dengan ralat, perkara utama ialah memahami puncanya. Dan banyak perkara boleh menyebabkan pepijat dalam program. Pada satu ketika, pencipta Java bertanya kepada diri mereka sendiri apakah yang perlu dilakukan dengan ralat pengaturcaraan yang paling mungkin? Mengelaknya sepenuhnya adalah tidak realistik, pengaturcara mampu menulis perkara yang anda tidak boleh bayangkan. :) Jadi, kita perlu memberi bahasa mekanisme untuk bekerja dengan ralat. Dalam erti kata lain, jika terdapat ralat dalam program anda, anda memerlukan sejenis skrip untuk tindakan seterusnya. Apa sebenarnya yang perlu dilakukan oleh program apabila ralat berlaku? Hari ini kita akan berkenalan dengan mekanisme ini. Ia dipanggil " pengecualian dalam Java ".

Apakah pengecualian?

Pengecualian ialah situasi luar biasa dan tidak dirancang yang berlaku semasa program sedang berjalan. Terdapat banyak pengecualian. Contohnya, anda menulis kod yang membaca teks daripada fail dan memaparkan baris pertama.

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 bagaimana jika tiada fail sedemikian! Program ini akan menghasilkan pengecualian: FileNotFoundException. Output: Pengecualian dalam benang "utama" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Sistem tidak dapat mencari laluan yang ditentukan) Di Java, setiap pengecualian diwakili oleh kelas yang berasingan. Semua kelas pengecualian ini berasal daripada "nenek moyang" biasa— Throwablekelas induk. Nama kelas pengecualian biasanya menggambarkan secara ringkas mengapa pengecualian itu berlaku:
  • FileNotFoundException(fail tidak ditemui)

  • ArithmeticException(pengecualian berlaku semasa menjalankan operasi matematik)

  • ArrayIndexOutOfBoundsException(indeks berada di luar sempadan tatasusunan). Sebagai contoh, pengecualian ini berlaku jika anda cuba memaparkan kedudukan 23 tatasusunan yang hanya mempunyai 10 elemen.
Secara keseluruhannya, Java mempunyai hampir 400 kelas sedemikian! Mengapa begitu ramai? Untuk menjadikannya lebih mudah untuk pengaturcara bekerja dengannya. Bayangkan ini: anda menulis program, dan semasa ia berjalan ia menghasilkan pengecualian yang kelihatan seperti ini:

Exception in thread "main"
Uhhhh. :/ Itu tidak banyak membantu. Tidak jelas apa maksud ralat itu atau dari mana ia datang. Tiada maklumat berguna di sini. Tetapi kepelbagaian besar kelas pengecualian dalam Java memberikan pengaturcara perkara yang paling penting: jenis ralat dan kemungkinan puncanya (tertanam dalam nama kelas). Ia adalah perkara yang agak lain untuk dilihat

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Ia segera jelas apa masalahnya dan di mana untuk mula menggali untuk menyelesaikan masalah itu! Pengecualian, seperti contoh mana-mana kelas, adalah objek.

Menangkap dan mengendalikan pengecualian

Java mempunyai blok kod khas untuk bekerja dengan pengecualian: try, catchdan finally. Pengecualian: menangkap dan mengendalikan - 2 Kod di mana pengaturcara percaya pengecualian mungkin berlaku diletakkan di dalam tryblok. Itu tidak bermakna pengecualian akan berlaku di sini. Ini bermakna ia mungkin berlaku di sini, dan pengaturcara menyedari kemungkinan ini. Jenis ralat yang anda jangka akan berlaku diletakkan di dalam catchblok. Ini juga mengandungi semua kod yang harus dilaksanakan jika pengecualian berlaku. 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!");
   }
}
Output: Ralat! Fail tidak dijumpai! Kami meletakkan kod kami dalam dua blok. Dalam blok pertama, kami menjangkakan bahawa ralat "Fail tidak ditemui" mungkin berlaku. Ini adalah trybloknya. Dalam yang kedua, kami memberitahu program apa yang perlu dilakukan jika ralat berlaku. Dan jenis ralat khusus: FileNotFoundException. Jika kita meletakkan kelas pengecualian yang berbeza dalam kurungan blok catch, maka FileNotFoundExceptiontidak 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!");
   }
}
Output: Pengecualian dalam utas "utama" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Sistem tidak dapat mencari laluan yang ditentukan) Kod dalam catchblok tidak dijalankan, kerana kami "mengkonfigurasi" blok ini untuk menangkap ArithmeticException, dan kod dalam tryblok melemparkan jenis yang berbeza: FileNotFoundException. Kami tidak menulis sebarang kod untuk dikendalikan FileNotFoundException, jadi program memaparkan maklumat lalai untuk FileNotFoundException. Di sini anda perlu memberi perhatian kepada tiga perkara. Nombor satu. Sebaik sahaja pengecualian berlaku pada beberapa baris dalam tryblok, kod yang berikut tidak akan dilaksanakan. Pelaksanaan program segera "melompat" ke catchblok. Sebagai contoh:

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!");
   }
}
Output: Bahagi dengan sifar Program melonjak ke blok tangkapan! Ralat! Anda tidak boleh membahagi dengan sifar! Pada baris kedua blok try, kami cuba membahagi dengan 0, menghasilkan ArithmeticException. Akibatnya, baris 3-9 blok trytidak akan dilaksanakan. Seperti yang kami katakan, program ini serta-merta mula melaksanakan catchblok. Nombor dua. Terdapat beberapa catchblok. Jika kod dalam tryblok mungkin tidak membuang satu, tetapi beberapa jenis pengecualian yang berbeza, anda boleh menulis catchblok untuk setiap satu daripadanya.

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 telah menulis dua catchblok. Jika a FileNotFoundExceptionberlaku dalam tryblok, maka blok pertama catchakan dilaksanakan. Jika ArithmeticExceptionberlaku, blok kedua akan dilaksanakan. Anda boleh menulis 50 catchblok jika anda mahu. Sudah tentu, adalah lebih baik untuk tidak menulis kod yang boleh membuang 50 jenis pengecualian yang berbeza. :) Ketiga. Bagaimanakah anda tahu pengecualian mana yang mungkin dilemparkan oleh kod anda? Nah, anda mungkin boleh meneka sebahagian daripada mereka, tetapi mustahil untuk anda menyimpan segala-galanya dalam kepala anda. Oleh itu, pengkompil Java mengetahui pengecualian yang paling biasa dan situasi di mana ia mungkin berlaku. Contohnya, jika anda menulis kod yang pengkompil tahu mungkin membuang dua jenis pengecualian, kod anda tidak akan dikompil sehingga anda mengendalikannya. Kita akan melihat contoh perkara ini di bawah. Sekarang beberapa perkataan tentang pengendalian pengecualian. Terdapat 2 cara untuk mengendalikan pengecualian. Kami telah menemui yang pertama: kaedah boleh mengendalikan pengecualian itu sendiri dalam catch()blok. Terdapat pilihan kedua: kaedah ini boleh membuang semula pengecualian ke atas timbunan panggilan. Apakah maksudnya? Sebagai contoh, kami mempunyai kelas dengan printFirstString()kaedah yang sama, yang membaca fail dan memaparkan baris pertamanya:

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 dikompil, kerana ia mempunyai pengecualian yang tidak dikendalikan. Dalam baris 1, anda menentukan laluan ke fail. Pengkompil tahu bahawa kod tersebut boleh menghasilkan FileNotFoundException. Dalam baris 3, anda membaca teks daripada fail. Proses ini dengan mudah boleh mengakibatkan IOException(ralat input/output). Sekarang pengkompil berkata kepada anda, "Kawan, saya tidak akan meluluskan kod ini dan saya tidak akan 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 cara untuk mengatasinya: anda perlu mengendalikan kedua-duanya! Kami sudah tahu tentang kaedah pengendalian pengecualian pertama: kami perlu meletakkan kod kami dalam satu tryblok dan menambah dua catchblok:

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();
   }
}
Tetapi ini bukan satu-satunya pilihan. Kami hanya boleh membuang pengecualian lebih tinggi daripada menulis kod pengendalian ralat di dalam kaedah. Ini dilakukan menggunakan kata kunci throwsdalam 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 kata kunci throws, kami menunjukkan senarai yang dipisahkan koma bagi semua jenis pengecualian yang mungkin dilemparkan oleh kaedah tersebut. kenapa? Sekarang, jika seseorang ingin memanggil printFirstString()kaedah dalam program, dia (bukan anda) perlu melaksanakan pengendalian pengecualian. Sebagai contoh, katakan di tempat lain dalam program itu salah seorang rakan sekerja anda menulis kaedah yang memanggil printFirstString()kaedah anda:

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");
}
Kami mendapat ralat! Kod ini tidak akan disusun! Kami tidak menulis kod pengendalian pengecualian dalam printFirstString()kaedah. Akibatnya, tugas ini kini terletak di bahu mereka yang menggunakan kaedah tersebut. Dalam erti kata lain, methodWrittenByYourColleague()kaedah kini mempunyai 2 pilihan yang sama: ia mesti sama ada menggunakan try-catchblok untuk mengendalikan kedua-dua pengecualian, atau membuangnya semula.

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");
}
Dalam kes kedua, kaedah seterusnya dalam timbunan panggilan—yang memanggil methodWrittenByYourColleague()—perlu mengendalikan pengecualian. Itulah sebabnya kami memanggil ini "membuang atau melepaskan pengecualian". Jika anda melontarkan pengecualian ke atas menggunakan kata kunci throws, kod anda akan disusun. Pada ketika ini, pengkompil nampaknya berkata, "Baiklah, okey. Kod anda mengandungi banyak kemungkinan pengecualian, tetapi saya akan menyusunnya. Tetapi kita akan kembali kepada perbualan ini!" Dan apabila anda memanggil mana-mana kaedah yang mempunyai pengecualian yang tidak dikendalikan, pengkompil memenuhi janjinya dan mengingatkan anda tentangnya sekali lagi. Akhirnya, kita akan bercakap tentang finallyblok (maaf untuk kata-kata). Ini adalah bahagian terakhir dari try-catch-finallytriumvirat pengendalian pengecualian..

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!");
   }
}
Dalam contoh ini, kod di dalam finallyblok akan dilaksanakan dalam kedua-dua kes. Jika kod dalam tryblok dijalankan sepenuhnya tanpa membuang sebarang pengecualian, finallyblok akan dijalankan pada akhirnya. Jika kod di dalam tryblok diganggu oleh pengecualian dan program melompat ke catchblok, finallyblok masih akan berjalan selepas kod di dalam catchblok. Mengapa ini perlu? Tujuan utamanya adalah untuk melaksanakan kod mandatori: kod yang mesti dilakukan 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 menyampaikannya kepada BufferedReaderobjek. Kita mesti menutup pembaca kita dan melepaskan sumber. Ini mesti dilakukan walau apa pun—apabila program berfungsi sebagaimana mestinya, dan apabila ia mengeluarkan pengecualian. Blok finallyadalah tempat yang sangat mudah untuk melakukan ini:

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();
       }
   }
}
Kini kami pasti bahawa kami akan menjaga sumber, tanpa mengira apa yang berlaku apabila program dijalankan. :) Bukan itu sahaja yang anda perlu tahu tentang pengecualian. Pengendalian ralat adalah topik yang sangat penting dalam pengaturcaraan. Banyak artikel dikhaskan untuknya. Dalam pelajaran seterusnya, kita akan mengetahui jenis pengecualian yang ada dan cara membuat pengecualian anda sendiri. :) Jumpa awak nanti!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION