Pengecualian di Java, Bagian 1: Dasar-dasar penanganan pengecualian

Pengecualian Java adalah tipe pustaka dan fitur bahasa yang digunakan untuk mewakili dan menangani kegagalan program. Jika Anda ingin memahami bagaimana kegagalan direpresentasikan dalam kode sumber, Anda telah datang ke tempat yang tepat. Selain gambaran umum tentang pengecualian Java, saya akan membantu Anda memulai dengan fitur bahasa Java untuk melempar objek, mencoba kode yang mungkin gagal, menangkap objek yang dilempar, dan membersihkan kode Java Anda setelah pengecualian dilemparkan.

Di paruh pertama tutorial ini Anda akan belajar tentang fitur bahasa dasar dan tipe pustaka yang telah ada sejak Java 1.0. Di paruh kedua, Anda akan menemukan kapabilitas tingkat lanjut yang diperkenalkan di versi Java yang lebih baru.

Perhatikan bahwa contoh kode dalam tutorial ini kompatibel dengan JDK 12.

unduh Dapatkan kodenya Unduh kode sumber untuk aplikasi contoh dalam tutorial ini. Dibuat oleh Jeff Friesen untuk JavaWorld.

Apa itu pengecualian Java?

Kegagalan terjadi saat perilaku normal program Java terganggu oleh perilaku yang tidak terduga. Perbedaan ini dikenal sebagai pengecualian . Misalnya, program mencoba membuka file untuk membaca kontennya, tetapi file tersebut tidak ada. Java mengklasifikasikan pengecualian menjadi beberapa jenis, jadi mari kita pertimbangkan masing-masing.

Pengecualian yang diperiksa

Java mengklasifikasikan pengecualian yang timbul dari faktor eksternal (seperti file yang hilang) sebagai pengecualian yang dicentang . Compiler Java memeriksa bahwa pengecualian tersebut ditangani (dikoreksi) di mana pengecualian itu terjadi atau didokumentasikan untuk ditangani di tempat lain.

Penangan pengecualian

Sebuah handler pengecualian adalah urutan kode yang menangani pengecualian. Ini menginterogasi konteks - artinya membaca nilai yang disimpan dari variabel yang berada dalam lingkup pada saat pengecualian terjadi - kemudian menggunakan apa yang dipelajari untuk memulihkan program Java ke aliran perilaku normal. Misalnya, penangan pengecualian mungkin membaca nama file yang disimpan dan meminta pengguna untuk mengganti file yang hilang.

Pengecualian waktu proses (tidak dicentang)

Misalkan sebuah program mencoba membagi integer dengan integer 0. Ketidakmungkinan ini menggambarkan jenis pengecualian lain, yaitu pengecualian waktu proses . Tidak seperti pengecualian yang diperiksa, pengecualian waktu proses biasanya muncul dari kode sumber yang ditulis dengan buruk, dan karenanya harus diperbaiki oleh pemrogram. Karena compiler tidak memeriksa apakah pengecualian waktu proses ditangani atau didokumentasikan untuk ditangani di tempat lain, Anda dapat menganggap pengecualian waktu proses sebagai pengecualian yang tidak dicentang .

Tentang pengecualian waktu proses

Anda dapat mengubah program untuk menangani pengecualian waktu proses, tetapi lebih baik memperbaiki kode sumbernya. Pengecualian waktu proses sering muncul dari meneruskan argumen yang tidak valid ke metode perpustakaan; kode panggilan buggy harus diperbaiki.

Kesalahan

Beberapa pengecualian sangat serius karena membahayakan kemampuan program untuk melanjutkan eksekusi. Misalnya, program mencoba mengalokasikan memori dari JVM tetapi tidak ada cukup memori bebas untuk memenuhi permintaan tersebut. Situasi serius lainnya terjadi ketika sebuah program mencoba memuat file kelas melalui Class.forName()pemanggilan metode, tetapi file kelas tersebut rusak. Pengecualian semacam ini dikenal sebagai kesalahan . Anda tidak boleh mencoba menangani kesalahan sendiri karena JVM mungkin tidak dapat memulihkannya.

Pengecualian dalam kode sumber

Pengecualian dapat direpresentasikan dalam kode sumber sebagai kode kesalahan atau sebagai objek . Saya akan memperkenalkan keduanya dan menunjukkan kepada Anda mengapa objek lebih unggul.

Kode kesalahan versus objek

Bahasa pemrograman seperti C menggunakan kode kesalahan berbasis integer untuk merepresentasikan kegagalan dan alasan kegagalan - yaitu pengecualian. Berikut ini beberapa contohnya:

if (chdir("C:\\temp")) printf("Unable to change to temp directory: %d\n", errno); FILE *fp = fopen("C:\\temp\\foo"); if (fp == NULL) printf("Unable to open foo: %d\n", errno);

