Informatik

Referenzen

In der Einführungsphase ist dir etwas Merkwürdiges begegnet: Die Methode tausche(int[] pWerte, int pI, int pJ) gab nichts zurück und veränderte trotzdem das Feld des Aufrufers.

Jetzt klären wir, warum.

Das Experiment

Sage ohne Rechner voraus, was ausgegeben wird. Nimm dir Zeit – die zweite Ausgabe überrascht die meisten.

Auflösung
zahl nach dem Aufruf:  10
feld[0] nach dem Aufruf: 99

Bei der Zahl bleibt alles beim Alten, beim Feld nicht. Und das, obwohl beide Methoden aussehen, als täten sie dasselbe.

Die Erklärung

  • Bei einem elementaren Typ (int, double, boolean, char) steht der Wert direkt in der Variablen. Beim Methodenaufruf bekommt der Parameter eine Kopie des Wertes. Ändert die Methode ihn, ändert sie nur ihre Kopie.
  • Bei einem Objekttyp (Felder, String, alle eigenen Klassen) steht in der Variablen eine Referenz – ein Verweis auf das Objekt im Speicher. Beim Aufruf bekommt der Parameter eine Kopie der Referenz, nicht des Objekts. Beide Referenzen zeigen also auf dasselbe Objekt.

Java übergibt immer den Wert der Variablen. Bei Objekttypen ist dieser Wert eben eine Referenz.

elementarer Typ                 Objekttyp
                                
zahl                            feld ──────┐
┌────┐                          ┌────┐     │
│ 10 │                          │  ● │     ▼
└────┘                          └────┘   ┌────┬────┬────┐
                                         │ 10 │ 20 │ 30 │
pZahl (Kopie)                   pFeld ───┘└────┴────┴────┘
┌────┐                          ┌────┐
│ 10 │                          │  ● │
└────┘                          └────┘

Zwei Namen, ein Objekt

a) Erkläre, warum erstes[0] sich mitverändert, obwohl nur zweites beschrieben wurde.

b) erstes und drittes haben genau denselben Inhalt. Warum ist erstes == drittes trotzdem false?

c) Was müsste man tun, damit zweites eine unabhängige Kopie ist?

Auflösung

a) int[] zweites = erstes; kopiert nur die Referenz, nicht das Feld. Danach gibt es zwei Namen für dasselbe Objekt. Man nennt das Aliasing.

b) == vergleicht bei Objekttypen die Referenzen – also die Frage „ist es dasselbe Objekt?“. Es sind zwei verschiedene Objekte, die zufällig dasselbe enthalten.

Genau derselbe Punkt wie bei den Zeichenketten in der Einführungsphase: == prüft Identität, nicht Gleichheit.

c) Man muss ein neues Feld anlegen und die Werte einzeln kopieren:

int[] zweites = new int[erstes.length];
for (int i = 0; i < erstes.length; i++) {
    zweites[i] = erstes[i];
}

In dieser Umgebung gibt es kein Arrays.equals und kein Arrays.copyOf. Beides musst du selbst schreiben – was didaktisch kein Verlust ist: Wer es einmal geschrieben hat, weiß, was dahintersteckt.

Objekte in Feldern

Bei Feldern von Objekten wird es doppelt referenziell.

Man unterscheidet zwei Arten des Kopierens:

  • Eine flache Kopie legt ein neues Feld an, das auf dieselben Objekte verweist. Ändert man ein Objekt, sieht man die Änderung in beiden Feldern.
  • Eine tiefe Kopie legt zusätzlich für jedes Objekt ein neues an. Beide Felder sind dann vollständig unabhängig.

Bei Feldern elementarer Typen fallen beide zusammen – dort gibt es keine Objekte, die man teilen könnte.

Aufgabe: Kopieren und vergleichen

Ergänze die Methoden so, dass alle Tests grün werden. Achte besonders auf den Unterschied zwischen flacher und tiefer Kopie.

Tipp: Der Unterschied in einer Zeile

Flache Kopie: neu[i] = pPunkte[i]; – dieselbe Referenz.

Tiefe Kopie: neu[i] = pPunkte[i].kopie(); – ein neues Objekt.

Genau diese eine Zeile macht den ganzen Unterschied. Deshalb ist es wichtig, sich beim Programmieren immer bewusst zu machen, welche der beiden man gerade baut.

Lösung. Erfrage das Passwort bei deiner Lehrkraft.

Warum das wichtig wird

Referenzen sind kein Detail für Fortgeschrittene. Sie sind die Grundlage der nächsten Kapitel:

  • Eine verkettete Liste besteht aus Knoten, von denen jeder eine Referenz auf den nächsten hält.
  • Ein Baum ist ein Knoten mit Referenzen auf zwei Teilbäume.
  • Ein Graph ist eine Menge von Knoten mit Referenzen aufeinander.

Ohne Referenzen gäbe es keine dynamischen Datenstrukturen. Und dass null bedeutet „hier geht es nicht weiter“, ist dort die zentrale Abbruchbedingung.

Zusatzaufgabe

Die Klasse Punkt hat jetzt eine Methode istGleich. In echtem Java heißt so eine Methode üblicherweise equals.

a) Benenne sie um und schreibe die Tests entsprechend.

b) Probiere aus, was passiert, wenn du zwei gleiche Punkte mit == vergleichst, obwohl du equals definiert hast.

c) Erkläre, warum das Umbenennen allein nicht ausreicht, damit == plötzlich Inhalte vergleicht – und warum das gut so ist.


Selbsttest

Referenzen

Teilbare URL erstellen

Abschnitte auswählen