MarktundTechnik Home-Page Previous Page TOC Index Next Page See Page



4. Tag:

Arbeiten mit Objekten

von Laura Lemay

Wir beginnen die heutige Lektion mit einer klaren Aussage: Da Java eine objektorientierte Sprache ist, haben Sie eine Menge mit Objekten zu tun. Wir erstellen, ändern und verschieben in dieser Lektion Objekte. Ferner ändern wir Variablen von Objekten, rufen ihre Methoden auf, kombinieren Sie mit anderen Objekten und entwickeln selbstverständlich Klassen. Außerdem verwenden wir die von Ihnen erstellten Objekte.

Heute lernen Sie alles über Java-Objekte in ihrem natürlichen Umfeld. Zu den heutigen Themen zählen:

Erstellen neuer Objekte

Wenn Sie ein Java-Programm schreiben, definieren Sie verschiedene Klassen. Wie Sie am 2. Tag gelernt haben, dienen Klassen als Masken für Objekte. Zum großen Teil benutzen Sie die Klassen lediglich, um Instanzen zu erstellen. Dann arbeiten Sie mit diesen Instanzen. In dieser Lektion lernen Sie, wie man ein neues Objekt aus einer Klasse erstellt.

Sie erinnern sich an die Zeichenketten aus der gestrigen Lektion? Sie haben gelernt, daß ein String-Literal - eine Reihe von Zeichen zwischen doppelten Anführungszeichen - eine neue Instanz der Klasse String mit dem Wert der jeweiligen Zeichenkette erzeugt.

Die String-Klasse ist in dieser Hinsicht ungewöhnlich. Es ist zwar eine Klasse, dennoch gibt es eine einfache Möglichkeit, anhand eines Literals Instanzen von dieser Klasse anzulegen. Für die anderen Klassen gibt es diesen Kurzcode nicht. Um Instanzen dieser Klassen zu erstellen, müssen sie explizit mit dem Operator new angelegt werden.

Literale für Zahlen und Zeichen erstellen keine Objekte. Die primitiven Datentypen für Zahlen und Zeichen erstellen Zahlen und Zeichen, sind aber vom Gesichtspunkt der Effizienz keine Objekte. Sie können sie in sogenannte Objekt-Wrapper einbinden, wenn Sie sie wie Objekte behandeln wollen (hierüber lernen Sie später mehr).

Verwendung von new

Um ein neues Objekt zu erstellen, benutzen Sie new mit dem Namen der Klasse, von der Sie eine Instanz anlegen wollen, und Klammern:

String str = new String();

Random r = new Random();

Motorcycle m2 = new Motorcycle();

Die Klammern sind wichtig. Sie dürfen auf keinen Fall weggelassen werden. Die Klammern können inhaltslos bleiben. In diesem Fall wird ein ganz einfaches Objekt erstellt. Die Klammern können aber auch Argumente enthalten, die die Anfangswerte von Instanzvariablen oder andere Anfangsqualitäten des Objektes bestimmen. Zahl und Typ von Argumenten, die Sie mit new verwenden können, werden von der Klasse anhand einer speziellen Methode namens Constructor vorgegeben. Sie lernen später in dieser Woche noch alles über Constructor-Methoden.

Von einigen Klassen können keine Instanzen ohne Argumente erstellt werden. Um sicherzugehen, überprüfen Sie die Klasse.

Betrachten wir als Beispiel die Date-Klasse, die Datumsobjekte erstellt. Listing 4.1 ist ein Java-Programm, das die drei verschiedenen Arten des Erstellens eines Date-Objekts mit new aufzeigt:

Listing 4.1: Lauras Date-Programm

1:   import java.util.Date;


2:
3: class CreateDates {
4:
5: public static void main (String args[]) {
6: Date d1, d2, d3;
7:
8: d1 = new Date();
9: System.out.println("Date 1: " + d1);
10:
11: d2 = new Date(71, 7, 1, 7, 30);
12: System.out.println("Date 2: " + d2);
13:
14: d3 = new Date("April 3 1993 3:24 PM");
15: System.out.println("Date 3: " + d3);
16: }
17: }


Date 1: Sun Nov 26 19:10:56 PST 1995


Date 2: Sun Aug 01 07:30:00 PDT 1971
Date 3: Sat Apr 03 15:24:00 PST 1993


Unter Windows 95 weicht die Darstellung von dem hier gezeigten ab.

In diesem Beispiel werden anhand unterschiedlicher Argumente für new drei verschiedene Datumsangaben erstellt. Die erste Instanz (Zeile 8) benutzt new ohne Argumente, was ein Date-Objekt für das heutige Datum erstellt (siehe erste Zeile der Ausgabe).

Das zweite Date-Objekt dieses Beispiels hat fünf Ganzzahlargumente. Die Argumente stellen ein Datum dar: Jahr, Monat, Tag, Stunden und Sekunden. Wie die Ausgabe zeigt, wird dadurch ein Date-Objekt für einen bestimmten Tag erstellt: Sonntag, erster August 1971, 7.30 Uhr.

Die dritte Version von Date hat ein Argument - eine Zeichenkette, die das Datum als Textkette darstellt. Beim Erstellen des Date-Objekts wird die Zeichenkette automatisch analysiert und ein Date-Objekt mit dem betreffenden Datum und Uhrzeit wird ausgegeben (siehe dritte Zeile der Ausgabe). Die Date-Zeichenkette kann viele verschiedene Formate haben. Informationen hierüber finden Sie in der API-Dokumentation über die Date-Klasse (Teil des Pakets java.util).

Was new bewirkt

