boolean, char, byte, short, int, long long, float, double

10 Objekte in Java Sie kennen nun die sogenannten imperativen Bestandteile von Java: Variablen, Verzweigungen und Schleifen. Unser n¨achstes Ziel is...
Author: Maya Bretz
2 downloads 1 Views 138KB Size
10

Objekte in Java

Sie kennen nun die sogenannten imperativen Bestandteile von Java: Variablen, Verzweigungen und Schleifen. Unser n¨achstes Ziel ist, Ihnen die objektorientierten Bestandteile zu vermitteln. In Kapitel ?? wurde unabh¨ angig von Java erl¨autert, was Klassen und Objekte sind. Wenn Ihnen die Inhalte nicht mehr gel¨aufig sind, lesen Sie es bitte noch einmal. Genau wie in Kapitel ?? beschrieben, ist auch in Java ein Objekt etwas, das einen Zustand, eine Schnittstelle und Verhalten besitzt. Der Zustand eines Objektes ergibt sich aus seinen Attributen oder Variablen. Seine Schnittstelle und sein Verhalten ist durch die in seiner Klasse beschriebenen Methoden definiert. In diesem Kapitel soll nur gezeigt werden, wie man Objekte in Java benutzen kann, welchen Lebenszyklus ein Objekt hat und wie sich Objekte von den Ihnen bisher bekannten Datentypen wie int oder float unterscheiden. Um ein Objekt zu erzeugen, muß zun¨achst seine Klasse definiert sein. Wie man eigene Klassen definiert, soll erst in Kapitel 12 beschrieben werden. Um das Verhalten von Objekten zu demonstrieren, werden wir in diesem Kapitel eine von Java bereitgestellte Klasse verwenden.

10.1

Objekte und primitive Datentypen

Bisher haben Sie mit den primitiven Datentypen von Java und mit Strings gearbeitet. Die primitiven Datentypen (sie fangen mit einem kleinen Buchstaben an) sind: boolean, char, byte, short, int, long long, float, double Primitive Datentypen sind also die logischen Datentypen, Zeichen sowie alle bisher benutzten Zahlen. Primitive Datentypen heißen so, da man als Programmierer selbst keine primitiven Datentypen definieren kann — alle primitiven Datentypen sind von den Java–Sch¨opfern fest eingebaut worden. Vielleicht kommt diese Benennung aber auch daher, daß primitive Daten im Vergleich mit Objekten so “primitiv” sind. Primitive Datentypen sind keine Klassen (wir erkl¨aren gleich einige Konsequenzen), und darum sind ihre Werte und Variablen auch keine Objekte. Alle primitiven Datentypen haben gemeinsam, daß man die m¨oglichen Werte eines primitiven Datentyps durch Literale angegeben kann: Beispiel: Angabe einiger Werte primitiver Datentypen durch Literale. int i = 1; char c = ’a’; double f = 4.2390823;

Sie haben auch schon Objekte und Klassen kennengelernt, ohne daß Sie es wussten. Zeichenketten sind n¨ amlich keine primitiven Datentypen sondern Objekte der Klasse String. Da wir bisher nur wenige F¨ahigkeiten von Strings benutzt haben, konnten Sie aber bisher vermutlich noch keinen Unterschied zwischen 79

Strings und den primitiven Datentypen feststellen. In diesem Kapitel wird sich das etwas a ¨ndern. Objekte kann man in der Regel nicht durch Literale erzeugen. Strings und Objekte der in Kapitel 11 behandelten Array–Klassen bilden hier die große Ausnahme. Die Java–Erfinder waren sich dar¨ uber im klaren, daß Strings unheimlich h¨ aufig verwendet werden und haben daher die M¨oglichkeit geschaffen, Strings durch Literale zu erzeugen. Beispiel: Hier werden einige Objekte der Klasse String erzeugt: String einString = "Hallo, Welt"; String name = "Homer"; String wunsch = "Pizza";

