1. 概要

このチュートリアルでは、Javaのfinallyキーワードについて説明します。 エラー処理でtry/catchブロックと一緒に使用する方法を説明します。 最後にはコードの実行を保証することを目的としていますが、JVMがコードを実行しない例外的な状況について説明します。

また、finallyブロックが予期しない結果をもたらす可能性があるいくつかの一般的な落とし穴についても説明します。

2. 最後に何ですか?

最後には、tryキーワードとともに使用するコードのブロックを定義します。 これは、メソッドが完了する前に、tryおよび任意のcatchブロックの後に常に実行されるコードを定義します。

finallyブロックは、例外がスローされたかキャッチされたかに関係なく実行されます

2.1. 簡単な例

try-catch-finallyブロックのfinallyを見てみましょう。

try {
    System.out.println("The count is " + Integer.parseInt(count));
} catch (NumberFormatException e) {
    System.out.println("No count");
} finally {
    System.out.println("In finally");
}

この例では、パラメーター count の値に関係なく、JVMは finally ブロックを実行し、「Infinally」を出力します。

2.2. catchブロックなしでfinallyを使用する

また、catchブロックが存在するかどうかに関係なく、afinallyブロックとtryブロックを使用できます

try {
    System.out.println("Inside try");
} finally {
    System.out.println("Inside finally");
}

そして、出力を取得します。

Inside try
Inside finally

2.3. 最後にが役立つ理由

通常、 finally ブロックを使用して、接続のクローズ、ファイルのクローズ、スレッドの解放などのクリーンアップコードを実行します。これは、例外に関係なく実行されるためです。

注: finally ブロックの代わりに、 try-with-resourcesを使用してリソースを閉じることもできます。

3. 最終的にが実行されたとき

JVMがfinallyブロックを実行するときのすべての順列を見てみましょう。これにより、理解が深まります。

3.1. 例外はスローされません

try ブロックが完了すると、例外がなかった場合でも、finallyブロックが実行されます。

try {
    System.out.println("Inside try");
} finally {
    System.out.println("Inside finally");
}

この例では、tryブロックから例外をスローしていません。 したがって、JVMは、tryブロックとfinallyブロックの両方ですべてのコードを実行します。

これは以下を出力します:

Inside try
Inside finally

3.2. 例外がスローされ、処理されない

例外があり、それがキャッチされない場合でも、finallyブロックは実行されます。

try {
    System.out.println("Inside try");
    throw new Exception();
} finally {
    System.out.println("Inside finally");
}

未処理の例外が発生した場合でも、JVMはfinallyブロックを実行します。

そして、出力は次のようになります。

Inside try
Inside finally
Exception in thread "main" java.lang.Exception

3.3. 例外がスローされ、処理されます

例外があり、それが catch ブロックによってキャッチされた場合でも、finallyブロックは実行されます。

try {
    System.out.println("Inside try");
    throw new Exception();
} catch (Exception e) {
    System.out.println("Inside catch");
} finally {
    System.out.println("Inside finally");
}

この場合、 catch ブロックがスローされた例外を処理し、JVMが finally ブロックを実行して、次の出力を生成します。

Inside try
Inside catch
Inside finally

3.4. tryブロックからのメソッドリターン

メソッドから戻っても、最終的にブロックの実行が妨げられることはありません。

try {
    System.out.println("Inside try");
    return "from try";
} finally {
    System.out.println("Inside finally");
}

ここで、メソッドに return ステートメントがある場合でも、JVMは finally ブロックを実行してから、呼び出し元のメソッドに制御を渡します。

出力を取得します。

Inside try
Inside finally

3.5. catchブロックからのメソッドの戻り値

catchブロックにreturnステートメントが含まれている場合でも、finallyブロックは次のように呼び出されます。

try {
    System.out.println("Inside try");
    throw new Exception();
} catch (Exception e) {
    System.out.println("Inside catch");
    return "from catch";
} finally {
    System.out.println("Inside finally");
}

try ブロックから例外をスローすると、catchブロックが例外を処理します。 catch ブロックにはreturnステートメントがありますが、JVMは呼び出し元のメソッドに制御を渡す前に finally ブロックを実行し、次のように出力します。

Inside try
Inside catch
Inside finally

4. 最終的にが実行されない場合

JVMはfinallyブロック内でステートメントを実行することを常に期待していますが、JVMがfinallyブロックを実行しない場合があります。

オペレーティングシステムがプログラムを停止した場合、プログラムがすべてのコードを実行する機会が得られないことは、すでに予想されているかもしれません。 保留中のfinallyブロックの実行を同様に防ぐために実行できるアクションもいくつかあります。