Bei Verwendung des new-Operators passieren mehrere Dinge: Erstens wird die neue Instanz der jeweiligen Klasse angelegt und Speicher dafür zugewiesen. Zweitens wird eine bestimmte Methode, die in der jeweiligen Klasse definiert ist, aufgerufen. Diese spezielle Methode nennt man Constructor.

Ein Constructor ist eine spezielle Methode zum Erstellen und Initialisieren neuer Instanzen von Klassen. Constructors initialisieren das neue Objekt und seine Variablen, erzeugen andere Objekte, die dieses Objekt braucht, und führen im allgemeinen andere Operationen aus, die für die Ausführung des Objekts nötig sind.

Mehrere Constructor-Definitionen in einer Klasse können je eine andere Zahl oder einen anderen Typ von Argumenten haben. In diesem Fall können Sie bei Verwendung von new verschiedene Argumente aus der Argumentenliste spezifizieren, dann wird der richtige Constructor für jene Argumente aufgerufen. Auf diese Weise können die verschiedenen Versionen von new, die oben aufgeführt wurden, unterschiedliche Aufgaben erfüllen.

Wenn Sie eigene Klassen anlegen, können Sie beliebig viele Constructors definieren, um das Verhalten einer Klasse zu bestimmen. Am 7. Tag lernen Sie, wie Constructors erstellt werden.

Speichermanagement

Speichermanagement ist in Java dynamisch und automatisch. Wenn Sie in Java ein neues Objekt erstellen, wird ihm automatisch die richtige Speichermenge zugeteilt. Sie brauchen für Objekte nicht explizit Speicherplatz zuteilen. Das übernimmt Java für Sie.

Wenn Sie ein Objekt nicht mehr benötigen, wird der diesem Objekt zugeteilte Speicher ebenfalls automatisch wieder freigegeben. Ein Objekt, das nicht mehr gebraucht wird, hat keine lebenden Referenzen mehr (es wird keinen Variablen mehr zugewiesen, die Sie noch verwenden oder die in Arrays gespeichert sind). Java hat einen Papierkorb, der nach unbenutzten Objekten sucht und den diesen Objekten zugeteilten Speicherplatz zurückfordert. Sie brauchen also Speicherplatz nicht explizit freistellen. Sie müssen nur sicherstellen, daß keine Objekte, die Sie loswerden wollen, irgendwo verwendet werden. Am 21. Tag lernen Sie alles über den Java-Papierkorb.

Öffnen und Einrichten von Klassen- und Instanzvariablen

Nun haben Sie ein eigenes Objekt mit definierten Klassen- oder Instanzvariablen. Wie funktionieren diese Variablen? Ganz einfach! Klassen- und Instanzvariablen verhalten sich genauso wie die lokale Variablen, die Sie gestern gelernt haben. Lediglich die Bezugnahme auf sie unterscheidet sich geringfügig von den üblichen Variablen im Code.

Werte holen

Um den Wert einer Instanzvariablen zu holen, verwenden Sie die Punkt-Notation.

Bei der Punkt-Notation hat eine Instanz- oder Klassenvariable zwei Teile: Das Objekt auf der linken Seite des Punkts und die Variable rechts davon.

Ist beispielsweise ein Objekt der Variablen myObject zugewiesen und hat dieses Objekt eine Variable namens var, nehmen Sie auf den Wert dieser Variablen wie folgt Bezug:

myObject.var;

Diese Art des Zugreifens auf Variablen ist ein Ausdruck (der einen Wert ausgibt), und was auf beiden Seiten des Punkts steht, ist ebenfalls ein Ausdruck. Das bedeutet, daß Sie den Zugriff auf Instanzvariablen verschachteln können. Beinhaltet die var-Instanzvariable selbst ein Objekt und dieses Objekt eine eigene Instanzvariable namens state, können Sie wie folgt darauf Bezug nehmen:

myObject.var.state;

Punktausdrücke werden von links nach rechts bewertet, deshalb beginnen Sie mit der Variablen var von myObject, was zu einem anderen Objekt mit der Variablen state zeigt. Am Schluß erhalten Sie den Wert der state-Variablen.

Werte ändern

Die Zuweisung eines Wertes zu dieser Variablen ist ebenso einfach. Sie setzen einfach einen Zuweisungsoperator rechts neben den Ausdruck:

myObject.var.state = true;

Listing 4.2 ist ein Beispiel eines Programms, das die Instanzvariablen in einem Point-Objekt testet und ändert. Point ist Teil des Pakets java.awt und bezieht sich auf einen Koordinatenpunkt mit einem x- und einem y-Wert.

Listing 4.2: Die TestPoint-Klasse

1:   import java.awt.Point;


2:
3: class TestPoint {
4:
5: public static void main (String args[]) {
6: Point thePoint = new Point(10,10);
7:
8: System.out.println("X is " + thePoint.x);
9: System.out.println("Y is " + thePoint.y);
10:
11: System.out.println("Setting X to 5.");
12: thePoint.x = 5;
13: System.out.println("Setting y to 15.");
14: thePoint.y = 15;
15:
16: System.out.println("X is " + thePoint.x);
17: System.out.println("Y is " + thePoint.y);
18:
19: }
20: }


X is 10


Y is 10
Setting X to 5.
Setting y to 15.
X is 5
Y is 15

In diesem Beispiel erstellen Sie zuerst eine Instanz von Point, wobei X und Y 10 sind (Zeile 6). Die Zeilen 8 und 9 geben diese Einzelwerte aus, und Sie können die Punkt-Notation hier in Funktion sehen. Die Zeilen 11 bis 14 ändern die Werte dieser Variablen auf 5 bzw. 15. Die Zeilen 16 und 17 geben die Werte von X und Y in der geänderten Form wieder aus.