Einen prinzipiellen Unterschied zwischen Klassen und primitiven Datentypen k¨ onnen Sie vermutlich immer noch nicht erkennen. Vielleicht f¨allt Ihnen aber eine ganz banale Sache f¨ allt auf: Die primitiven Datentypen werden alle kleingeschrieben, der erste Buchstabe der String–Klasse hingegen wird großgeschrieben. Eigentlich ist es Java egal, ob Klassennamen mit einem großen oder einem kleinen Buchstaben beginnen. Es ist aber u ¨blich, alle Klassennamen mit einem Großbuchstaben beginnen zu lassen. Bitte merken Sie sich das: Alle Klassennamen sollten mit einem Großbuchstaben beginnen. Primitive Daten und Objekte unterscheiden sich durch das, was man mit ihnen anstellen kann. Den wichtigsten Unterschied k¨onnen wir Ihnen hier leider noch nicht erkl¨ aren, da Sie vermutlich noch nicht wissen, was wir unter Vererbung verstehen. F¨ ur die fortgeschritteneren Leser sei hier erw¨ahnt, daß man von primitiven Datentypen nicht erben kann und daß primitive Datentypen keine Extension einer anderen Klasse sind. Einen fast genauso wichtigen Unterschied zwischen Objekten und primitiven Daten werden Sie in diesem Kapitel aber noch kennenlernen: W¨ahrend primitive Daten in einer Variablen gespeichert werden, ist eine Variable, in der Objekte gespeichert werden, nur ein Verweis auf ein Objekt. Objekte haben im Unterschied zu primitiven Daten eine Identit¨at (dazu mehr in Abschnitt 10.3). Einen weiteren Unterschied zwischen Objekten und primitiven Daten k¨onnen wir Ihnen hier schon demonstrieren. Sie erinnern sich sicher, daß wir in Kapitel ?? geschrieben haben, daß Objekte Verhalten und einen Zustand haben und daß Objekte miteinander kommunizieren k¨onnen. Primitive Daten haben hingegen nur einen Zustand. Sie sind nicht aktiv und besitzen weder eine Schnittstelle noch Verhalten. Man kann mit primitiven Daten zwar rechnen oder sie verwenden, man kann primitiven Daten jedoch keine Botschaften schicken. Kurz gesagt: Primitive Daten sind nichts als dumme Daten ohne Eigenleben. Einen ersten Eindruck vom Unterschied zwischen primitiven Daten und Objekten liefert das folgende Beispiel. Es demonstriert, wie man String–Objekten Botschaften schicken kann. Beispiel: Im folgenden Beispiel erzeugen wir einige String–Objekte und schicken ihnen dann Botschaften.

80

Klasse StringBotschaften 1 2 3 4 5 6 7 8 9 10

class StringBotschaften { public static void main( String[] args ) { String name = "Homer Simpson"; String wunsch = new String("Pizza"); System.out.println( "Bart Simpson".substring(0,4) ); System.out.println( name.substring(0,5) ); System.out.println( name.length() ); System.out.println( name.concat(" will ").concat( wunsch ) ); } }

In Zeile 3 des Beispiels wird ein neues String–Objekt erzeugt, welches danach u ¨ ber die String–Variable name angesprochen werden kann. Auch in Zeile 4 wird ein String–Objekt erzeugt, welches den Text "Pizza" enth¨ alt. Hier wird das Objekt jedoch nicht einfach durch Angabe des String–Literals sondern durch Verwendung des new–Operators erzeugt. Der new–Operator wird normalerweise immer ben¨ otigt, um ein Objekt einer Klasse zu erzeugen. Da Strings auch durch Literale erzeugt werden k¨ onnen, kann man auf den new–Operator bei Strings aber meist verzichten. In Zeile 5 wird demonstriert, wie man an ein Objekt eine Botschaft schicken kann. Hier wird an das String–Objekt "Bart Simpson" die Botschaft substring(0,4) geschickt. Das String–Objekt liefert daraufhin ein String–Objekt zur¨ uck, das seine ersten vier Buchstaben, also den Text "Bart", enth¨ alt. In Zeile 6 wird auf die gleiche Weise ein String–Objekt erzeugt, das die ersten 5 Buchstaben des durch name bezeichneten Objektes enth¨ alt. In Zeile 7 wird an das durch name bezeichnete Objekt die Botschaft length() geschickt. Daraufhin liefert das Objekt die L¨ ange des enthaltenen Textes. In Zeile 8 wird an das durch name bezeichnete Objekt die Botschaft concat(" will ") geschickt. Die Botschaft concat veranlaßt ein String–Objekt, ein neues Objekt zu erzeugen, indem es das in der concat–Botschaft mit u angt. ¨ bergebene Objekt an sich selbst anh¨ Das "Homer Simpson"–Objekt erzeugt also das neue String–Objekt "Homer Simpson will ". An dieses String–Objekt wird dann die Botschaft "concat(wunsch)" geschickt, woraufhin das String–Objekt "Homer Simpson will Pizza" erzeugt wird.

