W3docs

Java final-Schlüsselwort

Java-Variablen konstant machen, Methoden-Überschreibung verhindern und Klassenvererbung mit dem final-Schlüsselwort unterbinden.

final bedeutet „einmal setzen, dann nicht mehr ändern." Es gilt für vier Dinge, und die Wirkung hängt davon ab, worauf es angewendet wird:

  • Auf eine Variable kann die Bindung nicht neu zugewiesen werden.
  • Auf eine Methode kann die Methode nicht von einer Unterklasse überschrieben werden.
  • Auf eine Klasse kann die Klasse nicht erweitert werden.
  • Auf einen Parameter kann der Parameter innerhalb der Methode nicht neu zugewiesen werden.

Der gemeinsame Nenner ist „keine weitere Änderung nach der ersten Bindung." Was genau verboten wird, hängt davon ab, was markiert ist.

Lokale final-Variablen

Die einfachste Form: Eine lokale final-Variable kann nach ihrer ersten Zuweisung nicht neu zugewiesen werden.

void demo() {
  final int max = 100;
  max = 50;          // ERROR: cannot assign a value to final variable max
}

Die Initialisierung muss nicht bei der Deklaration erfolgen — die erste Zuweisung kann aufgeschoben werden, solange sie genau einmal vor jedem Lesezugriff erfolgt:

final int x;
if (condition) x = 1;
else           x = 2;
System.out.println(x);   // ok — x was definitely assigned

final bei einer lokalen Variable ist hauptsächlich ein Dokumentationswerkzeug — es signalisiert einem späteren Leser (und dem Compiler), dass sich dieser Wert ab diesem Punkt nicht mehr ändert. Lambda- und Inner-Class-Captures erfordern es: Jede lokale Variable, die darin verwendet wird, muss final oder effektiv final (nie neu zugewiesen) sein.

Final-Parameter

Ein final-Parameter kann im Methodenrumpf nicht neu zugewiesen werden:

void greet(final String name) {
  name = name.toUpperCase();    // ERROR
}

Manche Teams fordern final bei jedem Parameter; andere empfinden es als visuellen Lärm. Beides ist in Ordnung — entscheidend ist Konsistenz innerhalb eines Codebasis. Die Gewohnheit, Parameter zu mutieren, die es entmutigt, ist eine echte Fehlerquelle.

Final-Felder

Ein final-Feld wird genau einmal zugewiesen — entweder bei der Deklaration, in einem Instanz-Initialisierer oder im Konstruktor — und danach nie wieder:

public class Point {
  private final int x, y;

  public Point(int x, int y) {
    this.x = x;        // assigned once
    this.y = y;
  }

  // no setX or setY — there's no way to change the values
}

Das ist der Kern einer unveränderlichen Klasse. Jedes Feld ist final; nichts kann das Objekt nach der Konstruktion mutieren.

Der Compiler prüft, dass jedes final-Feld auf jedem Konstruktorpfad genau einmal zugewiesen wird. Fehlt ein Pfad, gibt es einen Kompilierfehler. Wird zweimal zugewiesen, gibt es ebenfalls einen Kompilierfehler.

static final — Konstanten

Die Standardform für eine Konstante ist public static final mit einem UPPER_SNAKE-Namen:

public static final double PI       = 3.141592653589793;
public static final int    MAX_RETRY = 3;

Statisch, weil kein Grund besteht, eine Kopie pro Instanz zu halten; final, weil es eine Konstante ist. Bei primitiven und String-Konstanten inliniert der Compiler den Wert an jeder Aufrufstelle. Eine Konsequenz: Wenn der Wert einer solchen Konstante geändert wird, behalten Klassen, die darauf verwiesen haben, den alten Wert, bis sie neu kompiliert werden — das Literal wurde zum Zeitpunkt ihrer eigenen Kompilierung in ihren Bytecode eingebettet.

final und Referenzen

Ein häufiges Missverständnis: final friert die Referenz ein, nicht das Objekt, auf das sie zeigt. Bei einem finalen Array oder einer finalen Liste können die Inhalte trotzdem mutiert werden:

final int[] nums = {1, 2, 3};
nums[0] = 99;        // ok — mutating the array, not reassigning the reference
nums = new int[5];   // ERROR — reassigning the reference

final List<String> names = new ArrayList<>();
names.add("Rex");    // ok — mutates the list
names = List.of();   // ERROR

Wenn auch die Inhalte einer Liste eingefroren sein sollen, kann sie mit List.copyOf(...) oder Collections.unmodifiableList(...) umwickelt werden.

Final-Methoden

final bei einer Methode bedeutet, dass keine Unterklasse sie überschreiben darf:

public class Shape {
  public final String describe() {       // can never be overridden
    return getClass().getSimpleName() + " area=" + area();
  }
  public double area() { return 0; }
}

public class Circle extends Shape {
  public String describe() { return "circle"; }   // ERROR
}

Eine Methode wird mit final markiert, wenn das Überschreiben Invarianten brechen würde, auf die die Klasse angewiesen ist. Das ist eine bewusste Designentscheidung — die meisten Methoden sind in echten Codebasen nicht final — aber für Templates, die sich nicht ändern dürfen, ist es das richtige Mittel.

Final-Klassen

final bei einer Klasse bedeutet, dass keine Klasse sie erweitern darf:

public final class Money { ... }

public class CryptoMoney extends Money { }    // ERROR

Dies ist sinnvoll, wenn Erweiterung das Design zunichte machen würde. Die Standardbibliothek macht das routinemäßig: String, Integer, Long, Double und die übrigen primitiven Wrapper sind alle final — ihre Erweiterung würde ermöglichen, veränderbares Verhalten in einen Typ einzuschmuggeln, den die Sprache als unveränderlich behandelt.

final ist keine Unveränderlichkeit

Ein final-Feld allein macht ein Objekt nicht unveränderlich; es verhindert lediglich, dass die Referenz geändert wird. Echte Unveränderlichkeit erfordert:

  1. Jedes Feld final.
  2. Die Klasse selbst final, oder sorgfältig konstruiert, um zu verhindern, dass Unterklassen veränderbaren Zustand hinzufügen.
  3. Keine Methode, die ein veränderbares internes Objekt preisgibt (defensive Kopien beim Herausgeben).
  4. Keine Methode, die einem Aufrufer erlaubt, den Zustand darüber zu mutieren.

Records (behandelt in records) bündeln das meiste davon automatisch.

Ein ausgearbeitetes Beispiel

java— editable, runs on the server

Was kommt als nächstes

private-Felder und final-Unveränderlichkeit sind die Bausteine des nächsten Konzepts: den Zustand vollständig hinter den Methoden einer Klasse zu verbergen. Das Kapitel zur Kapselung führt diese Fäden zusammen.

Übungen

Übung
Ein Feld, das als final int[] xs = {1, 2, 3}; deklariert ist — welche Zuweisung wird vom Compiler abgelehnt?
Ein Feld, das als final int[] xs = {1, 2, 3}; deklariert ist — welche Zuweisung wird vom Compiler abgelehnt?
Was this page helpful?