Previous Page TOC Index Next Page See Page

Java-Grundlagen

Gestern haben Sie eine Anwendung und ein Applet unter Java geschrieben. Heute werden Sie die Sprache genauer kennenlernen, so daß Sie die grundlegenden Konstrukte kennen und wissen, wie sie für die Programmierung in Java eingesetzt werden.


Technischer Hinweis
Es gibt so viele Unterschiede zwischen Java, C/C++ und Pascal/Delphi, daß es sinnvoll ist, die heutige Lektion durchzuarbeiten, auch wenn Sie schon Programmiererfahrung mitbringen. C/C++-Programmierer beispielsweise werden überrascht sein, daß Java kein typedef verwendet, während Pascal/Delphi-Programmierer erstaunt feststellen werden, daß vor einer else-Anweisung ein Semikolon angegeben werden muß!

Die folgenden Konstrukte stellen die elementaren Bausteine Ihrer Java-Programme dar, etwa Variablen, Typen, Ausdrücke, Operatoren, Arrays, Strings, Bedingungen und Schleifen. Heute lernen Sie die folgenden Dinge kennen:

Programmanweisungen

In einer Programmanweisung erfolgt die eigentliche Arbeit. Es gibt drei Kategorien von Anweisungen: einfache Anweisungen, zusammengesetzte Anweisungen und Kommentare. Einfache und zusammengesetzt Anweisungen werden kompiliert und werden Teil der Binärdarstellung Ihrer Anwendung. Alle kompilierten Anweisungen müssen mit einem Semikolon enden (;). Kommentare werden nicht kompiliert und sind nur im Quellcode sichtbar. Sie können jedoch Monate später Bedeutung erlangen, wenn Sie (oder jemand, der Ihren Code warten soll) versuchen, herauszufinden, was der Code bewirken soll.

Einfache Anweisungen

Einfache Anweisungen können Werte zuweisen, eine Aktion ausführen oder eine Methode aufrufen.


Als Methode wird in Java ein Unterprogramm bezeichnet (Unterroutine, Funktion oder Prozedur).


Hier folgen einige Beispiele für einfache Anweisungen:

currpoint = new Point(x,y);
if (i==8) j = 4;
g.drawString("Hello world!", 10, 30);
repaint();

Java unterstützt verschiedene Anweisungstypen, unter anderem Zuweisung, Block, Bedingung, Deklaration, Schleifen und Methodenaufrufe. Bis auf die Methodenaufrufe werden all diese Typen heute beschrieben, die Methodenaufrufe folgen morgen.

Block-Anweisungen


Eine Block-Anweisung (oder einfach ein Block) ist eine Gruppe anderer Programmanweisungen, die in geschweifte Klammern eingeschlossen ist ({}). Es handelt sich dabei um dieselbe Klammerung wie in C/C++ und sie ist analog zu dem begin..end-Paar in Pascal/Delphi. Manchmal spricht man auch von einer zusammengesetzten Anweisung.


Im allgemeinen können Blöcke überall dort eingesetzt werden, wo auch einfache Anweisungen erlaubt sind. Der Block erzeugt einen neuen Gültigkeitsbereich für die darin enthaltenen Anweisungen. Das bedeutet, Sie können lokale Variablen in einem Block deklarieren und nutzen, die nach Ausführung des Blocks aufhören zu existieren.

Hier folgt ein Beispiel, das die unterschiedlichen Gültigkeitsbereiche demonstriert. In der folgenden Methodendefinition wird y zweimal deklariert: einmal für die Methode als Ganzes, einmal innerhalb des Blocks:

void testblock() {
int x = 10;
int y = 35;
[ // Blockanfang
int y = 50;
System.out.println("Im Block:");
System.out.println("x:" + x);
System.out.println("y:" + y); // einmal
] // block end
System.out.println("y:" + y); // zweimal
}

Wenn y zum ersten Mal ausgegeben wird, liegt der Gültigkeitsbereich innerhalb des Blocks, wo y redefiniert wurde, sein Wert ist also 50. Wenn y zum zweiten Mal ausgegeben wird, liegt der Gültigkeitsbereich innerhalb der Methode aber außerhalb des Blocks, der Wert von y ist also 35.

Blöcke werden in der Regel nicht so eingesetzt, wie in diesem Beispiel gezeigt. Häufig finden sie in Kontrollstrukturen Verwendung, die Sie später noch kennenlernen werden.

Kommentare

Kommentare sind Anweisungen, die nicht kompiliert werden, und die beliebige Texte enthalten können, die Sie dem Code zur Information mitgeben wollen. Java stellt drei Arten von Kommentaren zur Verfügung: einzeilige, mehrzeilige und Dokumentations-Kommentare.


Java erlaubt keine verschachtelten Kommentare. Mit anderen Worten, Sie können in einem Kommentar keinen zweiten Kommentar angeben.

Kommentare sind eine praktische interne Dokumentation Ihres Programms. Sie können Kommentare einfügen, um zu beschreiben, wie ein bestimmter Teil eines Programms entwickelt wurde, warum eine bestimmte Datenstruktur gewählt wurde oder welche Abhängigkeiten der Code aufweist. Wenn Sie eine externe Dokumentation besitzen, können Sie in Kommentaren darauf verweisen und so interne und externe Dokumentation verknüpfen.

Einzeilige Kommentare

Ein einzeiliger Kommentar kann in einer eigenen Zeile plaziert werden. Er wird durch zwei Schrägstriche (//) am Zeilenanfang gekennzeichnet werden:

// So sieht ein einzeiliger Kommentar aus

Ein einzeiliger Kommentar kann auch am Ende einer Codezeile plaziert werden, hinter dem Semikolon. Das wird manchmal auch als Inline-Kommentar bezeichnet:

x = y; // Dies ist ein Inline-Kommentar

Mehrzeilige Kommentare

Ein mehrzeiliger Kommentar wird durch eine Kombination aus Schrägstrich und Stern (/*) am Anfang gekennzeichnet und endet mit einem Ste

/* Für diese Berechnung wird die Anzahl der Tage
pro Zyklus auf 30 gesetzt */

Hier eine alternative Darstellung mehrzeiliger Kommentare:

/* Die Prozedur AvgDailyBal berechnet den durchschnittlichen
* täglichen Saldo, das ist die Summe aller noch nicht bezahlten
* Rechnungen dividiert durch die Anzahl der Tage im aktuellen
* Monat des Rechnungszyklus
*/

Beachten Sie, daß die Sterne am Anfang der Mittelzeile in diesem Beispiel nur dem visuellen Effekt dienen. Sie können einen beliebigen Stil für Ihre mehrzeiligen Kommentare verwenden, solange sie mit /* beginnen und mit */ enden. Der Compiler ignoriert einfach alles, was zwischen diesen Zeichenpaaren steht.

Dokumentations-Kommentare

Dokumentations-Kommentare sind speziell darauf ausgelegt, eine öffentliche Klassendokumentation im HTLM-Format zu erzeugen. Auch die HTML-Seiten, die Java dokumentieren, wurden unter Verwendung dieser Technik erzeugt, indem Dokumentations-Kommentare in den Quellcode aufgenommen wurden. Alles, was zwischen Schrägstrich-Stern-Stern (/**) und Stern-Schrägstrich (*/) steht, wird als Teil dieses speziellen Kommentars betrachtet. Diese Kommentare werden mit dem Utility javadoc aus dem Code extrahiert, die auch bestimmte Schlüsselwort-Variablen versteht, die mit dem At-Zeichen (@) beginnen, wie etwa @author oder @version:

/** Diese öffentliche Klasse soll die
* Funktionalität der Klasse awt.Graphics
* erweitern, um Firmenlogos anzuzeigen.
* @author mmm
* @version 3.51
*/
public class mySpecialClass extends java.awt.Graphics {
...
}

Dokumentations-Kommentare werden in der Regel unmittelbar vor der Klassendeklaration plaziert, wie in diesem Beispiel gezeigt.

Variablen, Literale und Datentypen

Variablen sind Platzhalter mit Namen, in denen während der Programmausführung Werte gespeichert werden. Bevor Sie eine Variable verwenden können, müssen Sie festlegen, welche Art Wert sie aufnehmen soll. Das hängt davon ab, welchen Datentyp Sie für die Deklaration der Variablen verwenden. Nachdem eine Variable deklariert wurde, kann sie durch Zuweisung eines Werts initialisiert und in Ausdrücken verwendet werden. Bei dem zugewiesenen Wert kann es sich um einen literalen Wert (eine Zahl, einen Buchstaben oder einen String) oder um das Ergebnis eines Ausdrucks handeln.

Java kennt drei Kategorien von Variablen: Klassenvariablen, Instanzvariablen und lokale Variablen. Alle drei werden auf dieselbe Weise deklariert. Allerdings erfolgt der Zugriff auf Klassen- und Instanzvariablen etwas anders als auf lokale Variablen. Klassen- und Instanzvariablen werden in der nächsten Lektion beschrieben. Heute lernen Sie die lokalen Variablen kennen, die in Methoden deklariert und verwendet werden.


Technischer Hinweis
Java unterstützt das Konzept der globalen Variablen nicht, die in allen Teilen eines Programms zur Verfügung stehen. Statt dessen werden Instanz- und Klassenvariablen verwendet, um eine Kommunikation zwischen den Objekten zu realisieren.


Datentypen

Wenn Sie eine Variable mit einem bestimmten Datentyp deklarieren, definieren Sie, welche Art von Werten dort abgelegt werden können. Eine Variable beispielsweise, die mit dem Typ byte deklariert wurde, kann einen Wert zwischen -128 und 127 aufnehmen. Es gibt acht Wert-Datentypen und zwei Referenz-Datentypen.

Die acht Werttypen beinhalten Integer-, Fließkomma-, Boolesche und Zeichen-Datentypen, wie in Tabelle 2.1 gezeigt. Sie werden manchmal auch als elementare Typen bezeichnet. Alle Datentypen in Java haben Standardwerte sowie konsistente Eigenschaften über alle Plattformen hinweg.

Tabelle 2.1: Die Wert-Datentypen von Java

Datentyp

Größe

Standardwert

Wertebereich

byte

8 Bit

0

-128 bis +127

short

16 Bit

0

-32768 bis +32767

int

32 Bit

0

-2147483648 bis +2147483647

long

64 Bit

0

-9223372036854775808 to +9223372036854775807

float

32 Bit

0.0

-3.40282347E+38 (min)




-1.40239846E-45 (max)

double

64 Bit

0.0

-1.79769313486231570E+308 (min)




4.94065645841246544E-324 (max)

boolean

1 Bit

false

false, true

char

16 Bit

\u0000

\u0000 bis \uFFFF

Alle Integer-Typen (byte, short, int, long) sind vorzeichenbehaftet. Fließkomma-Werte (float und double) folgen dem IEEE-Standard 754 für Zahlen mit einfacher und doppelter Genauigkeit. Neben den normalen numerischen Werten können Fließkomma-Operationen vier spezielle Werte zurückgeben, die als Konstanten definiert sind: POSITIVE_INFINITY, NEGATIVE_INFINITY, NEGATIVE_ZERO und NaN (Not a Number, keine Zahl). Die boolean-Werte werden nicht zu Integern ausgewertet wie in anderen Sprachen; sie können nur die Werte true oder false darstellen.

Wenn die char-Zeichenwerte etwas seltsam aussehen, dann, weil Java den Zwei-Byte-Unicode-Zeichenstandard unterstützt. In diesem Buch werden Sie jedoch nur die ASCII- oder Latin-1-Untermenge der Unicode-Zeichen verwenden.


Technischer Hinweis
Der Unicode-Zeichensatz umfaßt 16 Bit und beinhaltet 34168 Zeichen. Die Unicode-Untermenge, die die ASCII-Zeichen repräsentiert, liegt zwischen \u0020 und \u007E, für Latin-1 zwischen \u0020 und \u00FF. Weil ASCII und Latin-1 8-Bit-Zeichensätze sind, sind sie beide nicht einfache Untermengen von Unicode. Unicode wird schnell zum Standard auf der gesamten Welt, weil es die Symbole und Zeichen der meisten natürlichen Sprachen darstellen kann. Mehr über Unicode finden Sie auf der Web-Seite des Unicode-Konsortiums unter http://www.unicode.org/.

Die beiden Referenz-Typen nehmen Objekte (in der nächsten Lektion beschrieben) und Arrays auf. Der Referenz-Typ object kann eine Klasseninstanz aufnehmen und hat den Standardwert null. Der Referenz-Typ array kann Elemente beliebiger Werte oder Referenz-Typen aufnehmen und hat als Standardwert den Standardwert für den Elementtyp. Mit anderen Worten, die Elemente eines byte-Arrays haben die Standardwerte 0, die Elemente eines object-Arrays haben die Standardwerte null.


Technischer Hinweis
In Java gibt es keine typedef-Anweisung wie in C/C++. Um neue Typen zu deklarieren, deklarieren Sie zuerst eine neue Klasse und dann können Sie Variablen als Typ dieser Klasse deklarieren. Eine genauere Beschreibung erhalten Sie in der nächsten Lektion.

Variablennamen

Ein Variablenname kann mit einem Buchstaben, einem Unterstrich (_) oder einem Dollarzeichen ($) beginnen und eine beliebige Kombination aus Buchstaben und Ziffern enthalten. Symbole sind problematisch, weil viele davon in Java-Operatoren verwendet werden. Außerdem ist es empfehlenswert, nicht den Unterstrich (_) oder das Dollarzeichen ($) als erstes Zeichen zu verwenden, auch wenn das erlaubt ist, um Konflikte beim Linken von C/C++-Bibliothekn zu vermeiden.

Der De-facto-Standard ist, in Variablennamen nur Buchstaben und Ziffern zu verwenden, wobei das erste Wort in Kleinbuchstaben dargestellt wird, das zweite und alle folgenden mit einem großen Anfangsbuchstaben. Eine Variable beispielsweise, die das Rentenalter angibt, könnte als rentenAlter bezeichnet werden, eine Variable für einen Versicherungshöchstwert als versWertLimit.

Java-Schlüsselwörter können nicht als Variablennamen verwendet werden. Es gibt zwei Schlüsselwörter, die Tabelle 2.2 aufgelistet sind, die zwar nicht mehr verwendet aber dennoch von Java reserviert werden. Sie sind durch das Kreuz (å) markiert.

Tabelle 2.2: Java-Schlüsselwörter

abstract

boolean

break

byte

case

catch

char

class

const å

continue

default

do

double

else

extends

final

finally

float

for

goto å

if

implements

import

instanceof

int

interface

long

native

new

package

private

protected

public

return

short

static

super

switch

synchronized

this

throw

throws

transient

try

void

volatile

while





Vergessen Sie nicht, daß in Java die Groß-/Kleinschreibung berücksichtigt wird. Mit anderen Worten, die Variablen mit den Namen watchOut, WatchOut und WATCHOUT sind nicht dieselben. Achten Sie also bei der Codierung auf konsistente Schreibweisen.

Variablendeklaration

Nachdem Sie wissen, welchen Typ und welchen Namen eine Variable haben soll, können Sie sie deklarieren. Java bietet mehrere Möglichkeiten zur Variablendeklaration, die an einer beliebigen Stelle in der Methode plaziert werden kann. Eine Variable ist von dem Moment an, wo sie deklariert wurde, bis zur nächsten schließenden geschweiften Klammer (}) gültig. Hier hört die Variable auf zu existieren und verschwindet aus dem Gültigkeitsbereich.


Die Gruppe der Programmanweisungen, in denen eine Variable existiert, wird auch als ihr Gültigkeitsbereich bezeichnet. Wenn ein Programm ausgeführt wird, wird die Variable n der Deklarationsanweisung erzeugt. Alle Anweisungen, die der Variablendeklaration folgen, können auf die Variable zugreifen und sie verändern, bis eine schließende geschweifte Klammer (}) auftritt. Von dem Moment an existiert die Variable nicht mehr im Speicher und befindet sich außerhalb des Gültigkeitsbereichs.


