Java異常処理と内部クラスの詳細解説

6. 異常処理(例外)

6.1 異常とは

Javaにおける「異常(Exception)」とは、プログラム実行中に発生する予期しない状況を指します。構文エラーやロジックミスは異常とはみなされません。たとえば、ファイルが存在しない、ネットワーク接続が切断された、ユーザー入力が不正などの状況が該当します。

6.2 異常の階層構造

すべての異常は java.lang.Throwable を継承しています。

  • Error:JVMが復旧不能な深刻な問題(例:StackOverflowError, OutOfMemoryError)。通常はコードで処理しない。
  • Exception:アプリケーションで処理可能な問題
    • RuntimeException(非検査例外):コンパイル時にチェックされない(例:NullPointerException, ArrayIndexOutOfBoundsException
    • その他のException(検査例外):コンパイル時に処理が必須(例:IOException, SQLException

6.3 JVMのデフォルト動作

異常がキャッチされない場合、JVMはスタックトレースを出力し、プログラムを終了します。

6.4 検査例外 vs 非検査例外

  • 検査例外(Checked Exception):コンパイル時に処理が強制される。未処理の場合、コンパイルエラーとなる。
  • 非検査例外(Unchecked Exception):コンパイル時にチェックされない。開発者は必要に応じて処理する。

6.5 異常処理の基本モデル:「投げる」と「捕まえる」

  • throw:異常オブジェクトを生成・送出
  • catch / throws:異常を処理または上位に委譲

6.6 try-catch-finally構文

try {
    // 異常が発生する可能性のあるコード
} catch (特定の異常 e) {
    // 異常処理
} finally {
    // 必ず実行されるコード(リソース解放など)
}

注意点:tryブロック内で宣言されたローカル変数は、ブロック外で使用できません。初期値を明示的に与えることで、異常発生時でも安全に参照できます。

int result = 0; // 初期値を明示
try {
    result = Integer.parseInt(input);
} catch (NumberFormatException e) {
    e.printStackTrace();
}
System.out.println(result); // 安全に使用可能

6.7 throwsによる委譲

メソッド内で検査例外を処理せず、呼び出し元に委譲する場合に使用します。

public void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    // ...
}

オーバーライド時のルール

  • 親クラスのメソッドが検査例外をthrowsしていない場合、子クラスもthrowsできない
  • throwsできる検査例外の範囲は、親クラスのものより狭く(または同じ)でなければならない
  • 実行時例外については制限なし

6.8 手動での異常送出(throw)

ビジネスロジックに基づき、意図的に異常を送出できます。

public void validateAge(int age) throws IllegalArgumentException {
    if (age < 0) {
        throw new IllegalArgumentException("年齢は0以上でなければなりません");
    }
}

6.9 カスタム異常クラス

既存の異常クラスでは表現が不十分な場合、独自の異常クラスを定義します。

public class InvalidIdException extends Exception {
    private static final long serialVersionUID = 1L;

    public InvalidIdException() { super(); }
    public InvalidIdException(String message) { super(message); }
}

7. 内部クラス(Inner Class)

7.1 内部クラスの種類

  • メンバ内部クラス:外部クラスのメンバとして定義
  • ローカル内部クラス:メソッド内に定義
  • 匿名内部クラス:名前を持たず、即座にインスタンス化される

7.2 メンバ内部クラス

class Outer {
    private int x = 10;

    class Inner {
        void display() {
            System.out.println(x); // 外部クラスのprivateメンバに直接アクセス可能
        }
    }

    static class StaticInner {
        // 静的内部クラスは外部クラスの非静的メンバにアクセス不可
    }
}

インスタンス化方法

// 非静的内部クラス
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

// 静的内部クラス
Outer.StaticInner staticInner = new Outer.StaticInner();

7.3 匿名内部クラス

インターフェースや抽象クラスの実装を一時的に必要とする場合に使用します。

Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("タスク実行");
    }
};

関数型インターフェースでは、ラムダ式で置き換え可能です。

タグ: Java 例外処理 内部クラス カスタム例外 匿名クラス

8月25日 05:58 投稿