CodeGym /コース /JAVA 25 SELF /IOException と FileNotFoundException: エラー処理

IOException と FileNotFoundException: エラー処理

JAVA 25 SELF
レベル 38, レッスン 0
使用可能

1. 入出力例外の階層

Java でファイルや外部リソースを扱うときは、ほぼ必ず入出力例外に出会います。これは、読み取りや書き込みで問題が起きた場合に投げられる(throw される)オブジェクトです。たとえば、ファイルが見つからない、アクセス権がない、あるいはディスクが突然「疲れて」しまった場合などです。

この講義の主役

Java にはこれらの例外の階層があります。主なものは以下です。

  • IOException — すべての入出力エラーの基底クラス。ファイル、ストリーム、ネットワークで何か問題が起きたら、たいていはこれ(または多数いるそのサブクラス)の出番です。
  • FileNotFoundException — IOException の「子クラス」。存在しないファイルを開こうとした、またはパスが間違っているときに発生します。

その他の派生:

  • EOFException — こちらも IOException の直接の「子」。読み取り中に予期せずファイル終端に到達したことを示します。
  • MalformedInputException — 「孫」: CharacterCodingException を継承し、CharacterCodingException は IOException を継承します。指定したエンコーディングでファイルを正しく解釈できない場合に発生します(たとえば "UTF-8" を想定しているのに壊れたバイト列が来たなど)。
  • ほかにも SocketException、ZipException など用途特化の「親戚」があります。階層が深くなるほど、対象とする状況はより限定的で特殊になります。

簡略図:

java.lang.Exception
└── java.io.IOException
    ├── java.io.FileNotFoundException
    ├── java.io.EOFException
    ├── java.nio.charset.MalformedInputException
    └── ... (その他)

豆知識:
Java ではファイル操作の多くで IOException の宣言または処理が必要です。いわゆる checked 例外で、コンパイラがエラー処理の書き忘れを防いでくれます。

2. これらの例外が発生するのはいつ、なぜか

存在しないファイルを開く

最もよくあるのは、開こうとしたファイルが存在しないケースです。時刻表自体に存在しないバスを待っているようなものです。

FileInputStream fis = new FileInputStream("abracadabra.txt"); // 例外発生: FileNotFoundException

権限なしでのファイル書き込み

書き込み権限のないフォルダにファイルを書こうとすると、状況に応じて FileNotFoundException や IOException が発生します.

FileOutputStream fos = new FileOutputStream("/system/secret.txt"); // 例外発生: FileNotFoundException または IOException

媒体の破損などによる読み書きエラー

ファイルは存在していても、ディスク障害、他プログラムによる占有、突然の停電などで、さまざまなメッセージを伴う IOException が発生することがあります。

その他の原因

  • 読み取り専用で開いているのに、書き込みを行おうとした。
  • パスが長すぎる、または不正な文字が含まれている。
  • ファイルが別のプロセスで使用中。
  • ディスク容量が不足している。
  • チェックと使用の間に、別プロセスによりファイルが削除された。

3. try-catch による処理

ファイル、ストリーム、ネットワークを扱うときは常に try-catch を使いましょう。いわばエアバッグのようなもので、問題が起きてもアプリが落ちず、適切に対応できます。

例外はどう捕捉すべきか

Java では、より具体的な例外を先に、より一般的な例外を後に捕捉します。最初に一般的な catch(IOException e)を書いてしまうと、より狭い FileNotFoundException などが到達できません。

正しい構造:

try {
    // ファイル操作
} catch (FileNotFoundException e) {
    // 「ファイルが見つからない」場合の処理
} catch (IOException e) {
    // その他の入出力エラーの処理
}

なぜか?
FileNotFoundException は IOException の特殊ケースだからです。先に一般的なものを捕まえると、特殊ケースは自分の catch に到達できません。

コード例: ファイルを開くときのエラー処理

import java.io.*;

public class FileReaderExample {
    public static void main(String[] args) {
        String filename = "notes.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line = reader.readLine();
            System.out.println("ファイルの最初の行: " + line);
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("ファイルが見つかりません: " + filename);
        } catch (IOException e) {
            System.out.println("ファイルの読み取り中にエラーが発生しました: " + e.getMessage());
        }
    }
}

