例外処理

Javaは、例外処理としてtry,catch,finally節を構文として提供しています。Throwableインターフェイスを実装したExceptionクラスを利用して、
例外が発生しうる処理にtry,catch,finally節を記述します。

例外とは、プログラムの実行中に発生し、プログラムの命令の通常の流れを中断させるイベントのことです。

例外処理の構文

try,catch,finally
try{

//例外の起こりえる処理

doAnythingOne();

doAnythingTwo();

doAnythingLast();

} catch ( 例外クラスA e ){

//catch節

doSomethingA();

} catch ( 例外クラスB e ){

//catch節

doSomethingB();

} finally {

//finally節

doFinal();

}

例外処理のサンプル

例外処理のサンプルの動きを確認して、理解度を高めましょう。


package jp.co.yourcompany.education.samples;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;

import jp.co.yourcompany.education.log.InitApplication;

public class JavaTrySample {

    /**
     * 正常データ
     */
    public static final String TEST_FILE = "c:¥¥projects¥¥education¥¥data¥¥input¥¥sample_data.txt";

    /**
     * 存在しないファイル
     */
    public static final String VALID_TEST_FILE = "c:¥¥projects¥¥education¥¥data¥¥input¥¥invalid_data.txt";

    /**
     * ファイルのエンコード
     */
    public static final String FILE_ENCODING = "UTF-8";

    /**
     * ログ
     */
    public static final Logger log = Logger.getLogger( JavaTrySample.class.getName() );

    /**
     * デフォルトコンストラクタ
     * ログの初期化処理を実施する。
     */
    public JavaTrySample(){
        InitApplication app = InitApplication.getInstance();
        app.init();
    }

    /**
     *  全ての例外処理を実行する。
     */
    public void execAllSample(){

        log.info("正常ファイルの読取りを行います。");
        fileReadSample( JavaTrySample.TEST_FILE );
        log.info("正常ファイルの読取りが正常に終了しました");

        log.info("存在しないファイルの読取りを行います。");
        fileReadSample( JavaTrySample.VALID_TEST_FILE );
        log.info("存在しないファイルの読取りが正常に終了しました");

    }

    /**
     * ファイルの読見込み処理
     * @param filePath 読み込むファイルのエラー
     */
    private void fileReadSample( String filePath ){

        //アプリケーションが動作するPCに保存されているファイル
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        try {
            log.info("STEP1 init Stream");

            fis = new FileInputStream( filePath );
            isr = new InputStreamReader( fis , JavaTrySample.FILE_ENCODING );
            br = new BufferedReader( isr );

            log.info("STEP2 loop start");

            String line;
            while ( ( line = br.readLine() ) != null ) {
                System.out.println(line);
            }

            log.info("STEP3 try block end.");

        } catch (FileNotFoundException e) {
            log.log( Level.SEVERE , "指定されたファイル{0}が存在しません。" , new String[]{ filePath } );
            log.log( Level.SEVERE , "例外内容:" , e );
        } catch (UnsupportedEncodingException e) {
            String[] params = { filePath , JavaTrySample.FILE_ENCODING };
            log.log( Level.SEVERE , "指定されたファイル{0}の文字コードが、{1}ではありません。" , params );
            log.log( Level.SEVERE , "例外内容:" , e );
        } catch (IOException e) {
            log.log( Level.SEVERE , "指定されたファイル{0}読取時に例外が発生しました。" , new String[] { filePath }  );
            log.log( Level.SEVERE , "例外内容:" , e );
        } finally {
            try{
                if( br != null ){
                    br.close();
                }
                if( isr != null ){
                    isr.close();
                }
                if( fis != null ){
                    fis.close();
                }
                log.info("STEP4 finally block end.");
            } catch ( IOException e ){
                log.log( Level.SEVERE , "指定されたファイル{0}のclose時に例外が発生しました。" , new String[]{ filePath } );
                log.log( Level.SEVERE , "例外内容:" , e );
            }
        }
    }

