Jose M. Fernández Bemerkungen an den Autor
|
Zusammenfassung: Der
Aufbau dieser Serie von Artikeln entspricht von der Struktur her dem Aufbau
klassischer Bücher über strukturierte Programmierung. Der erste
Artikel in dieser Reihe gab eine kurze Einführung in die Charakteristika
der Sprache Java, so daß wir jetzt fortfahren und Datentypen, Variablen,
Kontrollstrukturen usw. näher betrachten können. Dieses wird
zu einem zentralen Begriff führen: den Klassen. Das Konzept der Klassen
ist die Basis dieser Programmiersprache. Um mit diesem Konzept vertraut
zu werden ist der erste Teil dieses Artikels so schematisch
wie möglich.
Rein formal setzt sich ein Java Programm aus einem Satz Kommentare,
Bezeichner, Zeichenfolgen, Separatoren, Leerzeichen und Schlüsselwörtern
zusammen.Der Compiler erhält einen Code, der in einem betimmten Format
geschrieben ist, expandiert Zeichenketten von 8 bit ASCII auf 16 bits.
Der so erweiterte Tastaturcode ermöglicht auch die Verwendung von
Buchstaben aus nicht-latinen Sprachen. Leerzeichen, Tabulatoren und Zeilenrückschub
werden vom Compiler, so sie nicht zum Symbolsatz gehören, gelöscht.
Es ist also nicht wichtig wie ein Java Programm
eingerückt wird und
wieviele TABs und Leerzeilen man verwendet.
Kommentare können in Java wie folgt eingefügt werden:
Unter Bezeichnern versteht man Variablen-Namen, Klassen-Namen und Methoden-Namen. Diese Bezeichner können sich aus jeder beliebigen Zeichenfolge (Groß- wie Kleinbuchstaben) zusammensetzen, den Unterstrich und das Dollarzeichen enthalten, ebenso Zahlen, mit denen sie allerdings nicht beginnen dürfen.
Java benutzt einige spezielle Zeichen als Trenner. Der meistgrauchte ist das Semikolon, daneben werden aber auch folgende Zeichen(folgen) benutzt:
Symbol | Beschreibung |
() | Enthält die Übergabeparameterliste bei der Definition und dem Aufruf von Methoden. Klammern enthalten auch Ausdrücke in Kontrollabfolgen, ferner werden sie zur Änderung des Ranges eines Ausdruckes und bei der Typenkonvertierung benutzt. |
{} | Enthalten die Werte automatisch inizialisierter Vektoren, definieren Code Blöcke, werden in Klassen eingesetzt und zur Abtrennung von Porgrammblöcken (Scopes). |
[] | Definieren Matrix Typen und werden zum Bezug auf Werte einer Matrix verwendet. |
; | Zeilentrenner. |
, | Dient der Trennung aufeinanderfolgender Bezeichner bei der Deklaration von Variablen und der Trennung von Zeilen in einer FOR Schleife. |
. | Wird bei der Trennung der verschiedenen Namensteile in Packages, d.h. Container, die einzelne Klassen bzw. Gruppen von Klassen und Interfaces enthalten (packet, sub packet und Klasse) benutzt und bei der Trennung einer Variablen oder Methode von einer referenzierten Variablen. |
Schlüsselwörter sind Bezeichner, die in der Sprache Java eine
spezielle Bedeutung haben und die somit in keiner anderen als der durch
die Sprache definierten Art und Weise benutzt werden können. Die nächste
Tabelle führt all diese Schlüsselwörter auf:
abstract | double | int | super |
boolean | else | interface | switch |
break | extends | long | synchronized |
byte | false | native | this |
byvalue | final | new | threadsafe |
case | finally | null | throw |
catch | float | package | transient |
char | for | private | true |
class | goto | protected | try |
const | if | public | void |
continue | implements | return | while |
default | import | short | |
do | instanceof | static |
Im vorhergehenden Artikel wurde erwähnt, Java sei vollkommen Objekt-orientiert. In Java gibt es jedoch aus Effizienzgründen acht "einfache" Datentypen. Diese Basis Typen sind keine Objekte. Sie haben - zur Sicherstellung der Portabilität - streng definierte Bereiche.
Diese einfachen Datentypen verteilen sich auf die folgenden vier Gruppen:
TYP | NAME | SPEICHER-
BEDARF |
BEREICH |
Integer | long | 64 | -9.223.372.036.854.775.808
bis 9.223.372.036.854.775.807 |
int | 32 | -2.147.483.648
bis 2.147.483.647 |
|
short | 16 | -32.768 bis 37.767 | |
byte | 8 | -128 bis 127 | |
Floating point | float | 32 | 3.4 e-038 bis 3.4 e+038 |
double | 64 | 1.7 e-308 bis 1.7 e+308 | |
Character | char | 16 | Unicode |
Boolean | boolean | "true" oder "false" |
Allgemein werden Variablen in der folgenden Weise deklariert:
identifier type [=value] [,identifier [=value]......]; |
Beispiele:
int a = 4, b, c=7; char c; myclass class; |
Die meisten Programmiersprachen definieren zwei Arten von Scopes: globale und lokale. Dies paßt nun allerdings nicht in das Konzept einer Objekt-orientierten Sprache wie Java. In Java gibt es zwei Scopes, den der Klasse und den der Methode.
(type) value |
int a; char b; a=(int) b; |
int a; double d= 125.43; a=(int) d;Die Variable hat den Wert 125. byte b; int i=257; b=(byte) i;b hat den Wert 1 als Ergebnis der Division von 257 mit 256, da 256 der Bereich des Typs byte ist. byte b; double d= 340.123; b=(byte) d;b hat aus dem gleichen Grund den Wert 84; |
Operator | Beschreibung |
+ | Addition |
- | Subtraktion |
* | Multiplikation |
/ | Division |
% | Modulo (Rest erhaltend) |
++ | Inkrement |
+= | Addition und Zuweisung |
-= | Subtraktion und Zuweisung |
*= | Multiplikation und Zuweisung |
/= | Division und Zuweisung |
%= | Modulo und Zuweisung |
-- | Dekrement |
int a=38; double d=41.95; int c=a%10;< double e=d%10; |
Operatoren mit Zuweisung sind nützlich in Situationen wie den folgenden Beipielen:
a = a + 4 ist gleichbedeutend mit a+=4;
a = a % 2 ist gleichbedeutend mit a%=2;
Allgemein können Zeilen wie
var=var op Ausdruck ersetzt werden mit: var op= expression;
Operator | Description |
~ | unäres bitweises NOT |
& | bitweises AND |
| | bitweises OR |
/\ | Exklusives bitweises OR |
>> | Rechts verschieben |
>>>> | Rechts verschieben und mit Nullen füllen |
<< | Links verschieben |
&= | bitweises AND und Zuweisung |
|= | bitweises OR und Zuweisung |
/\= | exklusives bitweises OR und Zuweisung |
>>= | Rechts verschieben und Zuweisung |
>>>>= | Rechts verschieben gefüllt mit Nullen und Zuweisung |
<<= | Links shift und Zuweisung |
Operator | Beschreibung |
= = | Gleich |
!= | Ungleich |
> | Größer als |
< | Kleiner als |
>= | Größer oder gleich |
<= | Kleiner oder gleich |
Operator | Bschreibung |
& | logisches AND |
| | logisches OR |
/\ | logisches XOR (exklusives OR) |
|| | shortcut OR |
&& | shortcut AND |
! | unäres logisches NOT |
&= | AND Zuweisung |
|= | OR Zuweisung |
/\= | XOR Zuweisung |
= = | Gleich |
!= | Ungleich |
?: | Ternäres If-then-else |
Die allgemeine Form eines ternären Operators ist:
Ausdruck1 ? Ausdruck2 : Ausdruck3
Ist Ausdruck1 True, so wird der Ausdruck2 ausgeführt, ansonsten Ausdruck3
Rangfolge der Operatoren:
( ) | [ ] | . | |
++ | -- | ~ | ! |
* | / | % | |
+ | - | ||
>> | >>>> | << | |
> | >= | < | <= |
= = | != | ||
& | |||
' | |||
| | |||
&& | |||
|| | |||
?: | |||
= | Op= |
Gruppe | Struktur | Beschreibung |
Auswahl | if |
if (Bedingung1) Anweisung1; else Anweisung2; |
mehrere if's |
If (Bedingung1) Anweisung1; else if (Bedingung2) Anweisung2; else if (Bedingung3) Anweisung3; . . else AnweisungN; |
|
switch |
switch (Variable){ case Wert1: Anweisung1; break; case Wert2: Anweisung2; break; . ... default : AnweisungN; } |
|
Schleife | while |
while (Bedingung) { Anweisung; } |
do while |
do { Anweisung; } while (Bedingung) |
|
for |
for (Initialisierung, Bedingung, Abbruch) { Anweisung; } |
|
Sprung | break | Eine switch-Struktur verlassen
Eine Schleif verlassen |
continue | Verläßt die gerade durchlaufene Iteration, aber bleibt in der Schleife | |
return | Verläßt explizit eine Methode |
Eine Klasse ist ein Modell oder ein Muster für ein Objekt, ein Objekt die Instanz einer Klasse. Java erlaubt keine globalen Funktionen oder Variablen, so daß alles, was das Programm auszuführen hat, über Klassen definiert werden muß.
Eine Klasse ist definiert mit dem reservierten Bezeichner "Class". Eine
allgemeine Definition ist z.B.:
Class klassen_name { Type instanz1_der_variable; Type instanz2_der_variable; . . ... type instanz_variable; type methoden_name(parameter_liste){ //Quellcode der Methode } . . .. type name_der_methode(parameter_liste){ //Quellcode der Methode } } |
Um ein Objekt einer Klasse zu erhalten müssen zwei Schritte befolgt werden:
Dem Objekt muß dynamisch Speicher zugewiesen werden.
Der zweite Schritt wird unter Verwendung des "new" Operators ausgeführt:
variable = new name_der_Klass(); |
Hier ist "variable" eine Variable einer Klasse, die erzeugt werden soll und name_der_Klass der Name der Klasse, die der neuen zugrunde liegt. Graphisch kann dies folgendermaßen repräsentiert werden:
type name_der_methode (parameter_liste) { //Quellcode der Methode } |
Die parameter_liste ist eine Sequenz von Typ-Bezeichner Paaren die durch Komma getrennt werden. Parameter sind Variablen, denen die Werte der Argumente von Methoden zugewiesen werden. Hat die Methode keine Parameter, so bleibt die Liste leer.
Methoden, die einen von void verschiedenen Wert übergeben benutzen:
return value; |
Java stellt eine Vielzahl von flexiblen und mächtigen Methoden zur Verfügung. Zunächst soll allerdings erst an einem Beispiel das bisher besprochene zusammengfaßt werden, bevor im weiteren die wichtigsten Aspekte von Methoden näher betrachtet werden.
Mit der folgende Klasse soll das Volumen eines rechtwinkligen Behälters
bestimmt werden:
Code | Kommentar |
class volumen { double laenge; double breite; double tiefe; void BerechneVolumen () { double inhalt ; inhalt = laenge*breite*tiefe; System.out.println("Volumen: " + inhalt); } } |
In diesem Beispiel wurde die Klasse "volumen" definiert, die drei instanzierte Variablen enthält: laenge, breite und tiefe. Daneben ist auch eine Methode definiert, die das Volumen aus diesen drei Werten berechnet. Kompilieren erzeugt eine Klasse volumen.class. |
Code | Kommentar |
class example { public static void main(String Arg[]){ volumen p1=new volumen(); volumen p2=new volumen(); p1.laenge = 16; p1.breite=3; p1.tiefe=2; // p2.laenge = 25; p2.breite=6; p2.tiefe=3; // p1.BerechneVolumen(); // p2.BerechneVolumen(); } } |
Es werden zwei Variablen (p1, p2) vom Typ volumen definiert
und zwei neue Operatoren erzeugt, auf die durch diese Variablen zugegriffen
werden kann.
Danach werden den Variablen der neu erzeugten Objekte Werte zugewiesen.
Ein Aufruf der Methode BerechneVolumen() durch p1 läßt: "Volumen: 96" auf dem Schirm erscheinen. Das selbe Objekt, aufgerufen durch p2, ergibt: "Volumen: 450" |
Methoden mit Parametern, Wertübergabe.
Die Mehrzahl der Methoden nutzt Parameter für einen allgemeineren Einsatz. Darüber hinaus können Methoden auch Werte zurückliefern, so daß Methoden Daten in den unterschiedlichsten Situationen bearbeiten können.
Dies wird demonstriert mit einer Verfeinerung des obigen Beispieles:
Code | Kommentar |
class Volumen { double BerechneVolumen (double l, double b, double t) { double volumen=l*b*t ; return volumen; } } |
Die Methode BerechneVolumen wurde derart geändert, daß ihr drei Parameter übergeben werden können. Außerdem liefert sie einen Wert vom Typ double zurück, welches mit dem Befehl return volumen erreicht wird. |
class beispiel { public static void main(String Arg[]){ volumen p1=new volumen(); volumen p2=new volumen(); double vol; vol=p1.BerechneVolumen(10,3,2); System.out.println("Volumen: " + vol); // vol=p2.BerechneVolumen(25,5,2); System.out.println("Volumen: " + vol); } } |
Der Aufruf der Methode findet mit der Übertragung der gewünschten Parameter statt. Der Rückgabewert der Methode wird der Variablen vol zugewiesen, welche vom gleichen Typ wie die Methode sein muß. |
Ein Konstruktor, der in eine Klasse eingebunden ist, hat genau den gleichen Namen wie die Klasse. Seine Syntax entspricht der einer Methode. Er wird nach der Erzeugung des Objektes und vor Beendigung (finalize) des Operators automatisch ausgeführt.
Konstruktoren geben impilzit den Typ der Klasse zurück. Der Konstruktor bewirkt die vollständige Initialisierung des Objektes. Auf diese Weise ist dafür gesorgt, daß der Code, der die Instanz des Objektes erzeugt, bereits auf den vollständigen Code des Objektes zugreifen kann. Variablen der Instanzen werden dabei vom Konstruktor initialisert. Wie im Fall der Methoden können auch Konstruktoren zum vielfältigeren Einsatz Parameter übergeben werden. Dies soll am folgenden Beispiel gezeigt werden:
Code | Kommentar |
Class volumen { double laenge; double breite; double tiefe; // capacity(double l, double b, double t){ laenge=l; breite=a; tiefe=p; } // void BerechneVolumen () { double volumen ; volume=lang*breit*tief; return volume; } } |
Dieser Klasse ist ein Konstruktor beigefügt worden, der der Erscheinung nach eine Methode mit dem selben Namen wie die Klasse ist, aber ohne jeglichen Typ. Der Konstruktor initialisiert die Instanzen der Variablen, die durch die erhaltenen Argumente deklariert werden. |
class Beispiel { public static void main(String Arg[]) { Volumen p1=new Volumen(10,5,2); Volumen p2=new Volumen(25,6,2); double vol; vol=p1.BerechneVolumen(); System.out.println("Volumen: " + vol); // vol=p2.BerechneVolumen(); System.out.println("Volumen: " + vol); } } |
Der neue Operator erzeugt die Instanzen der Klasse durch Übergabe der benötigten Parametern an den Konstruktor. |
Trotzdem kann die Methode "finalize()" zum Beenden einer Klasse eingesetzt werden. Diese Methode wird ausgeführt, wenn der Interpreter des Objekt zerstört. In diese Methode können alle Operationen aufgenommen werden, die vor dem Zerstören des Objektes noch ausgeführt werden sollen.
Überladen von Methoden.
Polymorphie (Benutzung gleichnamiger Methoden in einem Interface, d.h Vorgabe von Mehtoden, die eine Klasse implementieren muß) ist eine wesentliche Eigenschaft der OOP. In Java ist Polymorphie durch Überladen von Methoden implementiert.
Innerhalb einer Klasse können verschiedene Methoden mit dem gleichen Namen definiert werden, sie müssen sich dann aber durch verschiedene Parameter oder einen unterschiedlichen Rückgabetyp unterscheiden. Wird eine überladene Methode aufgerufen, so benutzt der Java Interpreter eben den Rückgabetyp und/oder die beim Aufruf übergebene Argumentliste um zwischen den verschiedenen Methoden zu unterscheiden.
Verschiedene Versionen einer überladenen Methode können unterschiedliche Aufgaben im Programm erledigen, was dem Grundgedanken der Polymorphie jedoch widerspräche. Überladene Methoden sollten in einer gewissen Besziehung zueinander stehen. Genau wie Methoden können auch Konstruktoren überladen werden.
Übergabe von Argumenten.
Grundsätzlich erlauben Programmiersprachen zwei unterschiedliche Arten der Übergabe von Argumenten:
Zugriffskontrolle.
Eine weitere wesentliche Eigenschaft der OOP ist die Kapselung, die eine Verbindung der Daten zu dem Teil des Codes darstellt, der die Daten benutzt. Darüber hinaus stellt Kapselung eine Zugriffkontrolle dar, d.h. nur ein Teil des Programmes verfügt über die Möglichkeit, auf die Mitglieder einer Klasse zuzugreifen.
In Java wird der Zugriff spezifiziert werden mit 'public', 'private' und 'protected'.
Ist eine Mehtode oder die Variable einer Instanz als 'public' definiert, so kann auf sie von jedem Teil des Programmes aus zugegriffen werden. Ist sie als 'private' definiert, so steht sie nur anderen Methoden der gleichen Klasse zur Verfügung. Methoden und Variablen einer Instanz werden standardmäßig als 'public' definiert.
Die Spezifizierung 'protected' wird bei der Vererbung eingesetzt, welche im folgenden behandelt werden soll.
Um zu erreichen, daß eine Klasse B die Eigenschaften der Superklasse
A erbt, muß die Klasse B folgendermaßen definiert werden:
Class B extends A { // Klassen Definition } |
Java erlaubt hierbei jedoch nicht die mehrfache Vererbung von verschiedenen Superklassen zu einer Unterklasse.
Wenn in einer Superklasse Mitgleider als 'private' definiert sind, so kann auf sie nicht in der erbenden Klasse zugegriffen werden. Muß von einer Unterklasse auf die direkt vorhergehende Superklasse zugegriffen werden, so wird die Referenz auf die Superklasse mit dem reservierten Bezeichner 'super' angegeben. Auf diese Weise können Konstruktoren oder Mitglieder der Superklasse aufgerufen werden, die ansonsten von der Unterklasse unsichtbar bleiben.
Weist eine Methode einer Unterklasse den gleichen Namen und Typ der Methode der Superklasse auf, so spricht man vom Überschreiben (overwrite). Diese Möglichkeit stellt die Grundlage einer mächtigen Eigenschaft von Java dar, die "dynamic selection method" genannt wird. Die Entscheidung, welche Methode beim Aufruf benutzt werden soll, fällt dabei dann erst zum Zeitpunkt der Programmausführung in Abhängigkeit vom Typ der referenzierten Variablen
Im nächsten Artikel werden die Möglichkeiten der Vererbung durch abstrakte Klassen, Interfaces usw. gezeigt.
Páginas
web mantenidas por Miguel Ángel Sepúlveda
© Jose M. Fernandez 1998 LinuxFocus 1998 |