Objekte als Exemplare einer Klasse in Java
Exemplare einer Klasse
Die Kombination aus einem Namen mit einer Liste von Argumenten, die ihm in runden Klammern direkt nachgestellt wird, wird hier als eine Nachricht bezeichnet. Beispiel: die Nachricht »dumpStack()«.
In Java kann vor eine Nachricht noch ein Interpretierer geschrieben werden, der die Nachricht zur Laufzeit interpretieren soll. Beispiel: »java.lang.Thread.dumpStack()«, mit dem Interpretierer »java.lang.Thread«.
Nachricht und Interpretierer bilden einen Aufruf, und der Interpretierer wird auch als Empfänger, Ziel oder Kontext des Aufrufs oder der Nachricht bezeichnet. Beispiel: Der Aufruf »java.lang.Thread.dumpStack()«, mit dem Empfänger/Ziel/Kontext »java.lang.Thread«.
Bisher wurden als Empfänger von Nachrichten schon die Klassen vorgestellt. Beispiel: Die Klasse »java.lang.Thread«.
Nun sollen Objekte als Empfänger von Nachrichten vorgestellt werden.
Zu einer Klasse kann es zur Laufzeit auch sogenannte Exemplare geben.
Ein Exemplar einer Klasse wird auch als ein „Objekt “ bezeichnet.
Ein Objekt kann bestimmte Nachrichten interpretieren (ausführen).
Die Klasse des Exemplars legt fest, welche Nachrichten es interpretieren kann und wie es diese interpretiert.
Daher sagt man auch, daß eine Klasse der Typ ihrer Exemplare sei.
Wenn ein Objekt eine bestimmte Nachricht interpretieren kann, dann sagt man auch, daß es diese Nachricht unterstützt.
Die Nachrichten, die ein Exemplar einer Klasse interpretieren kann, sind im allgemeinen nicht dieselben Nachrichten, wie die Nachrichten, die diese Klasse selber interpretieren kann. Insofern ist zwischen einer Klasse als Typ ihrer Exemplare und derselben Klasse als Interpretierer von Nachrichten zu unterscheiden.
Im nächsten Abschnitt soll nun ein erstes Beispiel für ein Objekt vorgestellt werden.
Das Objekt »java.lang.System.out«
Der Ausdruck »java.lang.System.out« bezeichnet normalerweise ein bestimmtes Objekt, das eine Nachricht mit der Signatur »println( int )« interpretieren kann. (Beim der Interpretation dieser Nachricht wird normalerweise eine Textdarstellung des Argumentwertes ausgegeben.) Genauer gesagt bezeichnet dieser Ausdruck das statische Feld »out« der Klasse »java.lang.System«. Dieses Feld enthält einen Verweis auf ein Objekt, so daß der Ausdruck »java.lang.System.out« insgesamt für dieses Objekt steht.
In dem Aufruf »java.lang.System.out.println( 4 )« ist das Objekt »java.lang.System.out« der Interpretierer der Nachricht »println( 4 )«. Die Nachricht »println( 4 )« hat die Signatur »println( int )«, den Selektor »println« und das Argument »4«.
java.lang.System.out.println( 4 ) [UML Collaboration diagram]|
| |
| | println( 4 )
| V
|
.---------------------------------------.
| java.lang.System.out |
| -------------------- |
'---------------------------------------'
Daß der Quelltext »java.lang.System.out« ein Ausdruck ist, kann man auch daran erkennen, daß er eingeklammert werden kann.
Main.javapublic class Main
{ final public static void main( java.lang.String[] args )
{ ( java.lang.System.out ).println( 4 ); }}java.lang.System.out4
Der Ausdruck »java.lang.System.out« darf nicht für die Angabe einer Klasse gehalten werden. Diese Interpretation könnte naheliegen, weil Klassennamen mit großen Buchstaben beginnen sollten und der Ausdruck »java.lang.System.out« ja tatsächlich einen großgeschriebene Namen enthält. Doch tatsächlich steht nur der Bezeichner »java.lang.System« für eine Klasse, nämlich für die Klasse »java.lang.System«. Der Ausdruck »java.lang.System.out« ist jedoch das öffentliche statische Feld »out« dieser Klasse.
Exemplare einer Klasse
Noch ein weiteres Mißverständnis liegt nahe: Da die Klasse »java.lang.System« in dem Ausdruck »java.lang.System.out« erwähnt wird, könnte man denken, daß das Objekt »java.lang.System.out« ein Exemplar der Klasse »java.lang.System« sei. Doch obwohl dieses Objekt in einem statischen Feld dieser Klasse eingetragen ist, ist es ein Exemplar einer anderen Klasse.
Das Objekt »java.lang.System.out« ist ein Exemplar der Klasse »java.io.PrintStream«, es hat den Typ »java.io.PrintStream«.
(Genau genommen muß es nicht immer genau diesen Typ haben. Die Situation wird hier zunächst etwas vereinfacht.)
Die Klasse »java.io.PrintStream« legt für alle ihre Exemplare fest, welche Nachrichten diese interpretieren können und wie sie diese interpretieren.
Diese Klasse »java.io.PrintStream« legt beispielsweise für alle ihre Exemplare fest, daß diese Nachrichten mit der Signatur »println( int )« interpretieren können, und wie diese Nachrichten interpretiert werden. Man sagt auch, daß die Klasse »java.io.PrintStream« diese nichtstatische Signaturen enthielte.
println( int ) [Beschreibung]java.io
Class PrintStream
void println( int x )
Print an integer and then terminate the line.
Um die nichtstatische Methode »println( int )« der Klasse »java.io.PrintStream« zu bezeichnen, wird auch gelegentlich die Schreibweise »java.io.PrintStream#println( int )« verwendet, allerdings nicht im Quelltext, sondern in natürlichsprachigen Texten über Java. Mit der Schreibweise »java.lang.PrintStream#println( int )« ist die nichtstatische Methode »println( int )« für Exemplare der Klasse »PrintStream« gemeint. Die Schreibweise »java.lang.PrintStream.println( int )« würde hingegen eine (hypothetische) statische Methode der Klasse »java.lang.PrintStream« bezeichnen. Bei nichtstatischen Methoden wird also ein Nummernzeichen »#« nach dem Klassennamen verwendet, bei statischen Methoden jedoch ein Punkt ».«.
Ausdrücke
In einem Java -Quelltext kann der Interpretierer einer Nachricht durch einen Ausdruck repräsentiert sein. Das ist immer dann möglich, wenn der Typ des Ausdrucks, diese Nachricht enthält.
Dann sagt man, daß der Ausdruck diese Nachricht interpretieren kann, oder sonst, daß er diese Nachricht nicht interpretieren kann.
In dem Aufruf »java.lang.System.out.println( 4 )« soll der Ausdruck »java.lang.System.out« die Nachricht »println( 4 )« interpretieren. Da der Typ »java.io.PrintStream« dieses Ausdrucks diese Nachricht auch enthält, ist der Aufruf in dieser Hinsicht fehlerfrei.
Ein Ausdruck ist allerdings nicht der letztendliche Interpretierer einer Nachricht, sondern nur der Repräsentant des Interpretierers im Quelltext. Bei der Ausführung eines Programmes wird die Nachricht, falls der Ausdruck sie interpretieren kann, tatsächlich von dem Objekt ausgeführt, das dann der Wert des Ausdrucks ist.
In dieser Lektion wird allerdings der Unterschied zwischen dem Objekt »java.lang.System.out« und dem Ausdruck »java.lang.System.out« nicht besonders betont, da beide den gleichen Typ haben. Es gibt aber Situationen, in denen der Typ eines Ausdrucks sich vom Typ des durch den Ausdruck angegebenen Objekts unterscheiden kann und dann beispielsweise eine Nachricht, die vom Objekt ausgeführt werden könnte, vom Ausdruck nicht ausgeführt wird. Diese Situationen sollen aber in dieser Lektion noch nicht behandelt werden.
UML -Strukturbild
In einem UML-Strukturbild stellt ein Rechteck mit einem oben im Rechteck stehenden nichtunterstrichenen Klassennamen eine Klasse dar. Ein Objekt wird durch seinen oben im Rechteck stehenden Namen (Ausdruck) beschriftet, dem ein Doppelpunkt und die Klasse des Objektes folgt; dieser ganze Text wird unterstrichen. Die Beziehung „Instanz von“ zwischen einem Objekt und seiner Klasse wird durch einen gestrichelte Pfeil vom Objekt zu seiner Klasse dargestellt, der mit der Angabe »"instanceOf"« beschriftet wird. In der dritten Abteilung eines Klassenrechtecks können die Signaturen eingetragen werden, welche die Klasse für ihre Exemplare festlegt.
Objekt und Klasse [UML static structure diagram].-----------------------. .--------------------------------------.
| "Class" | | "Object" |
| java.io.PrintStream | "instanceOf" | System.out : java.io.PrintStream |
|-----------------------|<- - - - - - - - | -------------------------------- |
|-----------------------| | |
| + println( int ) | '--------------------------------------'
'-----------------------'
Man beachte daß die Angaben der Signaturen von Exemplare der Klasse im UML -Diagramm nicht unterstrichen werden, während statische Signaturen einer Klasse unterstrichen werden würden.
Ein Objekt sollte einen bestimmten Sinn haben
Die Nachrichten, die ein Objekt interpretieren kann, sollten inhaltlich zusammengehören und nicht einfach willkürlich in dem Objekt zusammengefaßt worden sein. Im besten Fall sollte das ganze Objekt dadurch einen einfach zu erfassenden Sinn erhalten.
So kann man sich vorstellen, daß das durch den Ausdruck »java.lang.System.out« bestimmte Objekt den Bildschirm (das Ausgabegerät) darstellt. Ein Aufruf, wie »java.lang.System.out.println( 4 )«, stellt dann dem Auftrag an dieses Objekt, also an den Bildschirm, dar, eine Darstellung des Wertes »4« auszugeben. Der Eintrag »println« ist ein Teil der Schnittstelle dieses Objektes, die man benutzen kann, um mit dem Objekt zu kommunizieren. Das Objekt enthält dann alle Methoden, die man von einem Textbildschirm erwarten kann. Es faßt also alles zusammen, was solch ein Textbildschirm kann.
Exemplare sind keine Klassen
Ein Exemplar einer Klasse darf nicht mit seiner Klasse gleichgesetzt werden.
Eine nichtstatische Methode eines Exemplars einer Klasse kann nicht wie eine statische Methode der Klasse aufgerufen werden kann.
Main.javapublic class Main
{ final public static void main( java.lang.String[] args )
{ java.io.PrintStream.println( 14 ); }}ConsolePrint.java:3: non-static method println(int) cannot be referenced from a static context
{ java.io.PrintStream.println( 14 ); }}
^
1 error
Nachrichten mit der Signatur »println(int)« können von Exemplaren der Klasse »java.io.PrintStream« interpretiert werden, aber nicht von der Klasse »java.io.PrintStream« selber.
Eine Klasse legt immer zwei Mengen von Methoden fest: Ihre eigenen „statischen“ Methoden und die „nichtstatischen“ Methoden ihrer Exemplare.
Objekte
Man darf nicht glauben, daß es selbstverständlich sei, was das Wort „Objekt“ überhaupt bedeutet. Der Begriff hat in Zusammenhang mit verschiedenen Programmiersprachen nämlich unterschiedliche Bedeutung.
In Java ist ein Exemplar einer Klasse ein Objekt.
Eine Klasse selber, wie die Klasse »Math«, ist kein Objekt, obwohl ihre Methoden ähnlich aufgerufen werden wie Methoden eines Objektes und es auch Programmiersprachen gibt, in denen Klassen selber Objekte sind. In Java hat eine Klasse allerdings in mancher Hinsicht Ähnlichkeiten mit einem Objekt, da sie auch Nachrichten interpretieren kann. Außerdem gibt es zu jeder Klasse ein Klassenobjekt, das diese Klasse repräsentiert. (Wenn man dieses Objekt als die Klasse selber interpretieren will, ist eine Klasse insofern doch eine Objekt. Dies ist aber dann nicht mit der Auffassung verträglich, nach der in einem Ausdruck wie »System.out« nicht etwa dieses Klassenobjekt, sondern die Klasse »System« selber Träger des Feldes »out« ist.)
„Instanzen“, „Exemplare“ und „Objekte“
Was ist der Unterschied zwischen einer Instanz, einem Exemplar und einem Objekt ?
Zwischen Instanz und Exemplar gibt es gar keinen Unterschied! Das englische Wort “instance ” bedeutet soviel wie „Exemplar“ wird aber auch als „Instanz“ übersetzt. Manche halten die Übersetzung „Instanz“ für falsch, sie hat sich aber teilweise eingebürgert.
Der Begriff „Exemplar“ ist hier eine Kurzform für „Exemplar einer Klasse“. In Java kann auch eine sogenannte Reihung (auch „Feld“ oder „Array“ genannt) ein Objekt sein, eine Reihung ist aber kein Exemplar einer Klasse. Wenn man von solchen Reihungen absieht, dann gibt es in Java keinen Unterschied zwischen Objekten und Exemplare. Ansonsten ist ein Objekt in Java eine Reihung oder ein Exemplar einer Klasse.
Abgesehen von der Tatsache, daß Reihungen zwar Objekte aber keine Exemplare einer Klasse sind, bedeuten die drei Begriffe „Instanz“, „Exemplar“ und „Objekt“ in Java also dasselbe und können für einander eingesetzt werden.
Übungsaufgaben
- Fehlerausgabe verwenden
- Das Objekt »java.lang.System.err« ist ein Exemplar der Klasse »java.io.PrintStream«. Schreiben Sie ein Programm, in dem dieses Objekt die Nachricht »println( 38 )« interpretiert.
Fehlerausgabe verwenden [UML Collaboration diagram]
|
| |
| | println( 38 )
| V
|
.-----------------------------------------------------------.
| java.lang.System.err : java.io.PrintStream |
| ------------------------------------------ |
'-----------------------------------------------------------'- Ablauf untersuchen
- Der Wert des Ausdrucks »java.lang.Thread.currentThread()« ist ein Exemplar der Klasse »Thread«, nämlich der gerade ablaufende „Ablauf“ (engl.: “thread ”).
- Die Interpretation der Nachricht "getName()" durch ein solches Exemplar produziert den Namen des Exemplars. Dieser Name ist also der Wert des Ausdrucks »java.lang.Thread.currentThread().getName()«.
- Geben Sie diesen Namen aus.
- Lassen Sie den aktuellen Ablauf die Nachricht »isDaemon()«, die Nachricht "isAlive()" und die Nachricht »getPriority()« interpretieren, um zu erfahren, ob der aktuelle Ablauf ein Dämonablauf ist, ob er lebendig ist bzw., um seine Priorität zu ermitteln. (Die Wert der Aufrufe sollen jeweils ausgegeben werden.) Es ist für die Bearbeitung dieser Übungsaufgabe nicht wichtig, die genaue Bedeutung dieser Aufrufe zu verstehen.
- Nachrichten von String-Exemplaren interpretieren lassen
- Der Ausdruck »"Hallo!"« (mit einem Anführungszeichen am Anfang und einem am Ende) bezeichnet ein Exemplar der Klasse »java.lang.String«. Lassen Sie dieses Objekt die Nachricht »length()« interpretieren, um die Anzahl der Zeichen dieses Textes als Wert des Aufrufs zu ermitteln.