Klassenvariablen

Wie Sie bereits gelernt haben, werden Klassenvariablen in der Klasse selbst definiert und gespeichert. Deshalb wirken sich ihre Werte auf die Klasse und alle ihre Instanzen aus.

Bei Instanzvariablen erhält jede neue Instanz der Klasse eine neue Kopie der Instanzvariablen, die diese Klasse definiert. Jede Instanz kann dann die Werte dieser Instanzvariablen ändern, ohne daß sich das auf andere Instanzen auswirkt. Bei Klassenvariablen gibt es nur eine Kopie der Variablen. Jede Instanz der Klasse hat Zugang zu der Variablen, jedoch gibt es nur einen Wert. Durch Änderung des Wertes dieser Variablen ändern sich die Werte aller Instanzen der betreffenden Klasse.

Sie definieren Klassenvariablen, indem Sie das Schlüsselwort static vor die Variable setzen. Sie lernen hierüber am 6. Tag mehr. Betrachten wir als Beispiel folgende teilweise Klassendefinition:

class FamilyMember {

   static String surname = "Johnson";

      String name;

      int age;

      ...

}

Instanzen der Klasse FamilyMember haben je einen eigenen Wert für Name (name) und Alter (age). Die Klassenvariable Nachname (surname) hat aber nur einen Wert für alle Familienmitglieder. Ändern Sie surname, wirkt sich das auf alle Instanzen von FamilyMember aus.

Um auf Klassenvariablen zuzugreifen, benutzen Sie die gleiche Punkt-Notation wie bei Instanzvariablen. Um den Wert der Klassenvariablen zu holen oder zu ändern, können Sie entweder die Instanz oder den Namen der Klasse links neben dem Punkt eingeben. Beide Ausgabezeilen in diesem Beispiel geben den gleichen Wert aus:

FamilyMember dad = new FamilyMember();

System.out.println("Family's surname is: " + dad.surname);

System.out.println("Family's surname is: " + FamilyMember.surname);

Da Sie eine Instanz benutzen können, um den Wert einer Klassenvariablen zu ändern, entsteht leicht Verwirrung über Klassenvariablen und darüber, wo der Wert herkommt (Sie erinnern sich, daß sich der Wert einer Klassenvariablen auf alle Instanzen auswirkt). Aus diesem Grund empfiehlt es sich, den Namen der Klasse zu verwenden, wenn auf eine Klassenvariable verwiesen wird. Dadurch wird der Code besser lesbar und Fehler lassen sich schneller finden.

Aufrufen von Methoden

Das Aufrufen von Methoden in Objekten läuft ähnlich ab wie die Bezugnahme auf seine Instanzvariablen: Auch in Methodenaufrufen wird die Punkt-Notation benutzt. Das Objekt, dessen Methode Sie aufrufen, steht links neben dem Punkt. Der Name der Methode und ihre Argumente stehen rechts neben dem Punkt:

myObject.methodOne(arg1, arg2, arg3);

Beachten Sie, daß nach jeder Methode Klammern folgen müssen, auch wenn die Methode keine Argumente hat:

myObject.methodNoArgs();

Führt die aufgerufene Methode zu einem Objekt, das selbst Methoden hat, können Sie Methoden wie Variablen verschachteln:

myObject.getClass().getName();

Sie können auch verschachtelte Methodenaufrufe und Referenzen auf Instanzvariablen kombinieren:

myObject.var.methodTwo(arg1, arg2);

System.out.println(), die Methode, die Sie bisher in diesem Buch benutzt haben, ist ein gutes Beispiel für die Verschachtelung von Variablen und Methoden. Die System-Klasse (Teil des Pakets java.lang) beschreibt systemspezifisches Verhalten. System.out ist eine Klassenvariable, die eine Instanz der Klasse PrintStream enthält. Diese Instanz zeigt auf die Standardausgabe des Systems. PrintStream - Instanzen enthalten die Methode println(), die eine Zeichenkette an diese Ausgabe schickt.

Listing 4.3 zeigt ein Beispiel des Aufrufens einiger Methoden, die in der String-Klasse definiert sind. Zeichenketten beinhalten Methoden zum Testen und Ändern von Zeichenketten auf ähnliche Weise, wie man von einer Zeichenkettenbibliothek in anderen Sprachen erwarten würde.

Listing 4.3: Mehrere Verwendungen von String-Methoden

1:   class TestString {


2:
3: public static void main (String args[]) {
4: String str = "Now is the winter of our discontent";
5:
6: System.out.println("The string is: " + str);
7: System.out.println("Length of this string: "
8: + str.length());
9: System.out.println("The character at position 5: "
10: + str.charAt(5));
11: System.out.println("The substring from 11 to 18: "
12: + str.substring(11, 18));
13: System.out.println("The index of the character d: "
14: + str.indexOf('d'));
15: System.out.print("The index of the beginning of the ");
16: System.out.println("substring \"winter\":"
17: + str.indexOf("winter"));
18: System.out.println("The string in upper case: "
19: + str.toUpperCase());
20: }
21: }


The string is: Now is the winter of our discontent


Length of this string: 35
The character at position 5: s
The substring from positions 11 to 18: winter
The index of the character d: 25
The index of the beginning of the substring "winter": 11
The string in upper case: NOW IS THE WINTER OF OUR DISCONTENT