Die einfachste Form einer Variablendeklaration gibt nur den Typ und den Namen der Variablen an. Die folgenden Deklarationsanweisungen erzeugen die Variablen retireAge und birthYear vom Typ short und die Variable insValueLimit vom Typ float:

short retireAge;
short birthYear;
float insValueLimit;

Wenn Sie Variablen desselben Typs deklarieren, können Sie sie in einer einzigen Zeile angeben, abgetrennt durch Kommata. Die drei Variablen der folgenden Deklaration haben alle den Typ short:

short retireAge, birthYear, stdRetireAge;

Initialisierung

Um einer Variablen bei der Deklaration einen Ausgangswert zuzuweisen, verwenden Sie das Gleichheitszeichen (=), den Zuweisungsoperator von Java, gefolgt von dem gewünschten Wert. Die folgende Deklarationsanweisung initialisiert insValueLimit mit 10000.00:

float insValueLimit = 10000.00;

Sie können mehrere Variablen initialisieren, indem Sie jeder davon einen Wert zuweisen und sie in einzelnen Zeilen deklarieren. In der folgenden Deklaration werden isInsured und isCurrent mit true initialisiert:

boolean isInsured = true;
boolean isCurrent = true;

Alternativ können Sie mehrere Variablen desselben Typs initialisieren, indem Sie sie in dieselbe Zeile schreiben und durch Kommata voneinander trennen. Jede dieser Möglichkeiten ist korrekt und nur eine Frage des Geschmacks und des Stils.

boolean isInsured = true, isCurrent = true;

Im nächsten Beispiel werden retireAge und birthYear nicht initialisiert, aber der letzten Variablen, stdRetireAge, wird der Wert 65 zugewiesen.

short retireAge, birthYear, stdRetireAge = 65;

Variablen können zwar den Standardwert des Typs annehmen, mit dem sie deklariert werden, aber der Java-Compiler gibt eine Warnung aus, wenn Sie versuchen, eine lokale Variable zu verwenden, ohne sie zuvor initialisiert zu haben.

Literale

Literale werden in Java verwendet, um Werte darzustellen. Im letzten Abschnitt, der die Zuweisung von Werten an Variablen beschrieb, sind alle zugewiesenen Werte (10000.00, true und 65) Literale.


Ein Literal ist ein Wert, der einer Wert-Datentyp-Variablen zugeordnet werden kann, etwa bei der Initialisierung in der Deklaration. Ein Literal kann aber auch direkt im Programm verwendet werden, beispielsweise der Wert 8 im Ausdruck i==8.


Es gibt Boolesche, numerische, Zeichen- und String-Literale. Jedes davon stellt spezielle Informationen dar, die Sie brauchen, um Entscheidungen dahingehend zu treffen, wie diese Werte repräsentiert werden.

Boolesche Literale

Die Booleschen Literale in Java können keine Integer-Werte annehmen. Sie werden nur zu true und false ausgewertet und haben den Typ boolean.

Numerische Literale

Ein numerisches Literal ist eine beliebige Zahl, die einem Integer- oder Fließkomma-Typ zugewiesen werden kann. Der Typ eines Literals wird durch spezielle Typbezeichner spezifiziert, die in Klein- oder Großbuchstaben dargestellt werden können.

Standardmäßig hat ein Integer-Literal wie etwa 8 den Wert int. Wenn ein Integer-Literal jedoch zu lang ist, um in einen int zu passen, etwa 3000000000 (3 Milliarden), wird automatisch ein long daraus gemacht. Sie können auch explizit angeben, daß ein Literal ein long sein soll, indem Sie am Ende der Zahl den Typbezeichner l oder L einfügen: 8L ist ein long-Literal. Alle Integer-Typen können als negative Zahlen verwendet werden, indem ihnen ein Minuszeichen vorangestellt wird: -78 ist ein negatives int-Literal.

Integer können auch als oktal oder hexadezimal dargestellt werden. Durch Voranstellen einer 0 wird eine Zahl zu einem oktalen Literal (Basis 8): 0347. Voranstellen von 0x oder 0X gibt an, daß es sich bei der Zahl um ein hexadezimales Literal (Basis 16) handelt: 0x2FA3. Sie können zwar das oktale oder hexadezimale Zahlensystem verwenden, um ein Literal darzustellen, aber im Speicher wird es als einer der Integer-Datentypen abgelegt, entsprechend der Regeln aus dem vorherigen Absatz.

Fließkomma-Literale haben immer den Typ double, egal, welchen Wert sie haben: 5.67 ist ein double-Literal. Sie können ein Literal zu einem float machen, indem Sie am Ende der Zahl ein f oder F einfügen: 5.67f wird zu einem float-Literal. Sie können auch die wissenschaftliche Notation verwenden, um Fließkomma-Literale darzustellen, indem Sie ein e oder E anfügen, gefolgt von dem Exponenten: 5.964e-4 ist die Darstellung von 0.0005964 in wissenschaftlicher Notation, die als double abgelegt wird. Um sie als float abzulegen, fügen Sie f oder F am Ende des Literals an: 5.964e-4F.

Zeichen-Literale

