throws と try catch

Revised: May/19th/2005; Since: Dec./14th/2003

throws リストと例外処理必須例外

クラス RuntimeException から派生したクラス型の例外は例外処理が任意で、非チェック例外 (unchecked exception) と呼ばれます。クラス Exception から派生したクラスのなかで、 RuntimeException から派生するものを除いたクラス型の例外は例外処理が必須で、チェック例外 (checked exception) と呼ばれます。

また、RuntimeException の場合は、throws リストに明記しなくても、自動的に呼び出し元にスローされます。例外処理が必須である Exception が発生すれ可能性のある処理の場合は、当該の処理を try catch でキャッチするか、当該の処理を含むメソッドの throws リストに明記しなければコンパイル・エラーとなります。

try catch の場合:自力で例外処理

修飾子 戻り値型 メソッド(引数リスト) {
	try {
		// 例外が発生する処理
	} catch (例外型 変数) {
		// 例外が発生した後の処理
	} finally {
		// 例外が発生してもしなくても実行する処理
	}
}

throws の場合:自分を呼び出したメソッドに例外処理を委譲

修飾子 戻り値型 メソッド(引数リスト) throws 例外型リスト {
	// 例外が発生する処理
}

チェック例外と非チェック例外

プログラムの中で、チェック例外と非チェック例外の何れを使うのかについての明確な指針はありません。コア・パッケージの中で投げられる例外についても、呼び出し元で try-catchを用いてラップすることで、何れかに変更できます。

try {
	// チェック例外IOExceptionが発生するかもしれない処理
	FileReader obj = new FileReader("filename.txt");}
} catch (IOException e) {
	// IOExceptionを捕捉して非チェック例外RuntimeExceptionでラップしてスロー
	throw new RuntimeException(e.toString());
}
全てチェック例外 (checked exception) とする
チェック例外は、呼び出し元で捕捉するか、さらに上位に投げるかしなければならないので、コード中に明文化することを強制できます。但し、例外処理の不要なネストや、メソッドの内容からは不適切な実装レベルの例外がスローされて、呼び出し元が混乱することがあります。また、補足しても異常終了するほかない場合は、捕捉のコードは全くの無駄です。
全て非チェック例外 (unchecked exception) とする
非チェック例外は、例外処理が任意なので、コンパイルエラーが減り、呼び出し元のコードが状況に応じて判断できます。但し、一番下の階層で発生した非チェック例外が、呼び出し元に届かない危険があり、JavaDocなどの仕様書類に明文化されない可能性があります。結果的に関係するクラスの責任者の属人的能力に頼ることになります。
チェック例外にするものと非チェック例外にするものについて取り決める

例外の仮想化

いずれの場合も、例外オブジェクトは、メソッドの処理内容に基づく例外オブジェクトでラップして仮想化しておくことが好ましい実装です。例えば、チェック例外であるSQLException, IOException, FileNotFoundExeption, SocketExceptionなどを補足せずにそのまま投げる場合は、処理内容と直接の関係がない、低レベル実装によって、スローされる例外が変わってしまいます。実装の修正によって、API(外部インタフェース)を変えず、修正範囲を該当クラス内に押さえ込むためには、捕捉して処理内容に関する例外でラップして仮想化する必要があります。実装を変更したら、ラップする部分も変更して、外部インタフェースとなる例外オブジェクトは変更しないようにできます。

例外処理を全くしないという選択もあります。このときは、全てを Exception でスローし、ロギング以外の例外処理を施しません。但し、コードで強制する仕組みを放棄し、文書化も不可能になるので、一時的にリリースするようなコード(スタブ、テストコード、過渡的コード)以外では現実的ではありません。

例外処理の例

ここでは、throws リストと try catch ブロックの組み合わせについて考えてみます。パターンとして考えなければならないのは、例外処理が任意な RuntimeException 系列の例外(非チェック例外)と、例外処理が必須な Exception 系列の例外(チェック例外)の処理です。

RuntimeException

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

次の例は、 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 と try catch

最初の例で見たとおり、 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

次の例は、例外処理が必須な 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

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)
例外処理はうまく行きましたか?

Exception と throws

次の例は、例外処理が必須であるメソッドに 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

例外処理が必須な 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)
例外処理はうまく行きましたか?


Copyright © 2003-2005 SUGAI, Manabu. All Rights Reserved.
SEO [PR] !uO z[y[WJ Cu