LoginSignup
4
7

More than 3 years have passed since last update.

【Java】例外処理

Posted at

例外処理とは

  • アプリ実行時に発生するエラーに対応する
    • 狭義の例外:開発者の責任では可否できないエラー
      • ファイルが存在しない
      • 接続先DBが停止していた
    • Javaではこれらのエラーに対応必須
  • 例外は型(クラス)である
    • 一般的な例外を上位クラス、個々の状況の例外を下位クラスとしたクラス階層として表す

try...catch構文

  • tryブロック:本来の処理
  • catchブロック:例外の型によって呼ばれる
    • 例外発生=例外がスローされる
    • 指定された例外変数(eやex)を介して例外オブジェクトにアクセス
    • ブロック単位でスコープを持つので複数catchブロックがあっても同じ変数名(e)でOK
  • 例外クラスのメソッド
    • getMessage():エラーメッセージ取得
    • getLocalizedMessage():ローカライズ対応したエラーメッセージ取得
    • getCause():エラー原因を取得
    • getStackTrace():スタックトレース取得
    • printStackTrace():スタックトレース出力
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class TryBasic {

  public static void main(String[] args) {
    try {
      var in = new FileInputStream("C:/data/nothing.gif");
      var data = -1;
      while ((data = in.read()) != -1) {
        System.out.printf("%02X ", data);
      }
    //FileInputStreamクラスでFileNotFoundException例外をスロー
    } catch (FileNotFoundException e) {
      System.out.println("ファイルが見つかりませんでした。");
    } catch (IOException e) {
      //指定された例外変数(e)を介して例外オブジェクトにアクセス
      e.printStackTrace();
    }
  }
}

スタックトレース

  • 例外発生までに経たメソッドの履歴
  • エントリポイントであるmainメソッドを基点に呼び出し順に記録
  • 意図しないメソッドが呼ばれてないか、過程が間違ってないか確認