chdir()Fungsi C (direktori perubahan) mengembalikan integer: 0 saat berhasil atau -1 saat gagal. Demikian pula, fopen()fungsi C (file terbuka) mengembalikan pointer nonnull (alamat integer) ke FILEstruktur saat berhasil atau pointer nol (0) (diwakili oleh konstanta NULL) saat gagal. Dalam kedua kasus tersebut, untuk mengidentifikasi pengecualian yang menyebabkan kegagalan, Anda harus membaca errnokode kesalahan berbasis integer variabel global .

Kode kesalahan menghadirkan beberapa masalah:

  • Integer tidak ada artinya; mereka tidak menjelaskan pengecualian yang mereka wakili. Misalnya, apa artinya 6?
  • Mengaitkan konteks dengan kode kesalahan itu canggung. Misalnya, Anda mungkin ingin menampilkan nama file yang tidak dapat dibuka, tetapi di mana Anda akan menyimpan nama file?
  • Bilangan bulat bersifat arbitrer, yang dapat menyebabkan kebingungan saat membaca kode sumber. Misalnya, menentukan if (!chdir("C:\\temp"))( !menandakan TIDAK) daripada if (chdir("C:\\temp"))menguji kegagalan lebih jelas. Namun, 0 dipilih untuk menunjukkan keberhasilan, dan if (chdir("C:\\temp"))harus ditentukan untuk menguji kegagalan.
  • Kode kesalahan terlalu mudah diabaikan, yang dapat menyebabkan kode buggy. Misalnya, programmer dapat menentukan chdir("C:\\temp");dan mengabaikan if (fp == NULL)centang. Selanjutnya programmer tidak perlu meneliti errno. Dengan tidak menguji kegagalan, program berperilaku tidak menentu ketika salah satu fungsi mengembalikan indikator kegagalan.

Untuk mengatasi masalah ini, Java menggunakan pendekatan baru untuk penanganan pengecualian. Di Java, kami menggabungkan objek yang mendeskripsikan pengecualian dengan mekanisme berdasarkan melempar dan menangkap objek ini. Berikut beberapa keuntungan menggunakan objek versus kode kesalahan untuk menunjukkan pengecualian:

  • Sebuah objek dapat dibuat dari kelas dengan nama yang bermakna. Misalnya, FileNotFoundException(dalam java.iopaket) lebih berarti dari 6.
  • Objek dapat menyimpan konteks di berbagai bidang. Misalnya, Anda dapat menyimpan pesan, nama file yang tidak dapat dibuka, posisi terbaru di mana operasi parse gagal, dan / atau item lain di bidang objek.
  • Anda tidak menggunakan ifpernyataan untuk menguji kegagalan. Sebagai gantinya, objek pengecualian dilemparkan ke penangan yang terpisah dari kode program. Hasilnya, kode sumber lebih mudah dibaca dan cenderung tidak bermasalah.

Throwable dan subkelasnya

Java menyediakan hierarki kelas yang mewakili berbagai jenis pengecualian. Kelas-kelas ini berakar pada java.langpaket ini Throwablekelas, beserta Exception, RuntimeExceptiondan Errorsubclass.

Throwableadalah superkelas utama di mana pengecualian terkait. Hanya objek yang dibuat dari Throwabledan subkelasnya yang dapat dilempar (dan kemudian ditangkap). Benda-benda seperti itu dikenal sebagai lemparan .

Sebuah Throwableobjek dikaitkan dengan pesan rinci yang menjelaskan pengecualian. Beberapa konstruktor, termasuk pasangan yang dijelaskan di bawah, disediakan untuk membuat Throwableobjek dengan atau tanpa pesan detail:

  • Throwable () membuat Throwablepesan tanpa detail. Konstruktor ini sesuai untuk situasi di mana tidak ada konteks. Misalnya, Anda hanya ingin mengetahui bahwa tumpukan kosong atau penuh.
  • Throwable (pesan String) membuat Throwabledengan messagesebagai pesan detail. Pesan ini dapat menjadi keluaran ke pengguna dan / atau login.

Throwablemenyediakan String getMessage()metode untuk mengembalikan pesan detail. Ini juga menyediakan metode berguna tambahan, yang akan saya perkenalkan nanti.

Kelas Exception

Throwablememiliki dua subclass langsung. Salah satu subclass ini adalah Exception, yang menjelaskan pengecualian yang timbul dari faktor eksternal (seperti mencoba membaca dari file yang tidak ada). Exceptionmendeklarasikan konstruktor yang sama (dengan daftar parameter yang identik) sebagai Throwable, dan setiap konstruktor memanggil Throwablerekannya. Exceptionmewarisi Throwablemetode; itu menyatakan tidak ada metode baru.

Java menyediakan banyak kelas pengecualian yang secara langsung merupakan subkelas Exception. Berikut tiga contoh:

  • CloneNotSupportedException menandakan upaya untuk menggandakan objek yang kelasnya tidak mengimplementasikan Cloneableantarmuka. Kedua tipe tersebut ada di dalam java.langpaket.
  • IOException memberi sinyal bahwa beberapa jenis kegagalan I / O telah terjadi. Jenis ini terletak di dalam java.iopaket.
  • ParseException memberi sinyal bahwa telah terjadi kegagalan saat mengurai teks. Jenis ini dapat ditemukan di dalam java.textpaket.