Zeichen-Literale in Java, dargestellt durch Unicode-Zeichen des Datentyps char, werden in einfache Anführungszeichen eingeschlossen ('), etwa 'a', '*' oder '8'. Nicht-druckbare Zeichen wie etwa der Tabulator oder Backspace können ebenfalls als Zeichen-Literale dargestellt werden, indem sogenannte Escape-Codes verwendet werden.


Ein Escape-Code entsteht, indem man dem nicht-druckbaren Zeichen oder dem Sonderzeichen einen Backslash (\) vorausstellt. Dadurch können sie als Zeichen-Literale verwendet werden.


Weil bestimmte Symbole von der Sprache Java verwendet werden, wie etwa einfache Anführungszeichen ('), werden auch diese unter Verwendung des Backslash (\) als Zeichen-Literale dargestellt, wie in Tabelle 2.3 gezeigt.

Tabelle 2.3: Escape-Codes

Escape-Code

Nicht-druckbares Zeichen oder Symbol

\b

Backspace

\f

Seitenvorschub

\n

Neue Zeile

\r

Return

\t

Tab

\\

Backslash

\"

Doppeltes Anführungszeichen

\'

Einfaches Anführungszeichen

\ddd

Oktal (z.B. \347)

\xdd

Hexadezimal (z.B. \x2FA3)

\udddd

Unicode-Zeichen (z.B. \u0C00)

In dieser Darstellung steht das Zeichen d in oktalen Codes für eine Ziffer (0-7), in hexadezimalen und Unicode-Zeichencodes für eine hexadezimale Ziffer (0-9, a-f, A-F).

String-Literale

Ein String ist einfach nur eine Kombination aus Zeichen-Literalen. Er wird in doppelte Anführungszeichen (») eingeschlossen, z.B. "Hello world!". Um einen Null-String darzustellen - d.h. einen String, der keine Zeichen enthält -, geben Sie einfach das doppelte Anführungszeichen zweimal hintereinander an, also »«.

Es folgt ein Code-Ausschnitt, dessen Anweisungen je ein String-Literal enthalten:

System.out.println("Dieser String gibt\nmehrere Zeilen aus.");
System.out.println("\"Ich will allein sein\", sagte sie.");
System.out.println("Die meisten 4.-Klässler sind \11 Jahre alt.");

In der ersten Zeile wird das \n-Literal verwendet, daß nach dem Wort gibt ein Neuezeile-Zeichen ausgegeben werden soll. Im zweiten Beispiel wird mit Hilfe des Literals \" ein doppeltes Anführungszeichen ausgegeben. Die dritte Zeile verendet ein oktales Literal, \11, um die Zahl 9 in der Ausgabe zu erzeugen. Und so sieht die Ausgabe aus:



Dieser String gibt
mehrere Zeilen aus.
"Ich will allein sein", sagte sie.
Die meisten 4.-Klässler sind 9 Jahre alt.


In Java sind Strings Instanzen der Klasse String und echte Objekte, keine Arrays wie in anderen Sprachen. Wenn Sie ein String-Literal verwenden, erzeugen Sie implizit eine Instanz der Klasse String und initialisieren sie mit dem Wert des String-Literals. Keines der anderen Literale macht dies, weil es sich dabei um elementare Wert-Datentypen handelt, und nicht um Objekte wie Strings. Mehr über Strings erfahren Sie später in dieser Lektion.

Ausdrücke und Operatoren

Ausdrücke und Operatoren ermöglichen Ihnen, Auswertungen von Daten vorzunehmen.


Ein Ausdruck ist eine Anweisung oder ein Teil einer Anweisung, deren variable und literale Terme einen bestimmten Wert ergeben, etwa i==8, was true oder false ergibt.


Ein Operator ist ein Symbol, das steuert, wie die Terme eines Ausdrucks ausgewertet oder manipuliert werden. In dem Beispiel i==8 teilt der Operator == dem Compiler mit, die Werte der Variablen i und des Literals 8, der sogenannten Operanden, zu vergleichen.


Sie können sich die Operatoren auch als Mini-Funktionen vorstellen, die Parameter (die Operanden) entgegennehmen und ein Ergebnis zurückgeben. Der Datentyp des Ergebnisses hängt von dem verwendeten Operator ab. Ein logischer Operator beispielsweise kann int-Werte als Operanden entgegennehmen, gibt aber ein Boolesches Ergebnis zurück. Der Operator und seine Operanden sind die kleinste Form eines Ausdrucks.

Jeder Operator hat eine bestimmte Priorität, die angibt, in welcher Reihenfolge er und seine Operanden in einem Ausdruck ausgewertet werden. Es gibt bei der Arbeit mit Operatoren noch mehrere andere Aspekte zu berücksichtigen. Es gibt drei Arten von Operatoren: unäre Operatoren, die einen Operanden entgegennehmen, binäre Operatoren, die zwei Operanden entgegennehmen, und ternäre Operatoren, die drei Operatoren entgegennehmen. Unäre Operatoren können auch ein Präfix oder Postfix darstellen und so ganz andere Ergebnisse bewirken. Binäre und ternäre Operatoren sind Infix-Operatoren.


Ein unärer Präfix-Operator wird vor dem Operanden plaziert:

op b


Ein unärer Postfix-Operator wird nach dem Operanden plaziert:

a op


Ein binärer Infix-Operator wird zwischen den beiden Operanden plaziert:

a op b


Ein ternärer Infix-Operator besteht eigentlich aus einem Paar von Operator-Symbolen, das drei Operanden entgegennimmt. Das erste Symbol des Paars wird zwischen dem ersten und dem zweiten Operanden plaziert, das zweite zwischen dem zweiten und dem dritten Operanden:

a op1 b op2 c

Java kennt zahlreiche Zuweisungsoperatoren, die Sie später in diesem Kapitel noch kennenlernen werden. Hier müssen Sie nur wissen, daß der Zuweisungsoperator (=) den Wert des Ausdrucks auf der rechten Seite der Variablen auf der linken Seite zuweist.


Technischer Hinweis
In Java führt der Zuweisungsoperator = dieselbe Operation aus wie in Pascal/Delphi der Zuweisungsoperator :=.


In den folgenden Abschnitten werden Sie die Aspekte der Auswertung von Ausdrücken und der Verwendung von Operatoren kennenlernen. Machen Sie müssen sich das hier noch nicht merken. Versuchen Sie einfach, sich die jeweiligen Konzepte vorzustellen.

Arithmetische Operationen

Operationen, die numerische Operanden entgegennehmen und numerische Ergebnisse erzeugen, werden als arithmetische Operationen bezeichnet. Die unären Operatoren beinhalten Inkrement, Dekrement, Plus und Minus. Die binären Operatoren umfassen Addition, Subtraktion, Multiplikation, Division und Modulo. Es gibt sogar eine arithmetische binäre Operation für Strings, die sogenannte Konkatenation.

Inkrement und Dekrement

Die Inkrement- (++) und Dekrement-Operatoren (--) sind sowohl unäre Präfix- als auch unäre Postfix-Operatoren. Wenn der Operator vor dem Operanden steht (Präfix), erfolgt die Operation, bevor Ihr Programm den resultierenden Wert weiterverwendet. Wenn der Operator hinter dem Operanden steht (Postfix), verwendet Ihr Programm den Wert und führt dann erst die Operation aus. Bei der Inkrementierung eines Werts wird 1 addiert, bei der Dekrementierung wird 1 subtrahiert. Mit anderen Worten, die Ausdrücken in den beiden:

myAge = myAge + 1;
daysLeftUntilVacation = daysLeftUntilVacation - 1;

stellen dieselben Werte dar wie die beiden folgenden Ausdrücke:

myAge++;
daysLeftUntilVacation--;

Der Wert kann ein Integer- oder Fließkomma-Wert sein oder ein beliebiger Ausdruck, der einen dieser numerischen Typen ergibt.

Betrachten Sie beispielsweise den folgenden Programmausschnitt:

int x, y, z = 7;
x = ++z; // In diesem Beispiel wird x der Wert 8 zugewiesen
y = z; // Der Variablen y wird ebenfalls der Wert 8 zugewiesen

Die Variable z wird zuerst inkrementiert, anschließend wird das Ergebnis, 8, der Variablen x zugewiesen. Weil z jetzt 8 ist, wird auch y dieser neue Wert zugewiesen. Betrachten Sie jetzt den folgenden Programmausschnitt:

int x, y, z = 7;
x = z++; // Hier wird x der Wert 7 zugewiesen
y = z; // Der Variablen y wird der Wert 8 zugewiesen

Der Wert der Variablen z, wird x zugewiesen. Anschließend wird z inkrementiert und enthält jetzt den Wert 8. Weil z gleich 8 ist, erhält y ebenfalls den Wert 8.

Vorzeichen

Um einem numerischen Wert oder einem Ausdruck ein Vorzeichen zu geben, stellen Sie ihm einfach das unäre Plus (+) für positive Zahlen oder das unäre Minus (-) für negative Zahlen voraus. Hier ein Beispiel:

int x, y = +4, z = -7;
x = y; // x wird der Wert 4 zugewiesen
x = -y; // x wird der Wert -4 zugewiesen
x = -z; // x wird der Wert 7 zugewiesen
x = z; // x wird der Wert -7 zugewiesen

Grundlegende Mathematik

Diese Gruppe der binären Infix-Operatoren führt die grundlegenden mathematischen Operationen aus, wie etwa Addition (+), Subtraktion (-), Multiplikation (*), Division (/) und Modulo (%). Die Operationen werden im folgenden gezeigt:

int x, y = 28, z = 8;
x = y + z; // x wird der Wert 36 zugewiesen.
x = y - z; // x wird der Wert 20 zugewiesen.
x = y * z; // x wird der Wert 224 zugewiesen.
x = y / z; // x wird der Wert 3 zugewiesen.
x = y % z; // x wird der Wert 346 zugewiesen.

Der Divisions-Operator (/) verwirft den Rest der Division und gibt nur den Integer-Bestandteil als Ergebnis zurück. Der Modulo-Operator (%) gibt nur den Rest der Division als Ergebnis zurück.

String-Mathematik

Wie kann man Strings addieren? Durch Konkatenation (+) mit dem binären Infix-Operator:

string helloWorld, hello = "Hello", space = " ", world = "World";
helloWorld = hello + space + world + "!"

Das hat denselben Effekt wie:

string helloWorld = "Hello World!"

Dem Konkatenations-Operator (+) können Sie beliebige Strings oder Zeichen-Literale als Operanden übergeben.

Relationale Operationen

Alle relationalen Operatoren geben ein Boolesches Ergebnis zurück. Der Ausdruck, den der Operator und seine Operanden bilden, ist entweder true oder false. Diese Operatoren werden auch als Vergleichsoperatoren bezeichnet. Neben arithmetischen Werten können Sie auch Objekte und Typen vergleichen.


Technischer Hinweis
In Java macht der Vergleichsoperator == (ist gleich) dasselbe wie der Vergleichsoperator = (ist gleich) in Pascal/Delphi.


Die arithmetischen relationalen Operatoren sind Kleiner (<), Kleiner-Gleich (<=), Größer (>), Größer-Gleich (>=), Gleich (==) und Ungleich (!=). Hier einige Beispiele für ihre Arbeitsweise:

int w = -8, x = -9, y = 28, z = 8;
boolean isThatSo;
isThatSo = y < z; // isThatSo erhält den Wert false.
isThatSo = x <= w; // isThatSo erhält den Wert true.
isThatSo = y > z; // isThatSo erhält den Wert true.
isThatSo = x >= w; // isThatSo erhält den Wert false.
isThatSo = y == z; // isThatSo erhält den Wert false.
isThatSo = w != z; // isThatSo erhält den Wert true.

Es gibt auch relationale Operatoren für den Vergleich von Objekten: Typvergleich (instanceof), Bezieht-sich-auf-dasselbe-Objekt (==) und Bezieht-sich-auf-ein-anderes-Objekt (!=). Diese beiden letzten Operatoren sehen aus wie die oben gezeigten Operatoren für den Vergleich auf Gleichheit (==) und Ungleichheit (!=), aber sie nehmen Objekte als Operanden entgegen.

Der Typvergleichs-Operator (instanceof) stellt fest, ob das Objekt im linken Operanden eine Instanz des Typs des rechten Operanden ist (oder die Schnittstelle implementiert). Ist dies der Fall, ist das Ergebnis gleich true, andernfalls oder wenn das Objekt null ist, ist das Ergebnis false.

Der Bezieht-sich-auf-dasselbe-Objekt-Operator (==) stellt fest, ob das Objekt im linken Operanden auf dieselbe Instanz verweist wie das Objekt im rechten Operanden. Wenn das der Fall ist, ist das Ergebnis gleich true, andernfalls ist es false.

Der Bezieht-sich-auf-ein-anderes-Objekt-Operator (!=) stellt fest, ob das Objekt im linken Operanden auf eine andere Instanz als das Objekt im rechten Operanden verweist. Wenn das der Fall ist, ist das Ergebnis gleich true, andernfalls ist es false.

Logische Operationen

Logische Operatoren nehmen Boolesche Operanden entgegen und geben ein Boolesches Ergebnis zurück. Es gibt einen unären logischen Präfix-Operatoren: Logisch NICHT (!). Fast alle anderen sind binäre Infix-Operatoren: logisch UND (&), logisch ODER (|), logisch XODER (^), bedingtes UND (&&) und bedingtes ODER (||). Schließlich gibt es noch einen ternären Infix-Operator: if-else (?:).

Das logische NICHT (!) wechselt einfach den Booleschen Wert, dem es vorangestellt wird. Ist der Operand true, ist das Ergebnis false, ist der Operand false, ist das Ergebnis true, also einfach immer das Gegenteil.

Der logische UND-Operator (&) wertet beide Operanden aus. Sind beide true, ist das Ergebnis true. Ist einer der Operanden false, ist das Ergebnis false. Der logische ODER-Operator (|) stellt fest, ob einer der beiden Operanden gleich true ist, dann ist das Ergebnis true, wenn beide false sind, ist das Ergebnis false. Der logische XODER-Operator (^) stellt fest, ob die Operanden unterschiedlich sind; einer muß true sein, der andere muß false sein, und wenn das so ist, ist das Ergebnis true; sind beide true oder beide false, ist das Ergebnis false. Tabelle 2.4 zeigt diese Ergebnisse.

Tabelle 2.4: Logische Boolesche Operationen und Ergebnisse

Linker

Rechter

UND (&)

ODER (|)


Operand

Operand

UND (&&)

ODER (||)

XODER (^)

true

true

true

true

false

true

false

false

true

true

false

true

false

true

true

false

false

false

false

false

Der bedingte UND-Operator (&&) und der bedingte ODER-Operator (||) sind gleich dem logischen UND (&) und ODER (|), aber die Auswertung erfolgt etwas anders.

Der bedingte UND-Operator (&&) stellt zuerst fest, ob der linke Operand false ist. In diesem Fall ist das Ergebnis false und die Auswertung wird beendet. Wenn der linke Operand true ist, wird auch der rechte Operand ausgewertet. Ist der rechte Operand ebenfalls true, ist das Ergebnis true, ist er false, ist das Ergebnis false.

Der bedingte ODER-Operator (||) stellt zuerst fest, ob der linke Operand true ist. In diesem Fall ist das Ergebnis true und die Auswertung wird beendet. Wenn der linke Operand false ist, wird auch der rechte Operand ausgewertet. Wenn der rechte Operand true ist, ist das Ergebnis true, ist er false, ist das Ergebnis false.

Der ternäre bedingte Infix-Operator (?:) ist eine Abkürzung für die if-else-Anweisung. Wenn der erste Operand gleich true ist, wird der zweite Operand ausgewertet/ausgeführt, andernfalls der dritte Operand:

isInsured ? payClaim() : doNothing();

Wenn in diesem Beispiel isInsured gleich true ist, wird die Methode payClaim() aufgerufen. Ist isInsured false, wird die Methode doNothing() aufgerufen. Mehr über Methodenaufrufe erfahren Sie in der nächsten Lektion.

Bitweise Operationen

Bitweise Operationen manipulieren die Bits im Speicherraum der Variablen. Bitweise Operatoren nehmen Integer-Operanden entgegen und geben ein Integer-Ergebnis zurück. Es gibt einen unären bitweisen Präfix-Operator: bitweise NICHT (ÿ). Alle anderen sind binäre Infix-Operatoren: bitweises UND (&), bitweises ODER (|), bitweises XODER (^), bitweiser Linksshift (<<), vorzeichenbehafteter Rechtsshift (>>) und Rechtsshift mit Auffüllen durch Nullen (>>>).

Der bitweise NICHT-Operator (ÿ) wechselt einfach die Bits des Integer-Werts, dem er vorausgestellt wird. Wenn der Integer-Wert beispielsweise 00101001 ist, würde die Anwendung des NICHT-Operators (ÿ) den Integer-Wert 11010110 ergeben.

In einer Programmanweisung wären die beiden Integer-Werte links und rechts vom bitweisen Operator angesiedelt, des besseren Vergleichs halber ist es aber besser, sie in einem Spaltenformat zu zeigen. Für das bitweise UND (&), das bitweise ODER (|) und das bitweise XODER (^) vergleichen Sie die Bits gleicher Positionen im Integer-Wert. Betrachten Sie beispielsweise die beiden folgenden Zahlen:

00101001
11101110

Das erste Paar ist (0,1), das zweite Paar ist (0,1) und das dritte Paar ist (1,1). Sie müssen die Paare vertikal in den Spalten bilden, so daß Sie jeweils dieselben Positionen für den Vergleich heranziehen. In diesem Beispiel stellt die Zahl 00101001 den linken Operanden, die Zahl 11101110 den rechten Operanden dar. Tabelle 2.5 zeigt die Ergebnisse der möglichen Bit-Paarungen in bitweisen Operationen.

Tabelle 2.5: Bitweise Operationen und ihre Ergebnisse

Betrachtetes Bit im linken Operanden

Betrachtetes Bit im rechten Operanden

UND (&)

ODER (|)

XODER (^)

1

1

1

1

0

1

0

0

1

1

0

1

0

1

1

0

0

0

0

0

Die bitweisen Shift-Operatoren sind bitweiser Linksshift (<<), vorzeichenbehafteter Rechtsshift (>>) und Rechtsshift mit Auffüllen durch Nullen (>>>). Diese Operatoren nehmen als linken Wert einen Integer-Wert entgegen, als rechten Operanden ein numerisches Literal, das angibt, um wie viele Stellen geshiftet werden soll.

Der bitweise Linksshift (>>) verschiebt jede Ziffer im Integer-Wert in dem linken Operanden um so viele Stellen nach links, wie das numerische Literal im rechten Operand angibt. Beim Verschieben »fallen« die Ziffern links heraus und rechts werden Nullen eingefügt, um die neue Zahl zu bilden. Weil das Vorzeichen herausfällt, wenn Sie nach links verschieben, sollten Sie bei der Verwendung dieses Operators vorsichtig sein.

Der vorzeichenbehaftete Rechtsshift (>>) verschiebt jede Ziffer im Integer-Wert im linken Operanden um so viele Stellen nach rechts, wie im rechten Operanden angegeben. In diesem Fall geht das Vorzeichen jedoch nicht verloren. Der Inhalt der ganz linken Position bleibt unverändert und die Zahl wird links mit dem Wert der linken Ziffer aufgefüllt.

Wenn Sie links explizit mit Nullen auffüllen wollen, verwenden Sie den Rechtsshift-Operator mit Auffüllen durch Nullen (>>>). Weil diese Operation immer Nullen einfüllt, behält sie das Vorzeichen nicht bei.

Zuweisungen

Java bietet zahlreiche Zuweisungsoperatoren. Der grundlegende Zuweisungsoperator (=) wird durch mehrere Operatoren übernommen, die Ihnen eine gleichzeitige Operation und Zuweisung ermöglichen. Sie haben die Form op=, wobei op ein Operator aus der folgenden Menge sein kann:

{ *, /, %, +, -, <<, >>, >>>, &, ^, | }

Statt also beispielsweise die folgenden Anweisungen zu verwenden:

totalCharges = totalCharges + newItemPrice;
isInsured = isInsured & hasRenewed;

könnten Sie auch folgendes schreiben:

totalCharges += newItemPrice;
isInsured &= hasRenewed;

Sie nehmen dieselben Operationen vor. Diese Zuweisungsoperatoren können viel Zeit sparen, wenn Sie sehr lange Namen verwenden, und außerdem wird Ihr Code besser lesbar.

Operatorpriorität

Sie haben jetzt alle Operatoren von Java kennengelernt, aber es bleibt noch ein Aspekt zu berücksichtigen. Die Priorität der Operatoren legt fest, in welcher Reihenfolge die Argumente eines Ausdrucks ausgewertet werden. Tabelle 2.6 zeigt die einzelnen Operatoren, aufgelistet ihrer Priorität nach, zusammen mit dem Operandentyp, den sie entgegennehmen, der ausgeführten Operation sowie der Plazierung des Operators im Ausdruck. Betrachten Sie beispielsweise den folgenden Programmausschnitt:

int g, x = 6, y = 7, z = 8;
g = x + y * z;

Hier ist die Multiplikation von y und z zuerst ausgeführt und ergibt 56. Das Ergebnis wird zu x addiert, was 62 ergibt. Dieses Ergebnis schließlich wird g zugewiesen. Aber was sollten Sie tun, wenn Sie x und y zuerst addieren und dann mit z multiplizieren wollen? Mit Hilfe von Klammern können Sie die Auswertungsreihenfolge beeinflussen:

int g, x = 6, y = 7, z = 8;
g = (x + y) * z;

In diesem Fall werden zuerst x und y addiert, ergeben 13, und dieser Wert wird mit z multipliziert, was 104 ergibt. Dieser Wert wird schließlich g zugewiesen. Operatoren derselben Prioritätsstufe werden von links nach rechts ausgewertet, außer verschachtelte unäre und ternäre Operatoren, die von rechts nach links ausgewertet

Tabelle 2.6: Operatorpriorität

Priorität

Operator

Operanden

Operation

Plazierung

1

++

arithmetisch

Inkrement

unär Präfix/Postfix


--

arithmetisch

Dekrement

unär Präfix/Postfix


+

arithmetisch

Plus (positiv)

unär Präfix


-

arithmetisch

Minus (negativ)

unär Präfix


!

Boolean

logisch NICHT

unär Präfix


~

Integer

bitweises NICHT

unär Präfix

2

*

arithmetisch

Multiplikation

binär Infix


/

arithmetisch

Division

binär Infix


%

arithmetisch

Modulo

binär Infix

3

+

String

Konkatenation

binär Infix


+

arithmetisch

Addition

binär Infix


-

arithmetisch

Subtraktion

binär Infix

4

<<

Integer

bitweiser Linksshift

binär Infix


>>

Integer

vorzeichenbehafteter Rechtsshift

binär Infix


>>>

Integer

Rechtsshift mit Auffüllen durch Nullen

binär Infix

5

<

arithmetisch

Kleiner

binär Infix


<=

arithmetisch

Kleiner-gleich

binär Infix


>

arithmetisch

Größer

binär Infix


>=

arithmetisch

Größer-gleich

binär Infix


instanceof

Objekt, Typ

Typvergleich

binär Infix

6

==

arithmetisch

Gleichheit

binär Infix


!=

arithmetisch

Ungleichheit

binär Infix


==

Objekt

bezieht sich auf dasselbe Objekt

binär Infix


!=

Objekt

bezieht sich auf ein anderes Objekt

binär Infix

7

&

Integer

bitweises UND

binär Infix


&

Boolean

logisches UND

binär Infix

8

^

Integer

bitweises XODER

binär Infix


^

Boolean

logisches XODER

binär Infix

9

|

Integer

bitweises ODER

binär Infix


|

Boolean

logisches ODER

binär Infix

10

&&

Boolean

bedingtes UND

binär Infix

11

||

Boolean

bedingtes ODER

binär Infix

12

?:

Boolean, beliebig, beliebig

bedingtes if-else

ternär Infix

13

=

Variable, beliebig

Zuweisung



op=

Variable, beliebig

Zuweisung mit Operation


Neben diesen Operationen gibt es noch zahlreiche mathematische Konstanten und Operationen, die in der Klasse java.math.lang definiert sind. Diese Funktionen können in diesem Buch nicht erklärt werden. Es gibt Konstantenwerte für e und Pi, trigonometrische Funktionen (Sinus, Cosinus, Tangens, Arkussinus, Arkuscosinus, Arkustangens) sowie Funktionen für exponentielles E, den natürlichen Logarithmus, Quadratwurzel, IEEE-Rest, Oberwert, Unterwert, Umwandlung in Polarkoordinaten, Exponenten, Rundung, Zufallszahlen, Absolutwert, Maximum und Minimum.

Arrays und Strings

Arrays sind eines der praktischsten Konstrukte in Java. Sie ermöglichen Ihnen, Objekte oder elementare Typen in einfach zu verwaltenden Strukturen zusammenzufassen. Strings sind ein Sonderfall von Arrays und voll-funktionale Objekte in Java. In diesem Abschnitt lernen Sie, diese Objekte zu erzeugen und zu manipulieren.


In der folgenden Beschreibung wird der Begriff Konstruktoren verwendet. Weitere Informationen über Konstruktoren finden Sie im nächsten Kapitel. Hier sollten Sie einfach nur davon ausgehen, daß Konstruktoren spezielle Methoden sind, die Ihnen ermöglichen, ein Objekt im Speicher zu erzeugen und ihm optional gleichzeitig einen Wert zuzuweisen.

Array-Objekte

In Java werden Arrays als voll-funktionale Objekte implementiert, sie können also auch wie Objekte verglichen oder manipuliert werden. Weil Arrays echte Objekte sind, haben sie Konstruktoren, Methoden und Variablen, die speziell für die Verwendung in Arrays geschaffen wurden.


Ein Array stellt eine Möglichkeit dar, eine Auflistung von Elementen zu speichern. Jeder Eintrag in dem Array enthält einen Eintrag, also ein Element. Sie können die Elemente in den Einträgen plazieren oder ihren Inhalt ändern.


Array-Elemente können Werte beliebigen Typs enthalten (elementare Datentypen oder Objekte), aber innerhalb eines Arrays können nicht mehrere Typen verwendet werden. Das bedeutet, alle Elemente eines Arrays müssen denselben Datentyp haben. Sie können beispielsweise ein Array mit Integern oder ein Array mit String-Objekten haben, aber nicht ein Array, das sowohl Integer- als auch String-Elemente enthält.

Um in Java ein Array zu erzeugen, gehen Sie in drei Schritten vor:

Deklaration von Array-Variablen

Der erste Schritt beim Erzeugen eines Arrays ist, eine Variable zu erzeugen, die das Array aufnimmt. Die Deklaration von Array-Variablen kann eines von zwei gleichermaßen möglichen Formaten annehmen. Beide Formate geben den Namen des Array an, den Typ der Objekte, die das Array aufnimmt, sowie ein leeres Klammernpaar ([]), das anzeigt, daß es sich bei der neuen Variablen um ein Array handelt. Hier einige typische Deklarationen von Array-Variablen:

int[] theTenBestGameScores;
Date games[];
int averageRBI[];

Die erste Deklaration gibt an, daß Sie ein Array des Typs int mit dem Namen theTenBestGameScores deklarieren. Die zweite deklariert ein Array vom Typ Date namens games. Die dritte deklariert ein int-Array namens averageRBI.

Wie Sie sehen, können die Klammern ([]) vor dem Typ oder nach dem Variablennamen angegeben werden. Der Java-Compiler akzeptiert beide Formen und die beiden Deklarationsstile können einfach kombiniert werden. Wenn die Klammern unmittelbar hinter dem Typ angegeben werden, sehen Sie sofort, daß Sie ein Array dieses Typs deklarieren. Der Java-Quellcode verwendet das in den beiden letzten Beispielen gezeigte Format, das Buch folgt deshalb diesem Standard und plaziert die Klammern unmittelbar hinter dem Variablennamen.

Array-Objekte erzeugen

Im zweiten Schritt erzeugen Sie ein Array-Objekt und weisen es dieser Variablen zu. Es gibt zwei Möglichkeiten, das zu realisieren:

Wenn Sie ein Array-Objekt mit new erzeugen, müssen Sie explizit angeben, wie viele Elemente das Array aufnehmen soll:

int games[] = new int[10];

Damit wird ein neues Array mit Integern mit 10 Elementen erzeugt. In diesem Fall wird jedes der 10 Elemente im Integer-Array mit dem Wert 0 initialisiert. Der initialisierte Wert hängt von dem Arraytyp ab, den Sie angelegt haben, wie in Tabelle 2.7 gezeigt.

Tabelle 2.7: Standardwerte für die Array-Initialisierung

Arraytyp

Standardwert für die Initialisierung

boolean

false

char

'\0'

byte, short, int, long float, double

0

Object, String

null

Sie können ein Array auch gleichzeitig erzeugen und initialisieren, wie das auch für andere Variablen möglich ist. Statt new zu verwenden, um das Array-Objekt zu erzeugen, schließen Sie die Elemente des Arrays in geschweifte Klammern ein, getrennt durch Kommata, die die Ausgangswerte darstellen:

float rates[] = { 12.9, 14.5, 16.5, 18.95, 23.0 };

Die Elemente innerhalb der geschweiften Klammern müssen denselben Typ haben wie die Variable, die das Array aufnimmt. Das Array wird mit einer Anzahl von Einträgen erzeugt, die mit der Anzahl der hier übergebenen Elemente übereinstimmt. In diesem Beispiel wird also ein Array mit fünf float-Elementen angelegt und es erhält den Namen rates. Das erste Elemente im Array hat den Wert 12.9, das zweite hat den Wert 14.5 usw.

Ein Versuch, den falschen Datentyp in einem Array abzulegen, verursacht einen Compiler-Fehler. Die folgende Codezeile beispielsweise würde bewirken, daß sich der Compiler über einen Typfehler beschwert:

float rates[] = { 'M', 'i', 'a', 'm', 'i' };

Ein Versuch, zur Laufzeit einen falschen Typ im Array abzulegen, verursacht eine ArrayStoreException.


Eine Ausnahme ist ein Fehler, der zur Laufzeit auftritt. Die Java-Klasse Exception wird in verschiedene Kategorien unterteilt, so daß Sie einen Hinweis darauf erhalten, was den Fehler verursacht hat, nachdem eine Ausnahme aufgeworfen wurde.


Um das Array für die oben gezeigten Werte korrekt zu deklarieren, müßten Sie ein Array vom Typ char anlegen:

char chArr[] = { 'M', 'i', 'a', 'm', 'i' };

Zugriff und Änderung von Array-Elementen

Nachdem Sie ein Array initialisiert haben, können Sie die Werte in den einzelnen Array-Einträgen auswerten und ändern. Um auf einen Wert zuzugreifen, der in einem Array gespeichert ist, verwenden Sie den Index-Ausdruck für das Array.

ArrayName[index];

arrayName ist die Variable, die das Array-Objekt aufnimmt. Der Index ist ein Integer oder ein Integer-Ausdruck, der den Eintrag des Arrays bestimmt, auf den zugegriffen werden soll.


In Java beginnen Indizes mit Null (0).


Betrachten wir das Beispiel noch einmal:

float rates[] = { 12.9, 14.5, 16.5, 18.95, 23.0 };

Hier folgt ein Code-Ausschnitt, der zeigt, wie diese Werte anderen Variablen zugewiesen werden könnten:

float platinumRate == rates[0]; // Der Wert ist 12.9
float goldRate == rates[1]; // Der Wert ist 14.5
float preferredRate == rates[2]; // Der Wert ist 16.5
float regularRate == rates[3]; // Der Wert ist 18.95
float probationRate == rates[4]; // Der Wert ist 23.0

Alle Array-Indizes werden überprüft, um sicherzustellen, daß sie innerhalb der Array-Grenzen liegen. Sie müssen also größer oder gleich Null sein, aber kleiner als die Array-Länge. Diese Prüfung findet beim Kompilieren Ihres Java-Programms oder bei seiner Ausführung statt. In Java ist es nicht möglich, auf einen Wert in einem Array-Eintrag zuzugreifen, der außerhalb der Array-Grenzen liegt. Betrachten Sie die beiden folgenden Anweisungen:

int myArr[] = new int[10];
myArr[10] = 73;

Ein Programm mit dieser Anweisung erzeugt in der zweiten Zeile einen Compiler-Fehler, wenn Sie versuchen, es zu kompilieren. Das Array in myArr hat nur 10 Einträge, numeriert von 0 bis 9. Das Element am Index 10 würde in Eintrag Nummer 11 vorliegen, der nicht existiert, deshalb beschwert sich der Java-Compiler.

Wenn der Array-Index zur Laufzeit berechnet wird (beispielsweise innerhalb einer Schleife) und außerhalb der Array-Grenzen liegt, erzeugt der Java-Interpreter eine ArrayIndexOutOfBoundsException. Wenn Sie zur Laufzeit versuchen, ein Array mit weniger als Null Elementen zu allozieren (indem Sie etwa einen Index-Ausdruck verwenden, der eine negative Zahl ergibt), erhalten Sie eine NegativeArraySizeException.

Um zu vermeiden, daß in Ihren eigenen Programmen versehentlich über das Array-Ende hinaus zugegriffen wird, ermitteln Sie die Anzahl der Array-Elemente, indem Sie seine Instanzvariable length auswerten. Diese Variable ist für alle Array-Objekte definiert, unabhängig von deren Typ:

int len = arr.length; // ergibt 10

Um den Wert eines Array-Elements zu verändern, geben Sie einfach hinter dem Array-Zugriffsausdruck eine Zuweisung an. Hier zwei Beispiele:

myArr[1] = 15;
sentence[0] = "The";

Arrays elementarer Typen wie etwa int oder float können Werte von einem Eintrag in einen anderen kopieren. Ein Array mit Objekten ist jedoch in Java ein Array mit Verweisen auf diese Objekte (irgendwie ähnlich den Zeigern). Wenn Sie einem Array-Eintrag einen Wert hinzufügen, erzeugen Sie damit einen Verweis auf dieses Objekt, so wie für eine einfache Objekt-Variable. Wenn Sie ein Array-Objekt einem anderen zuweisen, wie etwa in der folgenden Codezeile:

sentence[10] = sentence[0];

dann weisen Sie einfach den Verweis noch einmal zu. Sie kopieren nicht den Wert von einem Eintrag in einen anderen. Nachdem diese Codezeile ausgeführt ist, zeigen sentence[10] und sentence[0] auf dieselbe Speicherstelle.

Arrays mit Verweisen auf Objekte sind im Gegensatz zu den Arrays mit den Objekten selbst besonders praktisch, weil sie Ihnen ermöglichen, mehrere Verweise auf dieselben Objekte sowohl innerhalb als auch außerhalb der Arrays zu verwenden. Sie können beispielsweise ein Objekt, das in einem Array enthalten ist, einer Variablen zuweisen und auf dieses Objekt über die Variable oder über seine Array-Position zugreifen.

In java.lang.System gibt es die Methode arraycopy(), die Ihnen ermöglicht, Daten von einem Array in ein anderes zu kopieren. Die Syntax für diese Methode lautet:

arraycopy(srcArr, srcOffset, dstArr, dstOffset, copyLength)

Die Argumente sind wie folgt definiert:

Diese Methode alloziert keinen Speicher, das Ziel-Array muß also bereits existieren.

Mehrdimensionale Arrays

Java unterstützt mehrdimensionale Arrays. Man kann sich ein mehrdimensionales Arrays ganz einfach als Array mit Arrays vorstellen. In Java deklarieren Sie ein Array mit Arrays (und diese Arrays können wiederum Arrays enthalten usw.) und greifen auf ihre Elemente zu, indem Sie für jede Dimension einen Index spezifizieren. Hier folgt ein Beispiel für ein zweidimensionales Array mit Koordinaten:

int coords[][] = new int[12][12];
coords[0][0] = 1;
coords[0][1] = 2;

Mehrdimensionale Arrays können beliebig viele Dimensionen haben, für die Sie jeweils nur ein neues Klammernpaar ([]) einfügen müssen. Hier folgt ein Beispiel für eine sechsdimensionale Arraydeklaration:

int sixDimArr[][][][][][] = new int [2][4][8][3][][];

In diesem Beispiel wird den ersten vier Dimensionen explizit eine Größe zugewiesen, den beiden letzten dagegen nicht. Der Speicher wird für die ersten vier Dimensionen alloziert. Die beiden letzten Dimensionen werden erst später bei der Initialisierung alloziert. Sie können beliebig viele Dimensionen mit explizit definierter Größe angeben, gefolgt von beliebig vielen Dimensionen ohne Größenangabe, aber in eben dieser Reihenfolge. Das folgende ist in Java nicht erlaubt:

int threeDimArr[][][] = new int [3][][3]; // invalid

Außerdem müssen die einzelnen Teil-Arrays eines mehrdimensionalen Arrays nicht dieselbe Größe haben. Betrachten Sie das folgende Beispiel:

byte threeDimByteArr[][][] = new byte [2][4][3];

Dieses Array enthält acht Teil-Arrays, die jeweils drei byte-Elemente enthalten. Das Array threeDimByteArr enthält zwei Elemente, die Teil-Arrays sind. Diese beiden Teil-Arrays haben je vier Elemente, bei denen es sich ebenfalls um Teil-Arrays handelt. Diese vier Teil-Arrays haben je drei byte-Elemente. Sie können mehrdimensionale Arrays auch durch die Vorgabe literaler Werte deklarieren, etwa wie folgt:

String encryptStrArr[][] = new String {
{"ab", "cd", "ef", "gh", "ij"},
{"kl", "mn"}
{"op", "qr", "st", "uv"}
{"wx", "yz"}
};

Dieses Beispiel erzeugt ein mehrdimensionale String-Array, das vier Teil-Arrays mit 5, 2, 4 und 2 String-Elementen alloziert.

String- und StringBuffer-Objekte

In diesem Abschnitt werden Sie erfahren, wie man Strings deklariert und erzeugt, wie man auf String-Elemente zugreift und wie die Methoden zur String-Manipulation eingesetzt werden. Darüber hinaus werden Sie die Klasse StringBuffer kennenlernen.

Die Klasse String repräsentiert konstante Strings. Die String-Klasse stellt einige grundlegende Methoden zur Manipulation bereit, aber das Ergebnis muß immer einem zweiten String-Objekt zugeordnet werden. Jede Änderung in einem String-Wert macht eine Zuweisung an einen StringBuffer erforderlich. Die Klasse StringBuffer ermöglicht Ihnen, Strings direkt zu manipulieren und das Ergebnis der StringBuffer-Variablen zuzuweisen. Dazu muß in der Regel mehr Speicher alloziert werden, deshalb sollten Sie überall wo es möglich ist, String-Objekte verwenden.

Deklaration von String- und StringBuffer-Objekten

Die Deklaration einer String- oder StringBuffer-Variablen ist ganz einfach:

String myString;
StringBuffer myStringBuff;

Die Deklaration der Variablen setzt einfach nur den Namen. Für das StringBuffer- oder String-Objekt wird kein Speicher alloziert. Dazu müssen Sie es unter Verwendung eines String-Literals oder eines Konstruktoraufrufs initialisieren.

String-Objekte erzeugen

Weil Strings so häufig verwendet werden, definiert die Klasse String in Java mehrere Abkürzungen, damit Strings ohne den expliziten Aufruf eines Konstruktors aufgerufen werden können. Die gebräuchlichste Methode zur Allozierung eines String-Objekts ist, der deklarierten Variablen ein String-Literal zuzuweisen:

myString = "Wir sehn uns am Samstag.";

Es werden so viele Zeichen alloziert, wie im Literal enthalten sind. Eine andere Möglichkeit, einen neuen String zu erzeugen, ist die Verwendung einer der valueOf()-Methoden, um einen Wert eines anderen Typs, etwa einen Integer, in sein Textäquivalent zu konvertieren:

myString = String.valueOf(17);

Damit wird ein String erzeugt, der zwei Zeichen enthält, 1 und 7. Die Methode valueOf() kann elementare Typen (boolean, char, int, long, float, double), den Object-Typ sowie char-Arrays konvertieren. Für char-Arrays stellt die Methode valueOf() zwei Methodensignaturen bereit: eine, die das gesamte Array als Parameter entgegennimmt, und eine zweite, die ein char-Array, einen Offset sowie einen Zähler als Parameter entgegennimmt. Hier ein Beispiel für die zweite Methode:

char chArr[] = { 's', 'p', 'o', 'r', 't', 's' };
String chString = String.valueOf(chArr, 1, 4);

Dadurch wird ein String namens chString erzeugt, der die in chArr definierten Zeichen enthält, beginnend an Position 1 (dem Offset) und über 4 Zeichen gehend (dem Zähler). Das resultiert schließlich in dem Wert port.

Beachten Sie, daß diese Techniken zum Erzeugen von String-Objekten keine explizite Verwendung eines Konstruktors erforderlich machen. Darüber hinaus definiert die String-Klasse mehrere Konstruktoren, aus denen Sie auswählen können. Hier einige der gebräuchlicheren Beispiele:

Hier sehen Sie den Standard-Konstruktor, der keine Parameter entgegennimmt:

String str1 = new String();

Er erzeugt ein String-Objekt mit dem Namen strl1 und der Länge length gleich 0.

Dieser Konstruktor nimmt ein String-Literal als Parameter entgegen:

String str2 = new String("Guten Tag");

Er erzeugt das String-Objekt str2 mit dem Wert Guten Tag und einer Länge length von 9.

Der nächste nimmt ein chArr als Parameter entgegen:

char chArr[] = { 'S', 'a', 'm', 's' };
String str3 = new String(chArr);

Er erzeugt das String-Objekt str3 mit dem Wert Sams und einer Länge length von 4. (Diese Methode ist im Ergebnis identisch mit der valueOf()-Methode, die ein char-Array als Parameter entgegennimmt.)

Der nächste nimmt ein char-Array, einen Offset und einen Zähler als Parameter entgegen:

char chArr[] = { 'I', 'n', 't', 'e', 'r', 'n', 'e', 't' };
String str4 = new String(chArr, 5, 3);

Er erzeugt das String-Objekt str4 mit dem Wert net und einer Länge length von 3. Dieses Ergebnis wird beginnend an Offset 5 in chArray und über einen Zähler von 3 Zeichen ermittelt. (Diese Methode ist im Ergebnis identisch mit der valueOf()-Methode, die ein char-Array, einen Offset und einen Zähler als Parameter entgegennimmt.)

Der hier folgende letzte Konstruktor nimmt einen StringBuffer als Parameter entgegen:

String str5 = new(myStringBuff);

Er erzeugt das String-Objekt str5 mit dem Inhalt von myStringBuff.

Zugriff auf String-Elemente

Der Zugriff auf die Elemente eines Strings erfolgt auf dieselbe Weise wie der Zugriff auf Array-Elemente, und Sie können Integer oder Integer-Ausdrücke als Indizes verwenden, die auf die einzelnen Elemente des String-Arrays verweisen. Wenn ein String-Index zur Laufzeit außerhalb der Grenzen des Arrays liegt, wirft der Java-Interpreter eine StringIndexOutOfBoundsException auf.

Hier folgt ein Beispiel für eine for-Schleife, die alle Werte eines Strings mit dem Zeichen A initialisiert, indem sie auf die einzelnen Array-Elemente zugreift:

String aString[] = new String[3];
for (int i = 0; i < aString.length; i++)
aString[i] = 'A';

Neben dem Zugriff auf die Zeichen als Array-Elemente definiert die String-Klasse mehrere Methoden, die den Zugriff auf die Werte in Strings vereinfachen sollen. Die Klasse AccessString in Listing 2.1 zeigt einige dieser Methoden.

Listing 2.1: AccessString.java

1: class AccessString {
2: public static void main(String args[]) {
3: String str = "Time is too slow for those who wait.";
4: System.out.println();
5:
6: System.out.println("The string is: " + str);
7:
8: System.out.println("Length of string: "
9: + str.length());
10:
11: System.out.println("Character at position 17: "
12: + str.charAt(17));
13:
14: System.out.println("String begins with \"those\": "
15: + str.startsWith("those"));
16:
17: System.out.println("String ends with \"wait.\": "
18: + str.endsWith("wait."));
19:
20: System.out.println("Index of the first \"w\" character: "
21: + str.indexOf('w'));
22:
23: System.out.println("Index of the last \"w\" character: "
24: + str.lastIndexOf('w'));
25:
26: System.out.println("Substring from 0 to 2: "
27: + str.substring(0, 3));
28:
29: }
30: }

Nachdem Sie dieses Programm kompiliert und ausgeführt haben, sehen Sie die folgende Ausgabe im Ausführungsprotokoll-Fenster:



The string is: Time is too slow for those who wait.
Length of string: 36
Character at position 17: f
String begins with "those": false
String ends with "wait.": true
Index of the first "w" character: 15
Index of the last "w" character: 31
Substring from 0 to 2: Tim


In diesem Beispiel erzeugen Sie eine Instanz von String und geben dann die von den in der String-Klasse definierten Methoden ermittelten Werte aus:


Die Methode substring() bedarf einer genaueren Erklärung. Das Zeichen an der Anfangs-Indexposition ist im Teilstring enthalten, das Zeichen an der Ende-Indexposition nicht. Aus diesem Grund kann die Methode substring() eine zweite Form annehmen, die Ihnen ermöglicht, den Teilstring von einer bestimmten Indexposition bis zum Ende des Strings zu ermitteln. Um beispielsweise die beiden letzten Zeichen des Strings mit dem Wert Hello zu ermitteln, könnten Sie diese zweite Form einsetzen:

String str = "Hello";
String loStr = str.substring(3)); // die richtige Methode

Wenn Sie versuchen, die erste Form zu verwenden, um diese Aufgabe auszuführen, müßten Sie einen Anfang inklusive Index 3, aber ein Ende exklusive Index 5 angeben. Das wirft aber eine Ausnahme auf, weil die gültigen Indizes für str nur von 0 bis 4 reichen.

Es gibt noch andere Methoden, die mehrere Formen besitzen (wie etwa indexOf() oder lastIndexOf()), aber für dieses Beispiel wurde für jede Methode nur eine Form gezeigt. Detailliertere Informationen entnehmen Sie der Online-Dokumentation für das String-Objekt.

Strings vergleichen und manipulieren

Die String-Klasse bietet Methoden für den Vergleich und die Manipulation von Strings. Wenn Objekte verglichen werden, wird ein Boolescher Wert zurückgegeben, der angibt, ob die beiden Objekt-Variablen auf dasselbe Objekt im Speicher verweisen. Wenn Sie zwei String-Literale mit demselben Wert definieren, verweisen diese auf dieselbe Speicherstelle.


Java optimiert String-Literale. Das bedeutet, wenn Sie einen String-Wert speichern und dann versuchen, dasselbe Literal einem anderen String zuzuweisen, erkennt der Java-Compiler, daß das Literal bereits gespeichert ist, und gibt zuvorkommenderweise das existierende Objekt zurück, statt ein neues zu erzeugen. Sie müssen also explizit new verwenden, wenn Sie zwei verschiedene String-Objektinstanzen mit demselben Stringwert erzeugen wollen.

Angenommen, Sie haben new verwendet, um zwei separate Strings zu erzeugen, wie können Sie diese dann vergleichen? Die Klasse String definiert drei Vergleichsmethoden für diesen Zweck. Die erste Methode, equals(), berücksichtigt die Groß-/Kleinschreibung, vergleicht die entsprechenden Zeichen aus beiden Strings und ergibt true, wenn sie identische Werte aufweisen. Darüber hinaus definiert die Klasse String auch noch eine Version, die die Groß-/Kleinschreibung nicht berücksichtigt, equalsIgnoreCase(), die die entsprechenden Zeichen ohne Rücksicht darauf vergleicht, ob es sich um Groß- oder Kleinbuchstaben handelt.

Eine dritte Vergleichsmethode, compareTo(), vergleicht zwei Strings und gibt einen Integerwert zurück, der den numerischen Unterschied zwischen beiden zurückgibt:

Die Integer-Unicode-Werte der jeweiligen Zeichenpaare werden verglichen, bis eines gefunden wird, dessen Werte nicht übereinstimmen (die weiteren Zeichen werden ignoriert). In diesem Beispiel stimmt das erste Zeichenpaar, J und C, nicht überein, und weil die Methode von firstStr aufgerufen wird, subtrahiert die compareTo()-Methode den Integerwert für C (99) von dem für J (106). Das Ergebnis ist 7, ein positiver Integer, der anzeigt, daß JBuilder größer als C++Builder ist (wenigstes hinsichtlich des Strings). Ein negativer Integer weist darauf hin, daß der aufrufende String kleiner als der übergebene String ist. Null zeigt an, daß die beiden Strings gleich sind.

Strings, von denen Sie wissen, daß sie irgendwann manipuliert werden, sollten zwar in StringBuffer-Objekten untergebracht werden, aber die Klasse String definiert auch ein paar Techniken zum Ändern von String-Objekt-Werten. Sie können das Ergebnis jedoch nicht dem ursprünglichen String zuweisen, sondern Sie müssen einen neuen String definieren, der das Ergebnis dieser Methoden aufnimmt. Unter anderem handelt es sich um concat(), replace(), trim(), toLowerCase() und toUpperCase().

Die Klasse ChangeString in Listing 2.2 zeigt die Verwendung einiger dieser Methoden.

Listing 2.2: ChangeString.java

1: class ChangeString {
2: public static void main(String args[]) {
3: System.out.println();
4:
5: String aStr = " Geed ";
6: String bStr = "Merning!";
7: String cStr = aStr.concat(bStr);
8: System.out.println(cStr);
9:
10: String dStr = cStr.replace('e', 'o');
11: System.out.println(dStr);
12:
13: String eStr = dStr.toLowerCase();
14: System.out.println(eStr);
15:
16: String fStr = eStr.toUpperCase();
17: System.out.println(fStr);
18:
19: String gStr = fStr.trim();
20: System.out.println(gStr);
21:
22: }
23: }

Nachdem Sie das Programm kompiliert und ausgeführt haben, sollten Sie die folgende Ausgabe erhalten:



Geed Merning!
Good Morning!
good morning!
GOOD MORNING1
GOOD MORNING!


In diesem Beispiel verwenden Sie die verschiedenen Manipulationsmethoden, die in der Klasse String definiert sind:


Geed Merning!


Good Morning!


good morning!


GOOD MORNING!


GOOD MORNING!

Beachten Sie, daß Java für die Stringkonkatenation den Konkatenations-Operator (+) bereitstellt.


Wie Sie sehen, sind für die Manipulation eines Stringwerts viele Speicherzuweisungen erforderlich, weil Sie dem aufrufenden String das Ergebnis nicht zuweisen können. Wenn Sie für einen String so viele Manipulationen vornehmen wollen, dann sollten Sie besser einen StringBuffer verwenden. Mehr darüber erfahren Sie später in diesem Kapitel.

In java.lang.String sind noch weitere Methoden definiert, die hier nicht beschrieben wurden, unter anderem copyValueOf(), getBytes(), hashCode(), intern() und regionMatches(). Detailliertere Informationen entnehmen Sie bitte der Online-Java-Referenz des JBuilder.

StringBuffer-Objekte erzeugen

Für ein StringBuffer-Objekt gibt es neben der Länge length, die angibt, wie viele Zeichen es enthält, auch die Methode capacity, die angibt, für wie viele Zeichen der Puffer alloziert wurde. Um ein StringBuffer-Objekt zu erzeugen, verwenden Sie einen der drei Klassen-Konstruktoren, z.B.:

StringBuffer myStrBuff = new StringBuffer(myString);

Dieser Konstruktor nimmt einen String-Parameter entgegen. Er erzeugt den StringBuffer myStrBuff, der den Inhalt eines Strings namens myString beinhaltet, dessen Länge gleich dem Wert length von myString gesetzt wird. capacity wird dynamisch alloziert. Für den String-Parameter können Sie auch ein String-Literal verwenden.

Dieser Konstruktor nimmt keine Parameter entgegen und ist der Standard:

StringBuffer myStringBuff = new StringBuffer();

Er erzeugt den StringBuffer myStringBuff mit einer length von Null. Auch hier wird capacity dynamisch alloziert.

Der folgende Konstruktor nimmt einen Integer-Parameter entgegen, der length und capacity des StringBuffer darstellt:

StringBuffer aBuff = new StringBuffer(25);

Dieses Beispiel erzeugt den StringBuffer aBuff mit einer zunächst statisch allozierten capacity von 25, initialisiert mit Leerzeichen und einer length von 0.

Zugriff auf StringBuffer-Elemente

Der Zugriff auf einzelne Elemente von StringBuffer-Objekten kann unter Verwendung von Indizes erfolgen, so wie in allen anderen Arrays. Weil StringBuffer jedoch wirklich Objekte sind, definiert die Klasse StringBuffer eigene Methoden, die die Anzahl der Zeichen im Puffer, die Anzahl der Zeichen, die der Puffer aufnehmen kann, sowie das Zeichen an einer bestimmten Position zurückgeben können. Die Klasse AccessBuffer aus Listing 2.3 demonstriert diese Methoden.

Listing 2.3: AccessBuffer.java

1: class AccessBuffer {
2: public static void main(String args[]) {
3:
4: System.out.println();
5: StringBuffer aBuff = new StringBuffer("Time Flies!");
6: System.out.println("The contents of aBuff: " + aBuff);
7: System.out.println("Capacity: " + aBuff.capacity());
8: System.out.println("Length: " + aBuff.length());
9: System.out.println("Character at position 7: "
10: + aBuff.charAt(7));
11:
12: System.out.println();
13: StringBuffer bBuff = new StringBuffer();
14: System.out.println("The contents of bBuff: " + bBuff);
15: System.out.println("Capacity: " + bBuff.capacity());
16: System.out.println("Length: " + bBuff.length());
17:
18: System.out.println();
19: StringBuffer cBuff = new StringBuffer(25);
20: System.out.println("The contents of cBuff: " + cBuff);
21: System.out.println("Capacity: " + cBuff.capacity());
22: System.out.println("Length: " + cBuff.length());
23: }
24: }

Wenn Sie das Programm kompilieren und ausführen, sollten Sie die folgende Ausgabe erhalten:

The contents of aBuff: Time flies!
Capacity: 27
Length: 11
Character at position 7: i

The contents of bBuff:
Capacity: 16
Length: 0

The contents of cBuff:
Capacity: 25
Length: 0


In diesem Beispiel erzeugen Sie mehrere Instanzen von StringBuffer und geben dann die von den in der Klasse StringBuffer definierten Methoden ermittelten Werte zurück:


Sie werden bemerken, daß capacity beim dynamischen Allozieren manchmal die doppelte Länge von length des tatsächlichen Inhalts im Puffer zugewiesen wird. Dieser zusätzliche Overhead ist für StringBuffer erforderlich, damit sie ihren Inhalt manipulieren können. Deshalb sollten Sie String-Objekte verwenden, wenn Sie nicht unbedingt einen StringBuffer benötigen. Als nächstes erfahren Sie, wie die Manipulationsmethoden von StringBuffer verwendet werden.

Manipulation von StringBuffern

Die Klasse StringBuffer stellt mehrere Methoden zur Manipulation des Pufferinhalts zur Verfügung. Unter anderem handelt es sich dabei um setLength(), setCharAt(), append(), insert(), reverse() und toString(). Im Gegensatz zu String-Objekten, wo das Ergebnis einer String-Manipulation einer zweiten String-Variablen zugewiesen werden muß, kann ein StringBuffer-Objekt das Ergebnis der Methode direkt dem aufrufenden StringBuffer zuweisen. Die Klasse ChangeBuffer in Listing 2.4 zeigt diese Methoden zur Manipulation von Puffern, die in der Klasse StringBuffer definiert sind.

Listing 2.4: ChangeBuffer.java

1: class ChangeBuffer {
2: public static void main(String args[]) {
3: System.out.println();
4:
5: StringBuffer aBuff = new StringBuffer("Time plies!");
6: System.out.println("aBuff contents: " + aBuff);
7: System.out.println("Capacity, length: "
8: + aBuff.capacity() + ", "
9: + aBuff.length());
10: aBuff.setLength(10);
11: System.out.println("aBuff contents: " + aBuff);
12: System.out.println("Capacity, length: "
13: + aBuff.capacity() + ", "
14: + aBuff.length());
15: aBuff.setCharAt(5, 'f');
16: System.out.println("aBuff contents: " + aBuff);
17: System.out.println("Capacity, length: "
18: + aBuff.capacity() + ", "
19: + aBuff.length());
20: aBuff.append(" having fun!");
21: System.out.println("aBuff contents: " + aBuff);
22: System.out.println("Capacity, length: "
23: + aBuff.capacity() + ", "
24: + aBuff.length());
25: aBuff.insert(11, "when you're ");
26: System.out.println("aBuff contents: " + aBuff);
27: System.out.println("Capacity, length: "
28: + aBuff.capacity() + ", "
29: + aBuff.length());
30: String aStr = aBuff.toString();
31: System.out.println("The string is: " + aStr);
32: System.out.println("Length: " + aStr.length());
33:
34: StringBuffer bBuff = new StringBuffer("Bob//");
35: System.out.println("Original bBuff contents: " + bBuff);
36: bBuff.reverse();
37: System.out.println("Reversed bBuff contents: " + bBuff);
38: }
39: }

Nachdem Sie das Programm kompiliert und ausgeführt haben, sollten Sie die folgende Ausgabe sehen:



aBuff contents: Time plies!
Capacity, length: 27, 11
aBuff contents: Time plies
Capacity, length: 27, 10
aBuff contents: Time flies
Capacity, length: 27, 10
aBuff contents: Time flies having fun!
Capacity, length: 27, 22
aBuff contents: Time flies when you're having fun!
Capacity, length: 56, 34
The string is: Time flies when you're having fun!
Length: 34
Original contents of bBuff: Bob//
Reversed contents of bBuff: //boB


In diesem Beispiel verwenden Sie einige der Methoden, die in StringBuffer zur String-Manipulation definiert sind:


Wie in der Analyse bereits aufgezeigt, sind die Methoden append() und insert() überladen. Das bedeutet, sie haben mehrere Methodensignaturen. Die Methode append() kann elementare Datenwerte (boolean, char, double, float, int und long), Objekt-Werte (wie Object und String) und char-Array-Werte entgegennehmen. Es gibt zwei char-Array-Signaturen. Die erste nimmt das char-Array als Parameter entgegen, das zweite ein char-Array, einen Offset und einen Zähler. Die insert()-Methode nimmt einen Integer-Offset als ersten Parameter entgegen und für seinen zweiten Parameter kann sie dieselben elementaren oder Objekt-Werte entgegennehmen wie die append()-Methode.

Hier wurden nicht alle in java.lang.StringBuffer definierten Methoden gezeigt, beispielsweise ensureCapacity() und getChars(). Weitere Informationen über diese Methoden entnehmen Sie der Online-Java-Referenz von JBuilder.

Bedingungen und Schleifen

Sie könnten mit dem, was Sie bisher gelernt haben, schon Java-Programme schreiben, die jedoch nur wenig sinnvoll wären. Programme sowohl in Java als auch in anderen Programmiersprachen werden erst durch Steuerkonstrukte (Schleifen und Bedingungen) für den praktischen Einsatz geeignet. Diese Konstrukte sorgen dafür, daß basierend auf logischen Tests unterschiedliche Programmteile ausgeführt werden.

if-else-Bedingungen

Die if-else-Bedingung, die Ihnen ermöglicht, basierend auf einer einfachen Überprüfung verschiedene Codezeilen auszuführen, ist fast identisch mit den if-else-Anweisungen anderer Sprachen. Und hier die Syntax:

if bedingung
anweisung(en);
else anweisung(en);

Dem Schlüsselwort if folgt die bedingung, das ist eine Boolesche Auswertung. Der bedingung folgen unmittelbar anweisung(en) (entweder eine einzelne Anweisung oder ein Anweisungsblock), die ausgeführt werden, wenn die bedingung true ergibt. Das optionale Schlüsselwort else stellt die anweisung(en) zur Verfügung, die ausgeführt werden, wenn die bedingung false ist:

if (x < y)
System.out.println("x ist kleiner als y");
else System.out.println("x ist größer oder gleich y");


Es gibt drei häufige Syntaxfehler, die Neulinge in der Java-Programmierung bei der Verwendung von if-else-Anweisungen machen:

Hier folgt ein Beispiel für eine if-else-Bedingung, die einen Block im else-Teil der Anweisung verwendet:

if (engineState == true)
System.out.println("Motor läuft bereits.");
else {
System.out.println("Versuchen Sie, den Motor zu starten.");
if (gasLevel >= 1)
engineState = true;
else System.out.println("Kein Benzin - der Motor kann nicht gestartet werden!");
}

Dieses Beispiel verwendet in der ersten if-Anweisung die Prüfung (engineState == true), was eigentlich redundant und damit ein unnötiger Vergleich ist. Weil es sich bei engineState um eine Boolesche Variable handelt, können Sie einfach den Wert der Variablen verwenden, statt diesen mit true zu vergleichen:

if (engineState)
System.out.println("Motor läuft.");
else System.out.println("Motor steht.");

Wenn Sie if-else-Anweisungen verschachteln, müssen Sie manchmal unterscheiden, zu welchem if das else gehört. Hier ein Beispiel:

if (bedingung1)
if (bedingung2)
anweisung;
else anweisung;

In diesem Beispiel sieht der Leser anhand der Einrückung, daß das else zur ersten if-Anweisung gehört und ausgeführt werden sollte, wenn bedingung1 gleich false ist. Leider ist der Java-Compiler nicht so scharfsinnig. Er nimmt an, daß das else zu der if-Anweisung gehört, die ihm unmittelbar vorausgeht. Im obigen Beispiel würde er also annehmen, daß das else zum zweiten if gehört und ausgeführt wird, wenn bedingung2 false ergibt. Und so sorgen Sie dafür, daß Java die richtige Zuordnung trifft:

if (bedingung1) {
if (bedingung2)
anweisung;
}
else anweisung;

Auch wenn die zweite if-Anweisung keinen Block darstellt, können Sie die geschweiften Klammern ({}) verwenden, um das ganze zu erzwingen und seinen Gültigkeitsbereich so anzugeben, als handelte es sich um einen Block. Das verhindert, daß das else sich dem zweiten if zuordnet, weil durch den Gültigkeitsbereich dieser neue Block unsichtbar für Anweisungen außerhalb des Blocks wird.

Der Bedingungsoperator ?:

Eine Alternative zu der if-else-Anweisung in einer bedingten Anweisung ist der Bedingungsoperator, ein ternärer Operator.

Der Bedingungsoperator ist ein Ausdruck, d.h. er gibt einen Wert zurück (anders als die if-else-Anweisung, die einfach nur die Ausführung steuert). Der Bedingungsoperator ist vor allem für sehr kurze oder einfache Bedingungen sinnvoll.

Die Syntax für den Bedingungsoperator lautet:

bedingung ? true-ergebnis : false-ergebnis;

Bei der bedingung handelt es sich um einen Boolesche Ausdruck, der true oder false zurückgibt, ähnlich der bedingung in der if-else-Anweisung. Wenn die bedingung gleich true ist, gibt der Bedingungsoperator-Ausdruck den Wert von true-ergebnis zurück, ist sie gleich false, gibt er den Wert von false-ergebnis zurück.


true-ergebnis und false-ergebnis müssen Ausdrücke sein, die einen einzelnen Wert zurückgeben. Mit anderen Worten, Sie können keine Zuweisungen oder Block-Anweisungen für diese Operanden angeben.

Hier folgt ein Beispiel für eine Bedingung, die die Werte von x und y überprüft, den kleineren von beiden zurückgibt und diesen Wert der Variablen smaller zuweist:

int smaller = x < y ? x : y;

Der Bedingungsoperator hat eine sehr geringe Priorität. Die einzigen Operatoren, die eine noch geringere Priorität haben, sind die Zuweisungsoperatoren. Der Bedingungsoperator wird deshalb im allgemeinen erst ausgewertet, nachdem alle seine Unter-Ausdrücke ausgewertet sind. Weitere Informationen über die Prioritäten von Operatoren finden Sie in Tabelle 2.6.

Hier folgt die Auswertungsreihenfolge des obigen Beispiels:

Und hier derselbe Vergleich unter Verwendung der if-else-Anweisung:

int smaller;
if x < y
smaller = x;
else smaller = y;

Wie Sie aus diesen Beispielen sehen, können Sie unter Verwendung des Bedingungsoperators mit einer einzigen Codezeile sehr viel bewerkstelligen. Sie können jedoch keine Zuweisungen oder Block-Anweisungen als Operanden verwenden. Wenn Sie diese komplexeren Anweisungen brauchen, müssen Sie die if-else-Anweisung verwenden.

switch-Bedingungen

In allen Programmiersprachen ist es üblich, eine Variable mit einem Wert aus einer Wertemenge zu vergleichen und basierend auf dem Ergebnis unterschiedliche Aktionen auszuführen. Wenn Sie nur if-else-Anweisungen einsetzen, wird das sehr schnell unübersichtlich, abhängig von der Formatierung und dem Umfang der Wertemenge, für die die Vergleiche ausgeführt werden. Sie könnten beispielsweise eine if-else-Anweisung wie die folgende ausführen:

if (oper == '+')
addargs(arg1, arg2);
else if (oper == '-')
subargs(arg1, arg2);
else if (oper == '*')
multargs(arg1, arg2);
else if (oper == '/')
divargs(arg1, arg2);

Diese Form einer if-else-Anweisung wird auch als verschachteltes if-else bezeichnet, weil jede Anweisung wiederum ein weiteres if-else enthält usw. In diesem Beispiel werden vier Möglichkeiten abgetestet, deshalb ist der Code noch relativ übersichtlich. Weil diese Situation jedoch so häufig auftritt, gibt es einen speziellen Bedingungsausdruck, der sie verarbeitet: switch, manchmal auch als case-Anweisung bezeichnet.

Hier die Syntax für die switch-Bedingung:

switch (ausdruck) {
case konstante_1;
anweisung(en);
break;
case konstante_2;
anweisung(en);
break;
...
default:
anweisung(en);
}

Die switch-Struktur besteht aus mehreren case-Werten (konstante_1, konstante_2 usw.) und einer optionalen default-Anweisung. Der ausdruck (der einen elementaren Typ wie byte, char, short oder int zurückgeben muß), wird mit jedem der case-Werte verglichen. Wenn eine Übereinstimmung festgestellt wird, werden die anweisung(en) für den entsprechenden case-Wert ausgeführt, bis eine break-Anweisung erkannt wird oder das Ende der switch-Anweisung erreicht ist. Nachdem Sie alle case-Werte überprüft haben und keine Übereinstimmung festgestellt wurde, wird die default-Anweisung ausgeführt. Die Angabe der default-Anweisung ist optional, wenn sie also nicht verwendet wird und es keine Übereinstimmung mit einem der case-Werte gibt, wird die switch-Anweisung beendet, ohne irgendeine Aktion vorgenommen zu haben.


Weil die default-Anweisung optional ist, kann sie auch irrtümlicherweise weggelassen werden. Wenn Sie nicht wollen, daß irgendeine Aktion ausgeführt wird, falls es keine Übereinstimmung mit einem der case-Werte gibt, sollten Sie einen Kommentar für default eingeben, etwa wie folgt:

default: /* nichts tun */

Wenn nun jemand anderer den Code nach Wochen (oder Monaten) betrachtet, erkennt er sofort, daß es Absicht war, für die default-Anweisung keine Aktion auszuführen.


Und hier die verschachtelte if-else-Anweisung von vorhin, dargestellt als switch-Anweisung:

switch (oper)
case '+';
addargs(arg1, arg2);
break;
case '-';
subargs(arg1, arg2);
break;
case '*';
multargs(arg1, arg2);
break;
case '/';
divargs(arg1, arg2);
break;
default: /* nichts tun */ ;
}