An primitive Daten kann man keine Botschaften schicken. Beispielsweise gibt es keine Methode der ganzen Zahlen, die man als int a; a.tueWas(); // gibts nicht 5.quadrieren(); // auch ein Fehler aufrufen k¨ onnte.

10.2

Der Lebenszyklus eines Objektes

Primitive Daten brauchen nicht erst irgendwie erzeugt zu werden. Sie werden durch Literale gegeben und besitzen keinen Lebenszyklus. 81

Bei Objekten sieht das anders aus — sie haben einen gewissen Lebenszyklus: Bevor man ein Objekt verwenden kann, muß es erzeugt werden. W¨ahrend seiner Lebensdauer kann es mit anderen Objekten kommunizieren, und wenn es nicht mehr ben¨ otigt wird, wird es zerst¨ort. 10.2.1

Die Erzeugung von Objekten

Die Erzeugung eines Objektes geschieht durch Anwendung seines Bauplanes. Seine Klasse wird als Muster genommen, um die Attribute des Objektes, seine Schnittstelle und sein Verhalten zu definieren. Die Objekterzeugung geht so vor sich, daß zun¨achst Speicherplatz f¨ ur das Objekt bereitgestellt wird. Diesen Speicherplatz k¨onnen Sie sich als eine Sammlung von Variablen vorstellen: Ein Objekt ist eine Art Container, in dem einige Variablen enthalten sind. Dabei entspricht jedes in der Klasse definierte Attribut einer Variable. Die Variablen, die zu einem Objekt geh¨oren, nennt man auch Instanzvariablen, und der Zustand eines Objektes bestimmt sich aus den Zust¨anden seiner Instanzvariablen. Als erster Schritt wird also das Objekt mitsamt den enthaltenen Variablen angelegt. Da neu angelegte Variablen normalerweise einen undefinierten Zustand haben und Objekte mit nicht definiertem Zustand in der Regel unerw¨ unscht sind, wird anschließend ein in der Klasse definiertes Initialisierungsverhalten ausgef¨ uhrt, welches den Instanzvariablen bestimmte Werte zuweist. Dieses Initialisierungsverhalten wird in einer oder mehreren Methoden beschrieben, welche man in der objektorientierten Methodik als Konstruktoren bezeichnet. Man kann in Java das Anlegen und das Initialisieren des Objektes nicht voneinander trennen, insofern empfiehlt es sich davon zu sprechen, daß der Konstruktor ein Objekt erzeugt und initialisiert. Einem Konstruktor k¨ onnen auch zus¨atzliche Daten mitgegeben werden, die zur Initialisierung verwendet werden. Definition: Konstruktor Ein Konstruktor ist eine Methode, welche ein Objekt erzeugt und dabei seinen anf¨ anglichen Zustand initialisiert. Eine Klasse kann mehrere Konstruktoren f¨ ur ihre Objekte definieren. Jede Klasse erh¨ alt automatisch einen Standardkonstruktor. Daher ist es nicht unbedingt n¨ otig, in eigenen Klassen einen Konstruktor vorzusehen. Welcher Konstruktor in einer Klasse mit mehreren Konstruktoren beim Erzeugen eines Objektes benutzt wird, wird durch die Art der Objekterzeugung festgelegt (genaueres dazu werden Sie in Kapitel 12 erfahren). Der Standardkonstruktor einer Klasse kann man aufrufen, indem man den Ausdruck new Klasse() verwendet. Allgemeiner geschieht die Erzeugung eines Objektes durch einen Ausdruck der Form new Klasse( argument1, argument2, ..., argumentN ) 82

Konstruktor

Dabei sind argument1 bis argumentN Argumente, die zur Initialisierung des Objektes benutzt werden. Als Konstruktor wird ein solcher gew¨ahlt, der zu den angegebenen Argumenten paßt (falls es in der Klasse keinen solchen Konstruktor gibt, wird ein Kompilierfehler erzeugt). Was das genau bedeutet, erfahren Sie in Kapitel 12. Beispiel: Ein String kann außer per Literal auch per Konstruktor erzeugt werden. 1 2 3 4

String String String String

string1 string2 string3 string4

= = = =

