Zum Inhalt springen

Kurs:Java – ein schneller Einstieg/Elemente der Sprache

Aus Wikiversity

Elemente der Sprache

[Bearbeiten]

Wie jede andere Sprache auch, verfügt Java über essentielle Elemente für die Verständlichkeit. Zu diesen Elementen zählen Grammatik und Semantik, die ihrerseits wieder die Syntax bestimmen. Hier kann kein Informatikkurs über die "Theorie der Syntax" oder anderer linguistischer Gegebenheiten moderner Programmiersprachen abgehalten werden. Deshalb werden die elementaren Bestandteile nur kurz angesprochen und über Links auf deren Konsequenz verwiesen.

Semantik

[Bearbeiten]

Dieses Teilgebiet der Linguistik setzt sich mit der Bedeutung der Wörter auseinander. Bei Programmiersprachen ist die Semantik ein weitgehend statisches Gebilde, denn es existieren sog. Schlüsselwörter, deren Bedeutung nicht wandelbar ist. In Java zählen z.B. if, while, case zu diesen Schlüsselwörtern. Sie steuern das Ablaufverhalten von Sequenzen und geben dem Programmentwickler so die Möglichkeit, den gesamten Ablauf zu kontrollieren. Diese Worte zählen zu den Controls.

Schlüsselwörter sind auch die Zeichen +, -, *, ... Hier ist das "+"-Zeichen bereits eine Ausnahme, denn es kann als Additionsoperator, Vorzeichen oder concatenation-Anweisung dienen. Es kommt eben darauf an, in welchem Zusammenhang dieses Zeichen benutzt wird. Normalerweise werden diese Zeichen als Operatoren angesehen und auch so eingesetzt.

Grammatik

[Bearbeiten]

Grammatiken sind wohl eins der komplexesten Gebilde in Programmiersprachen. Sie bilden das Gerüst der Sprache und sind damit verantwortlich für die Interaktionen des Programmentwicklers und der Maschine. Mathematische Gegebenheiten (Punkt- vor Strichrechnung, Klammern, ...) sind ebenfalls zu berücksichtigen. Eine weitere Aufgabe ist die Wahrung struktureller Zusammenhänge. In natürlichen Sprachen werden dafür oft Satzzeichen wie Punkt und Komma eingesetzt, nicht so bei Programmiersprachen. Die Aufgabe des Kommas (ursprünglich eine Art der Klammerung) übernehmen hier die geschweiften Klammern. Sie kennzeichnen zusammenhängende Sequenzen und bilden sogenannte Blocks.

Sehr wichtig ist bei einer Grammatik auch, zwischen Aussagen und Ausdrücken zu unterscheiden. In beiden Fällen handelt es sich um syntaktische Gebilde, die auch untereinander "vermischt" werden können. So kann eine Aussage oft erst dann getroffen werden, wenn ein bestimmter Ausdruck bekannt ist. Es ist also sehr wichtig, den Unterschied zwischen Aussage (statement) und Ausdruck (expression) zu kennen.

Zahlen

[Bearbeiten]

Zahlen in Computern sind zunächst einmal Kombinationen aus mehr oder wenger langen Folgen von binären Zuständen. Letztere werden oft als 0 bzw. 1 dargestellt und sind damit numerische Repräsentanten von Bits. In Java ist das nicht anders, jedoch ist der Begriff Zahl hier natürlich eine Klasse (Number). Was eine Zahl ist, wird zunächst durch einen Wertebereich festgelegt. Damit ist nicht nur die schriftliche Darstellung gemeint, sondern auch der benötigte Speicherplatz im Computer.

Java kennt zwei elementare Zahlen, die über die Klasse Number instanziiert werden können - byte und short. Diese beiden Elementartypen belegen 1 Byte oder 2 Bytes (short). Ein Wertebereich ist immer sowohl im negativen als auch im positiven Bereich sinnvoll, womit die Bereiche ebenfalls feststehen. Jedes Byte nimmt demnach Werte von -127 bis 128 an und jeder short-Bereich solche von -32767 bis 32768. Nun werden häufig gebrochene Zahlen benötigt, etwa die dezimale Darstellung von 2/3. In diesem Fall werden sehr viel komplexere Abläufe benötigt, die als abstract Methods in der Klasse Number vorhanden sind. Auf diesen Methoden basieren die gängigen Klassen der Zahlen in Java. Es sei bereits jetzt auf den eklatanten Unterschied zwischen Typenangaben wie