In Zeile 4 erstellen Sie eine neue Instanz von String durch Verwendung eines Zeichenkettenliterals (das ist einfacher, als wenn man new verwendet und die Zeichen dann einzeln eingibt). Der Rest des Programms ruft verschiedene String-Methoden auf, um verschiedene Operationen auf diese Zeichenkette auszuführen:

  • Zeile 6 gibt den Wert der in Zeile 4 geschriebenen Zeichenkette aus: »Now is the winter of our discontent«.
  • Zeile 7 ruft die Methode length() im neuen String-Objekt auf. Diese Zeichenkette hat 35 Zeichen.
  • Zeile 9 ruft die Methode charAt() auf, die das Zeichen an der angegebenen Position ausgibt. Beachten Sie, daß Zeichenketten auf Position 0 beginnen, deshalb ist das Zeichen in Position 5 s.
  • Zeile 11 ruft die Methode substring() auf, die zwei Ganzzahlen benutzt, um einen Bereich zu bereichnen, und die Teilzeichenkette an diesem Anfangs- und Endpunkt ausgibt. Die Methode substring() kann auch mit nur einem Argument aufgerufen werden. Dadurch wird die Teilzeichenkette von dieser Position bis zum Ende der Zeichenkette ausgegeben.
  • Zeile 13 ruft die Methode indexOf() auf, die die Position der ersten Instanz eines bestimmten Zeichens (hier 'd') ausgibt.
  • Zeile 15 zeigt eine andere Verwendung der Methode indexOf(), die hier ein String-Argument verwendet und den Index des Beginns dieser Zeichenkette ausgibt.
  • Zeile 18 benutzt die Methode toUpperCase(), die eine Kopie der Zeichenkette in Großbuchstaben ausgibt.

Klassenmethoden

Klassenmethoden wirken sich wie Klassenvariablen auf die ganze Klasse, nicht auf ihre einzelnen Instanzen aus. Klassenmethoden werden üblicherweise für allgemeine Utility-Methoden benutzt, die nicht direkt auf eine Instanz der Klasse ausgeführt werden sollen, sondern lediglich vom Konzept her in diese Klasse passen. Die String-Klasse enthält beispielsweise die Klassenmethode valueOf(), die einen von vielen verschiedenen Argumenttypen (Ganzzahlen, boolesche Operatoren, andere Objekte usw.) annehmen kann. Die Methode valueOf() gibt dann eine neue Instanz von String aus, die den Zeichenkettenwert des Arguments enthält. Diese Methode wird nicht direkt auf eine vorhandene Instanz von String ausgeführt. Das Holen einer Zeichenkette von einem anderen Objekt oder Datentyp ist definitiv eine stringähnliche Operation. Deshalb ist es sinnvoll, sie gleich in der String-Klasse zu definieren.

Klassenmethoden sind auch nützlich, um allgemeine Methoden an einer Stelle (der Klasse) zusammenzufassen. Die Math-Klasse, die im Paket java.lang enthalten ist, umfaßt beispielsweise zahlreiche mathematische Operationen als Klassenmethoden. Es gibt keine Instanzen der Klasse Math. Trotzdem können Sie ihre Methoden mit numerischen oder booleschen Argumenten verwenden.

Um eine Klassenmethode aufzurufen, benutzen Sie die Punkt-Notation wie bei Instanzmethoden. Ebenso wie bei Klassenvariablen können Sie entweder eine Instanz der Klasse oder die Klasse selbst links neben den Punkt setzen. Allerdings ist die Verwendung des Namens der Klasse für Klassenvariablen aus den gleichen Gründen, die im Zusammenhang mit Klassenvariablen erwähnt wurden, empfehlenswert, da der Code dadurch übersichtlicher wird. Die letzten zwei Zeilen dieses Beispiels produzieren das gleiche Ergebnis:

String s, s2;

s = "foo";

s2 = s.valueOf(5);

s2 = String.valueOf(5);

Referenzen auf Objekte

Wie bei der Arbeit mit Objekten ist die Verwendung von Referenzen, die auf diese Objekte zeigen, ein wichtiger Aspekt. Wenn Sie Variablen für Objekte zuweisen oder Objekte als Argumente an Methoden weiterreichen, legen Sie Referenzen auf diese Objekte fest. Die Objekte selbst oder Kopien davon werden dabei nicht weitergereicht.

Ein Beispiel soll dies verdeutlichen. Prüfen Sie folgenden Code:

import java.awt.Point;

class ReferencesTest {

   public static void main (String args[]) {

      Point pt1, pt2;

      pt1 = new Point(100, 100);

      pt2 = pt1;

      pt1.x = 200;

      pt1.y = 200;

      System.out.println("Point1: " + pt1.x + ", " + pt1.y);

      System.out.println("Point2: " + pt2.x + ", " + pt2.y);

   }

}

In diesem Programm werden zwei Variablen vom Typ Point deklariert, und ein neues Point-Objekt wird pt1 zugewiesen. Danach wird der Wert von pt1 pt2 zugewiesen.

Hier ist die Herausforderung: Wie sieht pt2 aus, nachdem die Instanzvariablen x und y von pt1 geändert wurden?

Nachfolgend die Ausgabe dieses Programms:

Point1:   200, 200


Point2: 200, 200

Wie Sie sehen, hat sich auch pt2 geändert. Beim Zuweisen des pt1-Wertes auf pt2 wurde eine Referenz von pt2 auf das gleiche Objekt, auf das sich pt1 bezieht, erstellt. Durch Änderung des Objekts, auf das sich pt2 bezieht, ändert sich auch das Objekt, auf das pt1 zeigt, weil beide Referenzen auf das gleiche Objekt zeigen.