    /**
     * javaコマンドから実行されるmainメソッド
     * @param args コマンド引数(不要)
     */
    public static final void main( String[] args){
        JavaTrySample sample = new JavaTrySample();
        sample.execAllSample();
    }
}				
標準出力
2017/03/15 08:38:40.417 情報 jp.co.yourcompany.education.samples.JavaTrySample execAllSample 正常ファイルの読取りを行います。
2017/03/15 08:38:40.470 情報 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample STEP1 init Stream
2017/03/15 08:38:40.471 情報 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample STEP2 loop start
文字コード:UTF-8
改行コード:CRLF
Javaのファイル読み込み用サンプルデータ
2017/03/15 08:38:40.473 情報 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample STEP3 try block end.
2017/03/15 08:38:40.473 情報 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample STEP4 finally block end.
2017/03/15 08:38:40.474 情報 jp.co.yourcompany.education.samples.JavaTrySample execAllSample 正常ファイルの読取りが正常に終了しました
2017/03/15 08:38:40.475 情報 jp.co.yourcompany.education.samples.JavaTrySample execAllSample 存在しないファイルの読取りを行います。
2017/03/15 08:38:40.475 情報 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample STEP1 init Stream
2017/03/15 08:38:40.477 重大 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample 指定されたファイルc:\projects\education\data\input\invalid_data.txtが存在しません。
2017/03/15 08:38:40.479 重大 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample 例外内容:
java.io.FileNotFoundException: c:\projects\education\data\input\invalid_data.txt (指定されたファイルが見つかりません。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at jp.co.yourcompany.education.samples.JavaTrySample.fileReadSample(JavaTrySample.java:85)
	at jp.co.yourcompany.education.samples.JavaTrySample.execAllSample(JavaTrySample.java:67)
	at jp.co.yourcompany.education.samples.JavaTrySample.main(JavaTrySample.java:132)

2017/03/15 08:38:40.481 情報 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample STEP4 finally block end.
2017/03/15 08:38:40.482 情報 jp.co.yourcompany.education.samples.JavaTrySample execAllSample 存在しないファイルの読取りが正常に終了しました
				
ログファイル
    2017/03/15 08:38:40.477 重大 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample 指定されたファイルc:¥projects¥educatione¥data¥input¥invalid_data.txtが存在しません。
    2017/03/15 08:38:40.479 重大 jp.co.yourcompany.education.samples.JavaTrySample fileReadSample 例外内容:
    java.io.FileNotFoundException: c:\projects\education\data\input\invalid_data.txt (指定されたファイルが見つかりません。)
        at java.io.FileInputStream.open0(Native Method)
        at java.io.FileInputStream.open(FileInputStream.java:195)
        at java.io.FileInputStream.<init>(FileInputStream.java:138)
        at java.io.FileInputStream.<init>(FileInputStream.java:93)
        at jp.co.yourcompany.education.samples.JavaTrySample.fileReadSample(JavaTrySample.java:85)
        at jp.co.yourcompany.education.samples.JavaTrySample.execAllSample(JavaTrySample.java:67)
        at jp.co.yourcompany.education.samples.JavaTrySample.main(JavaTrySample.java:132)
				

内容について1つ1つ丁寧に検証していきます。
ファイル読取り処理が正常な処理が実行された場合には、

  1. 「STEP1 init Stream」がログに出力されます。
  2. Streamオブジェクトが初期化されます。
  3. 「STEP2 loop start」がログに出力されます。
  4. ループ処理でファイルの内容が標準出力出力されます。
  5. 「STEP3 try block end.」がログに出力されます。
  6. Streamオブジェクトのcloseメソッドが実行されます。
  7. 「STEP4 finally block end.」がログに出力されます。

正常なファイルを読み取らせた場合には、標準出力及びログファイルから上記実行順になっている事がわかります。

存在しないファイル読み取らせた場合には、

  1. STEP1 init Stream」がログに出力されます。
  2. FileNotFoundExceptionのcatch節が実行されました。
  3. エラーログに「指定されたファイルが{filePath}が存在しません。」のエラーログが出力されました。
  4. エラーログに「例外内容」及びFileFoundNotExceptionのエラーメッセージがエラーログに出力されました。
  5. 「STEP4 finally block end.」がログに出力されます。

この実行結果から、正常処理の流れである「STEP3 ...」「STEP4...」のログ出力が実行されなかった事がわかります。

例外処理の要点

例外処理の要点について説明します。

  1. 例外処理を行うcatch節で必ずエラーログに例外クラスを出力する。
  2. 例外処理にすべきでない例外をcatchしてはいけない。
    JavaTrySampleのfileReadSampleのcatch節に以下を追加
        } catch (NullPointerException e) {
        }
    							

    これは、プログラムの処理の流れと構成を正しく作れば起きえない事項です。
    nullポで落ちる機能は、バグです。例外ハンドリングで拾ってはいけません。

  3. catch節でExceptionクラス1つだけの定義で全ての例外を拾ってはいけません。
JavaTrySampleのfileReadSampleのcatch節を以下の1つで定義
    } catch (Exception e) {
        log.log( Level.SEVERE , "例外内容:" , e );
    }
				

JavaAPIに定義されたExceptionを継承した具体的な例外クラスを1つ1つ拾うようにします。