Pengecualian di Java, Bahagian 1: Asas pengendalian pengecualian

Pengecualian Java adalah jenis perpustakaan dan ciri bahasa yang digunakan untuk mewakili dan menangani kegagalan program. Sekiranya anda ingin memahami bagaimana kegagalan ditunjukkan dalam kod sumber, anda telah sampai di tempat yang betul. Sebagai tambahan kepada gambaran umum pengecualian Java, saya akan memulakan anda dengan fitur bahasa Java untuk melempar objek, mencuba kod yang mungkin gagal, menangkap objek yang dilemparkan, dan membersihkan kod Java anda setelah pengecualian dilemparkan.

Pada separuh pertama tutorial ini, anda akan belajar mengenai ciri bahasa asas dan jenis perpustakaan yang ada sejak Java 1.0. Pada separuh masa kedua, anda akan menemui keupayaan maju yang diperkenalkan dalam versi Java yang lebih baru.

Perhatikan bahawa contoh kod dalam tutorial ini sesuai dengan JDK 12.

muat turun Dapatkan kod Muat turun kod sumber misalnya aplikasi dalam tutorial ini. Dicipta oleh Jeff Friesen untuk JavaWorld.

Apakah pengecualian Java?

Kegagalan berlaku apabila tingkah laku normal program Java terganggu oleh tingkah laku yang tidak dijangka. Perbezaan ini dikenali sebagai pengecualian . Sebagai contoh, program cuba membuka fail untuk membaca kandungannya, tetapi fail itu tidak ada. Java mengklasifikasikan pengecualian kepada beberapa jenis, jadi mari kita pertimbangkan masing-masing.

Pengecualian yang diperiksa

Java mengklasifikasikan pengecualian yang timbul dari faktor luaran (seperti fail yang hilang) sebagai pengecualian yang diperiksa . Penyusun Java memeriksa bahawa pengecualian tersebut ditangani (diperbetulkan) di mana ia berlaku atau didokumentasikan untuk ditangani di tempat lain.

Pengendali pengecualian

An pengendali pengecualian adalah urutan kod yang mengendalikan pengecualian. Ini menginterogasi konteks - yang bermaksud membaca nilai yang disimpan dari pemboleh ubah yang berada dalam skop pada saat pengecualian terjadi - kemudian menggunakan apa yang dipelajarinya untuk mengembalikan program Java ke aliran tingkah laku normal. Sebagai contoh, pengendali pengecualian mungkin membaca nama fail yang disimpan dan memakaikan pengguna untuk menggantikan fail yang hilang.

Pengecualian waktu jalan (tidak dicentang)

Andaikan program cuba membahagi bilangan bulat dengan bilangan bulat 0. Kemustahilan ini menggambarkan jenis pengecualian lain, iaitu pengecualian runtime . Tidak seperti pengecualian yang diperiksa, pengecualian runtime biasanya timbul dari kod sumber yang ditulis dengan kurang baik, dan oleh itu harus diperbaiki oleh pengaturcara. Oleh kerana penyusun tidak memastikan pengecualian runtime dikendalikan atau didokumentasikan untuk dikendalikan di tempat lain, anda boleh menganggap pengecualian runtime sebagai pengecualian yang tidak dicentang .

Mengenai pengecualian runtime

Anda mungkin mengubahsuai program untuk menangani pengecualian runtime, tetapi lebih baik memperbaiki kod sumber. Pengecualian waktu jalan sering timbul daripada menyampaikan argumen yang tidak sah ke kaedah perpustakaan; kod panggilan kereta harus diperbaiki.

Kesalahan

Beberapa pengecualian sangat serius kerana menjejaskan kemampuan program untuk meneruskan pelaksanaan. Sebagai contoh, program cuba mengalokasikan memori dari JVM tetapi tidak cukup memori percuma untuk memenuhi permintaan. Situasi serius lain berlaku apabila program cuba memuatkan classfile melalui Class.forName()kaedah panggilan, tetapi classfile itu rosak. Pengecualian seperti ini dikenali sebagai kesalahan . Anda tidak boleh cuba mengatasi kesilapan sendiri kerana JVM mungkin tidak dapat memulihkannya.