byte und Byte
short und Short
int und Integer
long und Long
float und Float
double und Doubte
boolean und Boolean

hingewiesen. Die "linke" Seite ist jeweils ein primitiver Datentyp, die rechte erzeugt eine Instanz der entsprechenden Klasse mit sämtlichen Methoden. Die Deklarationen der "linken" Seite werden für einfache Variablen benutzt in denen Zahlenwerte gespeichert sind und die allein durch Angabe des Variablennamens den Berechnungen ihren wertmässigen Inhalt übergeben. Die "rechte" Seite erzeugt Objekte und das sind eben nicht nur einfache Variablen, sondern komlexe Gebild mit eigenen Methoden. Die Angabe eines Objektnamens führt auch keinesfalls zu einem Wert, sondern übergibt nur eine Referenz auf den Inhalt der Objekts. In "normalen" Anwendungen werden sich nur Deklarationen der "linken" Seite befinden. Erst wenn textuelle Eingaben in ein Programm als Zahlen interpretiert und verwendet werden sollen, sind Objekte der "rechten" Seite äußerst hilfreich.

Variablen

[Bearbeiten]

Der am häufigsten missbrauchte Begriff in der Programmierung ist "Variable"! Bevor überhaupt Aussagen über Variablen gemacht werden, soll erst einmal eine Definition dieses Begriffs erfolgen.

Definition: Eine Variable ist ein Behälter für Daten des spezifizierten Typs.

Jede Variable benötigt einen Namen, der zur Identifizierung der abgelegten Daten fungiert. Es handelt sich also um einen identifier. Der Typ einer Variable legt fest was für Daten die Variable enthalten kann und damit auch welche Operationen auf diese Daten anwendbar sind. Die korrekte Bezeichnung dieses Parts lautet type. Um Typ und Name zusammenzufassen und dem Programm zur Verfügung zu stellen ist folgende minimale Anweisung erforderlich:

type identifier;

Es handelt sich um eine Deklaration Auch dieser Begriff soll gleich definiert werden; nicht um Diskussionen zu verhindern, sondern um ihren Bezug aus den hier hergeleiteten Sätzen zu separieren.

Definition: Eine declaration ist die Assoziation von identifier und Attribut, ohne die Notwendigkeit Speicherplatz zu reservieren.

In der declaration kann außerdem der Schutzbereich festgelegt werden und durch entsprechende Positionierung im Quellcode ergibt sich noch ein Scope. Diese Gegebenheiten wurden im vorherigen Abschnitt detailliert besprochen.

Sichtbarkeit oder view

[Bearbeiten]

Variablen, aber auch Methoden, besitzen eine genau definierte Sichtbarkeit nach Außen. Mit Außen ist alles außer dem Objekt selbst gemeint, also alle anderen Objekte, Klassen usw. Es existieren verschieden Stufen der Sichtbarkeit, die sich aufteilen in:

  • private
Nur sichtbar im aktuellen Objekt. Nach außen hin völlig unsichtbar.
  • protected
Sichtbar innerhalb des package in welcher die Klasse residiert.
  • public
Sichtbar für alle.

Gültigkeitsbereich oder scope

[Bearbeiten]

Variablen besitzen, je nach Positionierung unterschiedliche Sichtbereiche. Deklarationen innerhalb von Methoden haben ein lokal scoping zu Folge, Deklarationen außerhalb von Methoden erzeugen hingegen member scoping. Je nach Ort der Deklaration werden Variablen denn auch in local und member variables unterteilt, mit erheblichen Unterschieden.

Lokale Variablen

[Bearbeiten]

Diese Variablen werden nicht vom Compiler initialisiert. Ihr Inhalt ist praktisch zufälliger Natur, wird aber in kritischen Fällen als solcher vom Compiler erkannt und entsprechend behandelt (meist mit einer Fehlermeldung).

Member- oder Klassenvariablen

[Bearbeiten]

Werden vom Compiler initialisiert. Die Initialisierung übernimmt dabei die immer vorhandene Creator-Methode. Referenzierende Variablen (siehe Datentypen) erhalten eine Nullreferenz, primitive Typen das jeweils neutrale Element der Zahlenmenge (0). Im Zweifelsfall sollte eine Initialisierung also bereits bei der Deklaration stattfinden.

Datentypen

[Bearbeiten]

