1. Jenis pengecualian

Jenis pengecualian

Semua pengecualian dibahagikan kepada 4 jenis, yang sebenarnya merupakan kelas yang mewarisi satu sama lain.

Throwablekelas

Kelas asas untuk semua pengecualian ialah Throwablekelas. Kelas Throwablemengandungi kod yang menulis timbunan panggilan semasa (jejak tindanan kaedah semasa) kepada tatasusunan. Kita akan belajar apa itu surih tindanan sedikit kemudian.

Operator lontaran hanya boleh menerima objek yang berasal dari Throwablekelas. Dan walaupun anda secara teorinya boleh menulis kod seperti throw new Throwable();, tiada siapa yang biasanya melakukan ini. Tujuan utama kelas Throwableadalah untuk mempunyai kelas induk tunggal untuk semua pengecualian.

Errorkelas

Kelas pengecualian seterusnya ialah Errorkelas, yang mewarisi Throwablekelas secara langsung. Mesin Java mencipta objek kelas Error(dan keturunannya) apabila masalah serius telah berlaku . Contohnya, kerosakan perkakasan, memori tidak mencukupi, dsb.

Biasanya, sebagai pengaturcara, tiada apa yang boleh anda lakukan dalam situasi di mana ralat sedemikian (jenis yang patut Errordilemparkan) telah berlaku dalam atur cara: ralat ini terlalu serius. Apa yang anda boleh lakukan ialah memberitahu pengguna bahawa program itu ranap dan/atau menulis semua maklumat yang diketahui tentang ralat pada log program.

Exceptionkelas

Kelas Exceptiondan RuntimeExceptionadalah untuk ralat biasa yang berlaku dalam pengendalian banyak kaedah. Matlamat setiap pengecualian yang dilemparkan adalah untuk ditangkap oleh catchblok yang tahu cara mengendalikannya dengan betul.

Apabila kaedah tidak dapat menyelesaikan kerjanya atas sebab tertentu, ia harus segera memberitahu kaedah panggilan dengan membuang pengecualian jenis yang sesuai.

Dalam erti kata lain, jika pembolehubah adalah sama dengan null, kaedah akan membuang NullPointerException. Jika hujah yang salah dihantar kepada kaedah, ia akan membuang InvalidArgumentException. Jika kaedah secara tidak sengaja membahagi dengan sifar, ia akan membuang ArithmeticException.

RuntimeExceptionkelas

RuntimeExceptionsialah subset daripada Exceptions. Kita juga boleh mengatakan bahawa RuntimeExceptionia adalah versi ringan bagi pengecualian biasa ( Exception) — lebih sedikit keperluan dan sekatan dikenakan ke atas pengecualian tersebut

Anda akan mengetahui perbezaan antara Exceptiondan RuntimeExceptionkemudian.


2. Throws: pengecualian diperiksa

Lemparan: pengecualian diperiksa

Semua pengecualian Java jatuh ke dalam 2 kategori: ditandai dan dinyahtanda .

Semua pengecualian yang mewarisi RuntimeExceptionatau Errordianggap pengecualian tidak ditanda . Semua yang lain disemak pengecualian .

Penting!

Dua puluh tahun selepas pengecualian yang diperiksa diperkenalkan, hampir setiap pengaturcara Java menganggap ini sebagai pepijat. Dalam rangka kerja moden yang popular, 95% daripada semua pengecualian tidak ditandai. Bahasa C#, yang hampir menyalin Java dengan tepat, tidak menambah pengecualian yang diperiksa .

Apakah perbezaan utama antara pengecualian yang disemak dan tidak disemak ?

Terdapat keperluan tambahan yang dikenakan ke atas pengecualian yang disemak . Secara kasarnya, mereka adalah ini:

Keperluan 1

Jika kaedah melemparkan pengecualian yang ditandai , ia mesti menunjukkan jenis pengecualian dalam tandatangannya . Dengan cara itu, setiap kaedah yang memanggilnya menyedari bahawa "pengecualian yang bermakna" ini mungkin berlaku di dalamnya.

Nyatakan pengecualian yang disemak selepas parameter kaedah selepas throwskata kunci (jangan gunakan throwkata kunci secara tidak sengaja). Ia kelihatan seperti ini:

type method (parameters) throws exception

Contoh:

pengecualian diperiksa pengecualian tidak ditanda
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

