S-JIS[2003-07-06/2020-09-29] 変更履歴

Javaの例外


例外とは

Javaでは、プログラム実行中に異常事態が発生してそれ以上正常に実行できない場合に、“例外(Exception)”と呼ばれるオブジェクトを作成し、メソッドの呼び出し元に渡す(返す)ことが出来る。[2012-11-24]

つまりJavaで言う「例外」とは、一般的な文章で使う「それは例外事項だ」「通常ではない」というようなニュアンスではなく、「Exception」(というクラス・オブジェクト)を表す名詞(専門用語)である。

メソッドでは 処理が正常に終わる場合はreturn文によって値を返すが、例外オブジェクトを返す場合はthrow文という専用の構文を使う。
「throw」は「投げる」という意味なので、例外オブジェクトを返すことを「例外を投げる」「例外をスローする」という言い方をする。
あるいは「例外を発生させる」「例外が発生する」という言い方をすることもある。

また、投げられた(スローされた)例外オブジェクトを受け取る構文としてtry〜catch文がある。
こちらは「catch」なので、「例外をキャッチする」「例外を捕捉する」という言い方をする。


例外とエラー

厳密には、Javaでスローすることが出来るのはThrowableというクラス(を継承したクラス)のオブジェクトである。[2012-11-24]
Throwableの直系のサブクラスにException(例外)とError(エラー)がある。
(C++はスローする為の専用の型というものは無く、数値でも何でもスローできる)

「エラー」はJavaランタイムシステムが発生させるもの(メモリー不足の時とか)なので、普通のプログラマーが直接扱うことは無い。
(とりあえずは、そういうものがあると知っていれば良い)


よく見る例外一覧

例外 説明 備考 更新日
java.io.InvalidClassException: クラス名; no valid constructor アクセス可能なデフォルトコンストラクター(引数なしコンストラクター)が無い。 シリアライズ 2007-06-13
java.io.IOException: invalid header field jarファイル生成時に、マニフェストファイルの属性指定が間違っている。 実行可能jarファイル 2007-01-09
java.io.NotSerializableException Serializableが正しく実装されていない。 シリアライズ 2004-06-12
java.lang.AssertionError assertに引っかかった。 assertの使い所 2004-06-12
java.lang.ClassCastException
JRE メッセージ
1.4.2 (メッセージ無し)
1.5 FromClass
1.6 FromClass cannot be cast to ToClass
変換できないクラスへキャストしようとした。
JDK1.4.2では何のクラスから何のクラスへキャストしようとしたかはメッセージに含まれず。
JDK1.5では何のクラスからキャストしようとしたかだけ含まれる(何のクラスへ、はソースを見る)。
JDK1.6ではどちらもメッセージに含まれるようになった。
  2007-02-28
java.lang.CloneNotSupportedException clone()メソッドをオーバーライドしているが、Cloneableをimplementsしていない時に発生する。
clone()をオーバーライドしたらthrowsから外してしまう方が便利。
clone() 2009-04-25
java.lang.IllegalArgumentException メソッドに渡された引数が想定外の値のときにこの例外をスローする。 類似:未サポート操作
使い方
2007-11-15
java.lang.IncompatibleClassChangeError AというクラスがBというクラスのメソッドを呼び出しているとき、Bのメソッドを修正してBのみコンパイルしてAが古いままだったりすると、AからBのメソッドを呼び出す時にこの例外が発生するらしい。   2003-07-06
java.lang.InternalError 内部エラー。
throwsで宣言されているが絶対に発生しない(はずの)場合などに、このエラーに置き換えてthrowする。
clone()の例 2009-04-25
java.lang.NoClassDefFoundError: クラス名 クラス(のファイル)が見つからない。
実行時のパッケージ指定が間違ってるかも。
または、環境変数CLASSPATHに「.」 (カレントディレクトリ)が入っていないかも。
以前動いていたのにこの例外が出るようになった場合は、他の何かをインストールしたせいで環境変数CLASSPATH等が書き換えられたかもしれない。→QuickTimePlayerの例
Javaアプリケーション実行方法
jarファイルを使った実行方法
2006-02-26
java.lang.NoClassDefFoundError: 指定されたクラス名 (wrong name: 見つかったクラス名) 指定されたクラスのファイルは見つかったが、そのクラス内部に書かれているクラス名(+パッケージ名)が指定されたクラスとは異なっている。(つまり指定されたクラスは見つかっていない) 2008-12-20
java.lang.NullPointerException nullのオブジェクトに対してインスタンスフィールドにアクセス/インスタンスメソッドを呼び出そうとした。
英語の略称は「NPE」、日本語の俗称は「ぬるぽ」。
NullPointerExceptionのメッセージ
  2009-03-28
