Javaでは例外処理の対象はError、Exceptionの2種類です。
さらに詳しく言えばExceptionはコンパイラーが例外処理が記述されていることをチャックする検査例外であるのに対し、ExceptionのサブクラスであるRuntimeExceptionは、例外自体が発生しないようにプログラマがロジックを工夫することが期待されているためコンパイラーは例外処理の記述をチェックしない事。
そして、例外処理の記述法などを説明しました。
ここでは、それらError、ExceptionとしてJavaが用意している例外クラスを説明し、さらにそれらを踏まえて独自に例外クラスを作って例外処理する方法を説明します。
Error
トラブルが発生した場合、JVMがErrorクラスまたは、そのサブクラスのインスタンスを生成しプログラムに通知します。プログラム処理ではこの種のトラブルは復旧出来ないので例外処理の記述は不要ですが、例外処理を記述してプログラムが強制終了させずに、トラブルが発生したことをユーザに知らせ、プログラムを正常終了させることはできます。
基本的には、このような事態が発生しないようにプログラムを十分テストしたり、実行環境を確認し、整えておく必要があります。
NoClassDefFoundError
JVMが実行対象のクラスファイルを発見できなかった。
コンパイル時点で存在していたクラスが、見つからない。
VirtualMachineError
JVMが壊れているか、または動作を継続するのに必要なリソースが足りなくなった。
StackOverflowError
JVMが「再帰呼び出し」などで、スタック領域の不足を検出した。
OutOfMemoryError
JVMがインスタンスを保存したり、クラスの定義情報を保存したりするヒープ領域が一杯になり、新しいオブジェクトを割り当てることができない。
Internal Error
JVM内で何らかの内部エラーが発生した。
ExceptionInInitializerError
JVMがstaticイニシャライザを処理している間に例外が発生(NullPointerExceptionなど)したとき、例外の通知相手がまだ存在しないのでErrorを発生させた。
Exception
プログラム処理による復旧が可能なで例外であり例外処理の記述が必須です。コンパイラーが例外処理の記述をチャックするので例外処理の記述がないとコンパイルが完了しません。
ClassNotFoundException
クラスの文字列名を使用してforName、findSystemClass、loadClassメソッドでロードしようとしたが、指定された名前のクラスの定義が見つからなかった。
IOException
入出力処理の失敗、または割り込みの発生による例外
FileNotFoundException
指定されたパス名で示されるファイルが開けなかった
SQLException
データベース操作時に発生した例外
RuntimeException
プログラム時の考慮によって回避できる例外です。コンパイラーは必要な考慮がされていることを前提に例外処理の有無をチャックしません。従って、プログラム上の考慮が不足していてもコンパイルは通ってしまうので、実行時に例外が発生する可能性があります。
ArithmeticException
ゼロ割等、不能演算が指示された
IllegalArgumentException
利用される側のオブジェクトが不正な引数を受取った
NumberFormatException
parselntメソッドが変換形式に合わない引数を受取った
例えば、Integerクラスのparselntメソッドに文字として英数字以外を渡す。
IndexOutOfBoundsException
配列や文字列、コレクションの範囲外アクセス
ArrayIndexOutOfBoundsException
配列の範囲外アクセス
public class Main01 {
public static void main(String[] args) {
String[] str = new String[1];
for(String s :str) {
System.out.println(s + " , " + str[1]);
}
}
}
>java Main01
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at Main01.main(Main01.java:5)
>
StringlndexOutOfBoundsException
文字列の範囲外アクセス
public class Main03 {
public static void main(String[] args) {
String str = "string";
System.out.println(str.charAt(6));
}
}
>java Main03
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 6
at java.lang.String.charAt(Unknown Source)
at Main03.main(Main03.java:4)
>
NullPointerException
オブジェクト参照がnullの状態で、インスタンスにアクセスする。
配列変数がnullなのに、配列のサイズを調べる。
public class Main02 {
public static void main(String[] args) {
String[][] str = null;
System.out.println(str.length);
}
}
>java Main02
Exception in thread "main" java.lang.NullPointerException
at Main02.main(Main02.java:4)
>
DateTimeException
Java SE 8で追加されたjava.time.LocalDateクラスでは従来のjava.util.Dateやjava.util.Calendarと異なり月は1から始まります。このため月に0を指定すると、構文上の誤りではないのでコンパイルエラーは発生せず実行時にこの例外が発生します。
ClassCastException
継承関係や実現関係にない型に互換性のないクラスをキャストしようとした。
IlegalStateException
利用される側のオブジェクトが、まだ利用するための準備が終わっていない。
以上はJavaが用意している例外クラスの一部です。その他の例外クラスについてはJava APIドキュメントで確認することが出来ます。
この様に数々の例外クラスが用意されていますが、プログラム開発上独自に値の確認をしたい変数などあるでしょう。これらの確認に自分で独自の例外を定義して、例外処理の仕組みを使えばスマートに不具合への対処を組み込むことが出来ます。
参考例として、映画館のチケットの代金を計算してみましょう。
通常料金は1800円として、夜10:00(22:00)からをレイトショーとして1300円に割引します。
上映は朝9:00から始まるとして、希望の上映開始時間を整数で入力してもらいます。
入力された数字が9より少なかったり、24以上だったりした場合は再入力。また、数字以外の文字が入力された場合も再入力してもらいます。
時間の入力範囲チェックにはRuntimeExceptionのサブクラスとして独自例外:OutOfTimeRangeExceptionを作成し、9~23以外の入力があった場合にそれを発行し、数字以外の誤文字入力は実行時例外として通知されるNumberFormatExceptionをメインクラスでそれぞれ例外処理しています。
独自例外OutOfTimeRangeException
public class OutOfTimeRangeException extends RuntimeException { }
レイトショー割引も考慮してチケット代金を求めるHowMuchIsTheFee
import java.util.Scanner;
public class HowMuchIsTheFee {
public static void main(String[] args) {
HowMuchIsTheFee hmticket = new HowMuchIsTheFee();
int ticketFee = 1800;
boolean lateShow = hmticket.isLateShow();
System.out.print("チケット代は");
if( lateShow ) {
System.out.print("レイトショー割引で");
ticketFee = 1300;
}
System.out.println(ticketFee + "円です。");
}
public boolean isLateShow() {
//22時以降はレイトショーで割引あり。
while( true ) {
Scanner sc = new Scanner(System.in);
System.out.print("購入するチケットは何時の回ですか?\n9以上24より少ない数字を入力してください。:");
String keyString = sc.next();
try {
int ticketTime = Integer.parseInt(keyString);
if(ticketTime<9 || ticketTime>=24) throw new OutOfTimeRangeException();
if(ticketTime>=22 && ticketTime<24) return true;
return false;
}
catch (NumberFormatException e) {
System.out.print("整数値以外の入力がありました。");
}
catch (OutOfTimeRangeException e) {
System.out.print("入力された数字に誤りがあります。");
}
finally {
System.out.println("再入力してください。\n");
}
}
}
}
ハイライトされたisLateShowメソッドが、希望の上映開始時間を受付し、入力値チェックを行い入力誤りがあれば例外処理の中でエラーメッセージを出し、入力に誤りが無ければレイトショーに該当するか否かを論理値としています。
実行結果は以下のようになります。
>java HowMuchIsTheFee
購入するチケットは何時の回ですか?
9以上24より少ない数字を入力してください。:aa
整数値以外の入力がありました。再入力してください。
購入するチケットは何時の回ですか?
9以上24より少ない数字を入力してください。:8
入力された数字に誤りがあります。再入力してください。
購入するチケットは何時の回ですか?
9以上24より少ない数字を入力してください。:24
入力された数字に誤りがあります。再入力してください。
購入するチケットは何時の回ですか?
9以上24より少ない数字を入力してください。:10
再入力してください。
チケット代は1800円です。
>java HowMuchIsTheFee
購入するチケットは何時の回ですか?
9以上24より少ない数字を入力してください。:23
再入力してください。
チケット代はレイトショー割引で1300円です。
>
例外処理に戻る