Java unterscheidet generell zwei Kategorien von data types. Es handelt sich dabei einmal um die in diesem Abschnitt besprochenen und um die referenzierenden. Letztere sind keinesfalls mit dem reservierten Speicherplatz zu verwechseln, sie dienen nur der Erreichbarkeit desselben. Objects, Arrays und Interfaces sind ausnahmslos der Kategorie reference zugehörig. Dieser Abschnitt widmet sich jedoch erst einmal den primitve Typen zu.

Primitives

[Bearbeiten]

Eine tabellarische Auflistung zeigt die vorhanden data types. Die Spalten sind unterteilt in Bezeichnung, Beschreibung und belegtem Speicherplatz.

  • byte:
ganze Zahl (1 Byte)
8-bit Zweierkomplement
  • short:
ganze Zahl ( 2 Bytes)
16-bit Zweierkomplement
  • int
ganze Zahl (4 Bytes)
32-bit Zweierkomplement
  • long
ganze Zahl (8 Bytes)
64-bit Zweierkomplement
  • float
Fliesskomma mit einfacher Genauigkeit
32-bit IEEE 754
  • double
Fliesskomma mit doppelter Genauigkeit
64-bit IEEE 754
  • char
Character (Buchstabe)
16-bit Unicode Character
  • boolean
Boolescher Wert (true / false)
wahr oder falsch

Konstanten oder Finale Variablen

[Bearbeiten]

Die Vermeidung des Begriffs "Konstanten" ist mittlerweile so auffällig, dass es wohl etwas Besonderes damit auf sich haben muss. Tatsächlich besteht eine Besonderheit in Java bezüglich Konstanten: ``Es gibt keine!

Konsequent, wie diese Sprache aufgebaut ist, entpuppen sich Konstanten als Wertsynonyme. Ihr Aufbau unterscheidet sich in keiner Weise von dem der Variablen. Nur eine Änderung des Inhalts darf nicht erfolgen. Warum sollte dann eine weitere Deklaration bereitgestellt werden, wenn doch allein das Änderungsverbot den Anforderungen genügt. Ein Beispiel:

final int constNull=0;

Dem Programm wird eine Variable zur Verfügung gestellt, die den Wert 0 enthält und unmittelbar nach der Initialisierung nicht mehr änderbar ist.

Expressions

[Bearbeiten]

Expressions oder Ausdrücke übernehmen die eigentliche Arbeit in Programmen. Wegen der häufig vorkommenden Vermischung der Begriffe Statement und Expression, erfolgt auch hier wieder eine Definition des Begriffs.

'Definition: Ein Ausdruck ist die sinnvolle Integration von Variablen, Operatoren, Methoden zu einem einzelnen Wert. Einige Beispiele mögen diese Definition verdeutlichen.

a * b + 5 - field[++idx]

Dieser Ausdruck benutzt ausschließlich arithmetische Operatoren und numerische Variablen. Die Kombination kann als sinnvoll angesehen werden, denn sonst würde der Compiler eine Fehlermeldung produzieren. "Der Ausdruck " + 4 + "+" + 5 + " liefert als Ergebnis " + (4+5) In diesem Fall wird erneut ein einzelner Wert ermittelt, der jedoch vom Typ String ist. Obwohl numerische Komponenten im Ausdruck enthalten sind, bleibt die textuelle Natur erhalten. Sämtliche numerischen Werte werden intern einer Methode "toString()" überantwortet, die eine entsprechende Anpassung vornimmt. Insbesondere der letzte Klammerterm ist zu beachten. Würden die Klammern fehlen, lieferte dieses Ausdruck am Ende den String "45".

Operatoren

[Bearbeiten]

Operatoren werden in drei Kategorien Unterteilt. Je nachdem ob sie sich nur auf einen Operanden auswirken (unary operator), auf deren zwei (binary operator) oder gar auf drei (ternary operator). Zu jeder dieser drei Kategorien ein Beispiel:

  • Kategorie unary
i++
Erhöhen der Variablen i um 1
  • Kategorie binary
i = 1
Belegen der Variablen i mit 1
  • Kategorie ternary
a ? b : c
wenn a true return b sonst return c

Soweit die Kategorien. Die folgenden Abschnitte werden sich mit allen Operatoren auseinandersetzen, auch mit solchen die nicht unbedingt als Opeartor angesehen werden.

Arithmetische Operatoren

[Bearbeiten]

