Zur Hauptnavigation springen [Alt]+[0] Zum Seiteninhalt springen [Alt]+[1]

Lebenszyklus von Objekten

Erzeugen von Objekten

Nachdem nun geklärt ist, was Objekte und Klassen sind und wie man geeignete Klassenentwürfe findet, wird in diesem Kapitel dargestellt wie Java mit Objekten umgeht. Vieles davon lässt sich im Prinzip aber auch auf andere Programmiersprachen übertragen.

Deklariert man in Java eine Variable, so wird je nach Typ der Variable ein genügend großer Speicherbereich reserviert: für eine int-Variable beispielsweise 32bit, für eine char-Variable 16bit. Dies ist bei Objekten anders. Deklariert man mit

CRT_Fernseher fernseher1;

die Variable fernseher1 vom Typ CRT_Fernseher, so wird dadurch kein Speicherplatz für ein CRT_Fernseher-Objekt reserviert, sondern nur für eine Referenz auf ein Objekt. Diese Variable speichert also nur einen Verweis auf ein Objekt und nicht das Objekt selbst. Der für eine Referenzvariable reservierte Speicher ist immer gleich groß, unabhängig von der Art des Objektes auf das die Variable zeigen soll.

Erst durch den new-Befehl wird im Heap (einem speziellen Speicherbereich für die Objekte) Speicherplatz reserviert1. Die Referenzvariable verweist dann auf diesen Speicherbereich. Dies kann man sich folgendermaßen vorstellen:

Eine primitive Variable ist wie eine Kiste mit Aufschrift (=Name der Variable). Die Größe der Kiste wird durch den Variablentyp festgelegt. Der Inhalt der Kiste gibt den Wert der Variable an.

int i;
Kiste
char c;
Kiste

Eine Referenzvariable ist genauso eine Kiste. Zu Beginn zeigt sie auf nichts (null-Pointer).

Katze k;
Kiste

Erst wenn ihr ein Wert zugewiesen wird, zeigt sie auf ein konkretes Objekt. Häufig entstehen Fehler dadurch, dass einer Referenzvariablen kein Objekt zugewiesen wird, bzw. gar kein Objekt erzeugt wird. Versucht man dann auf das Objekt zuzugreifen, erhält man eine null-pointer exception.

k = new Katze("Mauzi");
Verdeutlichung anhand Kiste und Wolke

In Java sind auch Arrays Objekte. Dies erkennt man daran, dass auch der Speicherplatz für Arrays mit dem new Operator reserviert werden muss. Es wird dann ein Speicherbereich im Heap reserviert, der für die angegebene Anzahl an Variablen des Grundtyps ausreichend ist. Automatisch haben die Array-Objekte dann auch ein Attribut length.

int[] nr = new int[5];
Verdeutlichung anhand Kiste und Wolke

Natürlich können auch Arrays erzeugt werden, die als Grundtyp eine Referenzvariable haben. Gerade dann kommt es oft zu null-Pointer exceptions, wenn nicht alle Speicherplätze mit Referenzen auf existierende Objekte belegt werden.

Tier[] ti = new Tier[5];
ti[3] = new Katze(“Mauzi“);
ti[1] = new Katze(“Freddi“);
Verdeutlichung anhand Kiste und Wolke

Manipulation von Objekten

Die unterschiedliche Behandlung von primitiven Variablen und Referenzvariablen hat auch Auswirkungen auf die Programmierung. So führt beispielsweise folgender Vergleich in der Regel nicht zum gewünschten Ergebnis, obwohl er sich korrekt kompilieren lässt.

Katze k1 = new Katze("Mauzi");
Katze k2 = new Katze("Mauzi");
if (k1 == k2) {....}
Verdeutlichung anhand Kiste und Wolke

Die beiden Referenzvariablen zeigen auf verschiedene Objekte. Damit kann der Vergleich nicht positiv ausfallen, auch wenn alle Attributwerte identisch sind. Die Attributwerte müssen einzeln vergleichen werden. Selbst folgender Vergleich schlägt noch fehl:

if (k1.rufname == k2.rufname) {...}

Diese beiden Strings würden nicht als identisch erkannt werden, da auch Strings in Java Objekte und keine einfachen Variablen sind2. Daher wird die Typbezeichnung String großgeschrieben. Die Situation auf dem Heap sieht also in Wirklichkeit eher so aus:

Verdeutlichung anhand Kiste und Wolke

Ein Vergleich der beiden String-Referenzvariablen muss also fehlschlagen. Die Klasse String stellt aber eine Vielzahl von Methoden zur Verfügung. Mit der speziellen Methode compareTo können tatsächlich die Inhalte der Strings verglichen werden.

if (k1.rufname.equals(k2.rufname)) {...}

Auch beim Verändern von Variablenwerten spielt dies eine Rolle:

int alter1 = 5;
int alter2 = alter1;

Katze k1 = new Katze(„Mauzi“);
k1.alter = 5;
Katze k2 = k1;

Es ist klar, dass nun in alter2 der Wert 5 und in k2.alter auch der Wert 5 steht. Ändert man aber nun den Wert von alter2, bzw. k2.alter, dann hat dies unterschiedliche Auswirkungen. Wird alter2 geändert, so behält alter1 den Wert 5, da es sich um unabhängige Variablen handelt, deren Wert nur zufällig am Anfang gleich gesetzt wurde. Ändert man aber k2.alter auf 6, so ist automatisch auch k1.alter gleich 6, da beide Referenzvariablen auf das gleiche Objekt verweisen. Bei diesem Objekt wird der Attributwert geändert.

Verdeutlichung anhand Kiste und Wolke

Löschen von Objekten

Um das Löschen von Objekten braucht man sich in Java glücklicherweise nicht zu kümmern. Die Java-Maschine merkt sich, welche Verweise auf ein Objekt existieren. Sobald kein Verweis mehr auf ein Objekt existiert, kann es nicht mehr angesprochen werden. Daher ist es überflüssig geworden. Der Garbage Collector von Java sorgt daher in regelmäßigen Abständen dafür, dass unnütz gewordene Objekte gelöscht und der Speicherplatz frei gegeben wird.

Möchte man einen Verweis auf ein Objekt entfernen, setzt man die Referenzvariable auf den Wert null.

k1 = null;

Anmerkung: In anderen Programmiersprachen müssen Objekte manchmal durch den Aufruf des Destruktors gelöscht werden.

Literaturtipp: In „Java – von Kopf bis Fuß“ (Kathy Sierra, O'Reilly Verlag) ist der Lebenszyklus von Objekten sehr gut dargestellt und beschrieben. Es finden sich dort auch einige Aufgaben, die sich auch für die Schule eignen.


1 Die lokalen Variablen und Parameter einer Methode legt Java auf dem Stack ab. Bei jedem Methodenaufruf wird ein Stack-Frame auf den Stack abgelegt, der Platz für die lokalen Variablen dieser Methode und einige Zusatzdaten bietet.

2 String-Objekte müssen nicht unbedingt mit dem new-Operator erzeugt werden, sondern können auch durch Zuweisung einer Konstanten implizit erzeugt werden.

 

 

Lebenszyklus von Objekten: Herunterladen [odt][61 KB]

 

Weiter zu Klassenentwurf