Innerhalb von switch-Anweisungen müssen Anweisungen nicht mit geschweiften Klammern ({}) als Blöcke gruppiert werden. Alle Anweisungen zwischen einer case-Anweisung und ihrer break-Anweisung werden automatisch als impliziter Block behandelt, so daß ein lokaler Gültigkeitsbereich entsteht.

Beachten Sie die break-Anweisung am Ende der einzelnen case-Fälle. Ohne das explizite break werden, nachdem eine Übereinstimmung gefunden ist, die Anweisungen für diese Übereinstimmung und alle weiteren Anweisungen in dem switch-Block ausgeführt, bis entweder ein break oder das Ende des Blocks erkannt wird (je nachdem, was zuerst auftritt). Normalerweise ist das nicht das gewünschte Verhalten, Sie sollten also sicherstellen, das break anzugeben, um einzugrenzen, welche Anweisungen bei einer Übereinstimmung ausgeführt werden sollen.

Andererseits kann diese Eigenschaft ganz nützlich sein. Betrachten Sie den Fall, wo Sie wollen, daß für mehrere case-Werte dieselben Anweisungen ausgeführt werden. Die Angabe immer wieder derselben Anweisung wäre redundant, deshalb bietet Java die Möglichkeit, daß für mehrere case-Werte dieselben Anweisungen ausgeführt werden. Durch Weglassen der Ergebnis-Anweisung für einen case-Wert »fällt« die Ausführung durch zum nächsten case-Wert, und von dort zum nächsten, bis eine Anweisung gefunden ist. Hier ein Beispiel für diese Art Konstrukt:

switch (x) {
case 2:
case 4:
case 6:
case 8:
System.out.println("x ist eine gerade einstellige Zahl");
break;
default:
System.out.print("x ist keine gerade einstellige Zahl");
}

Die wesentliche Einschränkung des switch in Java ist, daß diese Tests und Werte sich nur auf einfache elementare Datentypen beziehen können (und dabei nur die elementaren Datentypen, die in einen int umgewandelt werden können). Sie können keine größeren elementaren Typen (long, float, double), Strings oder andere Objekte in einer switch-Anweisung verwenden, und Sie können auch nur auf Gleichheit testen. Dadurch ist switch wirklich nur für die einfachsten Vergleiche sinnvoll. Verschachtelte if-else-Anweisungen dagegen können für beliebige Tests und beliebige Wertetypen eingesetzt werden.

for-Schleifen

Die for-Schleife prüft eine Bedingung. Wenn diese Bedingung erfüllt ist, also true ergibt, wird eine Anweisung oder ein Anweisungsblock wiederholt ausgeführt, bis die Bedingung false wird. Diese Art Schleife wird häufig für einfache Iterationen verwendet, in denen Sie einen Anweisungsblock mit einer bestimmten Anzahl von Wiederholungen ausführen und dann beenden, aber Sie können for-Schleifen auch für jede andere Aufgabe einsetzen.