Am häufigsten werden wohl Opeartoren für Berechnungen eingesetzt. Letztere sind arithmetischer Natur und werden deshalb zuerst betrachtet.

Binäre arithmetische Operatoren

[Bearbeiten]
  • +
a + b
Addiert Oparand a und Operand b
  • -
a - b
Subtrahiert Operand a von Operand b
  • *
a * b
Multipliziert Operand a und Operand b
  • /
a / b
Dividiert Operand a durch Operand b
  • %
a % b
Ermittelt den Rest der Division a / b

Vorsicht! Der "%"-Operator ist für mathematische Anwendungen sehr eingeschränkt. Er arbeitet nur mit dem standardisierten Repräsentantensystem.

Bei "modulo p" werden also nur die Repräsentanten von 0 bis (p-1) erkannt. Damit ist die Verwendung von -1 statt (p-1) ungültig.

Unäre arithmetische Operatoren

[Bearbeiten]

Die Strichoperatoren besitzen noch eine weitere, unäre Eigenschaft. Sie werden auch als Vorzeichen eingesetzt und haben dann folgende

  • +
+a
positives Vorzeichen
  • -
-a
Negation des Operanden a

In-/Decrementierende Operatoren

[Bearbeiten]

Strichoperatoren in ihrer in- und dekrementierenden Kurzform (++ / --) sind ebenfalls unär, haben aber noch die Möglichkeit ihre Funktionalität prefix oder postfix auszuüben.

  • ++
++a
a+1 vor Ausführung der Aktion
a++
a+1 nach Ausführung der Aktion
  • --
--a
a-1 vor Ausführung der Aktion
a--
a-1 nach Ausführung der Aktion

Relationen oder Vergleichsoperatoren

[Bearbeiten]

Diese Operatoren werden korrekterweise relationale Operatoren genannt, aber die Bezeichnung Vergleichsoperator hat sich in unserem Sprachraum durchgesetzt. Die folgende Tabelle zeigt wieder den Opeartor und seine Benutzung, in der dritten Spalte wird nun aber als Berschreibung angegeben wann das Ergebnis den logischen Wert true (wahr ) ergibt.

  • >
a > b
a ist größer als b
  • >=
a >= b
a ist größer oder gleich b
  • <
a < b
a ist kleiner als b
  • <=
a <= b
a ist kleiner oder gleich b
  • ==
a == b
a ist gleich b
  • !=
a != b
a ist ungleich b

Konditionaloperatoren

[Bearbeiten]

Oft voreilig als boolean operators bezeichnet. Conditional operators sind wohl meist gehasste Gebiet der Programmierung. Nicht weil sie besonders schwierig sind, sonder weil ihre Arbeitsweise im Detail wenig bekannt ist. Wird immer der gesamte Ausdruck ausgewertet oder endet die Auswertung nach dem Test relevanter Subterme? Ist die Konjunktion mit einer Punktoperation auf gleicher Priorität oder müssen die Ausdrücke komplett geklammert sein? Die folgende Tabelle wird um ein Beispiel ergänzt, das auf die feinen aber oft nervigen Unterschiede aufmerksam macht.

  • &&
a && b
a und b gleich true sind (Auswertung des Operators b ist abhängig von a)
  • ||
a || b
a oder b gleich true ist (Auswertung des Operators b ist abhängig von a)
  • !
!a
a gleich false ist
  • &
a & b
a und b gleich true sind (Es werden immer beide Operatoren ausgewertet)
  • |
a | b
a oder b gleich true ist (Es werden immer beide Operatoren ausgewertet)
  • ^
a ^ b
a und b unterschiedlich sind (Es werden immer beide Operatoren ausgewertet)

Beispiel für den Einsatz anhand einer Schleife mit Überprüfung der Abbruchbedingung, die jedoch nie erreicht wird:

do {
...
} while((idx < length) || (arr[++idx] != 0))

Offensichtlich soll irgendein Array durchsucht werden, das kürzer als eine vorgegebene Länge (length) sein kann. Die Schleife soll verlassen werden, wenn der laufende Index (idx) größer oder gleich der vorgegebenen Länge ist, oder an der unmittelbar folgenden Position (idx + 1) im Array der Wert 0 gefunden wird. Wenn der Index auch nur eimal kleiner als die vorgegebene Länge ist, wird diese Schleife nie verlassen. Die Abbruchbedingung für das Verlassen der Schleife exit besteht aus zwei Opearanden, wobei der zweite auch noch einen prefix short cut opearator (++idx) enthält.