new String(); "Andreas"; new String("Hallo, Welt"); new String( string3 );

Im obigen Beispiel wird in Zeile 1 ein String durch den Standardkonstruktor erzeugt (new String()). In der String–Klasse ist festgelegt, daß dieser Konstruktor den leeren String "" erzeugt. Der durch string2 bezeichnete String wird durch das String–Literal automatisch erzeugt — immer wenn der Java–Compiler ein String–Literal entdeckt, f¨ ugt er automatisch einen entsprechenden Konstruktoraufruf zum Erzeugen eines String–Objektes ein. In Zeile 3 wird ein String durch einen Konstruktor erzeugt, der als Argument einen String entgegennimmt. Es wird daraufhin ein neuer String erzeugt, der eine Kopie des Strings "Hallo, Welt" enth¨ alt. In Zeile 4 wird ein String erzeugt, der eine Kopie des Strings string3 enth¨ alt.

Hier noch ein Beispiel mit einer selbstgebauten Klasse: Beispiel: Angenommen, es sei eine Klasse Kind gegeben, welche ein Kind durch seinen Namen und sein Alter repr¨ asentiert. Die Klasse k¨ onnte wie folgt definiert sein: 1 2 3 4 5 6 7 8 9 10 11 12

class Kind { // Instanzvariablen eines Kindes: String name; int alter; // Konstruktor: public Kind( String neuerName, int neuesAlter ) { name = neuerName; alter = neuesAlter; } } In Zeile 4 und 5 sind zwei Attribute, name und alter definiert. In Zeile 8–11 ist eine Methode definiert, deren Name gleich dem Klassennamen ist. Dies ist ein Konstruktor. Wenn ein neues Kind erzeugt werden soll, k¨ onnte der Konstruktor wie folgt aufgerufen werden:

83

Kind einKind = new Kind( "Bart Simpson", 12 ); Hier wird zun¨ achst ein neues Kind–Objekt erzeugt, welches die beiden Variablen name und alter enth¨ alt. Anschließend wird der Konstruktor der Klasse (Zeile 8–11) ausgef¨ uhrt. Der Konstruktor initialisiert daraufhin das Attribut name zu "Bart Simpson" und das Attribut alter zu 12. Sie sehen an diesem Beispiel, daß ein Konstruktor eigentlich nichts anderes als eine Methode mit besonderem Namen ist.

10.3

Die Identit¨ at eines Objektes

Nachdem wir ein Objekt erzeugt haben, wollen wir es verwenden. Dazu m¨ ussen wir das Objekt identifizieren k¨onnen — wir ben¨otigen also einen Namen, unter dem wir es ansprechen k¨ onnen. Genau wie bei primitiven Datentypen verwenden wir auch f¨ ur Objekte Variablen. Es gibt dabei jedoch einen großen Unterschied: W¨ahrend primitive Daten in einer Variable gespeichert werden, wird ein Objekt durch eine Variable nur referenziert. Um primitive Daten in einer Variable zu speichern, verwendet man den Zuweisungsoperator. Auch bei der Zuweisung von Objekten verwendet man den Zuweisungsoperator. Er bewirkt hier aber nicht, daß eine Kopie angelegt wird sondern daß die zugewiesene Variable danach das Objekt referenziert. Es ist durchaus m¨ oglich, daß mehrere Variablen das gleiche Objekt referenzieren. Bitte merken Sie sich: Durch die alleinige Verwendung des Zuweisungsoperators wird kein neues Objekt erzeugt. Beispiel: Im folgendenProgrammfragment werden Zahlen in Variablen gespeichert. int i = 1; // speichere die Zahl 1 in i int j = i; // speichere die Zahl 1 in j double f = 3.141;// speichere 3.141 in f Die Situation nach der Ausf¨ uhrung dieses Programmfragments k¨ onnte man wie folgt darstellen: Zu jeder Variable geh¨ ort ein gewisser Teil des Speichers, in welchem der Wert der Variable gespeichert ist: int i

1

int j

1

double f

3.141

Beispiel: Nun soll demonstriert werden, wie Zuweisungsoperatoren auf Objekte wirden. In Zeile 3 wird ein String–Objekt erzeugt und durch die Variable name referenziert. In Zeile 4 wird eine weitere String–Variable definiert und verweist nach der Zuweisung auf das gleiche Objekt wie die Variable name. Durch die Zuweisung in Zeile 4 wird kein neues Objekt erzeugt !

84

In Zeile 5 hingegegen wird durch Verwendung des new–Operators ein neues String–Objekt erzeugt, welches eine Kopie des durch Name name referenzierten Strings enth¨ alt. Dies Objekt wird dann durch n2 referenziert. Klasse ReferenzDemo 1 2 3 4 5 6 7 8 9

class ReferenzDemo { public static void main( String[] args ) { String name = "Hallo, Welt"; String n1 = name; String n2 = new String( name ); System.out.println( n1 == name ); System.out.println( n2 == name ); } }

Die Situation nach Ausf¨ uhrung der Zuweisungen kann hier wie folgt graphisch dargestellt werden: Es gibt zwei String–Objekte mit gleichem Inhalt. Eines wird durch die Variablen name und n1 referenziert, und das andere wird durch die Variable n2 referenziert: String name

referenziert referenziert

String-Objekt Zustand: "Hallo, Welt"

String n1

String-Objekt String n2

referenziert

Zustand: "Hallo, Welt"

In Zeilen 6 und 7 des vorigen Beispiels verwenden wir den == Operator. Bisher haben Sie vermutlich gedacht, daß der ==–Operator zwei Ausdr¨ ucke auf Gleichheit pr¨ ufe. Das stimmt aber so nicht ganz: der ==–Operator pr¨ uft, ob zwei Ausdr¨ ucke identisch sind. Zwei primitive Daten mit gleichem Wert sind auch identisch. Zwei Variablen, die Objekte referenzieren, sind hingegen nur dann identisch, wenn sie beide auf das gleiche Objekt verweisen. In Zeile 6 des vorigen Beispiels wurde daher true und in Zeile 7 false ausgegeben. Um das nochmal ganz deutlich zu machen: Wir unterscheiden zwischen Gleichheit und Identit¨ at. Zwei Objekte sind gleich, wenn sie den gleichen Zustand haben. Zwei Objekte sind identisch, wenn sie beide das gleiche Objekt sind. Im obigen Beispiel sind die durch name und n2 referenzierten Objekte gleich, da sie beide den Text "Hallo,Welt" enthalten. Hingegen sind name und n2 nicht identisch, da sie auf unterschiedliche Objekte verweisen. Die Identit¨ at eines Objektes ist unabh¨angig von seinem Zustand. Auch wenn sich der Zustand eines Objektes ¨andert, bleibt seine Identit¨at erhalten. Es ist m¨ oglich, daß zwei nicht–identische Objekte den gleichen Zustand haben. Es ist aber nicht m¨ oglich, daß zwei identische Objekte unterschiedliche Zust¨ande haben. Bitte merken Sie sich folgendes:

85

Gleichheit und Identit¨at

Identit¨at

Identit¨ at: Jedes Objekt hat Identit¨at — die Identit¨at existiert unabh¨angig von Namen, mit denen wir das Objekt bezeichnen. Die Identit¨at eines Objektes ¨ andert sich vom Beginn seines Lebenszyklus bis zur Zerst¨ orung nicht. Sie ¨ andert sich auch nicht, wenn wir den Zustand des Objektes ver¨ andern. ¨ Ubrigens bestimmt Java, ob zwei Objekte identisch sind, indem es nachschaut, an welcher Stelle im Speicher sie gespeichert sind. Identisch sind genau diejenigen Objekte, die an gleichen Speicherstellen liegen. Oft m¨ ochte man verhindern, daß zwei nicht–identische Objekte mit gleichem Zustand existieren. In diesem Fall muß man das durch einen geeigneten Mechanismus sicherstellen. Beispiel: Es ist denkbar, daß zwei verschiedene Studenten den gleichen Namen haben. Will eine Universit¨ at verschiedene Studenten auseinanderhalten, eignet sich der Name also nicht zur Identifizierung. Aus diesem Grund wird jedem Studenten eine eindeutige Immatrikulationsnummer gegeben. Eine Verfahrensvorschrift bei der Immatrikulation stellt sicher, daß jede Immatrikulationsnummer nur einmal vergeben wird. Die Immatrikulationsnummer ist aus Sicht der Universit¨ at also die Identit¨ at des Studenten.

10.4

Die Kommunikation mit Objekten

Nachdem ein Objekt erzeugt wurde, k¨onnen wir mit ihm kommunizieren. Wir k¨ onnen es durch das Zusenden von Botschaften zu einem gewissen Verhalten veranlassen, und es kann selbst Botschaften an andere Objekte verschicken. Um einem Objekt eine Botschaft zu schicken, m¨ ussen wir eine Referenz auf das Objekt kennen. Wir k¨ onnen ihm nur Botschaften schicken, die es versteht. Welche Botschaften ein Objekt versteht und wie diese Botschaften aufgebaut sein m¨ ussen, ist in seiner Klasse beschrieben. Nochmal zur Erinnerung: Den Aufbau von Methoden definieren wir in der Schnittstelle der Klasse wie folgt: Klasse r methodenName(Klasse 1 e 1 , . . . , Klasse n e n ) Wenn wir nun ein Objekt auffordern wollen, eine Methode auszuf¨ uhren, schicken wir ihm eine Botschaft, deren Aufbau genau dieser Beschreibung entspricht. Wenn eine Methode kein Ergebnis zur¨ uckliefert, schreiben wir die Aufforderung an das Objekt einObjekt die Methode methodenName auszuf¨ uhren, wie folgt: einAusfuehrer.methodenName(eingabe 1 , . . . , eingabe n ) Dabei u ¨bergeben wir wir soviele zus¨atzliche Objekte eingabe 1 , . . . , eingabe n , wie in der Schnittstelle beschrieben. Das Objekt eingabe i muß dabei Mitglied der Klasse Klasse i sein. Wenn die Methode ein Ergebnis liefert, k¨onnen wir dieses Ergebnis abholen. Dazu ben¨ otigen wir ein Objekt ergebnis der Klasse Klasse r . Wir schreiben in diesem Fall: 86

ergebnis = einAusfuehrer.methodenName(eingabe 1 , . . . , eingabe n ). Beispiel: Zum Beispiel ist in der String–Klasse die Methode length definiert, welche die L¨ ange eines Strings zur¨ uckgibt. Sie nimmt keinen weiteren Parameter entgegen und liefert eine int–Zahl zur¨ uck. Die L¨ ange eines Strings kann man daher ermitteln, indem man dem String die Botschaft length() schickt: String meinString = "Ein Text"; int laenge = meinString.length(); int laenge1 = "Noch ein Text".length();

Bisher haben wir uns das Bild gemacht, daß wir mit Objekten kommunizieren, indem wir ihnen Botschaften schicken. Dieses Bild ist auch weiterhin richtig, und es entspricht hervorragend der objektorientierten Denkweise. Es ist nun leider an der Zeit, diese Denkweise ein wenig an Java anzupassen. In Java bedeutet der Punkt nach dem Objektnamen eigentlich nicht das Verschicken einer Botschaft. Vielmehr hat man in Java die Vorstellung, daß ein Objekt ein Container ist, welcher Instanzvariablen und Methoden enth¨alt. Auf die in einem Objekt enthaltenen Dinge kann man dann zugreifen, indem man objekt.enthaltenesDing schreibt. Der Punkt dient also sozusagen dazu, das Objekt aufzumachjen und auf die enthaltenen Dinge zuzugreifen. Insofern paßt die Vorstellung, man schicke einem Objekt Botschaften nicht hundertprozentig zu Java. Man kann die Schreibweise objekt.methode() auch als den Aufruf einer im Objekt enthaltenen Methode verstehen. Nat¨ urlich ist diese Vorstellung ¨ aquivalent zur Vorstellung, daß wir dem Objekt eine Botschaft schicken. Man kann den Punkt auch verwenden, um auf Instanzvariablen eines Objektes zuzugreifen, und hier h¨ ort die Analogie zum Verschicken von Botschaften auf. Weiter oben hatten wir ein Beispiel mit einer Kind–Klasse. Wir k¨onnen auf die Attribute eines Kindes dieser Klasse wie folgt zugreifen: Kind einKind; einKind.name = "Bart Simpson"; einKind.alter = 12; Hier dient der Punkt also tats¨achlich dazu, auf die in einem Objekt enthaltenen Variablen zuzugreifen. Vielleicht sollten wir daher in Zukunft lieber davon reden, daß ein Objekt Methoden enthalte, und daß wir, statt dem Objekt eine Botschaft zu schicken, eine im Objekt enthaltene Methode aufrufen. Vermutlich werden wir in Zukunft beide Sprechweisen verwenden — sie sind schließlich ¨aquivalent. Das Schicken von Botschaften scheint dabei auch immer noch anschaulicher, auch wenn es nicht so hundertprozentig zur Java–Philosophie paßt. 87

10.5

Welche Botschaften versteht ein Objekt ?

Wenn Sie selbst eine Klasse schreiben, wissen Sie nat¨ urlich, welche Methoden enthalten sind. Wie aber k¨ onnen Sie erfahren, welche Methoden fremde Klassen enthalten ? Zu Java geh¨ ort eine ganze Menge an Dokumentation. S¨amtliche in Java enthaltenen Klassen sind in der Dokumentation des JDK (Java Development Kit) beschrieben. Wie diese Dokumentation genau aufgebaut ist, erkl¨aren wir Ihnen, sobald wir Klassen (Kapitel 12) und Pakete (Kapitel 15) besprochen haben. In den folgenden Hausaufgaben und in den weiteren Texten werden Sie h¨aufig mit dieser Dokumentation arbeiten m¨ ussen. Hier soll Ihnen die Verwendung der JDK–Dokumentation anhand eines Beispiels demonstriert werden: Beispiel: Die Dokumentation zur String–Klasse finden Sie auf der WWW–Seite http://www.tu-bs.de:82/wir/EIP/jdk1.1.8/docs/api/java.lang.String.html

Bitte klicken Sie sich dorthin, indem Sie von unseren WWW–Seiten unter den Litaturlinks zu Java auf den relativ weit unten stehenden Link “Java–Api (lokal)” klicken. Sie gelangen dann auf eine Seite, auf der einige sogenannte “packages” aufgelistet sind. Dort klicken Sie bitte auf den als java.lang bezeichneten Link. Anschließend erhalten Sie eine Liste von Klassen, unter denen sich auch die Klasse String befindet. Wenn Sie nun auf den String betitelten Link klicken, gelangen Sie zu der oben genannten WWW–Seite. Auf dieser WWW–Seite sind alle Methoden beschrieben, die String– Objekte verstehen. Bitte lesen Sie den kompletten ersten Teil der Seite ¨ durch und schauen Sie sich unter der Uberschrift “Method Index” die Methode charAt an. Wenn Sie darauf klicken, sollten Sie die folgende Erl¨ auterung erhalten: public char charAt(int index) Returns the character at the specified index. An index ranges from 0 to length() - 1. Parameters: index - the index of the character. Returns: the character at the specified index of this string. The first character is at index 0. Throws: StringIndexOutOfBoundsException if the index is out of range. Sie erkennen, daß hier eine Methode namens charAt beschrieben wird, mit der man einen String auffordern kann, ein in ihm enthaltenes Zeichen zur¨ uckliefern. Beachten Sie, daß die Zeichen eines Strings mit 0 beginnend durchnumeriert werden. Das erste Zeichen eines Strings erh¨ alt man, also indem man ihm die Botschaft charAt( 0 ) schickt.

88

Mit Hilfe der obigen Beschreibung k¨ onnen wir ein Programm schreiben, das alle Zeichen eines Strings einzeln ausgibt. Dazu rufen wir in einer Schleife wiederholt die String–Methode charAt auf und geben das durch den String zur¨ uckgegebene Zeichen mit der Methode System.out.println aus. Um die L¨ ange des Strings zu ermitteln, schicken wir ihm die Botschaft length(). Bitte sehen Sie sich auch die Dokumentation zur length–Methode an. Klasse DemoStringMethods 1 2 3 4 5 6 7 8

10.6

class DemoStringMethods { static public void main( String[] args ) { String text = "Hallo, Welt"; for( int i = 0; i < text.length(); i++ ) { System.out.println( text.charAt( i ) ); } } }

Die Zerst¨ orung von Objekten

Wenn ein Objekt nicht mehr ben¨otigt wird, wird es zerst¨ort und entsorgt. Dabei wird der gesamte Speicherplatz, den das Objekt im Speicher belegt hatte, freigegeben. Gl¨ ucklicherweise muß man sich als Programmierer in Java u ¨berhaupt nicht um die Objektentsorgung k¨ ummern. Java kann n¨amlich selbst erkennen, ob ein Objekt noch ben¨ otigt wird oder nicht. Dazu enth¨alt Java eine Funktion, die zwischendurch immer wieder mal automatisch gestartet wird und sich als “M¨ ullsammler” bet¨ atigt — diese Funktion heißt sehr treffend der Garbage Collector14 Der Garbage Collector kann feststellen, auf welche der momentan existierenden Objekte Variablenreferenzen existieren. Immer wenn er ein Objekt entdeckt, das durch keine einzige Variable referenziert wird, sammelt er es ein und gibt den belegten Speicherplatz f¨ ur andere Objekte frei — er betreibt also Recycling, denn der so freigegebene Speicherplatz schafft Lebensraum im Speicher des Computers f¨ ur neue Objekte. Das tollste daran ist, daß Sie selbst sich u ¨berhaupt nicht um Ihre M¨ ullentsorgung k¨ ummern m¨ ussen — alle nicht mehr Objekte werden volautomatisch entsorgt. Vielleicht fragen Sie sich, ob das nicht etwas unsicher sei: Da kommt irgendso ein dahergelaufenes Programm und l¨oscht einfach Ihre Objekte. Bedenken Sie aber, daß Sie ohnehin keine M¨oglichkeit haben, ein Objekt, das Sie nicht durch eine Variable referenziert haben, zu benutzen. Insofern kann der Garbage Collector keinen Schaden anrichten — er l¨oscht niemals Objekte, auf die Sie noch zugreifen k¨ onnen. Noch ein Hinweis: Es ist zwar m¨oglich, den Garbage Collector von Hand zu starten. N¨ otig ist es aber meist nicht, da Java ihn ohnehin immer automatisch startet, wenn mehr Platz ben¨otigt wird. Und noch ein Hinweis: Selbstverst¨andlich werden nur Objekte gel¨oscht — bei primitiven Daten ist das nicht n¨otig, da sie in einer Variable enthalten sind 14 Garbage Collectoren sind ubrigens keine Erfindung von Java — das in den 70ern ent¨ wickelte Smalltalk besaß bereits einen Garbage Collector.

89

und ihre Lebensdauer daher durch die Lebensdauer der Variablen begrenzt ist. Objekte m¨ ussen nur deshalb gel¨oscht werden, da sie unabh¨angig von Variablen existieren. Beispiel: Im folgenden Programm werden in Zeile 3 und 4 zwei String– Objekte erzeugt. Die Zuweisung in Zeile 5 bewirkt, daß auf "String1" keine Referenz mehr existiert. Daher kann "String1" nach Ausf¨ uhrung von Zeile 5 vom Garbage Collector gel¨ oscht werden. In Zeile 6 wird a2 ein neues Objekt zugewiesen. Das Objekt "String2" darf jedoch noch nicht gel¨ oscht werden, da es noch durch die Variable a1 referenziert wird. Erst nach Ausf¨ uhrung von Zeile 7 wird das Objekt "String2" von keiner Variable mehr referenziert und kann vom Garbage Collector entsorgt werden. Klasse DemoGarbage 1 2 3 4 5 6 7 8 9

10.7

class DemoGarbage { public static void main() { String a1 = "String 1"; String a2 = "String 2"; a1 = a2; a2 = "String 3"; a1 = ""; } }

Zusammenfassung

Sie sollten nun die folgenden Fragen beantworten k¨onnen:

90

⊲ Welche primitiven Datentypen kennt Java ? ⊲ Nennen Sie einige Unterschiede zwischen primitiven Datentypen und Klassen ! ⊲ Wie erzeugt man ein Objekt ? ⊲ Was ist ein Konstruktor ? ⊲ Beschreiben Sie, was Sie unter der Identit¨at eines Objektes verstehen ! ⊲ Enthalten Variablen Objekte ? ⊲ Was bewirkt der Zuweisungsoperator bei primitiven Daten ? ⊲ Was bewirkt der Zuweisungsoperator bei primitiven Objekten ? ⊲ Was bewirkt der ==–Operator bei primitiven Daten ? ⊲ Was bewirkt der ==–Operator bei Objekten ? ⊲ Was ist der Unterschied zwischen Gleichheit und Identit¨at ? ⊲ Wie schickt man einem Objekt eine Botschaft ? ⊲ Wie kann man herausfinden, welche Methoden die Klasse String enth¨ alt ? ⊲ Wie werden Objekte zerst¨ort ? ⊲ Wie arbeitet der Garbage–Collector ? ⊲ Warum kann der Garbage–Collector keinen Schaden anrichten ?

91

Suggest Documents