Abbildung 4.1: Referenzen


Die Tatsache, daß Java Referenzen benutzt, gewinnt besondere Bedeutung, wenn Sie Argumente an Methoden weiterreichen. Sie lernen hierüber noch in dieser Lektion mehr.

In Java gibt es keine explizite Zeigerarithmetik oder Zeiger (Pointer), sondern nur Referenzen. Mit Java-Referenzen haben Sie aber die gleichen Möglichkeiten zur Hand wie mit Zeigern, allerdings ohne deren Komplexität und Problematik beim Suchen von Fehlern.

Konvertieren von Objekten und Primitivtypen

Eventuell haben Sie in einem Ihrer Java-Programme irgendwo einen Wert mit dem falschen Typ gespeichert. Vielleicht befindet sich eine Instanz in der falschen Klasse oder ein float soll eigentlich int sein oder umgekehrt. Um den Wert von einem Typ in einen anderen zu konvertieren, wenden Sie in Java einen Mechanismus namens Casting an.

Casting ist ein Mechanismus zum Konvertieren des Wertes eines Objekts oder eines primitiven Typs in einen anderen Typ. Casting ergibt ein neues Objekt oder einen neuer Wert. Casting wirkt sich nicht auf das ursprüngliche Objekt bzw. den ursprünglichen Wert aus.

Obwohl das Casting-Konzept an sich einfach ist, werden die Regeln, die bestimmen, welche Typen in Java in andere konvertiert werden können, durch die Tatsache komplizierter, daß Java sowohl primitive Typen (int, float, boolean) als auch Objekttypen (String, Point, Window usw.) hat. Aufgrund dieser drei Typen gibt es drei Formen von Casting und Umwandlungen, über die wir in dieser Lektion sprechen:

  • Konvertieren zwischen primitiven Typen: int in float in boolean.
  • Konvertieren zwischen Objekttypen: eine Klasseninstanz wird auf eine Instanz einer anderen Klasse abgebildet.
  • Konvertieren von primitiven Typen in Objekte und Herausziehen der primitiven Werte, um sie den Objekten zurückzugeben.

Konvertieren von Primitivtypen

Durch Konvertieren zwischen primitiven Typen können Sie den Wert eines Typs in einen anderen primitiven Typ umwandeln, z. B. um eine Zahl eines Typs einer Variablen zuzuweisen, die auf einem anderen Typ basiert. Primitive Typen werden am häufigsten in numerische Typen konvertiert. Boolesche Werte können nicht in einen anderen Primitivtyp konvertiert werden. Sie können aber 1 oder 0 in boolesche Werte konvertieren.

Ist der angestrebte Typ »größer« als der zu konvertierende Typ, müssen Sie eventuell kein explizites Casting anwenden. Meist kann ein Byte oder ein Zeichen automatisch z. B. als int, oder ein int als long, ein int als float oder etwas anderes als double behandelt werden. In diesem Fall gehen beim Konvertieren des Wertes keine Informationen verloren, weil der größere Typ mehr Genauigkeit bietet als der kleinere.

Um einen großen Wert auf einen kleineren Typ zu konvertieren, müssen Sie Casting explizit anwenden, weil bei dieser Umsetzung der Wert an Genauigkeit einbüßen kann. Explizites Casting sieht so aus:

(typename) value

In dieser Form ist typename der Name des Typs, auf den Sie konvertieren (z. B. short, int, float, boolean), und value ist ein Ausdruck, der den zu konvertierenden Wert ergibt. Dieser Ausdruck teilt die Werte von x durch den Wert von y und wandelt das Ergebnis in int um:

(int) (x / y);

Da Casting eine höhere Präzedenz hat als Arithmetik, müssen Sie Klammern eingeben, damit das Ergebnis der Division an das konvertierte int übergeben wird.

Konvertieren von Objekten

Mit einer Einschränkung können auch Klasseninstanzen in Instanzen anderer Klassen konvertiert werden: Beide betroffenen Klassen müssen durch Vererbung miteinander verbunden sein. Das bedeutet, daß Sie ein Objekt nur in eine Instanz der Sub- oder Superklassen seiner Klasse konvertieren können, nicht aber in eine beliebige Klasse.

Wie beim Konvertieren eines primitiven Wertes in einen größeren Typ müssen bestimmte Objekte nicht unbedingt explizit konvertiert zu werden. Insbesondere, weil die Subklassen von Instanzen normalerweise alle Informationen ihrer Superklassen enthalten, können Sie eine Instanz einer Subklasse irgendwo dort verwenden, wo eine Superklasse erwartet wird. Nehmen wir an, Sie haben eine Methode mit zwei Argumenten: eines vom Typ Object und eines vom Typ Number. Sie brauchen die Instanzen dieser bestimmten Klassen nicht an diese Methode weiterzugeben. Für das Object-Argument können Sie jede beliebige Subklasse von Object (anders ausgedrückt, jedes Objekt) und für das Number-Argument jede Instanz einer beliebigen Subklasse von Number (int, boolean, float usw.) weitergeben.

Durch Konvertieren eines Objekts in eine Instanz einer Superklasse dieses Objekts gehen die Informationen, die die ursprüngliche Subklasse bereitgestellt hat, verloren. Außerdem ist spezifisches Casting erforderlich. Um ein Objekt in eine andere Klasse zu konvertieren, wenden Sie die gleiche Casting-Operation wie bei den Grundtypen an:

(classname) object