Perhatikan bahwa setiap Exceptionnama subclass diakhiri dengan kata Exception. Konvensi ini memudahkan untuk mengidentifikasi tujuan kelas.

Biasanya Anda akan membuat subkelas Exception(atau salah satu subkelasnya) dengan kelas pengecualian Anda sendiri (yang namanya harus diakhiri dengan Exception). Berikut ini beberapa contoh subkelas khusus:

public class StackFullException extends Exception { } public class EmptyDirectoryException extends Exception { private String directoryName; public EmptyDirectoryException(String message, String directoryName) { super(message); this.directoryName = directoryName; } public String getDirectoryName() { return directoryName; } }

Contoh pertama menjelaskan kelas pengecualian yang tidak memerlukan pesan detail. Ini adalah pemanggilan konstruktor noargument default Exception(), yang memanggil Throwable().

Contoh kedua menjelaskan kelas pengecualian yang konstruktornya membutuhkan pesan detail dan nama direktori kosong. Konstruktor memanggil Exception(String message), yang memanggil Throwable(String message).

Objek yang dibuat dari Exceptionatau salah satu subkelasnya (kecuali untuk RuntimeExceptionatau salah satu subkelasnya) diperiksa pengecualiannya.

Kelas RuntimeException

Exceptionsecara langsung disubkelas oleh RuntimeException, yang menjelaskan pengecualian yang kemungkinan besar timbul dari kode yang ditulis dengan buruk. RuntimeExceptionmendeklarasikan konstruktor yang sama (dengan daftar parameter yang identik) sebagai Exception, dan setiap konstruktor memanggil Exceptionrekannya. RuntimeExceptionmewarisi Throwablemetode. Ini menyatakan tidak ada metode baru.

Java menyediakan banyak kelas pengecualian yang secara langsung merupakan subkelas RuntimeException. Contoh berikut adalah semua anggota java.langpaket:

  • ArithmeticException menandakan operasi aritmatika ilegal, seperti mencoba membagi integer dengan 0.
  • IllegalArgumentException memberi sinyal bahwa argumen ilegal atau tidak pantas telah diteruskan ke suatu metode.
  • NullPointerException memberi sinyal upaya untuk memanggil metode atau mengakses kolom instance melalui referensi null.

Objek yang dibuat dari RuntimeExceptionatau salah satu subkelasnya merupakan pengecualian yang tidak dicentang .

Kelas Error

Throwable's other direct subclass is Error, which describes a serious (even abnormal) problem that a reasonable application should not try to handle--such as running out of memory, overflowing the JVM's stack, or attempting to load a class that cannot be found. Like Exception, Error declares identical constructors to Throwable, inherits Throwable's methods, and doesn't declare any of its own methods.

You can identify Error subclasses from the convention that their class names end with Error. Examples include OutOfMemoryError, LinkageError, and StackOverflowError. All three types belong to the java.lang package.

Throwing exceptions

A C library function notifies calling code of an exception by setting the global errno variable to an error code and returning a failure code. In contrast, a Java method throws an object. Knowing how and when to throw exceptions is an essential aspect of effective Java programming. Throwing an exception involves two basic steps:

  1. Use the throw statement to throw an exception object.
  2. Use the throws clause to inform the compiler.

Later sections will focus on catching exceptions and cleaning up after them, but first let's learn more about throwables.

The throw statement

Java provides the throw statement to throw an object that describes an exception. Here's the syntax of the throw statement :

throw throwable;

The object identified by throwable is an instance of Throwable or any of its subclasses. However, you usually only throw objects instantiated from subclasses of Exception or RuntimeException. Here are a couple of examples:

throw new FileNotFoundException("unable to find file " + filename); throw new IllegalArgumentException("argument passed to count is less than zero");

The throwable is thrown from the current method to the JVM, which checks this method for a suitable handler. If not found, the JVM unwinds the method-call stack, looking for the closest calling method that can handle the exception described by the throwable. If it finds this method, it passes the throwable to the method's handler, whose code is executed to handle the exception. If no method is found to handle the exception, the JVM terminates with a suitable message.

The throws clause

You need to inform the compiler when you throw a checked exception out of a method. Do this by appending a throws clause to the method's header. This clause has the following syntax:

throws checkedExceptionClassName (, checkedExceptionClassName)*

A throws clause consists of keyword throws followed by a comma-separated list of the class names of checked exceptions thrown out of the method. Here is an example:

public static void main(String[] args) throws ClassNotFoundException { if (args.length != 1) { System.err.println("usage: java ... classfile"); return; } Class.forName(args[0]); }

This example attempts to load a classfile identified by a command-line argument. If Class.forName() cannot find the classfile, it throws a java.lang.ClassNotFoundException object, which is a checked exception.

Checked exception controversy

The throws clause and checked exceptions are controversial. Many developers hate being forced to specify throws or handle the checked exception(s). Learn more about this from my Are checked exceptions good or bad? blog post.