2015年10月30日金曜日

try、catch、throwと例外処理について

javaでプログラミングをしているとよく見かけるのがtry、catch、throwの3つです。しかし、普通に使っていれば例外のでないような簡単なプログラミングしかしたことのない初心者にはこの3つがなんのためのものなのかわかりません。

これらはなんのためのものなのでしょうか。これらは3つとも例外処理のためのものです。なお、javaでは例外のことをExceptionといいます。

この例外処理が発生するものというのはあらかじめ決まっており、例えばSimpleDateFormatのParse(String型データのDate型データへの変換)の際の例外ParseExceptionやファイル読み込み時の例外であるIOExceptionなどがあります。これら例外処理が発生するものをコードに書く際には必ずその例外が発生したときにどうするのかを書かなくてはいけません。その例外処理を記述・対処するのがtry, catch および throw です。

try と catch

tryとcatchは別々のものではなく2つでひとつのセットです。
まずtry文の中に例外処理が必要なコードを書きます。
次にcatch文のなかに「例外が発生したときに実行したい処理」を書きます。

以下の例ではtry文のなかに「Parse Exception」の発生するsdf.parse();を書いています。
その例外「Parse Exception」が起こったときにはcatch文内に書いてあるように「Parse dekinaiyo」と表示されます。Parseは解体とか分析とかいう意味で、sdf.parse()はString型の日付をDate型の日付へと変換しています。

Parse Exceptionというのはその変換に失敗したときにでる例外です。例えば「指定されたフォーマットとString型のデータのフォーマットが全然違う」とかそもそも「String型のデータではあるけど日付ではないものが指定されている」とかの場合にParseに失敗し、Parse exceptionが発生します。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Test1 {
public static void main(String[] args) {
showDate("01/02/2014 01:30:30");
showDate("aaa");
showDate("11/12/2015 07:42:31");
showDate("11-12-2015");
}
static void showDate(String date){
try{
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date1 = sdf.parse(date);
System.out.println(date1);
}catch(ParseException err){
System.out.println("Parse dekinaiyo");
}
}
}
実行結果。「aaa」や「11-12-2015(区切りが / じゃない=フォーマットが違う)」など
Parseできないもの(Parse exception)に対してはcatch文内が実行される。

実行結果を見ると変換に失敗したときにだけ(Parse exceptionが発生したときにだけ)「System.out.println("Parse dekinaiyo")」が実行されているのがわかりますね。

でも、こんなふうに「Parse dekinaiyo」とだけ表示してもあまり意味がないので、ふつう、ここには「printStackTrace」などを書きます。printStackTraceは例外(エラー)が起こった原因(というかエラーの過程)を画面に表示してくれるもので、主にデバッグに使用されます。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Test1{
public static void main(String[] args) {
showDate("01/02/2014 01:30:30");
showDate("aaa");
showDate("11/12/2015 07:42:31");
showDate("11-12-2015");
}
static void showDate(String date){
try{
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date1 = sdf.parse(date);
System.out.println(date1);
}catch(ParseException err){
err.printStackTrace();
}
}
}
実行結果。今度はParse dekinaiyoではなくて、例外(エラー)の原因が表示されています。
読むと「aaa」および「11-12-2015」っていう日付が変換できなかったことが書いてあります。
するとこの情報からプログラマーは対処する方法を考えることができます。

ちなみにtry文は一度書くだけでいいですが、そのtry文でParseExceptionだけでなくIOExceptionも発生する場合、その二つに対して個別にcatch文を書く必要があります。つまり、try文は一つでいいですが、catch文は例外処理の数だけ必要になります。

throwについて

throwは投げるという意味の英語で、その名の通り例外処理をそのメソッドで行わずに呼び出し元へぶん投げます。

たとえばthrowで例外処理をするmethod1をmainメソッドが呼び出すとします。

このとき、method1で仮に例外が発生したとすると、method1を呼び出すmainメソッドにその例外処理をさせます。method1を呼び出したmainメソッドはtry, catchによってその例外を処理しなければいけません。例外を発生させているのはmethod1なのですが、その例外処理はmainメソッドにぶん投げるわけです。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestTest {
public static void main(String[] args) {
try{
showDate("01/02/2014 01:30:30");
showDate("aaa");
showDate("11/12/2015 07:42:31");
showDate("11-12-2015");
}catch(ParseException err){
err.printStackTrace();
}
}
static void showDate(String date) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date1 = sdf.parse(date);
System.out.println(date1);
}
}
実行結果。上記のプログラムにおいて例外を発生させているのはshowDateメソッドだが、
この例外処理はthrowされているので、
呼び出し元のmainメソッドがtry catchによってその例外を処理する
このとき例外はmainメソッド内部で発生したとされるので、
例外発生時点で処理が中断し、catch文内が実行される。

上記のコードでは、実際に例外を発生させているのはshowDateメソッドです。しかし、その例外処理はthrowされているので、その例外は呼び出し元のmainメソッドで発生したと見なされ、例外処理はmainメソッドが行っています。