In diesem Fall ist classname der Name der Klasse, in die Sie das Objekt konvertieren wollen, und object ist eine Referenz auf das konvertierte Objekt. Casting erstellt eine neue Instanz der neuen Klasse mit allen Informationen, die das alte Objekt enthielt. Das alte Objekt besteht unverändert fort.

Nachfolgend ein fiktives Beispiel, in dem eine Instanz der Klasse GreenApple in eine Instanz der Klasse Apple konvertiert wird (wobei GreenApple theoretisch eine Subklasse von Apple ist):

GreenApple a;

Apple a2;

a = new GreenApple();

a2 = (Apple) a;

Abgesehen vom Konvertieren von Objekten in Klassen können Sie auch Objekte in Schnittstellen konvertieren, jedoch nur, wenn die Klasse oder eine Superklasse des Objekts die Schnittstelle implementiert. Durch Casting eines Objekts in eine Schnittstelle können Sie dann eine der Methoden dieser Schnittstelle aufrufen, auch wenn die Klasse des Objekts diese Schnittstelle nicht direkt implementiert. In Woche 3 lernen Sie mehr über Schnittstellen.

Konvertieren von Primitivtypen in Objekte und umgekehrt

Sie wissen jetzt, wie man einen primitiven Typ in einen anderen primitiven Typ und Objekte zwischen Klassen konvertiert. Wie kann man nun Primitivtypen in Objekte konvertieren?

Überhaupt nicht! Primitive Typen und Objekte sind in Java zwei völlig verschiedene Dinge, die nicht untereinander konvertiert und auf die kein automatisches Casting möglich ist. Allerdings enthält das Paket java.lang mehrere Sonderklassen, die je einem primitiven Datentyp entsprechen: Integer, Float, Boolean usw. Mit den in diesen Klassen definierten Klassenmethoden können Sie anhand von new für alle Primitivtypen ein Gegenstück zu einem Objekt erstellen. Die folgenden Codezeilen erstellen eine Instanz der Klasse Integer mit dem Wert 35:

Integer intObject = new Integer(35);

Da Sie Objekte verfügbar haben, können Sie diese Werte als Objekte behandeln. Möchten Sie die primitiven Werte zurückkonvertieren, gibt es auch dafür Methoden, z. B. intValue(). Diese Methode extrahiert einen primitiven int-Wert aus einem Integer-Objekt:

int theInt = intObject.intValue(); // gibt 35 aus

Schlagen Sie in der Java API-Dokumentation über diese Sonderklassen nach. Sie finden dort Erklärungen von Methoden zum Konvertieren von Primitivtypen in Objekte und umgekehrt.

Manipulieren von Objekten

In diesem Abschnitt werden verschiedene andere Informationen über das Arbeiten mit Objekten aufgeführt, insbesondere:

  • Vergleichen von Objekten
  • Kopieren von Objekten
  • Suchen der Klasse eines bestimmten Objekts
  • Ermitteln, ob ein Objekt eine Instanz einer bestimmten Klasse ist

Vergleichen von Objekten

Gestern haben Sie Operatoren zum Vergleichen von Werten gelernt: Gleich, Nicht gleich, Kleiner als usw. Die meisten dieser Operatoren funktionieren nur mit Primitivtypen, nicht mit Objekten. Falls Sie versuchen, andere Werte als Operanden zu verwenden, gibt der Java-Compiler Fehler aus.

Die Ausnahme zu dieser Regel bilden Operatoren für Gleichheit: == (Gleich) und != (Nicht gleich). Diese Operatoren können für Objekte benutzt werden und prüfen, ob sich zwei Operanden auf genau das gleiche Objekt beziehen.

Was müssen Sie tun, um Instanzen Ihrer Klasse zu vergleichen und aussagefähige Ergebnisse zu erzielen? Sie müssen Sondermethoden in Ihre Klasse einbinden und diese Methoden anhand der Methodennamen aufrufen.

Java umfaßt kein Konzept des Überladens (Overloading) von Operatoren, d. h. die Fähigkeit, das Verhalten der integrierten Operatoren dadurch zu definieren, daß Methoden in Klassen definiert werden. Die integrierten Operatoren gelten nur für Zahlen.

Ein gutes Beispiel dafür ist die String-Klasse. Hier sind zwei Zeichenketten, d. h. zwei unabhängige Objekte im Speicher mit den gleichen Werten möglich, also mit den gleichen Zeichen in der gleichen Reihenfolge. Nach dem Operator == sind diese zwei String-Objekte aber nicht gleich, weil sie zwar den gleichen Inhalt haben, aber nicht die gleichen Objekte sind.

Die String-Klasse definiert die Methode equals(), die jedes Zeichen in der Zeichenkette prüft und true ausgibt, wenn die zwei Zeichenketten die gleichen Werte haben. Dies wird in Listing 4.4 verdeutlicht.

Listing 4.4: Zeichenketten testen

1:   class EqualsTest {


2:
3: public static void main (String args[]) {
4: String str1, str2;
5: str1 = "She sells sea shells by the sea shore.";
6: str2 = str1;
7:
8: System.out.println("String1: " + str1);
9: System.out.println("String2: " + str2);
10: System.out.println("Same object? " + (str1 == str2));
11:
12: str2 = new String(str1);
13:
14: System.out.println("String1: " + str1);
15: System.out.println("String2: " + str2);
16: System.out.println("Same object? " + (str1 == str2));
17: System.out.println("Same value? " + str1.equals(str2));
18: }
19: }


String1: she sells sea shells by the sea shore.


String2: she sells sea shells by the sea shore.
Same object? true
String1: she sells sea shells by the sea shore.
String2: she sells sea shells by the sea shore.
Same object? false
Same value? true

