Help us understand the problem. What is going on with this article?

java例外処理の使用規定

More than 1 year has passed since last update.

例外処理する際の規定

try,catch,finallyを際の個人的に思う(か、広く守られている)規定についてまとめてみた。
ほか、守るべき内容があれば、コメントください。

tryでreturnするな

finallyは必ず実行される。

try {
    return 1;
} finally {
    return 2;
}
  1. 戻り値は2である。
  2. tryのreturnは、finallyのreturnに上書きされる可能性がある。

try/catchはループ外にする

Exception生成は無料ではない。

for内
int sum = 0;
for (int i = 0; i < 99900000; i++) {
    try {
        sum += i;
    } catch (Exception ex) {
    }
}
for外
int sum = 0;
try {
    for (int i = 0; i < 99900000; i++) {
        sum += i;
    }
} catch (Exception ex) {
}

時間を計測してみた結果、役21%の性能差がある。
for内:1312
for外:1042

JIT(Just In Time)を無効にしないと、コードが最適化されるため、差はなくなる。
JIT無効オプション:-Djava.compiler=none

例外を投げる前にオブジェクトを有効な状態に戻す

例外が発生したオブジェクトは使い続ける可能性がある。

サンプル
private static class A {
    private final int 最大サイズ = 10;
    private int 現在のサイズ = 0;
    String[] values = new String[20];
    public void add(String val) throws Exception {
        現在のサイズ++;
        if (現在のサイズ > 最大サイズ) {
            throw new Exception("最大サイズ超えた");
        }
        values[現在のサイズ - 1] = val;
    }
}
問題となるコード
現在のサイズ++;
if (現在のサイズ > 最大サイズ) {
    throw new Exception("最大サイズ超えた");
}
  1. 例外は投げたが、このオブジェクトの最大サイズは最大サイズ + 1のままである。
  2. values[現在サイズ]を実行した場合、不正処理になる可能性がある。

例外処理でプロセスを制御するな

正常処理で制御できるプロセスは、例外で処理すべきではない。
1. 可読性が落ちる
2. 例外処理はコストがかかる

例外でプロセスを制御
private static void showWithEx(String[] src) {
    try {
        int i = 0;
        while (true) {
            String s = src[i++];
        }
    } catch (ArrayIndexOutOfBoundsException ex) {
    }
}
改修後
private static void show(String[] src) {
    int i = 0;
    while (i < src.length) {
        String s = src[i++];
    }
}

例外のコスト計測

public static void main(String[] args) {
    final int COUNT = 100000;
    String[] src = {"ab", "cd", "ef"};
    long start = 0L;

    start = System.currentTimeMillis();
    for (int i = 0; i < COUNT; i++) {
        showWithEx(src);
    }
    System.out.println(System.currentTimeMillis() - start);

    start = System.currentTimeMillis();
    for (int i = 0; i < COUNT; i++) {
        show(src);
    }
    System.out.println(System.currentTimeMillis() - start);

    start = System.currentTimeMillis();
    for (int i = 0 ; i < COUNT; i++ ) {
        Object o = new ArrayIndexOutOfBoundsException();
    }
    System.out.println(System.currentTimeMillis() - start);
}
出力
0
156
63
  1. 改修後は、回数をいくら増やしても0だった。
  2. 改修前は156かかった
  3. new ArrayIndexOutOfBoundsException()は、63なので、生成以外のコストも無視できないね

膨大なtryは使うな

処理を全部一つのtryに入れがちで、一見綺麗、シンプルに見えるが、実は罠である。

  1. 「コードが長い = 例外が発生する可能性も高い」ので、どこで発生した例外なのかデバックし辛い
  2. 膨大なtryは、catch節も増え、それらのロジック関係も解決しないといけない
膨大tryはNG
public static void main(String[] args) {
    try {
        // 略
        String src;

        src = "123";
        int val1 = Integer.parseInt(src);

        src = "abc";
        int val2 = Integer.parseInt(src);
        // 略
        src = null;
        src.getBytes();
    } catch (NumberFormatException ex) {
        System.out.println(ex.getMessage());
    } catch (NullPointerException ex ) {
        System.out.println(ex.getMessage());
    }
}

Catch all使うな

上位例外クラス(Throwable)一つでcatchすることはNG。

  1. 各種例外に対して同じ対処しかできない。 catch内でif、instanceOfを使って処理は分けれるが・・・やめて・・・
  2. RuntimeExceptionのような、プログラム中止すべき場合もcatchされて、誰も知らないまま制圧されちゃう
使ってはいけないCatch_all
try {
    // Checked Exception
    File f = new File("存在しない");
    int b = new FileInputStream(f).read();
    // Runtime Exception
    Object o = null;
    o.getClass();
} catch (Throwable ex) {
    ex.printStackTrace();
}

例外を隠すな

catch,finallyが例外を投げた場合、例外内容が隠される場合がある。

public static void main(String[] args) {
    try {
        m1();
    } catch (Exception ex) {
        System.out.println("In main : " + ex.getMessage());
    }
}
private static void m1() throws Exception {
    try {
        System.out.println("m1 : try");
        throw new Exception("First EX");
    } catch (Exception ex) {
        System.out.println("m1 : catch");
        throw new Exception("Second EX");
    } finally {
        System.out.println("m1 : finally");
        throw new Exception("Third EX");
    }
}
出力
m1 : try
m1 : catch
m1 : finally
In main : Third EX.

根本原因はthrow new Exception("First EX");なのに、
拾ったのはthrow new Exception("Third EX");である。

例外を無視するな

例外をcatchしたが、「なにもしない若しくはログだけ出力する」のは、隠蔽工作であり
プログラムに健全性に大きな影響を与える。

例外無視
try {
    File f = new File("存在しない");
    int b = new FileInputStream(f).read();
} catch (FileNotFoundException ex) {
} catch (IOException ex) {
}

適切な例外処理
1. Checked例外に関しては、プログラム内で修復すべき
2. 修復できない例外は、throwsを使って呼び出し元へ修復を依頼する

おまけ

(常用)例外クラス体系

Ex-class.png

例外クラスの分類

  • Runtime Exception
    • RuntimeExceptionか継承したクラス
    • プログラムで修復不可能だと見られる例外
    • catchしなくてよい
  • Checked Exception
    • RuntimeException以外のクラス
    • プログラムで修復可能だと見られる例外
    • catchもしくはthrowsするように強制されちゃう
liguofeng29
フリーエンジニア。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした