Javaの例外処理について

これはJavaアドベントカレンダーの5日目です。

最近例外処理について考えなければいけないことがあったので、こうしたほうが良いよというのをまとめます。
他の言語からJavaに移ってきた場合に微妙なところで変な感じになることがあるので。

ここで扱うJavaの例外というのはThrowableとそのサブクラスのことです。

Javaにはtry〜catch〜finallyという例外を扱うための構文があります。

try {

} catch(Throwable t) {


} finally {

}

Java7からはtry-with-resourcesや例外のマルチキャッチという新しい構文も増えました。
そちらについて詳しく知りたい人は[twitter:@skrb]さんの現場で使える[最新]Java SE 7/8 速攻入門をご参照お願いします。

現場で使える[最新]Java SE 7/8 速攻入門

現場で使える[最新]Java SE 7/8 速攻入門



Javaでの例外を扱うためのクラスは以下の様な階層構造をとっています。

Throwable
├─Error
└─Exception
  └─RuntimeException

それぞれざっと説明すると
・Throwable
すべての例外の基底クラスとなる。このクラスもしくはこのクラスのサブクラスを例外としてthrowすることができる。
・Error
JVMが正しく動作していない場合にこのクラスもしくはこのクラスのサブクラスがthrowされる。この例外が正しくthrowされた場合、多くの場合において、JVMは正しく動作していない。
・Exception
ExceptionもしくはExceptionのサブクラスかつ、RuntimeExceptionのサブクラスでない例外がthrowされるメソッドはメソッドシグネチャに例外が出力されることを記載しなければいけない。
・RuntimeException
Exceptionのサブクラスではあるが、メソッドのシグネチャに記載しなくても自由にthrowできる。

とまあ、こんな感じ。

さて、基本的な説明は置いておいて、原則として以下の3点を考慮しながら使用すればよいのではないかと思っています。
1.ErrorやそのサブクラスはJVMが使用するものなのでthrowしない、catchもしない。
2.Throwableを直接throwやcatchをするとErrorやそのサブクラスもcatchしてしまうので、直接は使用しない。
3.アプリケーションから個人的に投げるExceptionはExceptionかRuntimeExceptionもしくはそのサブクラスとする。

慣れてきて、ちゃんと使いたいなら以下の様なところを気をつけてみてください。
1.ExceptionやRuntimeExceptionは極力直接throwしない。catchもしない。例外というひとくくりで扱うよりも細かく場合分けしたほうが扱いやすい場合が多いです。
2.RuntimeExceptionやそのサブクラスをthrowする場合はドキュメントに必ず記載する。ドキュメントに記載していない例外については投げない。
3.try〜catch構文については性能上特に影響が出ることはないので、どんどん使っても良い*1
4.例外のオブジェクト生成にはコストがかかるのでピーキーなチューニングが必要な場合は行わないほうが良い*2


最近、Javaが見直されていることもあり他言語を書いていたもののJavaを書き始めたという人も多いと思います。
他の言語と例外の名前が似てはいるものの微妙に違うことから使い間違えてる例を見ることが増えたので書いてみました。
それでは良いJavaライフを!

*1:厳密には影響が出るがJITで消えてしまう程度。参考:http://d.hatena.ne.jp/chiheisen/20111113/1321188865

*2:スタックトレースのためのエレメントの生成にコストがかかる。とはいっても9割9分のアプリケーションでは気にする必要はない程度です。