今まで例外処理として、try-catch、throws等について、理解されていないままプロジェクトに参画されている方を結構見てきました。
これからプロジェクトへ参画される方や、実は自分もよくわからないまま実装しちゃってる・・・って方向けに例外処理の実装方法を簡単に説明します。
目次
1.例外クラス
2.検査例外(checked例外)
3.非検査例外(unchecked例外)
4.例外処理
5.まとめ
1. 例外クラス
プログラム実行中に例外が発生すると、本来想定しているプログラムがそこで一旦終了され、実装した例外処理が実行されます。
Javaの、例外クラス(Throwable)は、大きく分けて、以下二つの例外に分類されます。
- 検査例外(checked例外)
- 非検査例外(unchecked例外)
それぞれの例外については後述で説明しますが、対象となるクラスは以下となります。
検査例外
- RuntimeExceptionを除く、Exceptionのサブクラス
非検査例外
- RuntimeExceptionのサブクラス
- Errorのサブクラス
図で表すと以下のイメージです。
2. 検査例外(checked例外)
コンパイラが例外処理を実装しているか検査する例外。
例外処理の実装が必須。(例外処理を実装しないとコンパイルエラーになる)
検査例外(checked例外)の対象クラスの例
3. 非検査例外(unchecked例外)
コンパイラが例外処理を実装しているか検査しない例外。
例外処理の実装が任意。(例外処理を実装していなくてもコンパイルエラーにならない)
非検査例外(unchecked例外)の対象クラスの例
Errorのサブクラス例
RuntimeExceptionのサブクラス例
- ArithmeticException
- ClassCastException
- DateTimeException
- ArrayIndexOutOfBoundsException
- NullPointerException
- NumberFormatException
4. 例外処理
検査例外をthrowするメソッドを利用する場合、例外処理を実装しないと、コンパイルエラーになってしまいます。
尚、例外のthrowとは、例外が投げられる(throw:スロー)ことを指します。
javaのthrowは、その処理の呼び出し元に対して例外を投げます。
以降、FileReaderクラスを例に説明していきます。
throwされる可能性がある例外クラス
FileReaderのコンストラクタを利用すると、FileNotFoundExceptionがthrowされる可能性があります。
throws句の後続に記載されている、FileNotFoundExceptionが、throwされる可能性がある例外クラスです。
public FileReader(String fileName)
throws FileNotFoundException
このように、throws句で定義された例外クラスを、利用する側で例外処理をしてあげる必要があります。
例外処理の実装方法
例外処理を行うには、以下の2択となります。
- try-catch句で処理する
- try-catch句を使わずにthrowsに定義する
try-catch句で処理する
public void hoge() {
try {
FileReader fileReader = new FileReader("/hoge.txt");
System.out.println("Success");
} catch (FileNotFoundException ex) {
ex.printStackTrace();
System.out.println("Failed");
}
System.out.println("Finish!!");
}
以下の箇所で、FileNotFoundException
がthrowされる可能性があるので、try-catchで囲ってあげます。
FileReader fileReader = new FileReader("/hoge.txt");
try句
例外が発生する可能性がある箇所を、tryブロックで囲います。
try {
FileReader fileReader = new FileReader("/hoge.txt");
System.out.println("Success");
}
System.out.println("Success")
で、FileNotFoundExceptionは発生しませんが、new FileReader("/hoge.txt")
で、FileNotFoundExceptionが発生しなかった場合のみ、実行させるために一緒にtryブロックにいれてあげます。
catch句
tryブロックの中で、発生した例外をcatch(キャッチ)した際の処理を記述します。
catch (FileNotFoundException ex) {
ex.printStackTrace();
System.out.println("Failed");
}
tryブロックの中で、FileNotFoundExceptionが発生したら、ここへすぐさま飛んできて、ブロック内の処理を実行します。
実際の運用では、ログの出力や、DBのcommitやrollbackをさせたりします。
この例ではhogeメソッドの呼び出し元からは、以下が実行され、例外が発生した事実はわかりません。
- コンソールにStackTrace(例外の情報)を出力
- コンソールに、
Failed
を出力
なお、例外が発生しなかった場合、catchブロック内の処理は実行されません。
もし、呼び出し元に、例外が発生したことを通知するために、対象の例外を同様にthrowする場合は以下のようになります。
public void hoge() throws FileNotFoundException { // throws句を追加
try {
FileReader fileReader = new FileReader("/hoge.txt");
System.out.println("Success");
} catch (FileNotFoundException ex) {
ex.printStackTrace();
System.out.println("Failed");
throw ex; // catchした例外をthrow
}
System.out.println("Finish!!");
}
catchした、例外を呼び出し元へ例外を通知する場合は、以下のようにcatchした例外をthrowします。
throw ex;
そうすると、このメソッドはFileNotFoundException
をthrowする可能性のあるメソッドとなるため、メソッド定義としてthrows句を追加します。
public void hoge() throws FileNotFoundException {
こうすることで、hogeメソッドは、FileNotFoundException
をthrowするメソッドとなります。
finally句で、正常時・異常時でも必ず処理させたい
上記の例では、例外が発生して、例外をthrowすると、後続の処理が続行されずに、
System.out.println("Finish!!");
が実行されません。
例外が発生しても、していなくても、実行させたい処理がある場合は、finallyブロック内に処理を記載してあげます。
finally {
System.out.println("Finish!!");
}
こうすることで、例外が発生しても、していなくても最終的に、必ずSystem.out.println("Finish!!")
が実行されます。
流れを表すとこんな感じ。
例外が発生した場合
例外が発生しなかった場合(正常系)
try-catch句を使わずにthrowsに定義する
public void hoge() throws FileNotFoundException {
FileReader fileReader = new FileReader("/hoge.txt");
System.out.println("Success");
System.out.println("Finish!!");
}
この書き方は、try-catch句を使わずに、throws FileNotFoundException
のみを定義します。
こうすることで、new FileReader("/hoge.txt")
で、FileNotFoundException
が発生した際に、すぐさまメソッドを終了し、呼び出し元へFileNotFoundException
をthrowします。
Success
のコンソール出力は実行されません。
全く使わないということはないですが、例外が発生した際に、適切なログ出力を施すことを考えると現実的にはtry-catch句で例外処理を行った上で、throwしてあげたりするのが現実出来かと思います。
5. まとめ
- 検査例外(RuntimeExceptionを除くExceptionのサブクラス)がthrowされるメソッドは、例外処理を施す
- 例外処理はtry-catch句で処理するか、throws句に定義させる
- try-catchでcatchさせたとしても、throwさせる場合は、throws句も定義する