Hier die Syntax für die for-Schleife:

for (initialisierung; test; inkrement) anweisung(en);

Der Anfang der for-Schleife besteht aus drei Teilen:

Der anweisung(en)-Teil der for-Schleife stellt die Anweisung oder den Anweisungsblock dar, die immer dann ausgeführt werden, wenn test true ergibt und die Schleife durchlaufen wird. Hier ein Beispiel für eine for-Schleife, die alle Werte eines String-Arrays mit Null-Strings initialisiert:

String strArray[] = new String[10];
for (int i = 0; i < strArray.length; i++)
strArray[i] = "";

Jede dieser Komponenten der for-Schleife kann leere Anweisungen enthalten, d.h. Sie können einfach ein Semikolon ohne Ausdruck oder Anweisung einfügen, und dieser Teil der for-Schleife wird dann ignoriert. Beachten Sie, daß Sie bei der Verwendung einer leeren Anweisung in Ihrer for-Schleife vielleicht die Initialisierung oder Inkrementierung von Schleifenvariablen oder Schleifeninidzes an anderer Stelle selbst vornehmen müssen.

Sie können auch eine leere Anweisung für den Rumpf Ihrer for-Schleife angeben, wenn alles, was Sie bewerkstelligen wollen, bereits im Schleifenanfang passiert. Das folgende Beispiel zeigt, wie die erste Primzahl größer 4000 gefunden wird. Auch hier ist es am besten, wenn Sie eine Kommentaranweisung einfügen, wo Sie eine leere Anweisung verwenden, damit offensichtlich wird, daß das bewußt so realisiert wurde:

