例外ってtry-catch以外でもできるんじゃね?
・・・って、思いませんか?
ifで排除すればええやん!
答え:NO!できない!
・・・です。
例:テキストファイルの中身を読み込んで、ターミナルに表示させるコード
// "data.txt"というテキストファイルの中身を読むコード
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
// lineというString型の変数に、上のコードで読んだデータを代入
String line = br.readLine();
このコードは、ファイルをうまく読み込めなかった際の処理がありません。
では、if文で空だった場合に分岐させてみましょう。
// "data.txt"というテキストファイルの中身を読む(つもりの)コード
BufferedReader br = new BufferedReader(new FileReader("data.txt")); // ← 問題発生ポイント1
String line = br.readLine(); // ← 問題発生ポイント2
// ファイルから読み込んだ行が空文字列だったらhogeする(つもりの)分岐
if (line != null && line.isEmpty()) { // lineがnullでないこともチェック
// hogeな処理
System.out.println("ファイルが空だよ!");
}
// lineがnullの場合はファイルの終端か、読み込みに失敗した可能性がある
このコード、実はこのままではコンパイルエラーになってしまいます。
コンパイルエラーになる理由
Javaには チェック例外 (Checked Exception) というものがあります。
これは、「プログラムの実行中に発生する可能性があり、開発者が対処すべき例外」としてJavaコンパイラが認識するものです。
new FileReader("data.txt")
は、指定されたファイルが見つからない場合に FileNotFoundException というチェック例外をスロー(発生)させる可能性があります。
また、
br.readLine()
も、読み込み中に何らかの問題が発生した場合に IOException というチェック例外をスローする可能性があります。
Javaコンパイラは、これらのチェック例外をスローする可能性のあるメソッドを呼び出す際、
- try-catch ブロックで例外を捕捉し、対処する
- メソッドに throws 宣言を追加し、このメソッドの呼び出し元に例外処理を委譲する
このどちらの対処も行われていないため、コンパイラは「おい、例外が起きたらどうするんだ!ちゃんと準備してくれよ!」とエラーを出すわけです。
if文では「予想外に対処できない」
それでは、「例外」を事前にチェックすればええやん!
例:if文でファイルの存在をチェック
import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
// ...
File file = new File("data.txt");
if (file.exists() && file.canRead()) {
// ファイルが存在し、読み取り可能なら処理を進める(つもり)
BufferedReader br = new BufferedReader(new FileReader(file)); // ここでも例外の可能性は残る
String line = br.readLine(); // ここも同様
if (line != null && line.isEmpty()) {
System.out.println("ファイルは空っぽでした。");
} else if (line == null) {
System.out.println("ファイルは存在したけど、中身がなかったか、読み込めませんでした。");
} else {
System.out.println(line);
}
// br.close(); // 読み込み処理の終了
} else {
System.out.println("ファイルが存在しないか、読み取り権限がありません。");
}
チェックできていそうな雰囲気はあります。
ただし、これでも以下の問題が発生します
チェックのタイミングと実際の操作の間の問題
file.exists() で存在を確認した直後に、別のプログラムによってファイルが削除されたら?
読み込み中のエラー
ファイルが存在し、読み取り権限があっても、ディスクの物理的なエラーや、ファイル形式の異常などで readLine() が IOException をスローする可能性は残ります。
網羅性の問題
ファイルI/O操作で起こりうる全ての予期せぬ出来事を、if文だけで網羅的にチェックするのは非常に複雑。
「テキストファイルがもともと空なのか、正しく読み込めていないのかわからないことをJava君(コンパイラ)が怖がっている」
ファイル操作には様々な失敗要因が潜んでいるため、それらへの備えを言語仕様として促しているのです。
そこで登場するのが
「例外処理 (try-catch)」
try-catch を使うことで、これらの「予想外の出来事」(例外)が発生した場合の処理を、正常系の処理とは分けて記述できます。
ファイル操作の場合、「ファイルが見つからなかったら新しく作る」といった処理も、catchブロック内に記述できるわけです。
import java.io.BufferedReader;
import java.io.FileNotFoundException; // 具体的な例外
import java.io.IOException; // より広範囲なI/O例外
public class FileReadExample {
public static void main(String[] args) {
BufferedReader br = null; // finallyブロックで閉じるためにtryの外で宣言
try {
// 【試行する処理】例外が発生するかもしれないコード
br = new BufferedReader(new FileReader("data.txt"));
String line = br.readLine();
if (line == null) {
System.out.println("ファイルは空か、読み込みに失敗しました。");
} else if (line.isEmpty()) {
System.out.println("ファイルは読み込めましたが、最初の行は空行でした。");
} else {
System.out.println("ファイルの内容: " + line);
// さらに読み進める場合はループ処理など
}
} catch (FileNotFoundException e) {
// 【例外発生時の処理】FileNotFoundException が発生した場合
System.out.println("エラー: ファイルが見つかりません! (" + e.getMessage() + ")");
// ここにテキストファイルを新規作成する処理も書けます
//例: File newFile = new File("data.txt"); newFile.createNewFile();
} catch (IOException e) {
// 【例外発生時の処理】その他のIOException が発生した場合
System.out.println("エラー: ファイルの読み込み中に問題が発生しました。 (" + e.getMessage() + ")");
} finally {
// 【後処理】例外の発生有無にかかわらず、最後に必ず実行される処理
if (br != null) {
try {
br.close(); // BufferedReaderを閉じる(これもIOExceptionの可能性あり)
} catch (IOException e) {
System.out.println("エラー: ファイルを閉じる際に問題が発生しました。 (" + e.getMessage() + ")");
}
}
}
}
}
まとめ:if文とtry-catchの使い分け
if文
プログラムの正常なロジックの一部としての期待される条件分岐に使います。
例えば、「ユーザーの入力が特定の値か?」
try-catch
プログラムの実行中に発生しうる予期せぬエラーや例外的な状況に対処するため使います。
特に、外部リソース(ファイル、ネットワーク、APIなど)へのアクセスや、失敗する可能性のある操作で重要です。
Javaのチェック例外に対しては、try-catchかthrows宣言が必須です。