Guide du mot-clé Java enfin

1. Vue d'ensemble

Dans ce didacticiel, nous allons explorer le mot-clé finally en Java. Nous verrons comment l'utiliser avec des blocs try / catch dans la gestion des erreurs. Bien que finalement, il vise à garantir l'exécution du code, nous discuterons des situations exceptionnelles dans lesquelles la JVM ne l'exécute pas.

Nous discuterons également de certains pièges courants où un bloc finally peut avoir un résultat inattendu.

2. Qu'est-ce que finalement?

définit enfin un bloc de code que nous utilisons avec le mot-clé try . Il définit le code qui est toujours exécuté après l' essai et tout bloc catch , avant que la méthode ne soit terminée.

Le bloc finally s'exécute indépendamment du fait qu'une exception soit levée ou interceptée .

2.1. Un exemple rapide

Regardons enfin dans un bloc try-catch-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"); } 

Dans cet exemple, quelle que soit la valeur du nombre de paramètres , la machine virtuelle Java exécute le bloc finally et imprime «In finally» .

2.2. Utiliser enfin sans bloc catch

De plus, nous pouvons utiliser un bloc finally avec un bloc try indépendamment du fait qu'un bloc catch soit présent :

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

Et nous obtiendrons la sortie:

Inside try Inside finally

2.3. Pourquoi est enfin utile

Nous utilisons généralement le bloc finally pour exécuter du code de nettoyage comme la fermeture de connexions, la fermeture de fichiers ou la libération de threads, car il s'exécute indépendamment d'une exception.

Remarque: try-with-resources peut également être utilisé pour fermer des ressources au lieu d'un bloc finally .

3. Quand est finalement exécuté

Jetons un coup d'œil à toutes les permutations de quand la JVM exécute enfin des blocs, afin que nous puissions mieux le comprendre.

3.1. Aucune exception n'est levée

Lorsque le bloc try se termine, le bloc finally est exécuté, même s'il n'y avait pas d'exception:

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

Dans cet exemple, nous ne lançons pas d'exception à partir du bloc try . Ainsi, la JVM exécute tout le code dans les blocs try et finally .

Cela produit:

Inside try Inside finally

3.2. L'exception est levée et non gérée

S'il y a une exception et qu'elle n'est pas interceptée, le bloc finally est toujours exécuté:

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

La JVM exécute le bloc finally même dans le cas d'une exception non gérée.

Et le résultat serait:

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

3.3. L'exception est levée et gérée

S'il y a une exception et qu'elle est interceptée par le bloc catch , le bloc finally est toujours exécuté:

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

Dans ce cas, le bloc catch gère l'exception levée, puis la machine virtuelle Java exécute le bloc finally et produit la sortie:

Inside try Inside catch Inside finally

3.4. Méthode renvoie du bloc try

Même le retour de la méthode n'empêchera pas finalement les blocs de s'exécuter:

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

Ici, même si la méthode a une instruction return , la JVM exécute le bloc finally avant de confier le contrôle à la méthode appelante.

Nous obtiendrons la sortie:

Inside try Inside finally

3.5. Méthode renvoie du bloc catch

Lorsque le bloc catch contient une instruction return , le bloc finally est toujours appelé:

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

Lorsque nous lançons une exception à partir du bloc try , le bloc catch gère l'exception. Bien qu'il existe une instruction return dans le bloc catch , la JVM exécute le bloc finally avant de passer le contrôle à la méthode appelante, et elle génère:

Inside try Inside catch Inside finally

4. Quand finalement n'est pas exécuté

Bien que nous nous attendions toujours à ce que la JVM exécute les instructions à l'intérieur d'un bloc finally , il existe certaines situations où la JVM n'exécutera pas de bloc finally .

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.

Bien que nous ne puissions pas lancer intentionnellement une exception du bloc finally comme dans cet exemple, nous pouvons toujours rencontrer ce problème. Cela peut se produire lorsque les méthodes de nettoyage que nous utilisons dans un bloc finally lèvent une exception.

6. Conclusion

Dans cet article, nous avons discuté de ce que font finalement les blocs en Java et comment les utiliser. Ensuite, nous avons examiné différents cas où la JVM les exécute, et quelques cas où ce ne serait pas le cas.

Enfin, nous avons examiné quelques pièges courants associés à l'utilisation de blocs finally .

Comme toujours, le code source utilisé dans ce didacticiel est disponible à l'adresse over sur GitHub.