for (i = 4001; notPrime(i); i += 2)
/* nichts tun */ ;

Achten Sie auf die Plazierung des Semikolons (;) nach der ersten Zeile in der for-Schleife. Betrachten Sie den folgenden Codeausschnitt:

for (int i = 0; i < 10; i++);
System.out.println("Schleife!");

Hier wollte man, daß der String Schleife! 10mal ausgegeben wird, aber statt dessen wird die Schleife nur 10mal durchlaufen und macht nichts außer Testen und Inkrementieren, um dann nur einmal Schleife! auszugeben. Warum? Aufgrund des fehlerhaft plazierten Semikolons (;) am Ende der ersten Zeile der for-Schleife.


Schreiben Sie kein Semikolon (;) hinter die erste Zeile der Schleife, es sei denn, das ist wirklich erforderlich!

while-Schleifen

Die while-Schleife wird genutzt, um eine Anweisung oder einen Anweisungsblock so lange zu wiederholen, wie eine bestimmte Bedingung true ist.

Hier die Syntax der while-Schleife:

while (bedingung) anweisung(en);

Die bedingung ist ein Boolescher Ausdruck, der ein Boolesches Ergebnis zurückgibt. Wenn er true zurückgibt, führt die while-Schleife die anweisung(en) aus und prüft dann die Bedingung erneut ab, bis die Bedingung false ergibt. Wenn die Bedingung beim ersten Prüfen false ist, werden die anweisung(en) der while-Schleife nicht ausgeführt.

Hier folgt ein Beispiel für eine while-Schleife, die die Elemente eines Arrays mit Integern (in arrInt) in ein Array mit Fließkommazahlen (in arrFloat) kopiert und dabei jedes Element in eine Fließkommazahl umwandelt. Um das Ganze interessanter zu machen, gibt es zwei Bedingung, die true sein müssen, damit die Schleife ausgeführt wird:

Um das zu bewerkstelligen, erfolgt in der Schleife ein zusammengesetzter Test, der mehrere Bedingungen auswertet. Bei der Verwendung des Operators && müssen beide Teile true sein, damit die Bedingung true ergibt. Diese Schleife verwendet außerdem das Postfix-Inkrement (++), um den Zähler nach jedem Schleifendurchgang zu erhöhen:

int count = 0;
while ( (count < arrInt.length) && (arrInt[count] != 0) ) {
arrFloat[count] = (float) arrInt[count];
count++;
}

Angenommen, arrInt hat die Länge 20. Wenn keiner der Werte aus arrInt 0 ist, würde diese while-Schleife 20mal ausgeführt, weil die Prüfung true ergibt, während die count-Variable bei der Iteration von 0 bis 19 true ergibt. Ist dagegen einer der arrInt-Werte 0, wird die Schleife zwischen 0- und 19mal ausgeführt, abhängig von der Position der ersten 0 in dem arrInt.

do-while-Schleifen

Bei der Beschreibung der while-Schleife haben wir darauf hingewiesen, daß sie möglicherweise überhaupt nicht ausgeführt wird, wenn die Bedingung gleich bei der ersten Auswertung false ergibt. Wenn Sie wollen, daß die Schleife mindestens einmal ausgeführt wird, brauchen Sie eine do-while-Schleife. Sie macht im wesentlichen dasselbe wie eine while-Schleife, mit der Ausnahme, daß die Anweisungen zuerst ausgeführt werden, und dann die Bedingung ausgewertet wird.


Technischer Hinweis
Die do-while-Schleife in Java entspricht der repeat-until-Schleife in Delphi/Pascal.

Hier die Syntax für die do-while-Schleife:

do anweisung(en) while bedingung;

Hier werden zuerst die anweisung(en) ausgeführt und anschließend wird die bedingung ausgewertet. Wenn die bedingung true ergibt, werden die anweisung(en) erneut ausgeführt, ergibt sie false, wird die Schleife beendet. Hier ein Beispiel, das 20 Zeilen ausgibt:

int x = 1;
do {
System.out.println("Schleifendurchgang " + x);
x++;
} while (x <= 20);

Merken Sie sich, daß do-while-Schleifen immer mindestens einmal ausgeführt werden und die Prüfung am Ende ausführen, während while-Schleifen am Anfang prüfen und möglicherweise nie ausgeführt werden.

Sie haben break bereits als Teil der switch-Anweisung kennengelernt; es beendet die Ausführung von switch und das Programm wird mit der Anweisung hinter dem switch-Konstrukt fortgesetzt. In einer Schleife macht das Schlüsselwort break dasselbe - es bricht die Ausführung der aktuellen Schleife unmittelbar ab. Wenn Sie verschachtelte Schleifen verwenden, wird die Ausführung in der nächsten äußeren Schleife fortgesetzt. Andernfalls setzt das Programm seine Ausführung einfach in der nächsten Anweisung hinter der Schleife fort.