if   (idx >= length)
      exit;
else {
      idx = idx + 1;
      if (arr[idx] == 0)
          exit;
     }

Der zweite Opearand wird nur ausgewertet, wenn die Bedingung des ersten der boolschen Wert false ergibt. Weil die Incrementierung des Index aber als short cut opratator im zweiten boolschen Operanden steckt, wird der Index nicht erhöht, weil der zweite Opearand überhaupt nicht zur Ausweruntg gelangt.

Logische Operatoren

[Bearbeiten]

Diese Operatoren haben zwar, bis auf eine Ausnahme, die gleiche Notation wie die Konditionaloperatoren, sind aber nicht identisch. Ihre Auswirkung ist von der aktuellen Algebra abhängig, womit prinzipiell der Typus der Operatoren gemeint ist. Dieser verwirrende Zusammenhang soll zunächst erläutert werden, bevor die Tabelle mit den Operatoren dergestellt wird.

Die Boolsche Algebra ist in Java ein abgeschlossenes System. In anderen Programmiersprachen ist dieser Unterschied nicht vorhanden. Wenn ein Boolscher Ausdruck mit 0 oder 1 statt true oder false assoziiert wird, kann das System entweder den logischen Wert true mit nicht Null oder den logischen Wert false mit nicht 1 gleichsetzen. Damit muss immer ein Schwellenwert berücksichtigt werden, der keinesfalls eindeutig ist. Im Gegensatz dazu steht die Abgeschlossenheit Boolscher Operationen bei Java. Hier ist gewährleistet, dass nicht true immer false bedeutet und nicht false nur true nach sich ziehen kann.

Nun bestehen Daten bekanntlich aus sinnvollen Kombinationen einzelner Bits, die ihrerseits nur als 0 oder 1 repräsentiert werden. Die Manipulation einzelner Bits ist die Aufgabe der logischen Operatoren. Die Bits können zwar auch Synonyme der Boolschen Werte true oder false angesehen werden, jedoch sind Bits mit einer Positionierung versehen, die dem Verbund (z.B. Byte) in dem sie residieren Werte oder andere Eigenschaften zuordnen. Die Binäre Verknüpfung der Werte 10 und 7 ergibt:

 1010 = 10
&0111 =  7
 ----
 0010 =  2

Logische Operatoren verknüpfen also immer alle Bits, derer sie habhaft werden können. Sie nehmen praktisch alle Boolschen Werte aus den Bytes und verküpfen sie im Verbund. Deshalb haben sie die gleiche Notation wie die Konditionaloperatoren.

Jede der hier aufgeführten Operationn wird mit den Operanden

a=1010
b=0111

durchgeführt.

  • &
a & b
Bitweise UND-Verknüpfung
Ergebnis: 0010
  • |
a | b
Bitweise ODER-Verknüpfung
Ergebnis: 1111
  • ^
a ^ b
Bitweise XOR-Verknüpfung
Erbebnis: 1101
  • ~
~a
Bitweise Negation (Einerkomplement)
Ergebnis: 0101

Shift Opeartoren

[Bearbeiten]

Verbunde aus Bits, seien sie als Byte oder Integer vorhanden, können auch verschoben werden. Diese Operation entspricht, je nach Richtung, einer Multiplikation oder einer Division mit einer Potenz von 2. In Java sind folgende Shift Operatoren vorhanden:

  • >>
a >> b
Verschiebung von a um b Bits nach rechts (a / 2b)
  • <<
a << b
Verschiebung von a um b Bits nach links ( a . 2b)
  • >>>
a >>> b
Verschiebung von a um b Bits nach rechts (unsigned)

Zuweisungsoperatoren

[Bearbeiten]

Java bietet nicht nur den üblichen binären Zuweisungsopeartor "=" an, sondern gestattet siene Verwendung auch als short cut operator mit

  • +=
a += b
a = a + b
  • -=
a -= b
a = a - b
  • *=
a *= b
a = a * b
  • /=
a /= b
a = a / b
  • %=
a %= b
a = a % b
  • &=
a &= b
a = a & b
  • |=
a |= b
a = a | b
  • ^=
a ^= b
a = a ^ b
  • <<=
a <<= b
a = a << b
  • >>=
a >>= b
a = a >> b
  • >>>=
a >>>= b
a = a >>> b