Der erste Teil dieses Programms (Zeilen 4 bis 6) deklariert die zwei Variablen str1 und str2, weist das Literal she sells sea shells by the sea shore. str1 und dann diesen Wert str2 zu. Wie Sie von Objektreferenzen her wissen, zeigen str1 und str2 jetzt auf das gleiche Objekt. Das beweist der Test in Zeile 10.

Im zweiten Teil wird ein neues String-Objekt mit dem Wert von str1 erstellt. Jetzt bestehen zwei verschiedene String-Objekte mit dem gleichen Wert. Sie werden mit dem Operator == (in Zeile 16) geprüft, um zu ermitteln, ob sie das gleiche Objekt sind. Die erwartete Antwort wird ausgegeben. Schließlich erfolgt das Prüfen mit der equals-Methode (in Zeile 17), um ihre Werte zu vergleichen.

Warum kann man anstelle von new nicht einfach ein anderes Literal verwenden, wenn man str2 ändert? String-Literale sind in Java optimiert. Wenn Sie ein String mit einem Literal erstellen und dann ein anderes Literal mit den gleichen Zeichen benutzen, weiß Java genug, um Ihnen das erste String-Objekt zurückzugeben. Die beiden Strings sind das gleiche Objekt. Um zwei separate Objekte zu erstellen, müßten Sie sehr umständlich vorgehen.

Kopieren von Objekten

Sie erinnern sich aus der Lektion über Objektreferenzen, daß Zuweisungsvariablen und die Weitergabe von Objekten als Argumente an Methoden nur die Referenz eines Objekts betreffen, daß dabei aber keine Kopien der Objekte erstellt werden. Wie also erstellt man Kopien von Objekten? Dafür gibt es zwei Methoden: copy() und clone().

Mit der Methode copy() (definiert in Object und damit für alle Objekte verfügbar) benutzt man ein einzelnes Argument, eine Instanz der gleichen Klasse, und kopiert die Werte aller Instanzvariablen des Arguments in die Instanzvariablen des aktuellen Objekts (dasjenige, in dem Sie die Methode aufrufen). Enthalten diese Instanzvariablen selbst Referenzen auf Objekte, werden nur die Referenzen, nicht die Objekte, kopiert.

Point pt1, pt2, pt3;

pt1 = new Point(0,0);

pt2 = new Point(100,100);

pt2.copy(pt1); // Werte von pt1 werden in pt2 kopiert; beide sind jetzt (0,0)

Die Methode clone() ist copy() ähnlich, außer daß clone() keine Argumente heranzieht. clone() erstellt eine neue Instanz der gleichen Klasse als Quellobjekt und kopiert dann die Werte von Instanzvariablen (entweder Primitivtypen oder Referenzen auf andere Objekte). clone() gibt eine Instanz der Klasse Object aus. Um sie als Instanz der Originalklasse verwenden zu können, muß der Casting-Mechanismus auf diese Instanz angewandt werden. Das folgende Beispiel clont das Point-Objekt in pt2 und speichert das Ergebnis in pt3:

pt3 = (Point) pt2.clone();

Bestimmen der Klasse eines Objekts

Möchten Sie die Klasse eines Objekts ermitteln? Hier ist eine Möglichkeit, dies bei einem Objekt zu erreichen, das der Variablen obj zugewiesen ist:

String name = obj.getClass().getName();

Was geschieht hier? Die Methode getClass() ist in der Klasse Object definiert und als solche für alle Objekte verfügbar. Das Ergebnis dieser Methode ist ein Class-Objekt (wobei Class selbst eine Klasse ist), die die Methode getName() hat. getName() gibt den Namen der Klasse als Zeichenkette aus.

Einen anderen nützlichen Test bietet der Operator instanceof. instanceof hat zwei Operanden: Ein Objekt links und den Namen einer Klasse rechts. Der Ausdruck gibt true oder false aus, je nach dem, ob das Objekt eine Instanz der benannten Klasse oder eines der Superklassen dieser Klasse ist:

"foo" instanceof String // true

Point pt = new Point(10,10);

pt instanceof String // false

Der Operator instanceof kann auch für Schnittstellen benutzt werden. Falls ein Objekt eine Schnittstelle implementiert, gibt der instanceof-Operator mit einem Schnittstellennamen auf der rechten Seite true aus. In Woche 3 lernen Sie alles über Schnittstellen.

Die Java-Klassenbibliothek

Wir beenden die heutige Lektion mit einem Blick auf die Java-Klassenbibliothek. Sie haben inzwischen einige Klassen davon kennengelernt, deshalb sind sie Ihnen nicht mehr ganz fremd.

Die Java-Klassenbibliothek enthält Klassen, die garantiert in jeder kommerziellen Java-Umgebung (z. B. HotJava oder Netscape 2.0) verfügbar sind. Diese Klassen befinden sich im Java-Paket und enthalten alle bisher in diesem Buch behandelten Klassen sowie viele weitere, über die Sie in späteren Lektionen mehr erfahren (das Buch deckt aber nicht alle Klassen ab).

Das Java Developer's Kit wird mit kompletter Dokumentation über die Java-Klassenbibliothek ausgeliefert. Unter anderem werden dort Instanzvariablen, Methoden, Constructors, Schnittstellen usw. aller Klassen beschrieben. Ferner befindet sich in Anhang B eine Übersicht über Java-API. Die Erforschung der Java-Klassenbibliothek ist eine der besten Methoden, um herauszufinden, was Java alles kann, wo die Grenzen der Sprache liegen, und bildet einen Ausgangspunkt für eigene Entwicklungen.