java.lang.OutOfMemoryError メモリー不足。
Java5.0(JDK1.5)まではスタックトレースが表示されなかったが、Java6(JDK1.6)から表示されるようになった。
「OOM」と略している人がいたけど、一般的かなぁ?
ヒープサイズ指定
antのオプション
antのjavac
2007-02-28
java.lang.reflect.InvocationTargetException Method#invoke()を使って呼び出した先のメソッドで発生した例外をラップしたもの。getCause()で、実際に発生した例外を取得できる。 invoke() 2004-06-12
java.lang.UnsatisfiedLinkError nativeなメソッドの呼び出しエラー。
loadLibraryの呼び出し忘れか、指定されたDLLまたはSOファイルが見つからないか、呼び出したいnative関数がDLLやSO内に見つからないか。
JNI 2006-06-18
java.lang.UnsupportedClassVersionError: クラス名 (Unsupported major.minor version Mj.Mi) クラスが要求しているバージョンJavaVMがサポートしているバージョンの範囲外なので、実行できない。
表示されているMj.Miは、クラスファイルのメジャーバージョン.マイナーバージョン
対処法としては、JavaVMに合わせたバージョンでクラスファイルを作り直す(コンパイルし直す)か、JavaVM(javaコマンド)を新しくする。
以前動いていたのにこの例外が出るようになった場合は、他の何かをインストールしたせいで環境変数PATH等が書き換えられたかもしれない。→Oracleの例
バージョン
native2ascii
2007-05-15
java.lang.UnsupportedClassVersionError: Bad version number in .class file …JDK1.5の場合のメッセージ
java.lang.UnsupportedClassVersionError: クラス名 : Unsupported major.minor version Mj.Mi  …JDK1.6の場合のメッセージ
java.lang.UnsupportedOperationException サポートされていない操作(実装されていないメソッド?)であることを示す。 類似:不正な引数
使い方
2008-06-04
java.net.SocketException Software caused connection abort: socket write error
Software caused connection abort: recv failed
通信相手がクローズして(RSTを受け取って)から送信や受信をしようとした。
  2006-05-07
java.nio.file.FileAlreadyExistsException ファイルが既に存在している。(IOExceptionのサブクラス) NIO2(JDK1.7) 2011-07-30
java.nio.file.DirectoryNotEmptyException ディレクトリーが空でない。(IOExceptionのサブクラス) NIO2(JDK1.7) 2011-07-30
java.nio.file.NoSuchFileException ファイルが存在しない。(IOExceptionのサブクラス) NIO2(JDK1.7) 2011-07-30
javax.naming.NamingException JNDIContext#lookup()をした際に、指定された名前が見つからない。 JNDI 2008-08-03

例外の基礎

例外が発生する可能性があるメソッドは、throws句発生する例外の種類を全て宣言する。
ただし、RuntimeException(の派生クラス)の場合はthrowsに明示的に書く必要は無い

例外はthrow文で発生させることが出来る。

	戻り型 メソッド(〜) throws 例外A, 例外B {
		〜
		throw new 例外A();
		〜
	}

throwsが在ってthrowが無いことも有り得る。例外が発生する可能性がある関数を呼び出している場合や、throwsで宣言はしたものの そもそも例外が発生しない場合など。

	戻り型 メソッド(〜) throws 例外A, 例外B {
		〜
		foo();	//例外が発生する可能性がある
		〜
	}

メソッドをオーバーライドしている場合、継承元のメソッドでthrowsによって例外が宣言されていたら、同じ例外(継承元メソッドで宣言されている例外)しか宣言できない。
全てを引き継ぐ必要は無く、1つも書かなくても構わない。書かなかった場合は「その例外は絶対発生しない」という意味になり、throwすることが出来なくなる。[2009-04-25]
(書かなかった場合は「暗黙に宣言されていることになる」と思っていたが、そうではないようだ(汗))

継承元で宣言されていない例外を追加することは出来ない。(メソッドを使用する側としては、想定していない例外は捕捉できない。つまり、使用する側が親クラスを使って変数宣言していると、当然「そこで定義されている例外しか来ない」と判断する。なので、継承したサブクラスの方で勝手に例外を追加してはいけない)
(ただしRuntimeException系はそもそもthrowsに書かなくてよいので、逆に、親メソッドの宣言とは無関係にthrowsに追加する事も出来る。[2009-04-25]


例外の捕捉にはtry〜catch〜finally文を使う。

	try {
		〜 //ここで起きた例外がcatchによって捕えられる
	} catch(例外クラス名 捕捉用変数) {
		〜
	} finally {
		〜
	}

複数の例外を捕捉したい場合は、複数のcatchを書く。例外の型が最初に(instanceofと同等の演算で)一致したcatchの処理が実行される。

catchの中で再びthrowすると、外のtry文に移る。
try文が無いと、現在の関数の外に例外を投げようとする。こういうケースが有り得る場合では、関数にthrowsが定義されていないとコンパイルエラーとなる。(ただし、RuntimeException及びその派生クラスなら、throwsが無くてもコンパイルエラーにならない)

finallyは例外が発生してもしなくても実行される。不要ならばfinallyブロック自体を書かなくてよい(逆にcatchブロックを省略してtryとfinallyだけにすることも可)。
正常終了の場合は、tryの中が終わった後に実行される。
例外が発生した場合は、catchの中が終わった後に実行される。
また、tryやcatchの中がreturn文で終了していたとしても、finallyは実行される!
finallyの中でreturnや新たな例外をスローした場合は、その新しい値が返る。しかしこれは行儀のよい方法ではない。元の返り値を握りつぶしてしまうことになるから)