Pengecualian dalam kod sumber

Pengecualian dapat ditunjukkan dalam kod sumber sebagai kod kesalahan atau sebagai objek . Saya akan memperkenalkan kedua-duanya dan menunjukkan kepada anda mengapa objek lebih unggul.

Kod ralat berbanding objek

Bahasa pengaturcaraan seperti C menggunakan kod ralat berdasarkan integer untuk mewakili kegagalan dan sebab kegagalan - iaitu, pengecualian. Berikut adalah beberapa contoh:

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 (ubah direktori) mengembalikan bilangan bulat: 0 pada kejayaan atau -1 pada kegagalan. Begitu juga, fopen()fungsi C (buka fail) mengembalikan penunjuk nonnull (alamat integer) ke FILEstruktur kejayaan atau penunjuk nol (0) (diwakili oleh pemalar NULL) pada kegagalan. Dalam kedua-dua kes, untuk mengenal pasti pengecualian yang menyebabkan kegagalan, anda mesti membaca errnokod ralat integer pembolehubah global .

Kod ralat menimbulkan beberapa masalah:

  • Integer tidak bermakna; mereka tidak menggambarkan pengecualian yang mereka wakili. Contohnya, apa maksud 6?
  • Mengaitkan konteks dengan kod ralat adalah canggung. Sebagai contoh, anda mungkin ingin mengeluarkan nama fail yang tidak dapat dibuka, tetapi di mana anda akan menyimpan nama fail tersebut?
  • Integer sewenang-wenangnya, yang boleh menimbulkan kekeliruan ketika membaca kod sumber. Sebagai contoh, menentukan if (!chdir("C:\\temp"))( !menandakan TIDAK) dan bukannya if (chdir("C:\\temp"))menguji kegagalan lebih jelas. Walau bagaimanapun, 0 dipilih untuk menunjukkan kejayaan, dan if (chdir("C:\\temp"))harus ditentukan untuk menguji kegagalan.
  • Kod ralat terlalu mudah untuk diabaikan, yang boleh menyebabkan kod kereta. Sebagai contoh, pengaturcara dapat menentukan chdir("C:\\temp");dan mengabaikan if (fp == NULL)cek. Tambahan pula, pengaturcara tidak perlu memeriksa errno. Dengan tidak menguji kegagalan, program berperilaku tidak menentu apabila salah satu fungsi mengembalikan petunjuk kegagalan.

Untuk menyelesaikan masalah ini, Java menerapkan pendekatan baru untuk menangani pengecualian. Di Jawa, kami menggabungkan objek yang menggambarkan pengecualian dengan mekanisme berdasarkan melempar dan menangkap objek ini. Berikut adalah beberapa kelebihan menggunakan objek berbanding kod ralat untuk menunjukkan pengecualian:

  • Objek boleh dibuat dari kelas dengan nama yang bermakna. Contohnya, FileNotFoundException(dalam java.iobungkusan) lebih bermakna daripada 6.
  • Objek dapat menyimpan konteks dalam pelbagai bidang. Sebagai contoh, anda boleh menyimpan mesej, nama fail yang tidak dapat dibuka, kedudukan terbaru di mana operasi penguraian gagal, dan / atau item lain di bidang objek.
  • Anda tidak menggunakan ifpernyataan untuk menguji kegagalan. Sebaliknya, objek pengecualian dilemparkan ke pengendali yang terpisah dari kod program. Kesannya, kod sumber lebih senang dibaca dan cenderung tidak bermasalah.

Lempar dan subkelasnya

Java menyediakan hierarki kelas yang mewakili pelbagai jenis pengecualian. Kelas-kelas ini berakar umbi dalam java.langpakej ini Throwablekelas, bersama-sama dengan mereka Exception, RuntimeExceptiondan Errorsubkelas.