Die Java-Klassenbibliothek enthält folgende Klassenpakete:

  • java.lang: Klassen, die die Sprache selbst betreffen; beinhaltet die Klassen Object, String und System, außerdem die Sonderklassen für die Primitivtypen (Integer, Character, Float usw.)
  • java.util: Utility-Klassen, darunter Date und einfache Sammelklassen wie Vector und Hashtable
  • java.io: Ein- und Ausgabeklassen zum Schreiben und Lesen von Datenströmen (z. B. Standardein- und -ausgaben) und zum Handhaben von Dateien
  • java.net: Klassen zur Netzunterstützung, darunter Socket und URL (eine Klasse zum Darstellen von Referenzen auf Dokumente im World-Wide Web)
  • java.awt (Abstract Window Toolkit): Klassen zum Implementieren einer grafischen Benutzeroberfläche, einschließlich Klassen für Window, Menu, Button, Font, CheckBox usw. Dieses Paket enthält auch Klassen zur Bildverarbeitung (java.awt.Image)
  • java.applet: Klassen zum Implementieren von Java-Applets, darunter die Applet-Klasse selbst sowie die AudioClip-Klasse

Zusätzlich zu den Java-Klassen kann Ihre Entwicklungsumgebung auch weitere Klassen enthalten, die andere Utilities oder Funktionen bieten. Diese Klassen können zwar nützlich sein, stehen aber anderen, die Ihr Java-Programm ausführen wollen, nicht zur Verfügung, weil sie nicht Teil der Java-Standardbibliothek sind. Das ist besonders bei Applets wichtig, weil Applets naturgemäß auf jeder Plattform mit jedem beliebigen javakundigen Browser laufen sollen. Nur Klassen aus dem Java-Paket stehen garantiert allen Browsern und Java-Umgebungen zur Verfügung.

Zusammenfassung

Objekte, Objekte und nochmals Objekte. Heute haben sie alles über Objekte gelernt: Wie sie erstellt, wie die Werte ihrer Variablen ermittelt und geändert und wie ihre Methoden aufgerufen werden. Außerdem haben sie gelernt, wie man Objekte kopiert und vergleicht, und wie sie in andere Objekte konvertiert werden können. Schließlich haben Sie etwas über die Java-Klassenbibliothek erfahren, die Ihnen unzählige Möglichkeiten zum Entwickeln eigener Programme liefert.

Sie haben inzwischen die Grundlagen erworben, um mit einfachen Dingen in der Java-Sprache umzugehen. Was noch übrig bleibt, sind Arrays, Bedingungen und Schleifen. Das lernen Sie morgen. Am 6. Tag lernen Sie, wie man Klassen in Java-Anwendungen definiert und verwendet. Nächste Woche stürzen Sie sich direkt auf Applets. Jedenfalls haben Sie bei fast allem, was Sie in Ihren Java-Programmen durchführen, immer wieder mit Objekten zu tun.


Fragen und Antworten

F: Mir ist der Unterschied zwischen Objekten und den primitiven Datentypen, z.B. int und boolean, noch nicht ganz klar.

A: Die primitiven Typen (byte, short, int, long, float, double und char) sind die kleinsten Elemente der Java-Sprache. Es sind keine Objekte, obwohl sie auf vielerlei Art wie Objekte gehandhabt werden. Sie können Variablen zugewiesen und zwischen Methoden weitergereicht werden. Die meisten Operationen werden aber auf Objekte ausgeführt.

Objekte stellen normalerweise Klasseninstanzen dar und sind daher viel komplexere Datentypen als einfache Zahlen und Zeichen. Sie enthalten aber meist Zahlen und Zeichen als Instanz- oder Klassenvariablen.

F: Mit den Beispielen über das Aufrufen einer Methode mit je einer anderen Zahl von Argumenten in dem Abschnitt über das Aufrufen von Methoden habe ich jedesmal ein anderes Ergebnis erhalten. Wie ist das möglich?

A: Das nennt man Methoden-Overloading. Durch Overloading kann der gleiche Funktionsname aufgrund des Arguments, mit dem er aufgerufen wird, ein anderes Verhalten haben, und sowohl Zahl als auch Typ von Argumenten können variieren. In Ihren eigenen Klassen definieren Sie separate Methodenunterschriften mit unterschiedlichen Argumenten und Definitionen. Wird diese Methode aufgerufen, ermittelt Java anhand der Zahl und des Typs von Argumenten, mit denen Sie sie aufgerufen haben, welche Definition auszuführen ist.

Sie lernen alles darüber am 6. Tag.

F: Warum können Operatoren in Java nicht überladen werden? Da Java angeblich auf C++ basiert, ist das seltsam.

A: Java basiert tatsächlich auf C++, wurde aber mit dem Ziel der Einfachheit entwickelt, deshalb wurden viele C++-Merkmale außer Acht gelassen. Das Operator-Overloading wurde weggelassen, weil der Operator mit jeder beliebigen Bedeutung definiert werden kann. Dadurch wird es schwierig, zu ermitteln, was ein bestimmter Operator zu einem bestimmten Zeitpunkt macht. Das kann im Extremfall zu einem völlig unlesbaren Code führen. Angesichts des potentiellen Mißbrauchs waren sich die Java-Entwickler einig darüber, gerade dieses Merkmal von C++ wegzulassen.


Copyright ©1996 Markt&Technik
Buch- und Software- Verlag GmbH
Alle Rechte vorbehalten. All rights reserved.

Schreiben Sie uns!

Previous Page TOC Index Next Page See Page