例外処理
プログラムが実行される際、データ型が違うだの、メモリが足りないだの、用意した配列サイズ以上のデータだの、ファイルがみつからないだの…と様々なエラーが出る。これを処理するのが例外処理
すなわち、色々エラー起きたけど、このあとどうするよ?っていう処理のこと。
例外処理には大きく二つある。
2つの例外
検査例外の代表として挙げられるのが、IOException
。 BufferedReader
とか使うとき、throws IOException
とかしないとコンパイルエラーする.
非検査例外の代表として挙げられるのが、ArrayIndexOutOfBoundsException
やNullPointerException
。配列のサイズ以上の入力とか、実行時にエラーがでる.
非検査例外 - 非チェック例外とも。コンパイラが例外処理を埋め込んでいるかという検査をしない。
非検査例外は、例外処理をわざわざ実装する必要がありません。(コンパイル通るし)
後ほど説明するtry-catch
など利用してエラー発生時の処理を書くことはもちろんできます。
検査例外 - チェック例外とも。コンパイラが例外処理を埋め込んでいるか検査する。
例外処理を書かないと、ダメです。
例外処理の書き方
throws
を用いた書き方
例外が発生しました!さあどうしましょう。というときに、親となるクラスや呼出しもとにぶん投げます。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class IOeTest{
public static void main(String[] args) throws IOException {
String fname = args[0]; // 1.
FileReader freader = new FileReader(fname); // 2.
BufferedReader buf = new BufferedReader(freader); // 3.
while(buf.ready()){
System.out.println(buf.readLine()); //4.
}
}
}
上記は、
- args[0] からファイル名を読み
- FileReaderでファイルを準備し、
- BufferedReaderでバッファにぶち込み、
- buf.readLine()で一行ずつ出力する
プログラムです。
当然
- ファイル名を指定しなければ、引数0なんてないよ、と1の場所でエラーが出る。
- 存在しないファイル名を指定すると、そんなファイルないよ、と2の場所でエラーが出る。
です。
1は非検査例外です。1を吐かせてみると...
Exception in thread "main" Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at IOeTest.main(IOeTest.java:7)
at IOeTest.main(IOeTest.java:7)
java.lang.ArrayIndexOutOfBoundsExceptionがでます。
2は検査例外です。吐かせてみると...
Exception in thread "main" Exception in thread "main" java.io.FileNotFoundException: 絶対ないプログラムやで.java (そのようなファイルやディレクトリはありません)
java.io.FileNotFoundException: 絶対ないプログラムやで.java (そのようなファイルやディレクトリはありません)
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.<init>(FileInputStream.java:154)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:109)
at java.base/java.io.FileReader.<init>(FileReader.java:60)
at IOeTest.main(IOeTest.java:7)
エラーとして、java.io.FileNotFoundExceptionがでてきます。
Try-Catch-Finally
上記では、エラーをIOException
クラスなどに投げることを明示していました。
try-catch-finallyを利用すると、自身で実装できます。
上記例を基に、やってみましょう。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.FileNotFoundException;
public class IOeTryTest{
public static void main(String[] args) {
String fname = args[0];
FileReader freader;
BufferedReader buf;
try{
System.out.println("TRY");
freader = new FileReader(fname);
buf = new BufferedReader(freader);
while(buf.ready()){
System.out.println(buf.readLine());
}
}catch(Exception e){
// Exception(例外処理)クラスのeを持つ。
// Standard Output
System.out.println("CATCH");
// Standard Error Output
System.err.println("error occured.");
System.err.println(e);
}finally{
System.out.println("FINALLY");
}
System.out.println("FINALLYあと");
}
}
上記プログラムでは、try
ブロックに続いてcatch
ブロック、finally
ブロックがあります。また、メソッドにthrows IOException
が実装されていないです。
ここで各ブロックを概説しましょう。
BLOCK | Description |
---|---|
try | エラーの発生が予期される処理 |
catch | エラー発生時の例外処理 |
finally | エラーに関わらず終了前にする処理 |
ここで、エラー発生時と正常な動作を実行して、前のプログラムと比べて見てください。
オーバーロード
次章のメソッドプログラミングでも説明しますが、オーバーロードと呼ばれる概念があります。引数を変え、それ以外の名前を統一することで、引数データ型や数によって処理の振り分けを行うことです。
fname = args[0];
をtry
ブロック内に移動し、catch
ブロックを下記のように振り分けできる状態をつくれば、エラーの種別によって処理が振り分けされます。
}catch(ArrayIndexOutOfBoundsException e){
// Standard Output
System.out.println("ArrayIndexOutOfBoundException catched");
}catch(IOException e){
System.out.println("IOException catched");
}
System.out.println("FINALLYあと");
try時のcatch/finally
try
時に、0個以上のcatch
またはfinally
が必須です。- つまり、最低1個の
catch
かfinally
が必要になります。
throw
catch
内で例外処理を行っている際、上位のIOException
クラスなどにエラーを投げて終了させたい場合があるとします。
その場合に用いられるのが、throw
です。
上位に投げるため、メソッドにthrows IOExcepton
などを付加する必要があります。(catchの引数が
IOException eなどではなく、単なる
Exception eの場合、
Exception`は付記せずとも予期されているので付記する必要はありません)
下記はthrow
をテストするために上記のIOTryTest.java
をコピーして編集したIOTryTest2.java
との差分です。差分とは、読んで字のごとく、差だけ算出することです。
差分には、Linuxのdiff
コマンドに-upN
オプションをつけて確認しています。
--- IOeTryTest.java 2020-06-26 14:29:55.232975007 +0900
+++ IOeTryTest2.java 2020-06-26 14:32:06.820360485 +0900
@@ -3,8 +3,8 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.FileNotFoundException;
-public class IOeTryTest{
- public static void main(String[] args) {
+public class IOeTryTest2{
+ public static void main(String[] args) throws IOException {
String fname = args[0];
FileReader freader;
BufferedReader buf;
@@ -15,13 +15,13 @@ public class IOeTryTest{
while(buf.ready()){
System.out.println(buf.readLine());
}
- }catch(Exception e){
+ }catch(IOException e){
// Exception(例外処理)クラスのeを持つ。
// Standard Output
System.out.println("CATCH");
// Standard Error Output
System.err.println("error occured.");
- System.err.println(e);
+ throw e;
}finally{
System.out.println("FINALLY");
}
上記では、3行目以降の差分と15行目以降の差分を確認できます。
-
が消えた行、+
が追加された行です。
上記のように、throw
を用いることでもできます。