Spezielle Operatoren

[Bearbeiten]

Es existieren noch einige Operatoren, die als solche nicht unmittelbar erkenntlich sind Sie haben wenig bis nichts mit den Operatoren der Arithmetik zu tun. Trotzdem sind sie existentiell für Sprachen wie Java.

Typecasting Operator

[Bearbeiten]

Oft werden unterschiedliche Genauigkeiten bei Berechnungen verlangt, weshalb einige Werte vor der Operation in den entsprechenden Datentyp gewandelt werden müssen. Der Typecasting Operator übernimmt diese Aufgabe. Anwendung:

(type)var

Beispiel:

(double)val

Die Variable "val" wird in den Typ double überführt. Die Variable "val" muß in diesem Beispiel dem Typ eines numerischen primitives entsprechen.

instanceof Operator

[Bearbeiten]

Oft muss festgestellt werden, welcher Klasse die Instanz eines Objektes entspricht. Dafür ist der instanceof Operator bestens geeignet. Eigentlich handelt es sich um einen Bytecode-Befehl der JVM. Eigentlich wird die Methode getClass(...) benutzt. Eigentlich dauert das zu lange und deshalb wird diese Operation hier aufgeführt.

Anwendung:

a instanceof b

Dabei verkörpert a den Namen eines Objektes und b die Bezeichnung einer Klasse. Wenn nun das Objekt (a) einer Instanz der Klasse (b) entspricht, wird true zurückgegeben. Dieser Operator arbeitet über die Grenzen der abgeleiteten Klassen hinaus. Ein Beispiel:

Ein Objekt a ist die Instanz einer Schaltfläche (Button), die Klasse b ist synonym zu Component. Die Anweisung a instanceof b liefert true, weil jede Schaltfläche eine Instanz der Klasse Component ist. Wenn b allerdings die Klasse Container verkörpert, würde die gleiche Anweisung false liefern, weil zwar jeder Container eine Instanz von Component ist, jedoch kein Button der Instanz eines Containers entspricht.

Klingt kompliziert? Ist es auch.

new Operator

[Bearbeiten]

Auch die Kreation neuer Objekte besitzt einen eigenen Operator. Dieser Operator benutzt direkt die Creator-Methode von Ojekten zu deren Instanzierung. Er ist der einzige Operator, der diese Methode benutzen kann.

dot Opeartor .

[Bearbeiten]

Der Zugriff auf objekinterne Elemente erfolgt über den dot (".") Operator. Wenn beispielsweise auf die member variable "day" eines Objektes "date" der Klasse "Date" zugegriffen werden soll wird der dot Operator in der Form

int Tag = date.day;

benutzt.

Klammer Operator ()

[Bearbeiten]

Dieser Operator (runde Klammer auf, runde Klammer zu) hat eigentlich zwei Aufgaben. Zunächst ist er unabkömmlich für die Deklaration von Methoden. Außerdem dient er als Container für die Argumentliste der deklarierten Methode. Auch wenn keine Argumente existieren, muß der () Operator angewendet werden, um dem Compiler die Assoziation von identifier und Methode mitzuteilen.

Array Operator []

[Bearbeiten]

Die Deklaration von Arrays erfolgt über diesen Operator. Auch der Zugriff auf einzelne Elemente von Arrays efolgt hierüber. Ein Beispiel:

double[] double_array = new double[15];

Ein Array mit 15 Elementen des numerischen primitive Typs "double" wird instanziert. Der identifier "double_array" ist zwar nur eine Referenz auf die Instanz des gesamten Arrays, über die Benutzung der [] Opearators kann jedoch auf einzelne Elemente zugegriffen werden. So liefert die Anweisung double_array[6] den Inhalt des sechsten Elements.

Statements

[Bearbeiten]

Statements sind Hilfsmittel für die Ablaufsteuerung in möglichst natürlicher Sprache. Diese Aussage mag sehr geschmeichelt erscheinen, aber Schleifenstrukturen in Maschinensprache sind bestimmt komplizierterer Natur als solche in einer Hochsprache. Natürlich kann Letztere mit einer natürlichen Sprache nicht verglichen werden, aber sie ist zumindest ein kleiner Schritt in die Richtung. In Java werden Statements in die folgenden vier grobe Kategorien unterteilt, die detailliert unter "Controls" aufgeführt sind:

  • Schleifen
while, do...while , for
  • Entscheidungen