Dalam contoh di sebelah kanan, kod kami membuang pengecualian yang tidak ditanda — tiada tindakan tambahan diperlukan. Dalam contoh di sebelah kiri, kaedah membuang pengecualian yang ditandai , jadi throwskata kunci ditambahkan pada tandatangan kaedah bersama-sama dengan jenis pengecualian.

Jika kaedah menjangkakan untuk membuang berbilang pengecualian yang ditandakan , kesemuanya mesti dinyatakan selepas throwskata kunci, dipisahkan dengan koma. Perintah itu tidak penting. Contoh:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

Keperluan 2

Jika anda memanggil kaedah yang telah menyemak pengecualian dalam tandatangannya, anda tidak boleh mengabaikan fakta bahawa kaedah itu membuangnya.

Anda mesti sama ada menangkap semua pengecualian tersebut dengan menambahkan catchblok untuk setiap satu, atau dengan menambahkannya pada throwsklausa untuk kaedah anda.

Seolah-olah kita berkata, " Pengecualian ini sangat penting sehingga kita mesti menangkapnya. Dan jika kita tidak tahu cara mengendalikannya, maka sesiapa yang mungkin memanggil kaedah kita mesti dimaklumkan bahawa pengecualian tersebut boleh berlaku di dalamnya.

Contoh:

Bayangkan bahawa kita sedang menulis kaedah untuk mencipta dunia yang dihuni oleh manusia. Bilangan awal orang diluluskan sebagai hujah. Jadi kita perlu menambah pengecualian jika terdapat terlalu sedikit orang.

Mencipta Bumi Catatan
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
Kaedah ini berpotensi membuang dua pengecualian yang diperiksa :

  • EmptyWorldException
  • LonelyWorldException

Panggilan kaedah ini boleh dikendalikan dalam 3 cara:

1. Jangan tangkap sebarang pengecualian

Ini paling kerap dilakukan apabila kaedah tidak tahu cara mengendalikan keadaan dengan betul.

Kod Catatan
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
Kaedah panggilan tidak menangkap pengecualian dan mesti memberitahu orang lain tentangnya: ia menambahkannya pada klausanya throwssendiri

2. Tangkap beberapa pengecualian

Kami menangani kesilapan yang boleh kami tangani. Tetapi yang kita tidak faham, kita buang ke kaedah panggilan. Untuk melakukan ini, kita perlu menambah nama mereka pada klausa lontaran:

Kod Catatan
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
Pemanggil hanya menangkap satu pengecualian yang diperiksaLonelyWorldException — . Pengecualian lain mesti ditambahkan pada tandatangannya, menunjukkannya selepas throwskata kunci

3. Tangkap semua pengecualian

Jika kaedah tidak membuang pengecualian kepada kaedah panggilan, maka kaedah panggilan sentiasa yakin bahawa semuanya berfungsi dengan baik. Dan ia tidak akan dapat mengambil sebarang tindakan untuk membetulkan situasi yang luar biasa.

Kod Catatan
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Semua pengecualian terperangkap dalam kaedah ini. Pemanggil akan yakin bahawa semuanya berjalan lancar.


3. Pengecualian pembalut

Pengecualian yang disemak kelihatan hebat dalam teori, tetapi ternyata menjadi kekecewaan yang besar dalam amalan.

Katakan anda mempunyai kaedah yang sangat popular dalam projek anda. Ia dipanggil dari ratusan tempat dalam program anda. Dan anda memutuskan untuk menambah pengecualian baru yang ditandakan padanya. Dan mungkin pengecualian yang disemak ini benar-benar penting dan sangat istimewa sehingga hanya main()kaedah yang tahu apa yang perlu dilakukan jika ia ditangkap.

Ini bermakna anda perlu menambah pengecualian yang ditandakan pada throwsklausa setiap kaedah yang memanggil kaedah super popular anda . Begitu juga dalam throwsklausa semua kaedah yang memanggil kaedah tersebut. Dan kaedah yang memanggil kaedah tersebut.

Akibatnya, throwsklausa separuh daripada kaedah dalam projek mendapat pengecualian diperiksa baharu . Dan sudah tentu projek anda dilindungi oleh ujian, dan kini ujian tidak disusun. Dan kini anda perlu mengedit klausa lontaran dalam ujian anda juga.

