【Java】异常处理

例外处理是指对于异常情况的特殊处理方法。

    • アプリ実行時に発生するエラーに対応する

狭義の例外:開発者の責任では可否できないエラー

ファイルが存在しない
接続先DBが停止していた

Javaではこれらのエラーに対応必須

例外は型(クラス)である

一般的な例外を上位クラス、個々の状況の例外を下位クラスとしたクラス階層として表す

嘗試…捕獲語法

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メソッドを基点に呼び出し順に記録

 

    意図しないメソッドが呼ばれてないか、過程が間違ってないか確認

最后的代码块

    • 例外の有無によらず最終的に実行されるブロック

 

    • 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…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);
  }
}
bannerAds