Die Möglichkeit, Schleifen zu verschachteln, kann Ihnen helfen, einen komplexen Datenfluß zu steuern, aber mehr als zwei oder drei Ebenen können dazu führen, daß der Code schwer lesbar und schlecht verständlich wird. Vermeiden Sie also das Verschachteln von Schleifen.

Betrachten Sie beispielsweise die folgende while-Schleife, die Elemente von einem Integer-Array in ein Fließkommazahlen-Array kopiert, bis das Ende des Arrays erreicht oder eine 0 erkannt wird. Den letzteren Fall können Sie innerhalb des Rumpfs von wihle prüfen und die Schleife gegebenenfalls mit break verlassen:

int count = 0;
while (count < arrInt.length) {
if (arrInt[count] == 0)
break;
arrFloat[count] = (float) arrInt[count];
count++;
}

Die break-Anweisung bewirkt, daß die Schleife beendet wird, sobald die Bedingung erfüllt ist. In diesem Fall wird die Schleife beendet, wenn einer der arrInt-Werte gleich 0 ist. Im Gegensatz dazu beendet das Schlüsselwort continue die Ausführung des aktuellen Schleifendurchgangs, und führt den nächsten Schleifendurchgang aus. Bei do-while-Schleifen bedeutet das, daß die Schleife die Ausführung oben wieder beginnt, while- und for-Schleifen werden mit der Auswertung der Bedingung fortgesetzt.

Das Schlüsselwort continue ist praktisch, wenn Sie die Schleife neu starten wollen, ohne die darin enthaltenen Anweisungen auszuführen. Betrachten Sie das obige Beispiel, wo ein Array in ein anderes kopiert wird. Sie können prüfen, ob das aktuelle Integer-Element gleich 0 ist. In diesem Fall können Sie die Schleife neu starten, so daß das resultierende Fließkommazahlen-Array keinen Null-Wert entgegennehmen muß. Beachten Sie, daß Sie möglicherweise einige Elemente im ersten Array überspringen, Sie nun zwei Array-Zähler verwalten müssen:

int iCount = 0;
int fCount = 0;
while (iCount < arrInt.length) {
if (arrInt[iCount] == 0) {
iCount++;
continue;
}
arrFloat[fCount++] = (float) arrInt[iCount++];
}

Dieses Beispiel durchläuft beide Arrays, und kopiert den Integer-Wert aus arrInt nur dann in arrFloat, wenn das Element in arrInt ungleich Null ist.

Schleifen mit Sprungzielen

Für break und continue kann optional ein Sprungziel angegeben werden, das Java mitteilt, mit welcher Programmanweisung es fortfahren soll. Ohne ein Sprungziel (Label) setzt break die Ausführung mit der nächsten Programmanweisung nach der schließenden Schleife fort und continue startet die umschließende Schleife neu. Die Verwendung von break und continue mit Sprungzielen ermöglicht Ihnen, eine Schleife außerhalb der aktuellen Schleife fortzusetzen oder mehrere Schichten verschachtelter Schleifen gleichzeitig zu verlassen.

Um eine Schleife mit Sprungzielen zu verwenden, fügen Sie das Label vor der Schleifeninitialisierung ein. Hinter dem Label wird kein Semikolon (;), sondern ein Doppelpunkt (:) angegeben. Wenn Sie jetzt break oder continue verwenden, fügen Sie nach dem Schlüsselwort das Label, also das Sprungziel ein:

out:
for (int i = 0; i < 10; i++) {
while (x < 50) {
if (i * x == 400)
break out;
...
} // Ende der while-Schleife
...
} // Ende der for-Schleife
... // Hier wird die Ausführung nach break out fortgesetzt

In diesem Codeabschnitt kennzeichnet out: den äußeren Block. Innerhalb der for- und der while-Schleife bewirkt die break-Anweisung für eine bestimmte Bedingung, daß beide Schleifen verlassen und die Ausführung mit der Codezeile hinter der for-Schleife fortgesetzt wird.

Das Programm Breakers in Listing 2.5 ist ein Beispiel für verschachtelte for-Schleifen und ein break mit Sprungziel. In der inneren Schleife werden beide for-Schleifen gleichzeitig verlassen, wenn die Summe der beiden Zähler größer vier ist.

Listing 2.5: Breakers.java

1: class Breakers {
2: public static void main(String args[]) {
3:
4: ers:
5: for (int i = 1; i <= 5; i++) {
6: for (int k = 1; k <= 3; k++) {
7: System.out.println("i ist " + i + ", k ist " + k);
8: if ((i + k) > 4)
9: break ers;
10: }
11: }
12: System.out.println("Beide Schleifen beendet");
13: }
14: }

Wenn Sie das Programm ausführen, sehen Sie die folgende Ausgabe:



i ist 1, k ist 1
i ist 1, k ist 2
i ist 1, k ist 3
i ist 2, k ist 1
i ist 2, k ist 2
i ist 2, k ist 3
Beide Schleifen beendet


In diesem Beispiel ist das Label ers: (Zeile 4). Die Schleifen werden ausgeführt (Zeilen 5 bis 11), solange i + k nicht größer als 4 ist (Zeile 8). Wenn die Summe von i und k größer 4 ist, bewirkt die break-Anweisung (Zeile 9), daß beide Schleifen in den äußeren Block gehen und die letzte Zeile wird ausgegeben (Zeile 12).

Zusammenfassung

Heute haben Sie zahlreiche Aspekte zu der Sprache kennengelernt, die JBuilder zugrunde liegt, Java. Sie haben Programmanweisungen kennengelernt und unter anderem auch die speziellen Dokumentations-Kommentare von Java. Datentypen, Variablen und Literale wurden vorgestellt. Darüber hinaus haben Sie die Operatoren gesehen, die in Java zur Verfügung stehen, und wie sie unter Berücksichtigung der Prioritäten in Java angewendet werden. Es wurde darauf hingewiesen, daß Java zahlreiche komplexe mathematische Funktionen bietet.

Sie haben viel über Arrays, String und Stringpuffer gelernt. Sie haben gesehen, wie Array-, String- und StringBuffer-Variablen deklariert werden, und wie ihre Objekte erzeugt werden. Sie haben die Methoden zum Zugriff auf die Daten in diesen Strukturen kennengelernt und wissen, wo Sie zusätzliche Informationen finden können. Sie können mehrdimensionale Arrays erzeugen und verwenden und Sie wissen, wann anstelle von String-Objekten StringBuffer-Objekte verwendet werden sollten.

Es gab zwei Themenbereiche, die Ihnen sicher häufig in Ihren eigenen Java-Programmen begegnen werden, nämlich Bedingungen und Schleifen. Bedingungen sind unter anderem die if-else- und die switch-Anweisungen, mit denen Sie zu bestimmten Codezeilen verzweigen können, abhängig von dem Ergebnis einer Booleschen Auswertung. Die Schleifenanweisungen umfassen die for-, while- und do-while-Schleifen, die Ihnen ermöglichen, bestimmte Anweisungen wiederholt auszuführen, bis eine bestimmte Bedingung erfüllt ist.

Nachdem Sie diese Sprachkonstrukte kennengelernt haben, werden Sie lernen, Klassen zu deklarieren und Methoden zu erzeugen, mit deren Hilfe Instanzen dieser Klassen miteinander kommunizieren können.

F&A

F Ich habe keine Möglichkeit gesehen, lokale Konstanten zu erzeugen. Unterstützt Java keine Konstanten?

A Doch, aber Sie können in Java keine lokalen Konstanten erzeugen. Sie können nur Instanzkonstanten und Klassenkonstanten erzeugen. Mehr darüber erfahren Sie in der nächsten Lektion.

F Was passiert, wenn Sie einer Variablen einen numerischen Wert zuweisen, die zu groß (oder zu klein) für diese Variable ist?

A Sie denken vielleicht, die Variable wird einfach nur in den nächst größeren Typ umgewandelt, aber das ist so nicht der Fall. Wenn es sich bei dem Wert um eine positive Zahl handelt, entsteht ein »Überlauf« für die Variable, d.h. die Variable kippt um und wird zum kleinsten negativen Wert für diesen Typ, von wo aus weitergezählt wird. Ist der Wert eine negative Zahl, entsteht ein »Unterlauf« für die Variable, d.h. sie wird zum höchsten Wert für diesen Typ und zählt von dort aus abwärts. Das kann zu fehlerhaften Ergebnissen führen, stellen Sie also sicher, daß Sie für numerische Werte die jeweils richtigen Typen deklarieren. Wenn Sie Zweifel haben, verwenden Sie den nächst größeren Typ.

F Wenn Arrays Objekte sind und mit new erzeugt werden müssen, wo ist dann die Klasse Array? Ich habe sie in den Klassenbibliotheken von Java nicht gefunden.

A Arrays werden in Java einigermaßen kompliziert implementiert. Die Klasse Array wird automatisch angelegt, wenn Ihr Java-Programm ausgeführt wird, deshalb können keine Unterklassen davon angelegt werden. Array stellt die grundlegende Umgebung für Arrays bereit, unter anderem die Instanzvariable length. Darüber besitzt hat jeder elementare Typ und jedes Objekt eine implizite Unterklasse von Array, die ein Array dieser Klasse oder dieses Objekts darstellt. Wenn Sie ein neues Array-Objekt erzeugen, haben Sie vielleicht nicht wirklich eine Klasse, aber es verhält sich so, als ob das der Fall wäre.

F Sie haben gesagt, daß man die Methode arraycopy() auf Arrays anwenden kann, aber kann ich sie auch für Instanzen der Klassen String und StringBuffer verwenden, die doch ebenfalls als Arrays implementiert sind?

A Ja, mit einer Ausnahme. Weil String-Werte nicht direkt verändert werden können, können Sie keinen String als Ziel-Array verwenden. wenn dagegen das Ziel-Array oder der StringBuffer bereits im Speicher alloziert sind, können Sie arraycopy() verwenden, um Daten hineinzukopieren.

F Wenn ein Array Werte beliebiger elementarer Datentypen oder Objekte aufnehmen kann, wie kann ich dann genau ermittelt, wieviel Speicher ein Array alloziert?

A Um zu berechnen, wieviel Speicher für ein Array alloziert wird, multiplizieren Sie die Anzahl der Elemente mit der Anzahl der Bits, die für den Datentyp dieses Elements verwendet werden. Addieren Sie 32 Bit für das Feld length (das immer ein int ist). Teilen Sie durch 8, um die Anzahl der Bytes zu erhalten, die für das Array alloziert werden. Wenn Sie beispielsweise ein Array mit 6 double-Elementen deklarieren wollen, die je 64 Bit belegen, können Sie den erforderlichen Speicher wie folgt berechnen:

((6 * 64) + 32) / 8

Es ergeben sich insgesamt 52 Byte Speicherbedarf.

F Sie haben in den Code-Listings von heute keine import-Anweisungen verwendet. Warum?

A Alle Methoden für Arrays, Strings und Stringpuffer sind Teil des Pakets java.lang, und die Klassen String und StringBuffer sind in java.lang.String bzw. java.lang.StringBuffer definiert. Weil das ganze java.lang-Paket implizit in alle Java-Programme importiert wird, ist es nicht erforderlich, eine explizite import-Anweisung anzugeben.

F Gibt es in Java eine goto-Anweisung?

A Java definiert das Schlüsselwort goto, aber es ist »reserviert« und wird momentan nicht verwendet. Aber ein break mit Sprungziel realisiert dasselbe.

F Ich habe eine Variable in einer Blockanweisung in einer if-else-Anweisung deklariert. Wenn das if-else abgearbeitet ist, verschwindet diese Variable. Was ist passiert?

A Blockanweisungen innerhalb geschweifter Klammern bilden einen lokalen Gültigkeitsbereich. Das bedeutet, wenn Sie eine Variable innerhalb eines Blocks deklarieren, ist sie nur innerhalb dieses Blocks sichtbar und verwendbar. Nachdem der Block abgearbeitet ist, stehen die Variablen, die dort deklariert wurden, nicht mehr zur Verfügung.

F Warum können in der switch-Anweisung keine String-Werte verwendet werden?

A Strings sind Objekte, und switch ist in Java nur für die elementaren Datentypen byte, char, short und int definiert. Um andere Typen zu vergleichen, müssen Sie verschachtelte if-else-Anweisungen verwenden, die allgemeinere Ausdrücke erlauben, unter anderem auch String-Vergleiche.

Workshop

Der Workshop bietet zwei Möglichkeiten, zu überprüfen, was Sie in diesem Kapitel gelernt haben. Der Quiz-Teil stellt Ihnen Fragen, die Ihnen helfen sollen, Ihr Verständnis für den vorgestellten Stoff zu vertiefen. Die Antworten auf die Fragen finden Sie in Anhang A. Der Übungen-Teil ermöglicht Ihnen, Erfahrungen in der Anwendung der Dinge zu sammeln, die Sie hier kennengelernt haben. Versuchen Sie, diese Dinge durchzuarbeiten, bevor Sie mit der nächsten Lektion weitermachen.

Quiz

Richtig oder Falsch?

Übungen

Schreiben Sie Programmanweisungen für die folgenden Aufgaben:

Erzeugen Sie die Klasse Splat, die Array-Indizes verwendet, den Konkatenations-Operator (+) sowie die Methoden substring() und valueOf(), um einen String zu erzeugen, der wie folgt auf dem Bildschirm ausgegeben wird:


I say, two will get you ten that chicken doesn't make it across the road. Nehmen Sie an dem Programm aus Listing 2.5 die folgenden Änderungen vor:


Führen Sie das Programm aus und prüfen Sie, ob die sechste Ausgabezeile aus Breakers.java (Listing 2.5),

i is 2, k is 3

im neuen Programm durch die folgende Ausgabe ersetzt wurde:

i is 3, k is 1

Stellen Sie sicher, daß dies die einzige Änderung in der Ausgabe ist.


© 1997 SAMS
Ein Imprint des Markt&Technik Buch- und Software- Verlag GmbH
Elektronische Fassung des Titels: JBuilder in 14 Tagen, ISBN: 3-87791-895-6

Previous Page Page Top TOC Index Next Page See Page