if...else, switch...case
  • Ausnahmen
try...catch...finally, throw
  • Verzweigungen
break, continue, label:, return

Controls

[Bearbeiten]

Die Ablaufsteuerung von Programmen wird durch Control Statements gesteuert. Diese Anweisungen verkörpern das Paradigma der strukturierten Programmierung, welches erstmals in der Programmiersprache ALGOL (ALGOrithmicLanguage) verwirklicht wurde. Soviel zur Historie.

while Statement

[Bearbeiten]

Ein Block von Statements wird solange abgearbeitet und ggf. wiederholt, bis die while condition den Wert false liefert. Ist die Bedingung bereits vor Eintritt in den Block erfüllt, wird der Block nie betreten. Im hiesigen Sprachgebrauch hat sich auch der Begriff "abweisende Schleife" etabliert. Generelle Form:

while (expression) {
 statement(s);
}

Beispiel: Die Werte von 10 bis 0 sollen tabellarisch ausgegeben werden.

int val = 10;
 while (val > -1) {
  System.out.println( val--);
 }

do...while-Statement

[Bearbeiten]

Ähnlich des while-Statements verhält sich diese Schleifenkonstruktion. Hier wird ein Block solange abgearbeitet, wie die while-Bedingung erfüllt ist. Die Schleifenblock wird aber, anders als beim while-Statement, mindestens einmal durchlaufen. Die Schleife wird verlassen, wenn die while-Bedingung den Wert false liefert. Generelle Form:

do {
statement(s);
} while (expression);

Beispiel: Die Werte von 10 bis 0 sollen tabellarisch ausgegeben werden.

int val = 10;
 do
 {
   System.out.println( val--);
 } while (val >= 0);

for Statement

[Bearbeiten]

Schleifen, deren Bearbeitungsgrenzen feststehen, können auch als for-Schleifen (im Sinne von ... bis ...) gestaltet werden. Diese Form stellt eine Alternative zum while Statement dar, bietet aber außerdem die Kapselung der Initialisierungswerte und ggf. der Inkremetierungswerte an. Generelle Form:

for (init; terminate; increment) {
 statement(s);
}

Beispiel: Die Werte von 10 bis 0 sollen tabellarisch ausgegeben werden.

for (int i = 10; i >= 0;i--) {
 System.out.println( i);
}

Auffällig ist auch noch die gekapselte Deklaration des laufenden Index. Die im Beispiel verwendete Variable i existiert nach der Abarbeitung der Schleife praktisch nicht mehr. Genauer gesagt: Der Index ist nicht mehr erreichbar, denn es handelt sich um eine lokale Variable innerhalb des for Statements.

if und if...else Statements

[Bearbeiten]

Die einfachste Form der Ablaufsteuerung ist das if Statement. Wenn eine Bedingung erfüllt ist, dann ... Generelle Form:

if (expression) {
 statement(s);
}

Beispiel: Wenn ein String mit großen Buchstaben beginnt soll er ausgeben werden.

if (str.charAt(0).isUpperCase()){
 System.out.println( str);
}

Nun noch ein Blick auf den alternativen Zusatz "else": Generelle Form:

if(expression)
     {
      statement(s);
     }
else {
      statement(s);
     }

Beispiel: Wenn ein String mit großen Buchstaben beginnt soll er ausgeben werden, andernfalls soll "zu klein." erscheinen.

if   (str.charAt(0).isUpperCase())
     {
      System.out.println( str);
     }
else {
      System.out.println( "zu klein.");
     }

switch Statement

[Bearbeiten]

Eine besondere Form des if Statements stellt das Switch Statement dar. Unterschiedliche Werte ein und desselben Ausdrucks werden differenziert. Generelle Form:

switch (Expression) {
 Case constants: statement(s); break;
 Case constants: statement(s); break;
 ...
 default: statement(s);
}

Beispiel: Die Wochentage sollen entsprechend ihrer Nummerierung (1=Montag, ... 7=Sonntag) ausgegeben werden.

String str;
...
switch (daynum) {
 case 1:  str = "Montag";     break;
 case 2:  str = "Dienstag";   break;
 case 3:  str = "Mittwoch";   break;
 case 4:  str = "Donnerstag"; break;
 case 5:  str = "Freitag";    break;
 case 6:  str = "Samstag";    break;
 case 7:  str = "Sonntag";    break;
 default: str = "under construction";
}
System.out.println( str);

