|
|
.JAVA GLOSSAR N-R
N
In Java werden Objekte
und Arrays mit Hilfe des new-Operators erzeugt. Sowohl das
Erzeugen eines Arrays als auch das Erzeugen eines Objekts
sind Ausdrücke, deren Rückgabewert das gerade erzeugte
Objekt bzw. Array ist. Um von einer Klasse ein Objekt anzulegen,
muss eine Variable vom Typ der Klasse deklariert und ihr mit
Hilfe des new-Operators ein neu erzeugtes Objekt zugewiesen
werden:
In Java wird jede selbstdefinierte Klasse
mit Hilfe des new-Operators instanziert. Mit Ausnahme von
Strings und Arrays, bei denen
der Compiler auch Literale zur
Objekterzeugung zur Verfügung stellt, gilt dies auch
für alle vordefinierten Klassen
der Java-Klassenbibliothek. Wie bei primitiven Variablen
lassen sich beide Anweisungen auch kombinieren. Das nachfolgende
Beispiel deklariert und initialisiert die Variable meinPorsche:
Auto meinPorsche = new Auto();
Kombinierte Deklaration und Initialisierung
einer Objektvariablen
Zum Zeitpunkt der Deklaration wird noch nicht
festgelegt, wie viele Elemente das Array haben soll. Dies
geschieht erst später bei seiner Initialisierung,
die mit Hilfe des new-Operators oder durch Zuweisung eines
Array-Literals ausgeführt wird.
|
|
Wenn es beim Aufruf
des Interpreters eine Fehlermeldung der Art NoClassDefFoundError
gibt, liegt das fast immer daran, dass der Name der Klasse
falsch geschrieben wurde oder dass keine oder eine falsch
benannte main-Methode vorhanden
ist. |
|
Die vordefinierte
Konstante null, die eine leere Referenz bezeichnet.
Soll festgestellt werden, ob eine Spalte
den Wert NULL hatte, so kann das nach dem Aufruf der get-Methode
durch Aufruf von wasNull abgefragt werden. wasNull gibt genau
dann true zurück, wenn die
letzte abgefragte Spalte einen NULL-Wert als Inhalt hatte.
Bei allen Spalten, die NULL-Werte enthalten können, muss
diese Abfrage also erfolgen. Bei den get-Methoden,
die ein Objekt als Ergebniswert haben, geht es etwas einfacher.
Hier wird null zurückgegeben, wenn der Spaltenwert NULL
war.
|
|
O
Enthält eine
Klasse keine extends-Klausel, so besitzt sie die implizite
Vaterklasse Object. Jede Klasse,
die keine extends-Klausel besitzt, wird direkt aus Object
abgeleitet. Jede explizit abgeleitete Klasse stammt am oberen
Ende ihrer Vererbungslinie von
einer Klasse ohne explizite Vaterklasse
ab und ist damit ebenfalls aus Object abgeleitet. Object ist
also die Superklasse aller anderen
Klassen. |
|
In den vorangegangenen
Abschnitten wurden mehrfach die Begriffe Membervariable,
Instanzvariable und Objektvariable
verwendet. Als Objektvariable bezeichnen wir stets eine Variable,
die ein Objekt aufnehmen kann, also vom Typ einer Klasse ist.
Das Gegenteil einer Objektvariable ist eine primitive Variable
. Die Begriffe Membervariable
und Instanzvariable werden synonym verwendet. Sie bezeichnen
eine Variable, die innerhalb einer Klasse definiert wurde
und mit jeder Instanz neu angelegt wird. |
|
Die Namensgebung
für Byte-Streams in Java folgt dem Prinzip, eine Klasse
für lesenden Zugriff als InputStream und eine solche
für schreibenden Zugriff als OutputStream zu bezeichnen
(Character-Streams werden dagegen als Reader und Writer bezeichnet).
Die Basisklassen haben demnach die Namen OutputStream und
InputStream. Sie sind jeweils Wurzel einer Hierarchie von
Klassen, die in vergleichbarer
Weise auch bei Character-Streams zu finden sind. Insbesondere
sind die Konzepte der Verkettung und Schachtelung von Streams
auf dieselbe Art und Weise realisiert. Auch die Byte-Streams
befinden sich im Paket java.io.
Der parameterlose Konstruktor
initialisiert einen OutputStream. Er ist protected
und wird in abgeleiteten Klassen
überlagert. Mit close wird der OutputStream geschlossen,
und flush schreibt die gepufferten
Daten physikalisch auf das Ausgabegerät und leert alle
Puffer. Wesentlich schneller sind die (älteren) OutputStream-Klassen,
die nicht mit Zeichen, sondern mit Bytes arbeiten. Sie führen
keine aufwendige Konvertierung durch, sondern geben je Zeichen
einfach dessen niederwertige 8 Bit aus. Das spart viel Zeit
und führte im Test zu einer nochmals um den Faktor drei
bis vier beschleunigten Ausgabe (wenn auch der FileOutputStream
in einen BufferedOutputStream eingeschlossen wurde).
Die OutputStream-Klassen sind also immer
dann den Writer-Klassen vorzuziehen, wenn entweder sowieso
Binärdaten ausgegeben werden sollen oder wenn sichergestellt
ist, dass keine UNICODE-Zeichen verwendet werden, die durch
das simple Abschneiden der oberen 8 Bit falsch ausgegeben
würden. Da der UNICODE-Zeichensatz in den ersten 256
Zeichen zum ISO-8859-1-Zeichensatz kompatibel ist, sollten
sich für die meisten europäischen und angelsächsischen
Sprachen keine Probleme ergeben, wenn zur Ausgabe von Zeichen
die OutputStream-Klassen verwendet werden.
|
|
P
package
|
Erstellen eigener Pakete |
Der Aufbau und die
Bedeutung der Paketnamen in der package-Anweisung entspricht
exakt dem der import-Anweisung.
Der Compiler löst ebenso wie beim import den dort angegebenen
hierarchischen Namen in eine Kette von Unterverzeichnissen
auf, an deren Ende die Quelldatei steht. Neben der Quelldatei
wird auch die Klassendatei in diesem Unterverzeichnis erstellt.
Pakete kann man selbst sehr einfach
mit Bordmitteln erstellen.
Um eine Klasse einem ganz bestimmten Paket
zuzuordnen, muss lediglich am Anfang des Quelltextes eine
geeignete package-Anweisung verwendet werden. Diese besteht
(analog zur import-Anweisung)
aus dem Schlüsselwort package und dem Namen des Pakets,
dem die nachfolgende Klasse zugeordnet werden soll. Die package-Anweisung
muss als erste Anweisung in einer Quelldatei stehen, so dass
der Compiler sie noch vor den import-Anweisungen
findet.
|
|
Elemente, die ohne
einen der drei bekannten Modifier deklariert wurden, werden
als package scoped oder Elemente mit Standard-Sichtbarkeit
bezeichnet. Sie sind nur innerhalb des Pakets
sichtbar, zu dem diese Klasse
gehört. In anderen Paketen
sind sie dagegen unsichtbar.
Klassen, Methoden,
Variablen mit Standard-Sichtbarkeit
sind nur innerhalb des Pakets
sichtbar, in dem sie definiert wurden. Sie sind beispielsweise
nützlich, um in aufwendigeren Paketen
allgemein zugängliche Hilfsklassen zu realisieren, die
außerhalb des Pakets unsichtbar bleiben sollen. Sie
können mitunter nützlich sein, um zu verhindern,
dass Elemente als public deklariert
werden.
|
|
In großen Programmsystemen
reichen Klassen als Strukturelemente
alleine nicht aus. Deshalb bietet Java mit den Packages
(oder Paketen) oberhalb der Ebene der Klassen
eine weitere Kollektion für Programmelemente an.
Ein Paket ist eine Sammlung von Klassen,
die einen gemeinsamen Zweck verfolgen oder aus anderen Gründen
zusammengefaßt werden sollen. Jede Klasse in Java ist
Bestandteil genau eines Pakets. Paketnamen können aus
mehreren Teilen bestehen und beliebig tiefe Hierarchien ausdrücken.
Der Name einer Methode oder einer Variablen
besteht damit grundsätzlich aus drei Elementen:
· Paketname
· Klassen-
oder Objektname
· Methoden-
bzw. Variablenname
Ein Beispiel für einen Methodennamen
ist java.lang.Math.sqrt. Es gibt
Mechanismen, mit denen es möglich ist, die Namen bei
ihrer Verwendung abzukürzen. Damit eine Klasse verwendet
werden kann, muss angegeben werden, in welchem Paket sie liegt.
Hierzu gibt es zwei unterschiedliche Möglichkeiten.
Jede Klasse in Java ist Bestandteil eines
Pakets. Der vollständige Name einer Klasse besteht aus
dem Namen des Pakets, gefolgt von einem Punkt, dem sich der
eigentliche Name der Klasse anschließt. Der Name des
Pakets selbst kann ebenfalls einen oder mehrere Punkte enthalten.
|
|
Serialisierung wird
häufig mit dem Begriff Persistenz gleichgesetzt, vor
allem in objektorientierten Programmiersprachen. Das ist nur
bedingt richtig, denn Persistenz bezeichnet genaugenommen
das dauerhafte Speichern von Daten auf einem externen Datenträger,
so dass sie auch nach dem Beenden des Programms erhalten bleiben.
Obwohl die persistente Speicherung von Objekten sicherlich
eine der Hauptanwendungen der Serialisierung
ist, ist sie nicht ihre einzige. Wir werden später Anwendungen
sehen, bei der die Serialisierung
von Objekten nicht zum Zweck ihrer persistenten Speicherung
genutzt werden. |
|
Sollen zum Beispiel
zwei Threads so miteinander verbunden
werden, dass einer von beiden Daten erzeugt, die der andere
verarbeitet, gibt es die Möglichkeit, beide mit Hilfe
einer Pipe zu synchronisieren. Dabei werden die beiden Threads
über einen ByteStream miteinander verbunden, der von
einem Thread geschrieben und
von dem anderen gelesen wird.
Dieses Piping-Konzept wird in Java durch
die Klassen PipedInputStream
und PipedOutputStream realisiert.
Beide Klassen werden immer paarweise
und immer in getrennten Threads
verwendet. Daten, die der eine Thread
in den PipedOutputStream schreibt,
kann der andere aus dem angebundenen PipedInputStream lesen.
|
|
Polymorphismus bedeutet
direkt übersetzt etwa "Vielgestaltigkeit" und
bezeichnet zunächst einmal die Fähigkeit von Objektvariablen,
Objekte unterschiedlicher Klassen
aufzunehmen. Das geschieht allerdings nicht unkontrolliert,
sondern beschränkt sich für eine Objektvariable
des Typs X auf alle Objekte der Klasse X oder einer daraus
abgeleiteten Klasse. Ein wichtiges Konzept objektorientierter
Programmiersprachen.
Ein Beispiel für Polymorphismus: Eine
Variable vom Typ einer Basisklasse nimmt zur Laufzeit unterschiedliche
Objekte aus abgeleiteten Klassen
auf. Da bereits in der Basisklasse die Definition von eineMethode
vorgenommen wurde, akzeptiert der Compiler den Aufruf dieser
Methode bereits bei Objekten dieser vermeintlich abstrakten
Klasse. Erst zur Laufzeit ist dann bekannt, welcher abgeleitete
Typ hinter jedem einzelnen Arrayelement steht, und das Laufzeitsystem
ruft die darin implementierte Variante der Methode eineMethode
auf.
|
|
Die Ausgabe von primitiven Datentypen
wird durch eine Reihe überladener Methoden
mit dem Namen print realisiert. Zusätzlich gibt es alle
Methoden in einer Variante println,
bei der automatisch an das Ende der Ausgabe eine Zeilenschaltung
angehängt wird. println existiert darüber hinaus
parameterlos, um lediglich eine einzelne Zeilenschaltung auszugeben.
Damit stehen folgende print-Methoden zur Verfügung (analog
für println):
Ein PrintStream bietet die Möglichkeit,
Strings und primitive Typen im
Textformat auszugeben. Er stellt eine Vielzahl von print-
und println-Methoden für unterschiedliche Datentypen
zur Verfügung. Seine Schnittstelle und Anwendung entspricht
der Klasse PrintWriter.
|
|
Neben Instanzvariablen
und -methoden können auch Klassenvariablen
und -methoden definiert werden. Alle Elemente einer Klassendefinition
können mit Hilfe der aus C++ bekannten Schlüsselwörter
public, private und protected
in ihrer Sichtbarkeit eingeschränkt
werden. Objektvariablen werden
als Referenzen implementiert. Mit ihrer Hilfe ist eine gegenüber
C/C++ eingeschränkte Zeigerverarbeitung möglich,
die das Erstellen dynamischer Datenstrukturen ermöglicht.
Methoden oder
Variablen vom Typ private sind
nur in der aktuellen Klasse sichtbar,
in allen anderen Klassen bleiben sie dagegen unsichtbar. Methoden
in abgeleiteten Klassen können daher nicht überlagert
werden. Für Aufrufer von Instanzen bleiben private-Variablen
verdeckt.
Mit Hilfe dieser Sichtbarkeitsebenen kann
der Zugriff auf Klassenelemente eingeschränkt werden.
private-Elemente sollten immer dann verwendet werden, wenn
implementierungsabhängige Details zu verstecken sind,
die auch in abgeleiteten Klassen
nicht sichtbar sein sollen. protected-Elemente
sind vor Zugriffen von außen geschützt, können
aber von abgeleiteten Klassen
verwendet werden. Die public-Elemente
schließlich bilden die für alle sichtbaren Teile
einer Klassendefinition und können daher als ihre Schnittstelle
angesehen werden. Nachfolgend werden die verschiedenen Sichtbarkeitsattribute
noch einmal genau beschrieben. Elemente mit Standard-Sichtbarkeit
verhalten sich innerhalb des Pakets
wie public- und außerhalb
wie private-Elemente.
Diese Einschränkung bedeutet überraschenderweise
nicht, daß die Methoden
einer Klasse nur auf die privaten Membervariablen
des eigenen Objekts zugreifen dürfen. Vielmehr ist ebenfalls
möglich, (quasi von außen) auf die private-Variablen
eines anderen Objekts zuzugreifen. Vorausgesetzt, es handelt
sich um eine Instanz derselben Klasse.
Aus diesen Ergebnissen allgemeingültige
Empfehlungen abzuleiten, ist schwierig. Zwar empfiehlt es
sich offensichtlich, Methoden
als private bzw. final zu deklarieren,
wenn sicher ist, dass sie in abgeleiteten Klassen
nicht aufgerufen bzw. überlagert werden sollen. Auch
könnte man versuchen, verstärkt Klassenmethoden
zu verwenden oder zur Vermeidung von polymorphen Aufrufen
die Vererbungshierachie zu beschränken
oder mit Hilfe des Attributs final
ganz abzuschneiden. All diese Entscheidungen hätten aber
einen starken Einfluss auf das Klassendesign der Anwendung
und könnten sich leicht an anderer Stelle als Sackgasse
herausstellen.
|
|
Elemente des Typs
protected sind in der Klasse
selbst und in Methoden abgeleiteter
Klassen sichtbar. Zusätzlich
können Klassen desselben Pakets
sie aufrufen.
Methoden oder
Variablen vom Typ protected sind
in der aktuellen Klasse und in abgeleiteten Klassen sichtbar.
Darüber hinaus sind sie für Methoden
anderer Klassen innerhalb desselben Pakets
sichtbar. Sie sind jedoch nicht für Aufrufer der Klasse
sichtbar, die in anderen Paketen
definiert wurden.
|
|
Elemente des Typs
public sind in der Klasse selbst (also in ihren Methoden),
in Methoden abgeleiteter Klassen
und für den Aufrufer von Instanzen der Klasse
sichtbar.
Membervariablen
und Methoden vom Typ public sind
im Rahmen ihrer Lebensdauer überall sichtbar. Sie können
daher in der eigenen Klasse und von beliebigen Methoden
anderer Klassen verwendet werden. Das Attribut public ist
zusätzlich auch bei der Klassendefinition selbst von
Bedeutung, denn nur Klassen,
die als public deklariert wurden, sind außerhalb des
Pakets sichtbar, in dem sie definiert
wurden. In jeder Quelldatei darf nur eine Klasse mit dem Attribut
public angelegt werden. Es gibt noch eine wichtige Besonderheit
bei der Deklaration von Klassen, die von anderen Klassen verwendet
werden sollen. Damit eine Klasse A eine andere Klasse B einbinden
darf, muß nämlich eine der beiden folgenden Bedingungen
erfüllt sein:
· Entweder gehören A und B zu
demselben Paket oder
· die Klasse B wurde als public deklariert.
Wenn also nur Default-Pakete
verwendet werden, spielt es keine Rolle, ob eine Klasse vom
Typ public ist, denn alle Klassen liegen in demselben Paket.
Werden aber Klassen aus externen Paketen
eingebunden, so gelingt das nur, wenn die einzubindende Klasse
vom Typ public ist. Andernfalls verweigert der Compiler deren
Einbindung und bricht die Übersetzung mit einer Fehlermeldung
ab.
|
|
Q
R
RMI
|
Remote Method Invocation |
Mit RMI (Remote Method Invocation) stellt
das JDK seit der Version 1.1 einen Mechanismus zur Verfügung,
der es ermöglicht, Objekte auf einfache Weise im Netz
zu verteilen und ihre Dienste anderen Arbeitsplätzen
zur Verfügung zu stellen. Die prinzipielle Arbeitsweise
von RMI läßt sich wie folgt skizzieren:
· In einem Remote-Interface
werden eine oder mehrere Methoden
definiert, die als aufrufbare Dienste anderen Arbeitsplätzen
zur Verfügung gestellt werden sollen.
· Eine Serverklasse implementiert
das Interface und erzeugt eine
oder mehrere Instanzen, die als Remote-Objekte bezeichnet
werden.
· Die Remote-Objekte werden bei einem
Namens-Service registriert, der von potentiellen Clients abgefragt
werden kann. Mit der RMI-Registry ist ein einfacher Namens-Service
bereits Bestandteil des RMI-Pakets.
· Clients beschaffen mit Hilfe der
RMI-Registry Referenzen auf die benötigten Objekte und
rufen die gewünschten Methoden
auf. Die beim Aufruf übergebenen Parameter werden an
das Remote-Objekt übertragen, und die passende Methode
wird dort ausgeführt. Der Rückgabewert wird an den
Client zurückübertragen. Die Referenzen auf die
Remote-Objekte werden als Remote-Referenzen bezeichnet.
RMI etabliert also eine Client-Server-Architektur
zwischen lokalen Java-Objekten und den von ihnen aufgerufenen
Remote-Objekten. Die eigentliche Kommunikation zwischen den
Teilnehmern ist fast vollständig unsichtbar.
Die Rollen von Client und Server sind dabei
keineswegs statisch festgelegt. So kann ein Client durchaus
Server-Funktionalitäten implementieren oder ein Server
kann zur Ausführung eines Client-Calls die Hilfe eines
anderen Remote-Objekts in Anspruch nehmen. Eine interessante
Eigenschaft von RMI besteht auch darin, fehlenden Code dynamisch
nachzuladen. Benötigt beispielsweise ein Server zur Ausführung
eines Auftrags eine bis dato unbekannte Klasse vom Client
(die natürlich ein ihm zur Compilezeit bekanntes Interface
implementiert), so kann er diese dynamisch vom Client laden
und - dank der Plattformunabhängigkeit von Java - auf
dem Server ausführen.
|
|
Tatsächlich
muss jede Klasse, deren Instanzen als Thread
laufen sollen, das Interface
Runnable implementieren (sogar die Klasse Thread
selbst). Um eine nicht von Thread
abgeleitete Instanz in dieser Weise als Thread
laufen zu lassen, ist in folgenden Schritten vorzugehen:
· Zunächst wird ein neues Thread-Objekt
erzeugt.
· An den Konstruktor
wird das Objekt übergeben, das parallel ausgeführt
werden soll.
· Die Methode start des neuen Thread-Objekts
wird aufgerufen.
Nun startet das Thread-Objekt
die run-Methode des übergebenen Objekts, das sie ja durch
die Übergabe im Konstruktor
kennt. Da dieses Objekt das Interface
Runnable implementiert, ist garantiert, daß eine geeignete
Methode run zur Verfügung
steht.
Auf eine ähnliche Weise lassen sich
auch Methoden, die ursprünglich nicht als Thread
vorgesehen waren, in einen solchen umwandeln und im Hintergrund
ausführen. Der Grundstein für die Umwandlung eines
gewöhnlichen Objekts in einen Thread
wird dabei immer bei der Übergabe eines Runnable-Objekts
an den Konstruktor des Thread-Objekts
gelegt.
|
|
return
|
Rückgabewert einer Methode |
Jede Methode in Java
ist typisiert. Der Typ einer Methode wird zum Zeitpunkt der
Definition festgelegt und bestimmt den Typ des Rückgabewerts.
Dieser kann von einem beliebigen primitiven Typ, einem Objekttyp
(also einer Klasse) oder vom Typ void
sein. Die Methoden vom Typ void
haben gar keinen Rückgabewert und dürfen nicht in
Ausdrücken verwendet werden. Sie sind lediglich wegen
ihrer Nebeneffekte von Interesse und dürfen daher nur
als Ausdrucksanweisung verwendet werden.
Hat eine Methode einen Rückgabewert
(ist also nicht vom Typ void),
so kann sie mit Hilfe der return-Anweisung einen Wert an den
Aufrufer zurückgeben. Die return-Anweisung hat folgende
Syntax:
return Ausdruck;
Wenn diese Anweisung ausgeführt wird,
führt dies zum Beenden der Methode, und der Wert des
angegebenen Ausdrucks wird an den Aufrufer zurückgegeben.
Der Ausdruck muss dabei zuweisungskompatibel zum Typ der Funktion
sein. Die Datenflussanalyse sorgt dafür, dass hinter
der return-Anweisung keine unerreichbaren Anweisungen stehen
und dass jeder mögliche Ausgang einer Funktion mit einem
return versehen ist. Der in C beliebte Fehler, einen Funktionsausgang
ohne return-Anweisung zu erzeugen (und damit einen undefinierten
Rückgabewert zu erzeugen), kann in Java also nicht passieren.
|
|
Quelle: Java - Glossary zusammengestellt aus „Go To Java 2“,
Zweite Auflage, Addison Wesley, Version 2.0 © 2000 Guido Krüger
|
|