【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);
}
}