注意:
ファイルの存在を事前に確認していても、try-catch は必ず残しましょう(たとえば、別プロセスが突然削除する可能性があります)。

4. 実践: エラー処理付きのコードを書く

簡単な例を作成し、ファイル不在やその他のエラーに正しく対応できるようにします。

ステップ 1: 存在しないファイルを開いてみる

import java.io.*;

public class NotesApp {
    public static void main(String[] args) {
        String filename = "my_notes.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line;
            System.out.println("あなたのメモ:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("おっと!メモのファイルが見つかりません: " + filename);
            System.out.println("ヒント: ファイルを作成するか、名前を確認してください。");
        } catch (IOException e) {
            System.out.println("ファイルの読み取り中にエラーが発生しました: " + e.getMessage());
        }
    }
}

ステップ 2: ファイル書き込みの処理を追加する

import java.io.*;

public class NotesWriter {
    public static void main(String[] args) {
        String filename = "my_notes.txt";
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true)); // append: true
            writer.write("新しいメモ!\n");
            writer.close();
            System.out.println("メモを追加しました!");
        } catch (IOException e) {
            System.out.println("ファイルへの書き込み中にエラーが発生しました: " + e.getMessage());
        }
    }
}

ステップ 3: エラーログ出力付きの汎用例

import java.io.*;

public class SafeFileCopier {
    public static void main(String[] args) {
        String source = "source.txt";
        String target = "target.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(source));
            BufferedWriter writer = new BufferedWriter(new FileWriter(target));
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine();
            }
            reader.close();
            writer.close();
            System.out.println("ファイルを正常にコピーしました!");
        } catch (FileNotFoundException e) {
            System.out.println("ファイルが見つかりません: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("入出力エラー: " + e.getMessage());
        }
    }
}

ヒント:
実際のアプリケーションでは、画面出力だけでなくファイルやジャーナルにもエラーを記録して、原因を後から追跡できるようにしましょう。

5. 表: 主要な入出力例外

例外 発生タイミング 対処方法
FileNotFoundException
ファイルが見つからない、パスが存在しない、権限がない ユーザーに通知し、パス/権限を確認し、必要ならファイルを作成する
EOFException
読み取り中の予期しない EOF 破損/不完全である旨を知らせ、可能ならデータを部分的に復旧する
IOException
汎用の入出力エラー(ディスク、権限、ロックなど) 詳細を確認し、操作を正しく終了し、可能であれば再試行する
MalformedInputException
不正なエンコーディングまたはファイル構造 破損を通知し、別のエンコーディング/ソースを試す

6. 入出力例外処理でありがちなミス

ミス No.1: Exception だけを捕まえる。 つい catch(Exception e)だけを書きたくなりますが、これでは何が起きたのかを区別できません。まずは具体的な例外(FileNotFoundException)を捕まえ、その後に一般的な IOException を捕まえるのがよいでしょう。

ミス No.2: エラー時にストリームを閉じない。 ファイルを開いた後に例外が発生すると、ストリームが閉じられないことがあります。try-with-resources を使うか、finally でリソースを閉じてください。

ミス No.3: 例外メッセージを無視する。 単に「エラー!」と出すだけではなく、詳細(e.getMessage())を表示しましょう。原因の特定が早くなります。

ミス No.4: 書き込み時に FileNotFoundException を処理しない。 FileNotFoundException は読み取りだけの話だと思われがちですが、実は書き込み時にも発生します(パスが不正、ファイル作成権限がないなど)。

ミス No.5: アクセス権を確認しない。 制限付き権限でアプリを実行していると、多くのファイル操作がエラーになります。常にこの点を考慮し、ユーザーに情報を提供しましょう。

1
タスク
JAVA 25 SELF, レベル 38, レッスン 0
ロック未解除
古代図書館で失われた記録の検索 📚
古代図書館で失われた記録の検索 📚
1
タスク
JAVA 25 SELF, レベル 38, レッスン 0
ロック未解除
王立アーカイブへの重要な報告の記録 📜
王立アーカイブへの重要な報告の記録 📜
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION