Leitfaden zum Java-Schlüsselwort finally

1. Übersicht

In diesem Tutorial werden wir das Schlüsselwort finally in Java untersuchen. Wir werden sehen, wie es zusammen mit Try / Catch- Blöcken bei der Fehlerbehandlung verwendet wird. Obwohl letztendlich die Ausführung von Code garantiert werden soll, werden wir Ausnahmesituationen diskutieren, in denen die JVM ihn nicht ausführt.

Wir werden auch einige häufige Fallstricke diskutieren, bei denen ein endgültiger Block ein unerwartetes Ergebnis haben kann.

2. Was ist endlich?

Definiert schließlich einen Codeblock, den wir zusammen mit dem Schlüsselwort try verwenden . Es definiert Code, der immer nach dem Versuch und einem beliebigen catch- Block ausgeführt wird, bevor die Methode abgeschlossen ist.

Der finally- Block wird unabhängig davon ausgeführt, ob eine Ausnahme ausgelöst oder abgefangen wird .

2.1. Ein kurzes Beispiel

Schauen wir uns endlich in einem Try-Catch-finally- Block an:

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

In diesem Beispiel unabhängig vom Wert der Parameter Zählung , führt die JVM den schließlich Block und druckt „In endlich“ .

2.2. Mit schließlich ohne einen Fang - Block

Wir können auch einen finally- Block mit einem try- Block verwenden, unabhängig davon, ob ein catch- Block vorhanden ist :

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

Und wir werden die Ausgabe bekommen:

Inside try Inside finally

2.3. Warum ist endlich nützlich

Im Allgemeinen verwenden wir den finally- Block, um Bereinigungscode wie das Schließen von Verbindungen, das Schließen von Dateien oder das Freigeben von Threads auszuführen, da dieser unabhängig von einer Ausnahme ausgeführt wird.

Hinweis: Try-with-Resources kann auch zum Schließen von Ressourcen anstelle eines finally- Blocks verwendet werden.

3. Wann wird es endlich ausgeführt?

Werfen wir einen Blick auf alle Permutationen, wenn die JVM schließlich Blöcke ausführt , damit wir sie besser verstehen können.

3.1. Es wird keine Ausnahme geworfen

Wenn der try- Block abgeschlossen ist, wird der finally- Block ausgeführt, auch wenn keine Ausnahme aufgetreten ist:

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

In diesem Beispiel wird keine Ausnahme vom try- Block ausgelöst. Somit führt die JVM den gesamten Code sowohl im try- als auch im finally- Block aus.

Dies gibt aus:

Inside try Inside finally

3.2. Ausnahme wird geworfen und nicht behandelt

Wenn es eine Ausnahme gibt und diese nicht abgefangen wird, wird der finally- Block weiterhin ausgeführt:

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

Die JVM führt den finally- Block auch im Falle einer nicht behandelten Ausnahme aus.

Und die Ausgabe wäre:

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

3.3. Ausnahme wird geworfen und behandelt

Wenn es eine Ausnahme gibt und diese vom catch- Block abgefangen wird , wird der finally- Block weiterhin ausgeführt:

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

In diesem Fall behandelt der catch- Block die ausgelöste Ausnahme, und dann führt die JVM den finally- Block aus und erzeugt die Ausgabe:

Inside try Inside catch Inside finally

3.4. Methode Gibt vom try Block zurück

Selbst die Rückkehr von der Methode verhindert nicht, dass endgültig Blöcke ausgeführt werden:

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

Obwohl die Methode eine return- Anweisung hat, führt die JVM hier den finally- Block aus, bevor die Steuerung an die aufrufende Methode übergeben wird.

Wir werden die Ausgabe bekommen:

Inside try Inside finally

3.5. Methode Gibt vom catch- Block zurück

Wenn der catch- Block eine return- Anweisung enthält , wird der finally- Block weiterhin aufgerufen:

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"); }

Wenn wir eine Ausnahme aus dem try- Block auslösen, behandelt der catch- Block die Ausnahme. Obwohl der catch- Block eine return-Anweisung enthält , führt die JVM den finally- Block aus, bevor die Steuerung an die aufrufende Methode übergeben wird, und gibt Folgendes aus:

Inside try Inside catch Inside finally

4. Wenn schließlich nicht ausgeführt wird

Obwohl wir immer erwarten, dass die JVM die Anweisungen innerhalb eines finally- Blocks ausführt , gibt es einige Situationen, in denen die JVM keinen finally- Block ausführt .

We might already expect that if the operating system stops our program, then the program would not get the chance to execute all of its code. There are also some actions we can take that will similarly prevent the execution of a pending finally block.

4.1. Invoking System.exit

In this case, we are terminating the JVM by calling System.exit and hence the JVM will not execute our finally block:

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

This outputs:

Inside try

4.2. Invoking halt

Similar to System.exit, a call to Runtime.halt also halts the execution and the JVM does not execute any finally blocks:

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

Thus, the output will be:

Inside try

4.3. Daemon Thread

If a Daemon thread enters the execution of a try/finally block and all other non-daemon threads exit before the daemon thread executes the finally block, the JVM doesn’t wait for the daemon thread to finish the execution of finally block:

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();

In this example, the runnable prints “Inside try” as soon as it enters the method and waits for 1 second before printing “Inside finally”.

Here, we start the regular thread and the daemon thread with a small delay. When the regular thread executes the finally block, the daemon thread is still waiting within the try block. As the regular thread completes execution and exits, the JVM also exits and does not wait for the daemon thread to complete the finally block.

Here's the output:

Inside try Inside try Inside finally

4.4. JVM Reaches an Infinite Loop

Here's a try block which contains an infinite while loop:

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

Though it's not specific to finally, it's worth mentioning that if the try or catch block contains an infinite loop, the JVM will never reach any block beyond that loop.

5. Common Pitfalls

There are some common pitfalls that we must avoid when we use the finally block.

Although it's perfectly legal, it's considered bad practice to have a return statement or throw an exception from a finally block, and we should avoid it at all costs.

5.1. Disregards Exception

A return statement in the finally block ignores an uncaught exception:

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

In this case, the method ignores the RuntimeException thrown and returns the value “from finally”.

5.2. Ignores Other return Statements

A return statement in the finally block ignores any other return statement in the try or catch block. Only the return statement in the finally block executes:

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

In this example, the method always returns “from finally” and completely ignores the return statement in the try block. This could be a very difficult bug to spot, which is why we should avoid using return in finally blocks.

5.3. Changes What's Thrown or Returned

Also, in the case of throwing an exception from a finally block, the method disregards the exception thrown or return statements in the try and catch blocks:

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

This method never returns a value and always throws a RuntimeException.

Obwohl wir möglicherweise nicht absichtlich eine Ausnahme vom finally- Block auslösen, wie in diesem Beispiel, kann dieses Problem dennoch auftreten. Dies kann auftreten, wenn Bereinigungsmethoden, die wir in einem finally- Block verwenden, eine Ausnahme auslösen.

6. Fazit

In diesem Artikel haben wir besprochen, was Blöcke schließlich in Java tun und wie man sie verwendet. Dann haben wir uns verschiedene Fälle angesehen, in denen die JVM sie ausführt, und einige, in denen dies möglicherweise nicht der Fall ist.

Zuletzt haben wir uns einige häufige Fallstricke angesehen, die mit der Verwendung von finally- Blöcken verbunden sind.

Wie immer ist der in diesem Tutorial verwendete Quellcode über GitHub verfügbar.