はじめに
Javaではコンパイル時に発生するコンパイルエラー
と実行時に発生する実行時エラー
があります。実行時に発生するエラーに対しては、処理が止まらないように対処できます。これが例外処理
です。この章では、例外処理の仕組みと種類、そして例外処理の方法について学習していきます。
目次
- 例外とは
- try-catch-finally文
- 例外クラス
- 複数のcatch文を使用した処理
- throws
- try-finally文
- throw
- 章のまとめ
目標
例外処理について理解し、例外に対処するコーディングをできるようにすること
例外とは
例外
とは、プログラム実行中に予定通り実行を継続できない状態が発生した
ことを言います。例えば、ユーザの入力に誤りがあった、間違ったファイルを実行しようとした、などの誤操作によるエラーはコンパイル時では対処できません。このようなエラーが発生したときに、それを検知
し、対処
する仕組みが提供されています。この仕組みを利用し、あらかじめ発生する可能性のある例外に対する処理を記述しておくことで、プログラムがそのエラーにより、終了してしまうことを避けることができます。
チェック例外と非チェック例外
例外は大きくチェック例外
と非チェック例外
に分けることができます。
チェック例外
チェック例外
とは、プログラムの論理ミスではなく、ファイル読み出しして指定したファイルが存在しないとか、通信先の相手が応答しないなどのプログラムの実行を継続できない状況が発生しうる可能性のある例外
を指します。これらは、発生する場所がファイル入力メソッドや、通信メソッドなどのように特定できる
ことができます。その為、例外が発生したかどうかチェックし、対処するコード
をコーディングする
必要があります。対処するコードをコーディングしないとコンパイルエラー
になります。
非チェック例外
非チェック例外
とは、コンパイラが発見できないプログラムの論理ミスによって発生する例外
です。発生する場所が予測できないので、通常は対処しません。例えば、ゼロによる除算や配列の範囲を超える要素アクセスや、nullによるオブジェクトの参照などが代表的な非チェック例外です。非チェック例外のことを実行時例外
ともいいます。
例題
新たにExceptionExec
クラスを作成し、実際に以下を入力してください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | ExceptionExec |
package chapter11; |
コンパイル例:> javac chapter11¥ExceptionExec.java
実行例:> java chapter11.ExceptionExec
A1
A2
A3
A4
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at exceptionPK.ExceptionExec.display(ExceptionExec.java:12)
at exceptionPK.ExceptionExec.main(ExceptionExec.java:7)
コンソールにjava.lang.ArrayIndexOutOfBoundsException
が発生したと表示されています。配列の範囲を超える要素が指定した為、例外が発生しています。
オブジェクト指向のJava言語では例外も例外クラスとして作成されています。いろいろな例外に対応するクラスがあらかじめ用意されており、例外が発生すると対応するクラスのオブジェクトが作成されます。ArrayIndexOutOfBoundsException
も例外クラスの一つで、java.lang
が先頭に付与されている為、java.langパッケージにあるクラスであることがわかります。
at alj_kensyu.ExceptionExec.display(ExceptionExec.java:12) |
上記のat alj_kensyu.ExceptionExec.display(ExceptionExec.java:12)
は例外の発生場所を示します。常に最上段の表示が発生場所を示します。これは次のように読み替えてください。
alj_kensyuパッケージの/ExceptionExecクラスの/displayメソッド/(ExceptionExec.javaファイルの12行目) |
またat alj_kensyu.ExceptionExec.main(ExceptionExec.java:7)
はdisplayメソッドを呼び出したメソッドを示します。
alj_kensyuパッケージの/ExceptionExecクラスの/mainメソッド/(ExceptionExec.javaファイルの7行目) |
このように例外発生した行と、それを呼び出したメソッドの行が遡って表示されることにより、原因の特定が容易になります。メソッドの呼び出しの過程は、コールスタック
というJVMが管理する記憶領域に記録されます。この仕組みを一般的にスタックトレース
といいます。
try-catch-finally文
例外に対処するためには、try-catch-finally文
を使います。
書式
try{
通常の処理(例外が発生すると思われる処理)
}catch(例外クラス 引数){
例外への対処
}finally{
最後に必ず実行する処理
}
先ほど作成したExceptionExec
クラスを以下のように修正し、実行してみてください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | ExceptionExec |
ExceptionExec.javapackage chapter11;
/**
* 実行するためのクラス
* @version 1.0
* @author Yamamoto
*/
public class ExceptionExec {
/**
* main()メソッド
*/
public static void main(String[] args) {
String[] str = {"A1","A2","A3","A4"};
display(str);
}
/**
* 表示機能
* @param str 出力文字列(配列)
*/
public static void display(String[] str){
try{
for (int i = 0; i < 5 ; i++){
System.out.println(str[i]);
}
}catch(ArrayIndexOutOfBoundsException e){
//ArrayIndexOutOfBoundsExceptionが発生したときの例外処理
System.out.println("catchブロック実行しました。");
}finally{
System.out.println("finallyブロック実行しました。");
}
}
}
コンパイル例:> javac chapter11¥ExceptionExec.java
実行結果:> java chapter11.ExceptionExec
A1
A2
A3
A4
catchブロック実行しました。
finallyブロック実行しました。
tryブロック
displayメソッドにtry-catch-finally文
が書いてあり、例外が発生する可能性のあるコードはtryブロック
の中に書きます。
try{ |
catchブロック
例外が発生すると、それ以降の処理を中止して、直ちにcatchブロック
に制御が移ります。catchの直後のカッコ内「()」に例外クラスを指定します。以下の場合、ArrayIndexOutOfBoundsException
が発生すると引数eに例外の詳細情報がセットされます。
catch(ArrayIndexOutOfBoundsException e){ |
finallyブロック
例外発生の有無にかかわらず、try-catchブロック
の最後に必ず実行する処理
を書きます。
finally{ |
なお、必要のない場合にはfinallyブロックを省略
することができます。
例外クラス
オブジェクト指向のJava言語では、例外
をそれぞれ一つのクラス
で表します。例外クラスの継承関係を以下に示します。
例外クラス
例外クラスのスーパークラスはException
です。サブクラスのRuntimeException
は、全て非チェック例外(実行時例外)のスーパクラスです。またIOException
は入出力関連の例外のスーパークラスです。
主要な実行時例外(非チェック例外)は以下です。
実行時例外(非チェック例外)
体験的に理解する方がしっかり覚えることができますので、以下コーディングし、実行結果を確認してみてください。
ClassCastException
例外クラス名 | 意味 |
---|---|
ClassCastException | キャストの失敗 |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | ClassCastExceptionExec |
package chapter11; |
コンパイル例:> javac chapter11¥ClassCastExceptionExec.java
実行結果:> java chapter11.ClassCastExceptionExec
Exception in thread "main" java.lang.ClassCastException: class chapter11.Animal cannot be cast to class chapter11.Cat (chapter11.Animal and chapter11.Cat are in unnamed module of loader 'app')
at chapter11.ClassCastExceptionExec.main(ClassCastExceptionExec.java:6)
NumberFormatException
例外クラス名 | 意味 |
---|---|
NumberFormatException | 数値形式文字列の書き誤り |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | NumberFormatExceptionExec |
package chapter11; |
コンパイル例:> javac chapter11¥NumberFormatExceptionExec.java
実行結果:> java chapter11.NumberFormatExceptionExec
Exception in thread "main" java.lang.NumberFormatException: For input string: "1.0"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatExceptionExec.java:68)
at java.base/java.lang.Integer.parseInt(Integer.java:652)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at chapter11.NumberFormatException.main(NumberFormatExceptionExec.java:7)
IllegalStateException
例外クラス名 | 意味 |
---|---|
IllegalStateException | 不正な状態での呼び出し |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | IllegalStateExceptionExec |
package chapter11; |
コンパイル例:> javac chapter11¥IllegalStateExceptionExec.java
実行結果:> java chapter11.IllegalStateExceptionExec
Exception in thread "main" java.lang.IllegalStateException: Scanner closed
at java.base/java.util.Scanner.ensureOpen(Scanner.java:1150)
at java.base/java.util.Scanner.next(Scanner.java:1465)
at chapter11.IllegalStateExceptionExec.main(IllegalStateExceptionExec.java:10)
IndexOutOfBoundsException
例外クラス名 | 意味 |
---|---|
IndexOutOfBoundsException | 不正な状態での呼び出し(配列や文字列操作で範囲を超えたとき) |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | IndexOutOfBoundsExceptionExec |
package chapter11; |
コンパイル例:> javac chapter11¥IndexOutOfBoundsExceptionExec.java
実行結果:> java chapter11.IndexOutOfBoundsExceptionExec
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 6
at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
at java.base/java.lang.String.charAt(String.java:711)
at chapter11.IndexOutOfBoundsExceptionExec.main(IndexOutOfBoundsExceptionExec.java:8)
ArrayIndexOutOfBoundsException
例外クラス名 | 意味 |
---|---|
ArrayIndexOutOfBoundsException | 配列の要素番号が範囲外 |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | ArrayIndexOutOfBoundsExceptionExec |
package chapter11; |
コンパイル例:> javac chapter11¥ArrayIndexOutOfBoundsExceptionExec.java
実行結果:> java chapter11.ArrayIndexOutOfBoundsExceptionExec
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
at chapter11.ArrayIndexOutOfBoundsExceptionExec.main(ArrayIndexOutOfBoundsExceptionExec.java:7)
NullPointerException
例外クラス名 | 意味 |
---|---|
NullPointerException | 使ったオブジェクトがNull |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | NullPointerExceptionExec |
NullPointerExceptionExec.javapackage chapter11;
public class NullPointerExceptionExec {
public static void main(String[] args) {
String str = null;
System.out.println(str.equals("a"));
}
}
コンパイル例:> javac chapter11¥NullPointerExceptionExec.java
実行結果:> java chapter11.NullPointerExceptionExec
Exception in thread "main" java.lang.NullPointerException
at chapter11.NullPointerExceptionExec.main(NullPointerExceptionExec.java:7)
ArithmeticException
例外クラス名 | 意味 |
---|---|
ArithmeticException | 算術エラー |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | ArithmeticExceptionExec |
package chapter11; |
コンパイル例:> javac chapter11¥ArithmeticExceptionExec.java
実行結果:> java chapter11.ArithmeticExceptionExec
Exception in thread "main" java.lang.ArithmeticException: / by zero
at chapter11.ArithmeticExceptionExec.main(ArithmeticExceptionExec.java:6)
複数のcatch文を使用した処理
ある処理が、種類の違う複数の例外を発生する場合があります。例えば、ファイルからデータを読み出す処理では、指定されたファイルが見つからない、ファイルが破損していて読み出せないなど、複数の例外が発生する可能性があります。このような場合はtry-catch-finally文の中でcatchブロックを複数
定義して対応します。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | FileReaderExec |
package chapter11; |
コンパイル例:> javac chapter11¥FileReaderExec.java
実行結果:> java chapter11.FileReaderExec
ファイルが見つかりません。
上記、例外が発生すると、catch文が上から順に検査され、最初に一致したcatch文だけが実行されます。最初のcatch文で、FileNotFoundException
を指定しています。2つ目のcatch文でIOException
を指定しています。
なお、IOException
はFileNotFoundException
のスーパークラスです。catchブロックで複数の例外を指定する場合は、サブクラスを先に指定し、最後にスーパークラスを指定することに注意してください。この順序を守らないとコンパイルエラー
になります。
throws
Java言語では例外が発生したメソッドで例外処理を行わないと、例外は呼び出し元のメソッドへ伝達される仕組みになっています。発生した例外がどこかで対応する例外処理に遭遇するまで、メソッド呼び出し経路(コールスタック)を遡って伝達されます。
例外がこのように伝達されることを利用すると、責任を放棄して呼び出し元メソッドに任せることも可能です。それにはメソッドの宣言部でthrows
キーワードを使って処理を放棄
する宣言
をします。これを例外を交わす
といいます。
先ほど作成したFileReaderExec
クラスを以下のように修正及び追加し、実行してみてください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter11 | |
クラス名 | FileReaderExec |
package chapter11; |
javac chapter11¥FileReaderExec.java
実行結果:java chapter11.FileReaderExec
ファイルが見つかりません。
※`System.err.println`は標準エラー出力といいます。標準エラー出力はエラー情報の出力するときに使います。
## try-finally文
例外に関係なく実行したい処理があるとき、try-finally構文を使います。
| 項目 | 名前 | 備考 |
|:------------|:----------|:----------|
| プロジェクト | alj_study | |
| パッケージ | chapter11 | |
| クラス名 | FileReaderExec2 | |
package chapter11;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderExec2 {
public static void main(String[] args) throws Exception{
FileReader in = null; //nullで初期化しておく。
try {
in = new FileReader("test.txt");
int c = in.read();
System.out.println((char)c);
} finally{
System.out.println("finallyブロック実行しました。");
}
}
}
コンパイル例:
javac chapter11¥FileReaderExec2.java
実行結果:java chapter11.FileReaderExec2
finallyブロック実行しました。
Exception in thread “main” java.io.FileNotFoundException: test.txt (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:212)
at java.base/java.io.FileInputStream.(FileInputStream.java:154)
at java.base/java.io.FileInputStream.(FileInputStream.java:109)
at java.base/java.io.FileReader.(FileReader.java:60)
at chapter11.FileReaderExec2.main(FileReaderExec2.java:14)
※finallyブロックは例外が発生しても、しなくてもtryの中の処理に続いて必ず実行されます。
## throw
メソッドでは、渡された引数の値が不正だったり、読み込んだデータが異常だった場合など、処理を中止せざるをえなくなる場合があります。そんなときに強制的に例外を起こすことによって、異常事態の発生を呼び出し側メソッドに伝えることができます。例外を起こすには`throw文`を使います。`throw文`は`強制的に例外を発生させる`ことができる例外文です。
構文
throw 例外クラスオブジェクトの参照 ;
|
package chapter11;
public class ThrowExec {
public static void main(String[] args) {
try {
display(args);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
public static void display(String[] n) throws Exception{
if(n.length == 0) {
throw new Exception("引数が指定されていない"); //例外を投げる
}else{
for(String str : n ){
System.out.println(str);
}
}
}
}
コンパイル例:
javac chapter11¥ThrowExec.java
実行結果(引数指定無し):java chapter11.ThrowExec
引数が指定されていない
実行結果(引数指定有り):java chapter11.ThrowExec 123 987 234
123
987
234
上記は、display()メソッドでは、引数で指定された配列の要素がない場合は、
例外を投げる処理(例外を自ら発生させる処理)を定義しています。
if(n.length == 0) {
throw new Exception("引数が指定されていない"); //例外を投げる
}else{
for(String str : n ){
System.out.println(str);
}
}
```
Exception
はチェック例外の為、例外処理であるtry-catch文
を定義するか、 throws
の宣言が必須です。
今回はdisplay()メソッドでthrows Exception
を宣言し、呼び出し元であるmain()メソッド
で、例外処理であるtry-catch文
を定義することで例外を対処しています。
今回は、ExceptionクラスのメソッドgetMessage()
を利用し、メッセージを表示させています。
例外クラスのメソッド
投げられた例外を処理する側のメソッドでは、例外クラスのメソッドを利用して例外処理を行うことができます。
例外クラスのスーパクラスであるExceptionクラス
には、色々なメソッドがあります。
メソッド名 | 内容 |
---|---|
printStackTrace() | スタックトレースを表示する |
toString() | 例外の種類とメッセージ文字列を返す |
getMessage() | メッセージ文字列を返す |
使い方については、以下リファレンスをご参考ください。
章のまとめ
以下の要点をしっかり理解してから次の章へ進んでください。
- 例外には
チェック例外
と非チェック例外
がある。 - 例外に対処するには例外の発生しそうなコードを
try-catchブロック
で囲む。 - catch文の引数には例外に対応する
例外クラス
を指定する。 - tryブロックで例外が発生すると直ちに
catchブロック
に制御が移る。 複数のcatch文
を書くことができるが、実行されるのは一つだけ
である。finallyブロック
はtry-catchブロックの実行後、必ず実行
される。- 例外クラスのスーパークラスは
Throwableクラス
である。 Throwableクラス
からErrorクラス
とExceptionクラス
が派生する。Exceptionクラス
からはRuntimeException
やIOException
などが派生する。RuntimeException
クラスは実行時例外(非チェック例外)のスーパークラスである。- catch文が複数あるときは、
サブクラスを補足するcatch文
を先に書く
。 - 例外処理を行わないと、次々に呼び出し元のメソッドに例外が
伝達
される。 - チェック例外はtry-catchブロックで必ず例外処理を行わなければならないが、
throwsキーワード
で例外処理を呼び出し元へ、任せることもできる。 - 例外を投げるには
throw文
を使う。 - チェック例外を投げる場合は、メソッドで
throws宣言
が必要である。