ほぷしぃ

Java言語入門 〜C言語を学んだ君へ〜

[第13回]例外処理

[1] 例外処理try-catch-finally

まずは、例外処理のもっとも基本的なtry-catch-finallyを見ていきましょう。
try-catchは例外が発生した場合にプログラムが終了することを防ぎます。

try-catch-finallyの形式

try {
    例外が発生する可能性のある処理
} catch (発生した例外クラス 例外の変数名) {
    例外ハンドラ
} catch (発生した例外クラス 例外の変数名){
    例外ハンドラ
}finally{
    最後に必ずしたい処理
}

tryブロックの中に「例外が発生する可能性の処理」を書きます。
例外とはプログラムで対処できる異常事態のことでした。
0で割り算をした、配列の範囲外を指定した等です。

catchブロックの中の引数には、tryブロックで起きる例外処理の種類を書きます。
発生した例外クラスは前ページで紹介したものです。
計算の例外が起きたら、ArithmeticException
配列の範囲外が指定されたら、ArrayIndexOutOfBoundsException
と、使い分けます。

例外の変数名はローカル変数なので、名前はなんでも構いません。

catchブロックの中身には例外が発生した場合に、実行される処理の内容を記述します。
発生した例外とは、引数に指定した例外のみです。
例外ハンドラとは、例外が発生した時の処理のことです。

finallyブロックに書かれた記述は例外が発生しても発生しなくても実行される処理です。

try-catchのルール

・ try1つにつき、catchかfinallyのどちらかを最低1つつけること
・ catchはいくつでもつけることができる
・ finallyをつけるブロックは1つのみ
・ catchの処理順番はif文と同じように上から順番に判断する
・ catchの引数は、サブクラスを先に書き、スーパークラスは後に書く

try-catch-finallyを使う上でこれだけは覚えておいてください。
では、実際にtry-catch-finallyを使ったプログラムを見てみましょう。

[2] プログラムで確認

では、説明が終わったところで実際にプログラム上での処理を見ていきましょう。

try-catch-finallyのプログラム

public class Java13_01{
    public static void main(String args[]){
        func();
    }
    
    public static void func(){
        int array[] = new int[5];
        
        try {
            for(int i = 0; i <= 10; i++){
                array[i] = i;
                System.out.println(array[i]);
            }
        }catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("例外"+e);
        }finally{
            System.out.println("例外チェックが終わりました");
        }
    }
}

実行結果



このプログラムでは、配列の要素が5なのにそれ以上のアクセスしたために例外が起きています。
配列の範囲外を指定しているので、ArrayIndexOutOfBoundsExceptionを使っています。
このプログラムを変更して以下のように書くこともできます。

少し変更したプログラム

public class Java13_01{
    public static void main(String args[]){
        try {
            func();
        }catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("例外"+e);
        }finally{
            System.out.println("例外チェックが終わりました");
        }
    }
    
    public static void func(){
        int array[] = new int[10];
        
        for(int i = 0; i <= 10; i++){
            array[i] = i;
            System.out.println(array[i]);
        }
    }
}

実行結果



同じ結果になりました。
呼び出し元で、try-catch-finallyを使ってメソッドを呼び出したときにも、同じように処理することができます。
これは、例外が発生した時に呼び出し元に自動的に戻り処理を見ているからです。
しかし、1つだけだとその様子があまりわからないと思います。

[3] 複数のtry-catch-finally処理

少し複雑なtry-catch-finally処理を見ていきます。
今回のプログラムはメソッドを次々と呼び出し、どのような処理が行われているのかを考えてみてください。
では、実際に複数のメソッド内でどのように処理をしているのか見てみましょう。

少し複雑なtry-catch-finallyのプログラム

public class Java13_02{
    public static void main(String args[]){
        try{
            System.out.println("main処理開始");
            meth1();
            System.out.println("main処理終了");
        }catch(Exception e){
            System.out.println("mainで例外発生:"+e);
        } finally{
            System.out.println("mainの例外チェック終了");
        }
    }
    
    
    static void meth1(){
        try{
            System.out.println("meth1処理開始");
            meth2();
            System.out.println("meth1処理終了");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("配列の要素外にアクセス:"+e);
        } finally{
            System.out.println("meth1の例外チェック終了");
        }
    }
    
    static void meth2(){
        try{
            System.out.println("meth2処理開始");
            meth3();
            System.out.println("meth2処理終了");
        }catch(NullPointerException e){
            System.out.println("空のインスタンスにアクセス:"+e);
        } finally{
            System.out.println("meth2の例外チェック終了");
        }
    }
    
    static void meth3(){
        try{
            System.out.println("meth3処理開始");
            int array[] = new int[3];
            array[4] = 10;
            
            meth4();
            
            System.out.println("meth3処理終了");
        }catch(ArithmeticException e){
            System.out.println("計算の例外が発生:"+e);
        } finally{
            System.out.println("meth3の例外チェック終了");
        }
    }
    
    static void meth4(){
        System.out.println("meth4の実行");
    }
}

実行結果



どうでしょうか。このような処理になりましたが、予想できましたか。
meth3()メソッド内で、配列要素範囲外による例外が発生しています。
まず、meth3()メソッドのcatchを見ますが、計算例外の場合の処理です。
実際に発生した例外は配列要素範囲外なので違います。

そこで、meth3()メソッドを呼び出したmeth2()メソッドに戻ります。
その前に、meth3()メソッドにfinallyがあり、先にfinallyブロック内の処理が行われます。

次にmeth2()メソッドでcatchをしてみます。
ここでは、空のインスタンスにアクセスしたかどうか見ています。
これも配列要素範囲外指定の例外処理ではありません。

さらに、この処理を飛ばし、meth2()メソッドを呼び出しているmeth1()メソッドに行きます。
meth1()メソッドのcatchを見てみると、配列要素範囲外の例外が書いてありますので catch内の処理を行います。
その後は次々と処理を呼び出し元に返し、プログラムが終了となります。
このプログラムから学んでほしいことは、

・ finallyがいつ実行されるか
・ 例外処理が見つからない時は呼び出し元に返していく

ということです。

これで、もっとも基本的なtry-catch-finallyが終わりました。
まだ例外処理のキーワードが残っていました。
throwthrowsです。次はそちらを学びましょう。



前のページへ ページのトップへ 次のページへ