W3docs

Java-Objekt-Klonen

Java-Objekte mit clone(), dem Cloneable-Interface und dem Unterschied zwischen flachen und tiefen Kopien kopieren.

Klonen eines Objekts bedeutet, ein neues Objekt mit demselben Zustand zu erzeugen — eine Kopie, die unabhängig vom Original verändert werden kann. Javas eingebaute Lösung ist Object.clone() zusammen mit dem Marker-Interface Cloneable, aber das Design hat genug Schwachstellen, dass moderner Code meist auf einen Copy-Konstruktor oder eine Factory-Methode zurückgreift. Dieses Kapitel zeigt beide Wege und die Falle, die zwischen ihnen liegt.

Der eingebaute Weg: Object.clone()

Object.clone() ist protected und erstellt eine flache Kopie des Objekts, Feld für Feld. Um es zu verwenden, muss man:

  1. Die Klasse das Interface Cloneable implementieren lassen — ein Marker-Interface ohne Methoden. Ohne es wirft clone() eine CloneNotSupportedException.
  2. clone() überschreiben, um es public zu machen und (üblicherweise) den Rückgabetyp auf die eigentliche Klasse einzuschränken.
public class Box implements Cloneable {
  int size;

  @Override
  public Box clone() {
    try {
      return (Box) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new AssertionError(e);   // can't happen — we implement Cloneable
    }
  }
}

super.clone() erzeugt die eigentliche Kopie. Der try/catch-Block ist Verwaltungsaufwand: Die geprüfte Ausnahme ist in Object.clone deklariert, aber da unsere Klasse Cloneable implementiert, ist die Ausnahme unerreichbar.

Flache Kopie: was sie tatsächlich tut

Eine flache Kopie dupliziert die unmittelbaren Felder. Referenzen innerhalb des Objekts werden als Referenzen kopiert, nicht als neue Objekte — Original und Klon teilen sich also, worauf diese Referenzen zeigen:

public class Person implements Cloneable {
  String name;
  int[]  scores;

  @Override
  public Person clone() {
    try { return (Person) super.clone(); }
    catch (CloneNotSupportedException e) { throw new AssertionError(e); }
  }
}

Person a = new Person();
a.scores = new int[]{1, 2, 3};
Person b = a.clone();
b.scores[0] = 99;
System.out.println(a.scores[0]);   // 99 — they share the same array

Bei primitiven und unveränderlichen Werten (String, Integer, LocalDate) ist eine flache Kopie in Ordnung. Bei veränderlichen Unterobjekten ist sie fast immer falsch — eine Änderung am Klon wirkt sich auch auf das Original aus.

Tiefe Kopie: die Lösung

Um eine wirklich unabhängige Kopie zu erhalten, überschreibt man clone() und kopiert veränderliche Felder rekursiv:

@Override
public Person clone() {
  try {
    Person copy   = (Person) super.clone();
    copy.scores   = scores.clone();          // arrays have their own clone()
    return copy;
  } catch (CloneNotSupportedException e) {
    throw new AssertionError(e);
  }
}

Arrays implementieren Cloneable nativ und werden mit .clone() kopiert. Collections dagegen nicht — bei einem List<String>-Feld schreibt man copy.items = new ArrayList<>(items); (siehe ArrayList). Bei einem Graphen eigener veränderlicher Typen muss jeder Typ im Graphen mitmachen.

Warum Cloneable einen schlechten Ruf hat

Einige Eigenheiten machen clone() umständlich:

  • Es ist ein Marker-Interface ohne clone()-Methode — Cloneable selbst legt nichts offen, der Vertrag liegt bei Object.clone().
  • Es umgeht Konstruktoren — die Felder des neuen Objekts werden von der JVM gefüllt, sodass im Konstruktor erzwungene Invarianten nicht erneut geprüft werden.
  • Unterklassen erben die Verpflichtung: Wenn Parent clone() überschreibt, muss jede Unterklasse die Tiefkopier-Logik synchron halten, oder sie erbt stillschweigend eine fehlerhafte flache Version.
  • Die geprüfte Ausnahme, der Cast, der super.clone()-Aufruf — jede Überschreibung wiederholt dieselben Formalitäten.

Die moderne Alternative: Copy-Konstruktor

Ein Copy-Konstruktor ist schlicht ein Konstruktor, der eine Instanz derselben Klasse entgegennimmt und deren Felder kopiert:

public class Person {
  String name;
  int[]  scores;

  public Person(Person other) {
    this.name   = other.name;
    this.scores = other.scores.clone();   // deep where it matters
  }
}

Person b = new Person(a);

Er durchläuft den normalen Konstruktor, sodass Invarianten geprüft werden. Er ist reines Java — kein Marker-Interface, keine CloneNotSupportedException, kein Cast. Unterklassen schreiben einfach ihren eigenen Copy-Konstruktor, der super(other) aufruft. Die Empfehlung von Effective Java lautet, Copy-Konstruktoren (oder statische copyOf-Factories) gegenüber clone zu bevorzugen.

Collections-ähnliche Klassen folgen diesem Muster bereits: new ArrayList<>(other), new HashMap<>(other), Set.copyOf(other).

Welchen Ansatz soll ich verwenden?

SituationEmpfohlener Ansatz
Neue, einfache Klasse unter eigener KontrolleCopy-Konstruktor oder statische copyOf-Factory
Klasse mit nur primitiven / unveränderlichen FeldernBeliebiger — selbst ein flaches clone() ist sicher
Klasse mit veränderlichen Feldern (Listen, Arrays, verschachtelte Objekte)Copy-Konstruktor mit expliziten Tiefkopien
Bestehende API, die Cloneable bereits erfordertclone() überschreiben und veränderliche Felder tief kopieren
Werttyp, der neu gestaltet werden kannUnveränderlich machen — dann ist gar keine Kopie nötig

Kurz gesagt: Für neuen Code ist ein Copy-Konstruktor vorzuziehen; auf clone() greift man nur zurück, wenn ein bestehender Vertrag es erzwingt.

Records und unveränderliche Typen

Records sind unveränderlich und brauchen daher überhaupt kein Klonen — einfach dieselbe Referenz überall weitergeben. Benötigt man eine veränderte Kopie, schreibt man kleine with...-Methoden:

record Point(int x, int y) {
  Point withX(int newX) { return new Point(newX, y); }
}

Dieser Stil — „eine neue Instanz mit einem geänderten Feld erzeugen" — ist üblicherweise klarer als Klonen gefolgt von Mutation.

Ein ausgearbeitetes Beispiel

java— editable, runs on the server

Was kommt als Nächstes

Der Großteil der Probleme rund ums Klonen entfällt, wenn die Klasse von Anfang an unveränderlich ist — nichts defensiv zu kopieren, keine Aliasing-Überraschungen, sicher für die gemeinsame Nutzung in Threads. Das nächste Kapitel erklärt, was es braucht, um eine Klasse so zu gestalten. Weiter zu Java unveränderliche Klassen.

Übungen

Übung
Was macht das standardmäßige `Object.clone()` mit einem Feld, das eine veränderliche Liste enthält?
Was macht das standardmäßige `Object.clone()` mit einem Feld, das eine veränderliche Liste enthält?
Was this page helpful?