DEV Community

TANIAOKA, Akihiro
TANIAOKA, Akihiro

Posted on

10章10

承知しました。コードにコメントを加えて、なぜコンパイルエラーが発生するのかを説明します。

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Sample {
    public static void main(String[] args) {
        try (FileInputStream is = new FileInputStream("sample.txt")) {
            // 以下の行でFileNotFoundExceptionを強制的にスローしているため、
            // FileInputStreamオブジェクトは正しく初期化されません。
            // これはtryブロックが完了しなかったことを意味します。
            throw new FileNotFoundException();
        } catch (Exception e) {
            // FileNotFoundExceptionがcatchブロックで捕捉されます。
            // ここで"A"が出力されます。
            System.out.println("A");
        } finally {
            // ここで問題が発生します。try-with-resources文では、
            // tryブロック内で正しく初期化されたリソースのみが
            // 自動的に閉じられることが保証されています。
            // しかし、このコードではFileInputStreamオブジェクト(is)の
            // 初期化がFileNotFoundExceptionをスローするために完了していません。
            // そのため、finallyブロックのスコープでは「is」は存在しないことになります。
            // これがコンパイルエラーの原因です。
            if (is != null) {
                is.close(); // コンパイルエラー: 変数isが見つかりません
            }
            // "B"が出力されますが、上述のコンパイルエラーによって
            // この行に到達することはありません。
            System.out.println("B");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

このコードスニペットはコンパイルエラーになる理由は、try-with-resources構文を使っていますが、リソース(この場合はFileInputStreamis)が初期化される前に例外がスローされるため、isは実際には作成されず、finallyブロックのスコープ外になります。通常、try-with-resources構文は、リソースの初期化後にそのリソースが閉じられることを保証するため、初期化が正常に完了した場合にのみリソースを閉じる操作が有効です。しかし、初期化が未完了のため、finallyブロックではis変数が認識されず、is.close()メソッドを呼び出そうとするとコンパイルエラーが発生します。この結果、コンパイル時にisが定義されていないというエラーが発生し、これが答えEの理由となります。

追記

コンパイルエラーを回避するためには、FileInputStreamの変数istryブロックの外で宣言し、nullで初期化する必要があります。これにより、finallyブロックでisを参照してもスコープ外にはならず、close()メソッドを安全に呼び出すことができます。以下にその修正版コードを示します。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Sample {
    public static void main(String[] args) {
        // 'try'ブロックの外でリソースを宣言し、nullで初期化する。
        FileInputStream is = null;
        try {
            is = new FileInputStream("sample.txt");
            // この行で例外を強制的にスローします。
            throw new FileNotFoundException();
        } catch (FileNotFoundException e) {
            // FileNotFoundExceptionがキャッチされ、"A"を出力します。
            System.out.println("A");
        } finally {
            // finallyブロックでリソースを安全にクローズします。
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    // close()メソッドからの例外をキャッチします。
                    e.printStackTrace();
                }
            }
            // "B"が出力されます。
            System.out.println("B");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

この変更により、FileInputStreamオブジェクトはtryブロック内で発生する可能性のある例外とは独立して存在するようになります。そのため、tryブロックで例外がスローされた後でも、finallyブロックでis変数にアクセスしてclose()メソッドを呼び出すことができます。また、close()メソッドが別の例外をスローする可能性があるため、close()メソッドの呼び出しを別のtry-catchブロックで囲んでいます。これでIOExceptionを適切に処理でき、プログラムの他の部分への影響を防げます。

補足

修正されたコードは、FileInputStreamtryブロックで正しく宣言されているため、コンパイルエラーは回避されます。コードの実行により表示される出力は次のようになります:

  1. tryブロックが実行されますが、すぐにFileNotFoundExceptionがスローされます。
  2. catchブロックが実行され、「A」と出力されます。
  3. finallyブロックが実行されますが、FileInputStreamnullでないので、close()メソッドが実行されます。その後、「B」と出力されます。

したがって、実際にコンソールに表示されるのは次の2行になります:

A
B
Enter fullscreen mode Exit fullscreen mode

ただし、ここではFileNotFoundExceptionをキャッチしているので、catchブロックにおいて他の例外タイプは捕捉されません。close()メソッドからのIOExceptionをキャッチするための追加のtry-catchブロックをfinally内に含めているため、この例外がスローされても処理されます。

Top comments (0)