Home > Informatik > Stufe Q1 > Referenzen

15.1 Referenzen, Allgemeines

Allgemeines - dynamischer Stack - sortierte Liste - NRW-Liste - doppelt verkettete Liste

Zeiger sind nichts Neues!

Wussen Sie eigentlich, dass wir bisher schon immer mit Zeigern gearbeitet haben, wenn wir Java-Programme geschrieben haben? Immer dann, wenn Sie ein Objekt einer Klasse deklarieren, verwenden Sie einen Zeiger! Schauen wir uns mal die einfache Klasse Bruch an:

Klasse Bruch
public class Bruch
{
    int zaehler, nenner;
    
    public Bruch(int z, int n)
    {
        zaehler = z;        
        nenner  = n;
    }
    
    public void show()
    {
       System.out.println(zaehler + "/" + nenner);
    }
}

Diese Klasse hatten wir bereits in der letzten Folge bei der Implementation des ADT List verwendet.

Die beiden Attribute zaehler und nenner dieser Klasse sind ganz normale primitive Datentypen, keine Zeiger. Das heißt, wenn wir mit einem Supermikroskop in die Speicherzellen von zaehler und nenner hineinschauen könnten, würden wir dort tatsächlich int-Zahlen entdecken, die den Zähler und den Nenner eines Bruches darstellen.

Klasse List

Die Klasse List aus der Folge 14.6 hatten wir ja so allgemein implementiert, dass beliebige Objekte in der Liste verwaltet werden können. Um das Zeiger-Prinzip zu verdeutlichen, verwenden wir jetzt eine etwas veränderte und vor allem stark vereinfachte Version der Klasse List. Diese Klasse kann nur Objekte der Klasse Bruch verwalten. Der Anfang des Quelltextes sieht so aus:

public class List
{
    private Bruch[] liste;
    private int count;
    
    public List()
    {
        liste = new Bruch[12];
        count = 0;
    }
    
    public void fill()
    {
        liste[0] = new Bruch(3,4);
        liste[1] = new Bruch(2,7);
        liste[2] = new Bruch(2,7);
        count = 3;
    }
    
    public void show()
    {
       for (int i=0; i < count; i++)
          liste[i].show();
    }
    
    public void showAdress()
    {
       for (int i=0; i < 12; i++)
          System.out.println(liste[i]);
    }    
}

Das Attribut count ist ein ganz normaler primitiver Datentyp. Mit dem Supermikroskop würden wir eine simple int-Zahl entdecken. Direkt nach der Initialisierung eines List-Objektes hätte dieses Attribut den Wert 0, und nach Ausführung der Methode fill den Wert 3.

Schauen wir uns nun das Attribut liste an. Was ist das für ein Datentyp? Offensichtich kein primitiver Datentyp! Mit unserem Supermikroskop würden wir in dem Attribut liste zwar eine Zahl sehen, diese Zahl steht jedoch nicht für einen Zahlenwert, sondern für eine Adresse im Arbeitsspeicher. Und zwar für die Adresse des Bereichs, in dem die eigentichen, die richtigen Daten der Liste untergebracht sind.

Graphische Darstellung der Speicherbelegung eines Bruch-Arrays

Eine ähnliche Abbildung haben Sie bereits in der Folge 7.4 des EF-Kurses gesehen. Das Attribut liste ist eine Referenz (Zeiger, Pointer, Verweis) auf einen anderen Bereich im Arbeitsspeicher. In der Abbildung ist dieser Bereich blau umrahmt. Die Tatsache, dass liste eine Referenz ist, wird in solchen Abbildungen immer durch Pfeile verdeutlicht.

In dem referenzierten Speicherbereich befindet sich nun eine endiche Anzahl von Array-Elementen. In unserer Abbildung sind genau 12 Elemente untergebracht. Der Array liste wurde offensichtlich mit

liste = new Bruch[12];

initialisiert. Allerdings sind keine int-Zahlen oder andere primitive Datentypen in diesen 12 Array-Elementen gespeichert, sondern wiederum Adressen. Diese Adressen zeigen nun auf Speicherbereiche, an denen sich die eigentlichen Objekte der Klasse Bruch befinden. In unserer Abbildung sind drei dieser Objekte bereits erzeugt worden, nehmen also tatsächlich Speicherplatz ein. Die anderen 9 Objekte sind noch nicht erzeugt worden, darum haben die 9 Zeiger, die auf diese noch nicht vorhandenen Objekte zeigen, den Wert null. "null" heißt hier so viel wie "eine konkrete Adresse ist noch nicht vorhanden".

Bessere Darstellung der Abbildung von eben

Zum Vergrößern bitte klicken

Schauen Sie sich nun diese Abbildung an. Die dicken roten Pfeile wurden ersetzt durch die tatsächlichen Speicheradressen der Bruch-Objekte. Wie kann man diese Speicheradressen herausfinden? Ganz einfach: Mit der Methode showAdress:

    public void showAdress()
    {
       for (int i=0; i < 12; i++)
          System.out.println(liste[i]);
    }

Hier wird nicht die show-Methode der Klasse Bruch aufgerufen, sondern die Array-Element selbst werden mit Hilfe der System.out.println-Methode ausgegeben. Und das Ergebnis dieser Ausgabe ist, dass man die Speicheradressen der Array-Elemente sieht, beispielsweise 55d29f62 für das erste Element mit dem Index 0. Offensichtlich werden die Adressen im Hexadezimalcode ausgegeben. Hier ein Screenshot der Konsolenausgabe, wenn man showAdress aufruft:

Screenshot der Konsolenausgabe

Was ja auch interessant ist: Die Adressen scheinen überhaupt nicht geordnet zu sein. Das zweite Element habe eine niedrigere Adresse als das erste Element, und das dritte Element scheint ganz woanderes im Arbeitsspeicher zu stehen.

Referenzen sind Variablen (Attribute, lokale Variablen, Parameter), welche keine konkreten Daten enthalten (zum Beispiel int-Zahlen, Buchstaben etc.), sondern die Adressen von Speicherbereichen. In den so referenzierten Bereichen können dann entweder konkrete Daten stehen, oder weitere Referenzen.

Unser Array aus Bruch-Objekten ist ein Beispiel für eine Referenz, die auf einen Array von Referenzen zeigt. Der Name des Arrays liste ist eine Referenz auf den eigentlichen Array, die Array-Elemente selbst sind wieder Referenzen auf die eigentlichen Bruch-Objekte.

Für Experten

Zeiger oder Pointer sind Referenzen, mit denen man rechnen kann (Zeigerarithmetik); in manchen Programmiersprachen wie C++ wird das sehr gerne gemacht. Weitere Einzelheiten dazu erfahren Sie in dem Lexikon-Artikel über Zeiger.

Weiter mit einem dynamischen Stack...