Throwableadalah superclass utama yang berkenaan dengan pengecualian. Hanya objek yang dibuat dari Throwabledan subkelasnya yang dapat dilemparkan (dan kemudian ditangkap). Objek seperti itu dikenali sebagai lempar .

A Throwableobjek dikaitkan dengan mesej detail yang menerangkan pengecualian. Beberapa pembina, termasuk pasangan yang dijelaskan di bawah, disediakan untuk membuat Throwableobjek dengan atau tanpa mesej terperinci:

  • Throwable () mencipta mesej Throwabletanpa perincian. Pembina ini sesuai untuk situasi di mana tidak ada konteks. Contohnya, anda hanya ingin mengetahui bahawa timbunan kosong atau penuh.
  • Throwable (String message) membuat Throwabledengan messagesebagai mesej perincian. Mesej ini dapat dikeluarkan kepada pengguna dan / atau dicatat.

Throwablemenyediakan String getMessage()kaedah untuk mengembalikan mesej terperinci. Ia juga menyediakan kaedah berguna tambahan, yang akan saya perkenalkan kemudian.

Kelas Pengecualian

Throwablemempunyai dua subkelas langsung. Salah satu subkelas ini adalah Exception, yang menerangkan pengecualian yang timbul dari faktor luaran (seperti cuba membaca dari fail yang tidak ada). Exceptionmenyatakan pembina yang sama (dengan senarai parameter yang sama) dengan Throwable, dan setiap pembina meminta Throwablerakan sejawatnya. kaedah Exceptionmewarisi Throwable; ia menyatakan tidak ada kaedah baru.

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

  • CloneNotSupportedException memberi isyarat percubaan untuk mengklon objek yang kelasnya tidak melaksanakan Cloneableantara muka. Kedua-dua jenis terdapat dalam java.langbungkusan.
  • IOException memberi isyarat bahawa beberapa jenis kegagalan I / O telah berlaku. Jenis ini terdapat dalam java.iobungkusan.
  • ParseException memberi isyarat bahawa kegagalan telah berlaku semasa menguraikan teks. Jenis ini boleh didapati dalam java.textbungkusan.

Perhatikan bahawa setiap Exceptionnama subkelas berakhir dengan perkataan Exception. Konvensyen ini memudahkan untuk mengenal pasti tujuan kelas.

Anda biasanya akan subkelas Exception(atau salah satu subkelasnya) dengan kelas pengecualian anda sendiri (yang namanya harus diakhiri dengan Exception). Berikut adalah beberapa contoh subkelas tersuai:

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 menerangkan kelas pengecualian yang tidak memerlukan mesej terperinci. Ini adalah pemanggil pembangun noargument lalai Exception(), yang memanggil Throwable().

Contoh kedua menerangkan kelas pengecualian yang konstruktornya memerlukan mesej terperinci dan nama direktori kosong. Pembina meminta Exception(String message), yang memanggil Throwable(String message).

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

Kelas RuntimeException

Exceptionsecara langsung RuntimeExceptiondiklasifikasikan oleh , yang menerangkan pengecualian kemungkinan besar disebabkan oleh kod yang kurang ditulis. RuntimeExceptionmenyatakan pembina yang sama (dengan senarai parameter yang sama) dengan Exception, dan setiap pembina meminta Exceptionrakan sejawatnya. kaedah RuntimeExceptionmewarisi Throwable. Ia menyatakan tidak ada kaedah baru.

Java menyediakan banyak kelas pengecualian yang secara langsung subkelas RuntimeException. Contoh berikut adalah semua ahli java.langpakej:

  • ArithmeticException menandakan operasi aritmetik yang tidak sah, seperti cuba membahagi bilangan bulat dengan 0.
  • IllegalArgumentException memberi isyarat bahawa hujah yang tidak sah atau tidak sesuai telah disampaikan kepada kaedah.
  • NullPointerException memberi isyarat percubaan untuk menggunakan kaedah atau mengakses medan contoh melalui rujukan nol.

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

Kelas Ralat

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.