java例外処理の使用規定

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

## try/catchはループ外にする
Exception生成は無料ではない。

```java:for内
int sum = 0;
for (int i = 0; i < 99900000; i++) {
    try {
        sum += i;
    } catch (Exception ex) {
    }
}
```
```java: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

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

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

```java:サンプル
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;
    }
}
```
```java:問題となるコード
現在のサイズ++;
if (現在のサイズ > 最大サイズ) {
    throw new Exception("最大サイズ超えた");
}
```
1. 例外は投げたが、このオブジェクトの最大サイズは`最大サイズ + 1`のままである。
2. `values[現在サイズ]`を実行した場合、不正処理になる可能性がある。

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

```java:例外でプロセスを制御
private static void showWithEx(String[] src) {
    try {
        int i = 0;
        while (true) {
            String s = src[i++];
        }
    } catch (ArrayIndexOutOfBoundsException ex) {
    }
}
```

```java:改修後
private static void show(String[] src) {
    int i = 0;
    while (i < src.length) {
        String s = src[i++];
    }
}
```

例外のコスト計測

```java:
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);
}
```
```text:出力
0
156
63
```
1. 改修後は、回数をいくら増やしても0だった。
2. 改修前は156かかった
3. new ArrayIndexOutOfBoundsException()は、63なので、生成以外のコストも無視できないね

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

1. 「コードが長い = 例外が発生する可能性も高い」ので、どこで発生した例外なのかデバックし辛い
2. 膨大なtryは、catch節も増え、それらのロジック関係も解決しないといけない

```java:膨大tryはＮＧ
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することはＮＧ。

1. 各種例外に対して同じ対処しかできない。
catch内でif、instanceOfを使って処理は分けれるが・・・やめて・・・
2. RuntimeExceptionのような、プログラム中止すべき場合もcatchされて、`誰も知らないまま制圧されちゃう`

```java:使ってはいけない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が例外を投げた場合、例外内容が隠される場合がある。

```java
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");
    }
}
```
```text:出力
m1 : try
m1 : catch
m1 : finally
In main : Third EX.
```
根本原因は`throw new Exception("First EX");`なのに、
拾ったのは`throw new Exception("Third EX");`である。

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

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

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

# おまけ
## (常用)例外クラス体系
![Ex-class.png](https://qiita-image-store.s3.amazonaws.com/0/104977/01cc4515-13f8-6d67-13d3-4b641d74cb28.png)

## 例外クラスの分類
* Runtime Exception
    * RuntimeExceptionか継承したクラス
    * プログラムで修復不可能だと見られる例外
    * catchしなくてよい
* Checked Exception
    * RuntimeException以外のクラス
    * プログラムで修復可能だと見られる例外
    * catchもしくはthrowsするように強制されちゃう