Dan kemudian semua kod anda (semua perubahan dalam ratusan fail) perlu disemak oleh pengaturcara lain. Dan pada ketika ini kami bertanya kepada diri sendiri mengapa kami membuat banyak perubahan berdarah pada projek itu? Hari bekerja dan ujian yang rosak — semuanya demi menambah satu pengecualian yang telah ditandakan ?

Dan sudah tentu, masih terdapat masalah yang berkaitan dengan pewarisan dan kaedah mengatasi. Masalah yang datang daripada pengecualian yang diperiksa adalah lebih besar daripada manfaatnya. Intinya ialah kini hanya sedikit orang yang menyukainya dan hanya sedikit orang yang menggunakannya.

Walau bagaimanapun, masih terdapat banyak kod (termasuk kod perpustakaan Java standard) yang mengandungi pengecualian yang disemak ini . Apa yang perlu dilakukan dengan mereka? Kita tidak boleh mengabaikan mereka, dan kita tidak tahu bagaimana untuk mengendalikannya.

Pengaturcara Java mencadangkan untuk membungkus pengecualian yang diperiksa dalam RuntimeException. Dalam erti kata lain, tangkap semua pengecualian yang ditandakan dan kemudian buat pengecualian yang tidak ditandakan (contohnya, RuntimeException) dan buangnya. Melakukannya kelihatan seperti ini:

try
{
   // Code where a checked exception might occur
}
catch(Exception exp)
{
   throw new RuntimeException(exp);
}

Ia bukan penyelesaian yang sangat cantik, tetapi tiada apa-apa jenayah di sini: pengecualian hanya disumbat di dalam RuntimeException.

Jika dikehendaki, anda boleh mendapatkannya dengan mudah dari sana. Contoh:

Kod Catatan
try
{
   // Code where we wrap the checked exception
   // in a RuntimeException
}
catch(RuntimeException e)
{
   Throwable cause = e.getCause();
   if (cause instanceof Exception)
   {
      Exception exp = (Exception) cause;
      // Exception handling code goes here
   }
}







Dapatkan pengecualian yang disimpan di dalam RuntimeExceptionobjek. Pembolehubah causemungkin null

Tentukan jenisnya dan tukarkannya kepada jenis pengecualian yang diperiksa .


4. Menangkap pelbagai pengecualian

Pengaturcara sangat tidak suka menduplikasi kod. Mereka juga mengemukakan prinsip pembangunan yang sepadan: Jangan Ulangi Sendiri (KERING) . Tetapi apabila mengendalikan pengecualian, terdapat keadaan yang kerap apabila tryblok diikuti oleh beberapa catchblok dengan kod yang sama.

Atau mungkin terdapat 3 catchblok dengan kod yang sama dan 2 catchblok lagi dengan kod yang serupa. Ini adalah situasi standard apabila projek anda mengendalikan pengecualian secara bertanggungjawab.

Bermula dengan versi 7, dalam bahasa Java menambahkan keupayaan untuk menentukan berbilang jenis pengecualian dalam satu catchblok. Ia kelihatan seperti ini:

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

Anda boleh mempunyai seberapa banyak catchblok yang anda mahu. Walau bagaimanapun, satu catchblok tidak boleh menentukan pengecualian yang mewarisi satu sama lain. Dalam erti kata lain, anda tidak boleh menulis catch ( Exception| RuntimeExceptione), kerana RuntimeExceptionkelas mewarisi Exception.



5. Pengecualian tersuai

Anda sentiasa boleh membuat kelas pengecualian anda sendiri. Anda hanya membuat kelas yang mewarisi RuntimeExceptionkelas. Ia akan kelihatan seperti ini:

class ClassName extends RuntimeException
{
}

Kami akan membincangkan butiran semasa anda mempelajari OOP, warisan, pembina dan kaedah mengatasi.

Walau bagaimanapun, walaupun anda hanya mempunyai kelas mudah seperti ini (sepenuhnya tanpa kod), anda masih boleh membuang pengecualian berdasarkannya:

Kod Catatan
class Solution
{
   public static void main(String[] args)
   {
      throw new MyException();
   }
}

class MyException extends RuntimeException
{
}




Buang yang tidak bertanda MyException .

Dalam pencarian Java Multithreading , kami akan menyelam lebih mendalam untuk bekerja dengan pengecualian tersuai kami sendiri.