Javaの例外処理について書きます。
自分はPHPメインでやってきましたので、この例外についてはうろ覚えでした。
PHPだと例外処理はあるんですが、まああんまり使わなかったり、理解がいい加減でもなんとかなります(笑)
例外とは
Javaでは文法エラーがあった場合にはコンパイル時にエラーになります。
しかし実際にプログラムを走らせたときのエラーに関してはコンパイル時では検知できません。
例としては
- 参照すべき配列の要素がない
- 参照すべき値がない(NULL)
- ファイルを読み込んだのにファイルが存在しない
- メモリ不足により実行できない
・・・
などなどです。
このような場合、Javaでは「例外」という事象が発生し、プログラムが強制終了されます。(例外をスローする、という表現をします。)
このままにしておくと、例外がスローされたときに原因追求ができません。
そこで業務ではログに吐いたりして記録を残すことが一般的ですが、今回はとりあえず標準出力に出す、ということにします。
例外の分類
一口に例外といってもプログラマのミスによるものと予測不可能なものがあります。
先ほどの1~4の例で言うと1~3まではプログラマが制御することが可能ですが、4のメモリうんぬんに関してはプログラマ側でどうすることもできません。
この場合の例外処理に関しては任意となっており、例外処理を書かなくても大丈夫です。
(Errorクラスによる例外)
ただし、1~3までは当然プログラマの責任なので例外処理は必須になります。
(Exceptionクラスによるもの)
例外を書かないとき
まずは例外を書かない場合のケースです。
サンプルコードはファイル読込処理を行っていますが、そのファイルが存在せず、IOExceptionという例外が発生するケースを想定します。
Sample.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class Sample { public static void main(String[] args) { Sample sp = new Sample(); sp.func0(); } public Sample() { } /** * 例外なし */ public void func0() { String filePath = "C:\\hogehoge.txt"; BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), UTF_8)); String str = br.readLine(); while (str != null) { System.out.print(str + "\n"); str = br.readLine(); } br.close(); } } |
実際にはC直下にhogehoge.txtは存在しません。
この場合、実行すると強制終了し、コンソールには下記のようなメッセージが出力されます。
1 2 3 4 |
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - 例外java.io.FileNotFoundExceptionは報告されません。スローするには、捕捉または宣言する必要があります at sample.Sample.func0(Sample.java:34) at sample.Sample.main(Sample.java:23) Java Result: 1 |
基本の書き方
上記のソースの例外を処理して見ましょう。
※以下、public static void main・・とSampleコンストラクタの部分はほとんど一緒なので省略し、funcの部分だけを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public void func1() { try { //ありえないディレクトリを取る String filePath = "C:\\hogehoge.txt"; BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), UTF_8)); String str = br.readLine(); while (str != null) { System.out.print(str + "\n"); str = br.readLine(); } br.close(); } catch (IOException e) { System.out.println(e.getMessage()); } } |
標準出力に「C:\hogehoge.txt (指定されたファイルが見つかりません。)」というメッセージが出力されます。
下記のように記述することで例外が発生した場合にそれを検知して処理を行うことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
try{ //ここに例外が起こる場合のソースを書く //catchには例外が起きた場合の処理を書く //IOException eとは発生する例外であるExceptionクラスとそのインスタンスを書く }catch(IOException e){ //ここに実際に起こった場合の処理を書く //System.out.println(e.getMessage()); //というのはIOExceptionのなかに実装されているgetMessageという //メソッドを呼び出し、メッセージを標準出力に出している。 } |
また例外を検知する処理を書きながらcatch節に何も書かないことを例外を握りつぶすといい、やってはいけません。
呼び出し元にthrow
上記は実際の発生箇所で例外処理をしていましたが、メソッドなどを記述する場合、呼び出し元に例外処理を送ることもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/** * 例外をキャッチするメソッド */ public void func3() { try { subFunc1(); } catch (IOException ioe) { System.out.println(ioe.getMessage()); } } /** * 例外を自分でcatchせずに呼び出し基に投げる * @throws IOException */ public void subFunc1() throws IOException { //ありえないディレクトリを取る String filePath = "C:\\hogehoge.txt"; BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), UTF_8)); String str = br.readLine(); while (str != null) { System.out.print(str + "\n"); str = br.readLine(); } br.close(); } |
上記のソースの場合実際に例外が起こるのはsubFunc1ですが、ここで例外処理はしません。
呼び出しもとのfunc3で実際の処理を行いますのでthrows IOExceptionと記述し、try~catchはfunc3に記述します。
オリジナルの例外を作成し、任意のタイミングで投げる
上記の例では全てJava側が自動的に例外を検知し、それをtry~catchで受けるというものでしたが、自分で例外クラスを作成し、任意のタイミングで例外を投げることができます。
Sample.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** * 例外をキャッチするメソッド */ public void func4() { try{ subFunc2(); }catch(SampleException se){ System.out.println(se.getErrorSampleMessage()); } } /** * 意図的な例外発生 * @throws SampleException */ public void subFunc2() throws SampleException{ String str = ""; if( str.length() != 0 ){ System.out.println(str); }else{ throw new SampleException("文字が空です。"); } } |
SampleException.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class SampleException extends Exception { private String errorSampleMesssage; public SampleException(){ } public SampleException(String errorSampleMesssage){ this.errorSampleMesssage = errorSampleMesssage; } public void setErrorSampleMessage(String errorSampleMesssage){ this.errorSampleMesssage = errorSampleMesssage; } public String getErrorSampleMessage(){ return this.errorSampleMesssage; } } |
上記の例ではSampleExceptionという例外クラスを作り、任意のタイミングでこのクラスに例外を投げます。
投げているのは
1 |
throw new SampleException("文字が空です。"); |
の部分です。throwと書くことで例外を意図的に発生させます。
※他にfinallyなんかの処理もありますが、また別の機会に・・・