メインコンテンツまでスキップ

例外処理

JAL の例外処理は,クラスファイルの例外テーブルを指定する記法です。保護範囲の開始・終了と,例外発生時の移動先をラベルで書きます。

例外を捕捉する

public class SafeDivision (major_version=55, minor_version=0) {
public static divide(II)I {
TryStart: [~ TryEnd, java/lang/ArithmeticException: Failed]
iload_0
iload_1
idiv
TryEnd:
ireturn
Failed:
pop
iconst_0
ireturn
}
}

二つの整数を割り,0 除算による ArithmeticException が発生した場合は 0 を返します。

記法意味
TryStart:保護範囲の開始位置。この位置の命令を含む
[~ TryEnd, ...]保護範囲の終了位置。TryEnd の位置の命令は含まない
java/lang/ArithmeticException: Failed対応する例外を Failed で処理する

例外ハンドラに移ると,それまでのオペランドスタックは破棄され,例外オブジェクト一つが積まれます。この例では例外を使わないため,pop で捨てています。内容を参照したければ astore で保存します。

通常の実行がハンドラへ流れ込まないようにしてください。この例では正常経路が ireturn で終了します。処理を続ける例なら goto でハンドラを飛び越えます。

複数の例外型

TryStart: [~ TryEnd,
java/io/FileNotFoundException: Missing,
java/io/IOException: Failed
]

ハンドラは例外テーブルの順に調べられます。先に広い型を置くと,後ろの狭い型のハンドラに届かなくなる場合があります。上の例では,FileNotFoundException をその親型の IOException より先に置いています。

-> は全例外を受けるハンドラを指定する

TryStart: [~ TryEnd -> Cleanup]

この記法は,指定した保護範囲に,例外型を限定しないハンドラを登録します。Cleanup には例外オブジェクトが一つ積まれた状態で移ります。

Java の finally のように,正常終了,return,捕捉済み例外の後に自動で後処理を挿入する指定ではありません。正常経路からの後処理呼び出しや,例外を保存して後処理の後で athrow する命令は,別途書く必要があります。ハンドラ内部まで保護したければ,その範囲も別に指定します。

例外を投げ直す

ハンドラで受け取った例外は,ローカル変数に保存して後から投げ直せます。次はハンドラ本体の断片です。

astore_2
aload_2
athrow

athrow は参照を消費して例外を送出します。null を渡すと NullPointerException が発生します。