4.1. System.exitを呼び出す

この場合、 System.exit を呼び出してJVMを終了しているため、JVMはfinallyブロックを実行しません。

try {
    System.out.println("Inside try");
    System.exit(1);
} finally {
    System.out.println("Inside finally");
}

これは以下を出力します:

Inside try

4.2. 停止の呼び出し

System.exit と同様に、 Runtime.halt の呼び出しも実行を停止し、JVMはfinallyブロックを実行しません。

try {
    System.out.println("Inside try");
    Runtime.getRuntime().halt(1);
} finally {
    System.out.println("Inside finally");
}

したがって、出力は次のようになります。

Inside try

4.3. デーモンスレッド

デーモンスレッドtry/ finally ブロックの実行に入り、デーモンスレッドが finally ブロックを実行する前に、他のすべての非デーモンスレッドが終了した場合、JVMデーモンスレッドがfinallyブロックの実行を終了するのを待ちません。

Runnable runnable = () -> {
    try {
        System.out.println("Inside try");
    } finally {
        try {
            Thread.sleep(1000);
            System.out.println("Inside finally");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};
Thread regular = new Thread(runnable);
Thread daemon = new Thread(runnable);
daemon.setDaemon(true);
regular.start();
Thread.sleep(300);
daemon.start();

この例では、 runnable は、メソッドに入るとすぐに「Insidetry」を出力し、1秒間待ってから「Insidefinally」を出力します。

ここでは、通常のスレッドとデーモンスレッドを少し遅れて開始します。 通常のスレッドがfinallyブロックを実行するとき、デーモンスレッドはtryブロック内で待機しています。 通常のスレッドが実行を完了して終了すると、JVMも終了し、デーモンスレッドが最終的にブロックを完了するのを待ちません。

出力は次のとおりです。

Inside try
Inside try
Inside finally

4.4. JVMが無限ループに到達

無限のwhileループを含むtryブロックは次のとおりです。

try {
    System.out.println("Inside try");
    while (true) {
    }
} finally {
    System.out.println("Inside finally");
}

finally に固有ではありませんが、tryまたはcatchブロックに無限ループが含まれている場合、JVMはそのループを超えるブロックに到達することはありません。 。

5. 一般的な落とし穴

finallyブロックを使用するときに避けなければならない一般的な落とし穴がいくつかあります。

完全に合法ですが、 returnステートメントを使用したり、finallyブロックから例外をスローしたりすることは悪い習慣と見なされます。これは絶対に避けてください。

5.1. 例外を無視する

finallyブロックのreturnステートメントは、キャッチされない例外を無視します。

try {
    System.out.println("Inside try");
    throw new RuntimeException();
} finally {
    System.out.println("Inside finally");
    return "from finally";
}

この場合、メソッドはスローされた RuntimeException を無視し、値「fromfinally」を返します。

5.2. 他のreturnステートメントを無視します

finallyブロックのreturnステートメントは、tryまたはcatchブロックの他のreturnステートメントを無視します。 finallyブロックのreturnステートメントのみが実行されます。

try {
    System.out.println("Inside try");
    return "from try";
} finally {
    System.out.println("Inside finally");
    return "from finally";
}

この例では、メソッドは常に「fromfinally」を返し、tryブロックのreturnステートメントを完全に無視します。 これは見つけるのが非常に難しいバグである可能性があるため、最終的にブロックでreturnを使用することは避けてください。

5.3. スローまたはリターンされるものを変更します

また、 finally ブロックから例外をスローする場合、メソッドは、tryおよびcatch[でスローされた例外またはreturnステートメントを無視します。 X180X]ブロック:

try {
    System.out.println("Inside try");
    return "from try";
} finally {
    throw new RuntimeException();
}

このメソッドは値を返すことはなく、常にRuntimeExceptionをスローします。

この例のようにfinallyブロックから意図的に例外をスローすることはできませんが、それでもこの問題が発生する可能性があります。 これは、finallyブロックで使用するクリーンアップメソッドが例外をスローしたときに発生する可能性があります。

6. 結論

この記事では、 finally ブロックがJavaで何をするのか、そしてそれらをどのように使用するのかについて説明しました。 次に、JVMがそれらを実行するさまざまなケースと、実行されない可能性のあるいくつかのケースを調べました。

最後に、finallyブロックの使用に関連するいくつかの一般的な落とし穴を調べました。

いつものように、このチュートリアルで使用されているソースコードは、GitHubからで入手できます。