finallyブロック

  • 例外の有無によらず最終的に実行されるブロック
  • tryブロックで利用したリソースの処理など
  • finallyブロックは一個だけ
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class TryFinally {

  public static void main(String[] args) {
    FileInputStream in = null;
    try {
      in = new FileInputStream("C:/data/nothing.gif");
      var data = -1;
      while ((data = in.read()) != -1) {
        System.out.printf("%02X ", data);
      }
    } catch (FileNotFoundException e) {
      System.out.println("ファイルが見つかりませんでした。");
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      //例外の有無によらずファイルクローズ
      try {
        if (in != null) {
          in.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

try-with-resource構文

  • try...finally構文よりもシンプルなので推奨
  • tryブロックを出るタイミングでリソース自動解放するのでfinally不要!
  • 注意:
    • AutoCloseableインターフェースを実装すること
      • Reader/Writer/InputStream/OutputStream/Connection/Statement/ResultSet
    • リソースの解放順序が変わる
      • 従来のtry...finally構文ではcatchブロック時点でリソース生きてた
      • try-with-resourceではtryブロックを出るとリソース解放
    • リソーススコープが異なる
      • try...finally構文ではブロックをまたいでリソース処理するのでリソース変数はtryブロックの外で宣言
      • try-with-resourceではスコープがtryブロック配下に限定(Java9以降ではtryブロック外で宣言されたリソース引用可能)
      • var input = new FileInputStream(...)
      • try(input){...}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class TryWithResources {

//ブロック先頭でリソース宣言
  public static void main(String[] args) {
    try (var in = new FileInputStream("C:/data/hogee.gif")) {
      var data = -1;
      while ((data = in.read()) != -1) {
        System.out.printf("%02X ", data);
      }
    //tryブロックを出るとリソース自動解放
    } catch (FileNotFoundException e) {
      System.out.println("ファイルが見つかりません。");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

例外クラスの階層構造

  • クラス階層という観点では、全部の例外/エラークラスはThrowableクラスを頂点とした階層ツリーに属する
    • Errorクラス:致命的エラー        ---- 【例外処理しない】
      • 配下のエラーはエラー例外
      • IOErrer:重大な入出力エラー、VirtualMachineError:仮想マシンレベルでの障害
    • Exceptionクラス             ---- 【例外処理する】
      • RuntimeExceptionクラス      ---- 【例外処理任意】
        • 配下のエラーは非検査例外(=実行時例外)
        • IllegalArgumentException:不正な引数、NullPointerException:オブジェクトがnull
      • その他配下のエラーは検査例外
        • FileNotFoundException:ファイル存在なし、SQLException:DBアクセス時の問題

例外処理の注意

  • Exceptionで捕捉しない
    • catch(Exception e)は全部の例外を捕捉
      • IOExceptionより具体的なFileNotFoundException
  • マルチキャッチ活用する
    • 複数の例外に対して同じ処理をする
    • 例外1|例外2(いずれかの場合)
  • catchブロック順
    • 複数ある場合、先頭の記述が優先
    • 下位の例外を先に、上位の例外を後に記述
//マルチキャッチ
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

public class TryMulti {

  public static void main(String[] args) {
    try {
      var in = new FileInputStream("C:/data/nasi.gif");
      var data = -1;
      while ((data = in.read()) != -1) {
        System.out.printf("%02X ", data);
      }
      var uri = new URI( "https://www.neko.example.com");
      System.out.println(uri.toString());
    //マルチキャッチ
    } catch (IOException | URISyntaxException e) {
      System.out.println("ファイルにアクセスできません。");
      e.printStackTrace();
    }
  }
}

例外をスロー

  • 標準ライブラリのスロー以外に開発者がスローできる
  • `throw 例外オブジェクト'で任意の例外オブジェクトを渡せる
  • スローする可能性のある例外をthrows句で宣言
    • throw:例外をスローする
    • throws:メソッド/コンストラクタで宣言
public FileInputStream(File file) throws FileNotFoundException{
//略

例外スローの注意

  • Exceptionをスローしない
  • 検査例外/非検査例外を適切に選択
  • 出来るだけ標準例外を使う
    • NullPointerException:オブジェクトがnull
    • IndexOutOfBoundsException:配列インデックスが範囲外
    • UnsupportedOperationException:要求操作が未サポート
    • IllegalArgumentException:メソッドに渡された引数に問題
    • IllegalStateException:意図した状態でないときにメソッド呼び出した
    • ArithmeticException:計算で例外的な条件が発生
    • NumberFormatException:数値形式正しくない
  • privateメソッドはassert命令を使う
    • 条件式がfalseの時にAssertionError例外
    • javaコマンドで-eaオプションを明示した時のみ動作
public class AssertBasic {
  private static double getTrapezoidArea(double upper, double lower, double height) {
    //引数0以下の場合に例外発生
    assert upper > 0 && lower > 0 && height > 0;
    return (upper + lower) * height / 2;
  }
  public static void main(String[] args) {
    System.out.println(AssertBasic.getTrapezoidArea(-2, 4, 0));
  }
}

例外再スロー

  • その場で処理せず処理は呼び出し元に委ねること
  • 基本、その場で個別処理ではなく、個々の処理を呼びアプリの流れそのものを制御する上位コードで処理すべき
  • 例外型を賢く判定できるようになっている(Java7~)
    • throwsで宣言するのはException型のみでOK
    • マルチキャッチ不要
public class TryRethrow {
//MySampleException, MyLibExceptionでrethrowメソッド発生する可能性
  public static void rethrow(boolean flag) throws MySampleException, MyLibException {
    try {
      if (flag) {
        throw new MySampleException();
      } else {
        throw new MyLibException();
      }
    //Exception型で受けてそのまま再スロー
    } catch (Exception e) {
      throw e;
    }
  }
}

翻訳例外

  • 個々の例外を一旦補足して上位のアプリの例外として束ね再スロー
  • 例外をアプリ独自の例外にまとめることができる
  • 呼び出し元で個々の例外を意識せず、より高いレベルの例外を意識すればいい
  • 翻訳された上位例外から本来の原因を取得するにはgetCauseメソッド
    • var ex = e.getCause();

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;

//翻訳例外でMySampleExceptionのみ意識すればいい
public class UseTrans {
  public void readHttpPages() throws MySampleException {
    try (var reader = Files.newBufferedReader(Paths.get("C:/data/link.txt"))) {
      var line = "";
      while ((line = reader.readLine()) != null) {
        var client = HttpClient.newHttpClient();
        var req = HttpRequest.newBuilder().uri(URI.create(line)).build();
        var res = client.send(req, HttpResponse.BodyHandlers.ofString());
        System.out.println(res.body());
      }
    } catch (IOException | InterruptedException e) {
      throw new MySampleException(e);
    }
  }
}

独自の例外クラス

  • 例外クラスはアプリで定義可能
  • Exception(派生クラス)を継承すること
  • コンストラクタをオーバーライドすること
    • Exceptionで定義されたコンストラクタを再定義しsuper呼び出しでOK
  • 独自のフィールドやメソッドの実装を持った例外も可能
//Exception(派生クラス)を継承
public class MySampleException extends Exception {
  //コンストラクタをオーバーライド
  public MySampleException() {
    super();
  }
  public MySampleException(String message) {
    super(message);
  }
  public MySampleException(String message, Throwable cause) {
    super(message, cause);
  }
  public MySampleException(Throwable cause) {
    super(cause);
  }
}
4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7