Unterschiede zwischen Final, finally und Finalize in Java

1. Übersicht

In diesem Tutorial geben wir einen Überblick über drei Java-Schlüsselwörter: final, finally und finalize.

Während diese Schlüsselwörter einander ähneln, hat jedes in Java eine ganz andere Bedeutung. Wir werden den Zweck jedes einzelnen von ihnen lernen und einige Beispiele durch ein bisschen Code sehen.

2. endgültiges Schlüsselwort

Schauen wir uns zunächst das endgültige Schlüsselwort an, wo und warum. Wir können das letzte Schlüsselwort auf Klassen-, Methoden-, Feld-, Variablen- und Methodenparameterdeklarationen anwenden .

Es hat jedoch nicht bei jedem den gleichen Effekt :

  • Eine Klasse zu machen endgültige Mittel , dass es nicht möglich sein , diese Klasse zu erweitern
  • Das Hinzufügen von final zu einer Methode bedeutet, dass diese Methode nicht überschrieben werden kann
  • Wenn final schließlich vor ein Feld, eine Variable oder einen Parameter gestellt wird, bedeutet dies, dass die einmal zugewiesene Referenz nicht mehr geändert werden kann (wenn sich die Referenz jedoch auf ein veränderliches Objekt bezieht, kann sich der interne Status ändern, obwohl sie final ist).

Einen ausführlichen Artikel zum endgültigen Keyword finden Sie hier.

Lassen Sie uns anhand einiger Beispiele sehen, wie das endgültige Schlüsselwort funktioniert.

2.1. endgültige Felder, Parameter und Variablen

Erstellen wir eine übergeordnete Klasse mit zwei int- Feldern, einem letzten und einem regulären nicht endgültigen:

public class Parent { int field1 = 1; final int field2 = 2; Parent() { field1 = 2; // OK field2 = 3; // Compilation error } }

Wie wir sehen können, verbietet uns der Compiler, field2 einen neuen Wert zuzuweisen .

Fügen wir nun eine Methode mit einem regulären und einem letzten Argument hinzu:

 void method1(int arg1, final int arg2) { arg1 = 2; // OK arg2 = 3; // Compilation error }

Ähnlich wie bei Feldern ist es nicht möglich, arg2 etwas zuzuweisen, da es als endgültig deklariert ist.

Wir können jetzt eine zweite Methode hinzufügen, um zu veranschaulichen, wie dies mit lokalen Variablen funktioniert:

 void method2() { final int localVar = 2; // OK localVar = 3; // Compilation error }

Es passiert nichts Überraschendes, der Compiler lässt uns localVar nach seiner ersten Zuweisung keinen neuen Wert zuweisen .

2.2. endgültige Methode

Nehmen wir nun an, wir machen method2 final und erstellen eine Unterklasse von Parent , sagen wir Child , in der wir versuchen, beide Methoden der Oberklasse zu überschreiben:

public class Child extends Parent { @Override void method1(int arg1, int arg2) { // OK } @Override final void method2() { // Compilation error } }

Wie wir sehen können, gibt es kein Problem beim Überschreiben von method1 () , aber beim Versuch, method2 () zu überschreiben, wird ein Kompilierungsfehler angezeigt .

2.3. letzte Klasse

Und zum Schluss machen wir die Child- Klasse endgültig und versuchen, eine Unterklasse davon zu erstellen, GrandChild :

public final class Child extends Parent { // ... }
public class GrandChild extends Child { // Compilation error }

Der Compiler beschwert sich erneut. Die Kinderklasse ist endgültig und kann daher nicht erweitert werden.

3. Endlich blockieren

Der finally- Block ist ein optionaler Block, der mit einer try / catch- Anweisung verwendet werden kann. In diesem Block enthalten wir Code, der nach der Try / Catch- Struktur ausgeführt werden soll, unabhängig davon, ob eine Ausnahme ausgelöst wird oder nicht .

Es ist sogar möglich, es mit dem try- Block ohne catch- Block zu verwenden, vorausgesetzt, wir fügen einen finally- Block hinzu. Der Code wird dann nach dem Versuch oder nach dem Auslösen einer Ausnahme ausgeführt.

Wir haben hier einen ausführlichen Artikel über die Ausnahmebehandlung in Java.

Lassen Sie uns nun in einem kurzen Beispiel einen finally- Block demonstrieren . Wir werden eine Dummy- main () -Methode mit einer try / catch / finally- Struktur erstellen :

public static void main(String args[]) { try { System.out.println("Execute try block"); throw new Exception(); } catch (Exception e) { System.out.println("Execute catch block"); } finally { System.out.println("Execute finally block"); } }

Wenn wir diesen Code ausführen, wird Folgendes ausgegeben:

Execute try block Execute catch block Execute finally block

Lassen Sie uns nun die Methode ändern, indem Sie den catch-Block entfernen (und der Signatur eine Throws-Ausnahme hinzufügen ):

public static void main(String args[]) throws Exception { try { System.out.println("Execute try block"); throw new Exception(); } finally { System.out.println("Execute finally block"); } }

Die Ausgabe ist jetzt:

Execute try block Execute finally block

Wenn wir jetzt die Anweisung throw new Exception () entfernen , können wir beobachten, dass die Ausgabe gleich bleibt. Unsere endgültige Blockausführung erfolgt jedes Mal.

4. Methode abschließen

Und schließlich ist die finalize- Methode eine geschützte Methode, die in der Object-Klasse definiert ist. Es wird vom Garbage Collector für Objekte aufgerufen , auf die nicht mehr verwiesen wird und die für die Garbage Collection ausgewählt wurden .

Like any other non-final method we can override this method to define the behavior an object must have when collected by the garbage collector.

Again, a detailed article covering the finalize method can be found here.

Let's see an example of how it works. We'll use System.gc() to suggest the JVM to trigger garbage collection:

 @Override protected void finalize() throws Throwable { System.out.println("Execute finalize method"); super.finalize(); }
 public static void main(String[] args) throws Exception { FinalizeObject object = new FinalizeObject(); object = null; System.gc(); Thread.sleep(1000); }

In this example, we override finalize() method in our object and create a main() method which instantiates our object and immediately drops the reference by setting the created variable to null.

After that, we call System.gc() to run the garbage collector (at least we expect it to run) and wait for a second (just to ensure that the JVM doesn't shut down before garbage collector has the chance to call finalize() method).

The output of this code execution should be:

Execute finalize method

Note that it's considered bad practice to override finalize() method as its execution depends on garbage collection which is in the hand of the JVM. Plus, this method has been deprecated since Java 9.

5. Conclusion

In diesem Artikel haben wir kurz die Unterschiede zwischen den drei Java-Schlüsselwörtern besprochen: final, finally und finalize .

Den vollständigen Code des Artikels finden Sie auf GitHub.