break Statement

[Bearbeiten]

Dieses Statement bewirkt das Verlassen eines Blocks. Sinnvoll wird es in Schleifen eingesetzt, um diese auf gewaltsame Weise zu verlassen. Unabdingbar ist dieses Statement auch bei der Anwendung des switch Statements, wie eben gezeigt.

Beispiel: Die Zeichen eines Strings unbekannter Länge sollen solange ausgegeben werden, bis ein Leerzeichen erkannt wird:

for(int i=0;;) {
char ch = str.charAt(i++);
 if  (ch != ' ')
      System.out.print( ch);
 else break;
}

Es besteht auch die Möglichkeit, das break Statement mit einem Ziel zu versehen. Der folgende Abschnitt zeigt diese Möglichkeit genauer.

label Statement

[Bearbeiten]

Ein Label oder ein Sprungziel ist zwar ein Anachronismus in modernen Programmiersprachen, trotzdem hält er sich tapfer. Hier soll nur kurz auf diese Möglichkeit eingegangen werden.

Beispiel: Wenn ein String das Zeichen '@' enthält, soll "at ", gefolgt vom Reststring ausgegeben werden.

int idx = 0;
while (i < str.length()) {
 if (str.charAt(i++) == '@') break mail;
}
...
mail: System.out.println( "at " + str.substring(i));

continue Statement

[Bearbeiten]

Mit dieser Anweisung wird das Überspringen von Blockabschnitten ermöglicht. Dieses Statement mag eine Alternative zu Statements der Form if...else sein.

Beispiel: In einem String sollen alle Leerzeichen durch '_' (underlines) ersetzt werden. Die Anzahl der ersetzten Zeichen soll ebenfalls ermittelt werden.

int numSpace = 0;
 for (int i=0; i < str.length(); i++) {
 if (str.charAt(i) != ' ') continue;
 numspace++;
 str.setCharAt( i, '_');
 }

return Statement

[Bearbeiten]

Dieses Statement erweist sich als ungemein praktisch um Methoden schnell und definiert zu verlassen. Damit kann jede Methode mit beliebig vielen Ausgängen versehen werden.

Beispiel: Ein String soll nach einer bestimmten Zeichenfolge durchsucht werden; das Ergebnis ist true oder false.

public boolean isInside( String str, String src) {
 return (str.indexOf( src) != 0);
}

exception Statements

[Bearbeiten]

Java erlaubt unter bestimmten Voraussetzungen die Anwendung der Versuch-und-Irrtum-Prinzips. So kann der Quelltext durchaus korrekt sein, aber bei der Ausführung des übersetzten Codes treten unerwartete Bedingungen auf. So könnte beispielsweise eine Internetverbindung während einer Übertragung unterbrochen werden, eine Datei während der Bearbeitung vom Systemadministrator umbenannt werden oder einfach nur eine Division durch Null stattfinden. Auf die Verwendung dieser Statements wird noch detailliert eingegangen, trotzdem sei hier bereits die generelle Form skizziert.

try     {
         statement(s);
        }
catch   (exceptiontype name)
        {
         statement(s);
        }
catch   (exceptiontype name)
        {
         statement(s);
        }
finally {
         statement(s);
        }

Blocks

[Bearbeiten]

Blocks - jetzt in deutsch Blöcke genannt - werden durch geschweifte Klammern (balanced curly braces) begrenzt und können Statements, Expressions und weitere Blöcke enthalten.

if   (val < 10)
     {
      System.out.println( "Wert kleiner als 10");
     }
else {
      System.out.println( "Wert nicht kleiner als 10");
     }

Zugegeben ein Beispiel, wie es minimalistischer kaum geht. Es zeigt jedoch zweierlei; nämlich die beiden Blöcke und außerdem verdeutlicht es die Tatsache, dass die curly brackets selbst kein Bestandteil der Blöcke sind. Diese Sequenz könnte auch ohne diese Klammern vom Compiler unterteilt werden. Die Klammern sind tatsächlich nur Delimiter und nicht im kompilierten Code vorhanden. Interessant werden Blöcke im Zusammenhang mit Variablendeklarationen. Hier treten interessante Aspekte auf, die oft zu Optimierungen eingesetzt werden.

if (value < limit)
   {
    Double number = new Double( (double)(value-limit));
    return number;
   }

Hier wird das Double-Objekt in dem Block erzeugt, in dem es benötigt wird.