throws,throw

throws、throw構文は、Exceptionを継承した例外クラスを呼び出し元のメソッドに通達させる方法です。

throws,throwの概要

throwsの概要図

Web3階層アーキテクチャにSpringFrameworkを用いて、Webサービスを実行している過程で例外が発生した概要図です。

SpringFrameworkなどを利用して、データベースにデータの保存を全体で行う概要です。

  1. データ層であるDaoクラスの処理Cメソッド実行中に例外が発生しました。
  2. このとき発生した例外は、java.sql.SQLExceptionなどのJavaAPIのメソッドのthrows節に定義された例外です。
  3. Daoクラスは、呼び出し元のServiceクラスの処理Bに例外を通達させるためにDaoExceptionのインスタンスをthrowします。
  4. Serviceクラスでは、DaoExceptionを例外処理でcatchします。
    この時Serviceクラスは、Daoクラスで具体的なJavaAPIの例外クラスが発生したかの詳細は知りません。
    Serviceクラスに対する処理要求である処理Cメソッドが失敗したことだけが判断できます。
  5. Serviceクラスでは、プレゼンテーション層のControllerクラスにServiceExceptionのインスタンスをthrowします。
  6. ※.例外クラスがサービス層からプレゼンテーション層に上がった事により、SpringFrameworkで設定していた
    トランザクションレイヤー(データベースのcommit,rollbackの管理)でrollbackが実行されます。
  7. Controllerクラスでは、ServiceExceptionを例外処理でcatchします。
  8. Controllerクラスは、例外処理を実行し、対応するエラー画面などに画面作成処理を委譲します。

DaoException,ServiceExceptionはExceptionを継承したプロジェクト独自の例外クラスです。

この処理の全体の流れのように、例外が発生した処理から呼び出し元に通達する義務があります。

そして、大きな機能の層(プレゼンテーション層、サービス層、データ層)を跨ぐときに、
その層からの共通の例外クラス(ServiceException,DaoExceptionなど)のインスタンスを生成し、呼び出し元に通達させます。

最後に、全体の最終処理をする(上記ではControllerクラス)クラスで例外のthrowが終了します。

throw節の構文

throw構文
throw 例外クラスのインスタンス;
throws構文
アクセス修飾子 戻り値 メソッド名( 引数 )throws 例外クラス,例外クラス2 {

//例外処理を含む処理

doSomething();

}

最も簡単なthrowのサンプル
    private void throwExceptionSample() throws ApplicationException{
        throw new ApplicationException();
    }
					

この例は、例外をスローするための最小限のサンプルです。実際にthrow構文を処理するブロックはcatch節です。

throw,throwsのサンプル

前頁のサンプルにthrow,throwsを追加したサンプルです。

1つのクラスで例外処理が閉じられているサンプル
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.exception.ApplicationException;
import jp.co.yourcompany.education.log.InitApplication;

/**
 * Java例外処理(try,catch,finally,throw,thorws)学習用のクラス
 * @author raita.kuwabara
 */
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(){

        try{

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

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

        }catch( ApplicationException e ){
            log.severe("例外が発生したため処理を異常終了しました。");
            log.log( Level.SEVERE , "例外内容:" , e );
        }

    }

    /**
     * ファイルの読見込み処理
     * @param filePath 読み込むファイルのエラー
     * @throws ファイルの読み込みエラー、対応していない文字コードエラー、ファイルclose時
     */
    private void fileReadSample( String filePath ) throws ApplicationException {

        //アプリケーションが動作する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 );
            throw new ApplicationException( e );
        } catch (UnsupportedEncodingException e) {
            String[] params = { filePath , JavaTrySample.FILE_ENCODING };
            log.log( Level.SEVERE , "指定されたファイル{0}の文字コードが、{1}ではありません。" , params );
            log.log( Level.SEVERE , "例外内容:" , e );
            throw new ApplicationException( e );
        } catch (IOException e) {
            log.log( Level.SEVERE , "指定されたファイル{0}読取時に例外が発生しました。" , new String[] { filePath }  );
            log.log( Level.SEVERE , "例外内容:" , e );
            throw new ApplicationException( 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 );
                throw new ApplicationException( e );
            }
        }
    }

    /**
     * javaコマンドから実行されるmainメソッド
     * @param args コマンド引数(不要)
     */
    public static final void main( String[] args){
        JavaTrySample sample = new JavaTrySample();
        sample.execAllSample();
    }
}
				
ApplicationException.java
package jp.co.yourcompany.education.exception;

/**
 * 例外クラスのサンプル
 * @author raita.kuwabara
 */
public class ApplicationException extends Exception{

    /**
     * デフォルトコンストラクタ
     */
    public ApplicationException() {
        super();
    }

    /**
     * デフォルトコンストラクタ
     * @param e 例外
     */
    public ApplicationException( Throwable e) {
        super( e );
    }
}
				

例外クラスは、java.lang.Exceptionクラスを継承して作成します。
高度なクラス設計ができ、システム独自の例外クラスを作成する場合には、
java.lang.Throwableインターフェイスをimplementsして例外を作成します。

例外処理の指針

本頁で示したthrowsの概要図で説明します。

  • DaoクラスCメソッドのように例外が発生したメソッドのcatch節でJavaAPIのExceptionをエラーログに出力します。
  • 大きな機能の単位に例外クラスを作成します。
  • 大きな機能の単位を跨ぐ際に作成した例外クラスに原因となったExceptionを引き渡しして、throwします。
  • 途中の各機能のメソッドでは、エラーログに簡潔にエラーメッセージを出力します。
  • 最後に例外をハンドリングするクラス(Controller)のメソッドでは、再度Exceptionのインスタンスをログに引き渡し、エラーログを出力します。
  • ログファイルは、行を追加する形式なのでファイルサイズが肥大化していきます。
    サーバーやPCのログの場合には、適度なサイズでログファイルのローテーションを実施します。
  • ハードウェアの制限のあるIoT,モバイル,組み込み系などは、予めログのサイズを決め、
    ログを一定サイズに増えないようにします。ログファイルは、直前のエラーだけ残すなどの方式を検討します。
  • ログファイルは、ビジネスシステムにおいて、運用性を高めるファイルです。 ログファイルを「elasticsearch」「Logstash」などの機能で集計しやすいようにログの出力形式を設計時に決め、
    決まった書式で例外が出力されるようにします。

サンプルのダウンロードと実行方法

  • java_sample16.zipファイルを「c:¥download¥java¥samples¥」に保存して下さい。
  • java_sample16.zipファイルを「c:¥projects」配下に展開して下さい。
  • コマンドプロンプトを起動して、「sample16.bat」を実行して下さい。
  • javac,jar,javaが実行されてJavaTrySampleが実行されます。
  • 「c:¥projects¥education¥logs」配下のログの実行結果を確認して下さい。
  • ※.コマンドプロンプトに例外が出力されます。(例外のサンプルのため)
  • Linux,Unix,iOSの方はバッチファイルはお手数ですが作成して下さい。改行コードが「CRLF」の点ご留意ください。