読者です 読者をやめる 読者になる 読者になる

酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Javaチュートリアル 第9章 例外

Javaチュートリアル 第9章 例外

Javaチュートリアル 第4版 (The Java Series)」を読みながら、Javaの復習をしています。

Javaチュートリアル 第4版 (The Java Series)

Javaチュートリアル 第4版 (The Java Series)

  • 作者: シャロンザクァワ,ジャコブロイヤル,アイザックラビノビッチ,マークホーバ,トーマスリーサ,スコットホンメル,Sharon Zakhour,Isaac Rabinovitch,Thomas Risser,Jacob Royal,Scott Hommel,Mark Hoeber,安藤慶一
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2007/11
  • メディア: 単行本
  • 購入: 12人 クリック: 505回
  • この商品を含むブログ (8件) を見る

例外の定義は、「プログラムの実行時に命令の通常の流れを妨害するイベント」です。

メソッド内でエラーが発生すると、実行時システムに例外オブジェクトを渡します。これを「例外をスロー(throw)する」と言います。

一旦、メソッドから例外が投げられると、その投げられた例外をキャッチ(catch)出来る例外ハンドラ(exception handler)を見つけるまで、コールスタックを辿り続けます。

キャッチもしくは明記の要件

例外を投げる可能性のあるコードは、下記のいずれかに含める必要があります。

  • その例外をキャッチするtry文
  • その例外を投げる可能性があると明記したメソッド

例外の3つの種類

エラーと実行時例外は、チェックなし例外(unchecked exception)とも呼ばれます。

  • チェック済み例外(checked exception)
  • エラー(error)
  • 実行時例外(runtime exception)

1.チェック済み例外(checked exception)

予期出来て回復の必要な例外です。「キャッチもしくは明記の要件」に縛られます。

2.エラー(error)

アプリケーションの外側で発生し、想定や回復の出来ない例外です。アプリケーションではこの例外をキャッチしても構わないが、スタックトレースを出力して終了するのも悪くないです。

3.実行時例外(runtime exception)

アプリケーションの内部で発生し、想定や回復の出来ない例外です。この種の例外は、ロジックミス等のプログラミング上のバグを表しています。

キャッチして継続するのも良いですが、一般的にはバグを修正すべきです。

キャッチと明記の回避

「キャッチもしくは明記の要件」の例外のメカニズムを欠陥だとみなし、チェック済み例外の代わりにチェックなし例外を使用するプログラマもいます。

例外のキャッチと処理

チェック済み例外と、実行時例外(チェックなし例外)をわざと発生させるクラスTestExceptionClassを書いてみました。

import java.io.*;  
public class TestExceptionClass {  
  
    // 実行時例外の発生するメソッド  
    public void run() {  
        // 10個分の配列を生成  
        Integer[] array = new Integer[10];  
  
        // ループの継続条件は本来i<10をすべきな所を  
        // i<=10にして、array[10]にアクセスする  
        for (int i=0; i<=10; i++)  
        {  
            array[i] = 256;  
        }  
    }  
  
    // チェック済み例外の発生するメソッド  
    public void run2() throws FileNotFoundException {  
        String hoge = null;  
  
        // ファイルパスの変わりにnullをつっこむ  
        FileReader fr = new FileReader(hoge);  
    }  
}

メソッドrun、run2の呼び出し元は下記の通りです。

public class Main {    
    public static void main(String[] args) {  
        TestExceptionClass testExceptCls = new TestExceptionClass();  
  
        // 実行時例外(チェックなし例外)を発生させる  
        testExceptCls.run();  
  
        // チェック済み例外を発生させる  
        testExceptCls.run2();  
    }  
}

これをコンパイルさせると、メソッドrun2にて下記の様なコンパイルエラーが発生します。

TestExceptionClass.java:33: 例外 java.io.FileNotFoundException は報告されません。スローするにはキャッチまたは、スロー宣言をしなければなりません。

FileReader fr = new FileReader(hoge);

チェック済み例外にも関わらず、例外ハンドラを用意していない為、コンパイルエラーが発生している様です。これによってjava.io.FileNotFoundExceptionをキャッチしていない事がコンパイル時点で発見することが出来ます。

しかし、チェックなし例外は実行してみないと例外が投げられる事はありません。つまりメソッドrunは実行してみない限りバグがある事は判りません。

メソッドrunは実行すると、例外java.lang.ArrayIndexOutOfBoundsExceptionが投げられ、下記の通りコールスタックが表示され、プログラムが終了します。

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
        at helloworld.TestExceptionClass.run(TestExceptionClass.java:26)
        at helloworld.Main.main(Main.java:18)

try ブロック

chatch ブロック

finally ブロック

全体を組み立てる

// チェック済み例外の発生するメソッド  
public void run2() {  
    // ありえないファイルパス  
    String hoge = "c:\\hogehoge";  
  
    try   
    {  
        // チェック済み例外を発生させる  
        FileReader fr = new FileReader(hoge);  
    }  
    catch (java.io.FileNotFoundException e)  
    {  
        // 例外 FileNotFoundExceptionをキャッチする  
        System.out.println("catch: " + e.getMessage());  
    }  
    finally  
    {  
        // ここはtryブロックを抜ける際に必ず実行される  
    }  
}  

メソッドがスルーする例外の明記

例外をスルーする

import java.io.*;  
import java.lang.*;  
public class TestExceptionClass {  
  
    // 例外  
    public void throwException(boolean isBool)   
            throws FileNotFoundException, NullPointerException  
    {  
        if(isBool)  
        {  
            //   
            throw new FileNotFoundException();  
        }  
        else  
        {  
            throw new NullPointerException();  
        }  
    }  
}  

メソッドのthrows節を書くことで、例外が投げられる可能性をメソッド呼び出し元に伝えることが出来ます(呼び元に例外をキャッチする事を強制出来る)。

例外 java.lang.NullPointerException は、チェックなし例外なのでメソッドのthrows節に明記する必要はありません。

Throwableとそのサブクラス

クラスThrowableにはErrorとExceptionという直系の子孫がいる。ほとんどの例外はクラスExceptionから派生したものである。

例外チェーン

import java.io.*;  
import java.lang.*;  
public class TestExceptionClass {  
  
    // 例外  
    public void throwException(boolean isBool)   
            throws FileNotFoundException, NullPointerException  
    {  
        if(isBool)  
        {  
            //   
            throw new FileNotFoundException();  
        }  
        else  
        {  
            throw new NullPointerException();  
        }  
    }  
} 

チェックされない例外をめぐる論争

雑感

例外についてイマイチ理解しきれていない感があるので復習が必要(特にクラスExceptionの継承と、例外チェーンについて)。