Revised: May/19th/2005; Since: Dec./14th/2003
クラス RuntimeException から派生したクラス型の例外は例外処理が任意で、非チェック例外 (unchecked exception) と呼ばれます。クラス Exception から派生したクラスのなかで、 RuntimeException から派生するものを除いたクラス型の例外は例外処理が必須で、チェック例外 (checked exception) と呼ばれます。
java.lang.Throwable
java.lang.Error
...例外処理できないアプリケーション外部で発生する深刻なエラー。java.lang.Exception
java.lang.RuntimeException
を継承する例外...例外処理任意(非チェック例外)。java.lang.RuntimeException
を継承しない例外...例外処理必須(チェック例外)。また、RuntimeException の場合は、throws リストに明記しなくても、自動的に呼び出し元にスローされます。例外処理が必須である Exception が発生すれ可能性のある処理の場合は、当該の処理を try catch
でキャッチするか、当該の処理を含むメソッドの throws リストに明記しなければコンパイル・エラーとなります。
修飾子 戻り値型 メソッド(引数リスト) { try { // 例外が発生する処理 } catch (例外型 変数) { // 例外が発生した後の処理 } finally { // 例外が発生してもしなくても実行する処理 } }
修飾子 戻り値型 メソッド(引数リスト) throws 例外型リスト { // 例外が発生する処理 }
プログラムの中で、チェック例外と非チェック例外の何れを使うのかについての明確な指針はありません。コア・パッケージの中で投げられる例外についても、呼び出し元で try-catchを用いてラップすることで、何れかに変更できます。
try { // チェック例外IOExceptionが発生するかもしれない処理 FileReader obj = new FileReader("filename.txt");} } catch (IOException e) { // IOExceptionを捕捉して非チェック例外RuntimeExceptionでラップしてスロー throw new RuntimeException(e.toString()); }
assertion
で実装できます。いずれの場合も、例外オブジェクトは、メソッドの処理内容に基づく例外オブジェクトでラップして仮想化しておくことが好ましい実装です。例えば、チェック例外であるSQLException, IOException, FileNotFoundExeption, SocketExceptionなどを補足せずにそのまま投げる場合は、処理内容と直接の関係がない、低レベル実装によって、スローされる例外が変わってしまいます。実装の修正によって、API(外部インタフェース)を変えず、修正範囲を該当クラス内に押さえ込むためには、捕捉して処理内容に関する例外でラップして仮想化する必要があります。実装を変更したら、ラップする部分も変更して、外部インタフェースとなる例外オブジェクトは変更しないようにできます。
例外処理を全くしないという選択もあります。このときは、全てを Exception
でスローし、ロギング以外の例外処理を施しません。但し、コードで強制する仕組みを放棄し、文書化も不可能になるので、一時的にリリースするようなコード(スタブ、テストコード、過渡的コード)以外では現実的ではありません。
ここでは、throws リストと try catch ブロックの組み合わせについて考えてみます。パターンとして考えなければならないのは、例外処理が任意な RuntimeException 系列の例外(非チェック例外)と、例外処理が必須な Exception 系列の例外(チェック例外)の処理です。
RuntimeException を継承する例外は、なにもしなくてもコンパイルを通ります。
次の例は、RuntimeException から派生した ArithmeticException が発生する例です。例外処理をまったく施していませんが、コンパイルを通ります。しかし、実行時に例外が発生しているため、実行するとプログラムが終了してしまいます。
class ThrowsRuntimeException { void excep() { int i = 10/0; // ArithmeticException } } class ThrowsRuntimeExceptionDemo { public static void main(String[] args) { ThrowsRuntimeException obj = new ThrowsRuntimeException(); obj.excep(); } }
C:\java>javac ThrowsRuntimeExceptionDemo.java C:\java>java ThrowsRuntimeExceptionDemo Exception in thread "main" java.lang.ArithmeticException: / by zero at ThrowsRuntimeException.excep(ThrowsRuntimeExceptionDemo.java:3) at ThrowsRuntimeExceptionDemo.main(ThrowsRuntimeExceptionDemo.java:10)
出力されている情報は、スタック・トレースと呼ばれるもので、Java 仮想マシン・スタック情報がプリントされています。下から順番に読んで行くと、「最初に main() メソッドが呼ばれ、そこから excep() メソッドが呼ばれて実行され、そこで ArithmeticException が発生しました」と読めます。スタックに対するトレース情報が入手できます。
次の例は、 RuntimeException を明示的に throws リストに指定したものです。起動するメソッドで明示的に throw しても、RuntimeException の系列の例外であれば、呼び出し元で例外処理を実施しなくとも、コンパイルは通ります。実行時には、例外が発生するので、そこで中断してプログラムが終了してしまいます。
class ThrowsRuntimeException2 { void excep() throws ArithmeticException { int i = 10/0; // ArithmeticException } } class ThrowsRuntimeExceptionDemo2 { public static void main(String[] args) { ThrowsRuntimeException2 obj = new ThrowsRuntimeException2(); obj.excep(); } }
C:\java>javac ThrowsRuntimeExceptionDemo2.java C:\java>java ThrowsRuntimeExceptionDemo2 Exception in thread "main" java.lang.ArithmeticException: / by zero at ThrowsRuntimeException2.excep(ThrowsRuntimeExceptionDemo2.java:3) at ThrowsRuntimeExceptionDemo2.main(ThrowsRuntimeExceptionDemo2.java:10)
最初の例で見たとおり、 RuntimeException は、明示的に throws リストに加えなくても、自動的に呼び出し元に投げられています。これを、 try catch で捕まえることができます。
class ThrowsRuntimeException3 { void excep() { int i = 10/0; // ArithmeticException } } class ThrowsRuntimeExceptionDemo3 { public static void main(String[] args) { ThrowsRuntimeException3 obj = new ThrowsRuntimeException3(); try { obj.excep(); } catch (ArithmeticException e) { System.out.println("算術例外が発生しているようです。"); e.printStackTrace(); } System.out.println("例外処理はうまく行きましたか?"); } }
C:\java>javac ThrowsRuntimeExceptionDemo3.java C:\java>java ThrowsRuntimeExceptionDemo3 算術例外が発生しているようです。 java.lang.ArithmeticException: / by zero at ThrowsRuntimeException3.excep(ThrowsRuntimeExceptionDemo3.java:3) at ThrowsRuntimeExceptionDemo3.main(ThrowsRuntimeExceptionDemo3.java:11) 例外処理はうまく行きましたか?
次の例は、例外処理が必須な Exception が発生する可能性のある例です。例外処理を施していないので、コンパイルを通りません。
import java.io.*; class ThrowsException { void excep() { // FileNotFoundException が発生する可能性がある // 例外処理は必須 FileReader obj = new FileReader("filename.txt"); } } class ThrowsExceptionDemo { public static void main(String[] args) { ThrowsException obj = new ThrowsException(); obj.excep(); } }
C:\java>javac ThrowsExceptionDemo.java ThrowsExceptionDemo.java:5: 例外 java.io.FileNotFoundException は報告されません 。スローするにはキャッチまたは、スロー宣言をしなければなりません。 FileReader obj = new FileReader("filename.txt"); ^ エラー 1 個
Exception を try catch で処理した例です。
import java.io.*; class ThrowsException2 { void excep() { try { FileReader obj = new FileReader("filename.txt"); } catch (FileNotFoundException e) { System.out.println("ファイルが見つかりません。"); e.printStackTrace(); } } } class ThrowsExceptionDemo2 { public static void main(String[] args) { ThrowsException2 obj = new ThrowsException2(); obj.excep(); System.out.println("例外処理はうまく行きましたか?"); } }
C:\java>javac ThrowsExceptionDemo2.java C:\java>java ThrowsExceptionDemo2 ファイルが見つかりません。 java.io.FileNotFoundException: filename.txt (指定されたファイルが見つかりません。) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:103) at java.io.FileInputStream. (FileInputStream.java:66) at java.io.FileReader. (FileReader.java:41) at ThrowsException2.excep(ThrowsExceptionDemo2.java:6) at ThrowsExceptionDemo2.main(ThrowsExceptionDemo2.java:17) 例外処理はうまく行きましたか?
次の例は、例外処理が必須であるメソッドに throws リストを指定した例です。呼び出し元で例外処理を実施していないので、コンパイルを通りません。
import java.io.*; class ThrowsException3 { void excep() throws FileNotFoundException { FileReader obj = new FileReader("filename.txt"); } } class ThrowsExceptionDemo3 { public static void main(String[] args) { ThrowsException3 obj = new ThrowsException3(); obj.excep(); System.out.println("例外処理はうまく行きましたか?"); } }
C:\java>javac ThrowsExceptionDemo3.java ThrowsExceptionDemo3.java:12: 例外 java.io.FileNotFoundException は報告されませ ん。スローするにはキャッチまたは、スロー宣言をしなければなりません。 obj.excep(); ^ エラー 1 個
例外処理が必須な Exception を発生する可能性がある処理を含んだメソッドには throws リストを指定して、呼び出し元では try catch で例外処理を実施する例です。
import java.io.*; class ThrowsException4 { void excep() throws FileNotFoundException { FileReader obj = new FileReader("filename.txt"); } } class ThrowsExceptionDemo4 { public static void main(String[] args) { ThrowsException4 obj = new ThrowsException4(); try { obj.excep(); } catch (FileNotFoundException e) { System.out.println("ファイルがみつかりません。"); e.printStackTrace(); } System.out.println("例外処理はうまく行きましたか?"); } }
C:\java>javac ThrowsExceptionDemo4.java C:\java>java ThrowsExceptionDemo4 ファイルがみつかりません。 java.io.FileNotFoundException: filename.txt (指定されたファイルが見つかりません。) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:103) at java.io.FileInputStream. (FileInputStream.java:66) at java.io.FileReader. (FileReader.java:41) at ThrowsException4.excep(ThrowsExceptionDemo4.java:5) at ThrowsExceptionDemo4.main(ThrowsExceptionDemo4.java:13) 例外処理はうまく行きましたか?