例外処理

算術処理でゼロ除算を行ったり、配列に対して、その要素数を超えた部分に値を代入しようとすると、Javaは例外として処理を中断させます。例外処理とは、プログラム上で予期しない事態が起こった場合にどのように対処するかを記述するものです。


例外の種類

Javaの例外は、「Throwable」クラスを継承したオブジェクトをスローする事で発生します。例外としてスローされるクラスは、次の3種類に分類されてます。


例外の種別一覧
種類 特徴 継承しているクラス
Exception このクラスがthrowされるソースはコンパイル時に例外処理の実装が強制されます。(検査例外) IOException、ClassNotFoundException、等
RuntimeException このクラスがthrowされるソースはコンパイル時に例外処理の実装が強制されません。(実行時例外) ArithmeticException、IndexOutOfBoundsException、等
Error 処理の継続が難しい致命的な場合であるため、例外処理も記述できません VirtualMachineError、等

実際にスローされるクラスは、「Throwable」クラスを直接継承しているのではなく、上記3種類(Exception、RuntimeException、Error)のクラスのいずれかから派生しています。各クラスの階層構造は次の通りです。

例外階層

例外処理の対象となるものは「Exception」、「RuntimeException」からの派生クラスになります。

▲PageTop

例外処理の構文

例外処理とは、おおよそ次のパターンに分類されます。

  1. スローされた例外をキャッチして処理を行う。
  2. スローされた例外をキャッチせずそのままスローする。
  3. 明示的に例外をスローさせる。

1.スローされた例外をキャッチして処理を行う場合

書式は次のようになります。

try {
    例外がスローされる可能性がある処理;
} catch(例外1の型 変数) {
    例外1をキャッチしたときの処理;
    ※catch節は例外の数だけ指定できます。
} catch(例外2の型 変数) {
    例外2をキャッチしたときの処理;
                  ・
                  ・
} finally {
    必ず実行される処理;
}

※try節で処理中に例外が発生すると、以降の処理は行われず直ちにcatch節へ処理が移行します。


・サンプルソース(Sample1201.java)

public class Sample1201 {
    public static void main (String[] args) {
        // 「IndexOutOfBoundsException」は実行時例外なのでスローされる可能性がありますが例外処理は強制されません。
        String className = args[0];

        try {
            // 検査例外の「ClassNotFoundException」がスローされる可能性があるので例外処理が強制されます。
            System.out.println(Class.forName(className) + "は存在しています。"); 
            System.out.println("例外は発生しませんでした。"); 
        }catch(ClassNotFoundException e) {
            System.out.println("例外が発生しました。");
            e.printStackTrace();
        }
    }
}

・実行結果

C:\dev\java>java Sample1201  ← 引数を指定せずに起動
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0  ← 実行時例外が発生。
        at Sample1201.main(Sample1201.java:4)

C:\dev\java>java Sample1201 Sample0000  ← 存在しないクラス「Sample0000」を指定
例外が発生しました。  ← 検査例外「ClassNotFoundException」が発生してcatch節に処理が移行している。
java.lang.ClassNotFoundException: Sample0000  ← キャッチしたExceptionの詳細(StackTrace)出力の結果
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:164)
        at Sample1201.main(Sample1201.java:8)

C:\dev\java>java Sample1201 Sample1201  ← 存在するクラス「Sample1201」を指定
class Sample1201は存在しています。
例外は発生しませんでした。
        

※例外をキャッチした後の処理について

安易にコンパイルを通す為だけに「try~catch~」を使用したと思われますが、例外をキャッチしたにもかかわらず何も処理を行わない(いわゆる例外の握りつぶし)ロジックを見かけることがあります。これはデバッグしにくい厄介なバグの温床になる可能性がありますので、例外の内容を出力する等の処理をきちんと施しましょう。


2.スローされた例外をキャッチせずそのままスローする場合

「throws」を使用すると、例外処理を行わず、発生した例外をそのままスローすることができます。書式は次の通りです。

メソッド throws 例外オブジェクトリスト {
    処理;
}

・サンプルソース(Sample1202.java)

public class Sample1202 {
    public static void main (String[] args) throws ClassNotFoundException {
        // 「IndexOutOfBoundsException」は実行時例外なのでスローされる可能性がありますが例外処理は強制されません。
        String className = args[0];
        // 検査例外の「ClassNotFoundException」がスローされる可能性があるので例外処理が強制されます。
        System.out.println(Class.forName(className) + "は存在しています。"); 
        System.out.println("例外は発生しませんでした。"); 
    }
}

・実行結果

C:\dev\java>java Sample1202
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at Sample1202.main(Sample1202.java:4)

C:\dev\java>java Sample1202 Sample0000
Exception in thread "main" java.lang.ClassNotFoundException: Sample0000
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:164)
        at Sample1202.main(Sample1202.java:6)

C:\dev\java>java Sample1202 Sample1202
class Sample1202は存在しています。
例外は発生しませんでした。
        

3.明示的に例外をスローさせる場合

「throwステートメント 」を使用して、例外を明示的にスローさせることが出来ます。書式は次の通りです。

throw 例外オブジェクト;

▲PageTop

独自の例外クラス

「Exception」クラスまたは「RuntimeException」クラスを継承して独自の例外クラスを作成する事も可能です。


・サンプルソース(Sample1203.java)

// 独自例外クラス
class Exception1203 extends Exception {
    Exception1203() { super(); }
    Exception1203(String s) { super(s); }
}

// 独自例外の呼び出しクラス
public class Sample1203 {
    public static void main (String[] args) {
        try {
            thrower(args);
            System.out.println("例外は発生しませんでした。");
        }catch(Exception1203 e) {
            e.printStackTrace();
        }
    }
    static int thrower(String[] ary) throws Exception1203 {
        if (ary.length == 0) {
            throw new Exception1203("Exception1203がスローされました。");
        }
        return 0;
    }
}

・実行結果

C:\dev\java>javac Sample1203.java [Enter]

C:\dev\java>java Sample1203 [Enter]
Exception1203: Exception1203がスローされました。
        at Sample1203.thrower(Sample1203.java:19)
        at Sample1203.main(Sample1203.java:11)
        

▲PageTop