finallyブロックは「常に呼ばれる」という理解でよいが、System.exit()でアプリケーションを終了する場合にはfinallyブロックは実行されないので要注意![2008-01-19]
“アプリケーションの深い所からexit()で終了する”ような実装はしない方がいい、という事か。

例外の実験例


ExceptionはThrowableの派生クラスであり、throw・throws・catchはThrowableに対して使える。[2004-06-12]
ただし、Throwableの派生クラスはException(例外)とError(エラー)だけであり、Errorはシステム的な障害を扱うので、普通はcatchして処理したりはしない。OutOfMemoryErrorなんか、catchして処理しようとするだけでメモリ不足になりそう(爆)
普通のアプリケーションが扱うのは、実質的にException(例外)だけとなる。


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

Javaで例外の勉強をしていると、「チェック例外」「非チェック例外」(あるいは「検査例外」「非検査例外」「実行時例外」)という言葉に出くわすことがある。[2012-11-24]

Javaの文法との関連で言うと、非チェック例外はRuntimeExceptionを継承したクラス(例外)で、チェック例外はRuntimeExceptionを継承していないクラス(例外)。
つまり、チェック例外をメソッドの外にスローする際にはthrowsが必要になる。

そもそも「チェック」とは何かというと、「例外が発生したかどうかをチェックする必要がある」という意味だと思われる。
throwsが宣言されたメソッドを呼び出したら、throwsに書かれた例外をキャッチする処理(あるいはさらにthrowsで呼び出し元に渡す)を書かなければならない。
「例外をキャッチするようコーディングする」=「例外の発生をチェックしている」ということになるのだろうか。

ではRuntimeExceptionを継承したクラス(非チェック例外)はキャッチ(チェック)しなくていいかというと、そうなのか?
(文法上はキャッチすることは出来る)

最初は、チェックという言葉のニュアンスから、「何らかのチェックをした結果として投げるのがチェック例外」だと思っていた。
とするとそんなのはほぼ全てに該当する訳で、「非チェック例外って何者?」と疑問だった。

「実行時例外」はRuntimeExceptionの日本語訳だろうか。これも、例外は常に実行時に発生するので、違和感を感じさせる名前だ^^;

といった事が曖昧に感じられる為、個人的にはチェック例外・非チェック例外という言葉は極力使わないようにしている。


ただ、チェック例外と非チェック例外という区分がなぜあるのか・その目的は?という点から考えると、

ということかもしれない。

チェック例外は、「異常系正常処理」(起こることが想定される(実際に起こり得る)異常に対する処理)な気がする。


NullPointerExceptionのメッセージ

nullのオブジェクトに対してメソッドを呼び出したりフィールドアクセスしたりしようとすると、NullPointerException(通称NPE)が発生する。[2020-03-21]

	String s = null;
	int len = s.length(); //NullPointerException

デフォルトでは、以下のような例外が出力される。例外メッセージが無いorz

$ java Example
Exception in thread "main" java.lang.NullPointerException
	at Example.main(Example.java:4)

Java14では、javaコマンドに-XX:+ShowCodeDetailsInExceptionMessagesオプションを付けて実行すると、NPEに詳細なメッセージが出るようになる。
(パフォーマンスにはほとんど影響しないとのこと)

Java15では、ShowCodeDetailsInExceptionMessagesオプションを付けなくても詳細なメッセージが表示される。[2020-09-29]

参考: きしださんの358: Helpful NullPointerExceptions

$ java -XX:+ShowCodeDetailsInExceptionMessages Example
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local1>" is null
	at Example.main(Example.java:4)

残念ながら、デフォルトでは、ローカル変数名やメソッドの引数名は、ちゃんとしたものが出ない。


コンパイル時にjavacコマンドに-gオプションを付けてコンパイルすると、ローカル変数名やメソッドの引数名も出るようになる。

$ javac -g Example.java

$ java -XX:+ShowCodeDetailsInExceptionMessages Example
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "s" is null
	at Example.main(Example.java:4)

Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま