#例外処理とは
- アプリ実行時に発生するエラーに対応する
- 狭義の例外:開発者の責任では可否できないエラー
- ファイルが存在しない
- 接続先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){...}
-
AutoCloseableインターフェースを実装すること
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アクセス時の問題
-
RuntimeExceptionクラス ---- 【例外処理任意】
-
Errorクラス:致命的エラー ---- 【例外処理しない】
#例外処理の注意
-
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);
}
}