Galileo Computing < openbook >
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


Java ist auch eine Insel (2. Aufl.) von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 1.4
Java ist auch eine Insel (2. Auflage)
gp Kapitel 2 Sprachbeschreibung
  gp 2.1 Anweisungen und Programme
  gp 2.2 Programme
    gp 2.2.1 Kommentare
    gp 2.2.2 Funktionsaufrufe als Anweisungen
    gp 2.2.3 Die leere Anweisung
    gp 2.2.4 Der Block
  gp 2.3 Elemente einer Programmiersprache
    gp 2.3.1 Textkodierung durch Unicode-Zeichen
    gp 2.3.2 Unicode-Tabellen unter Windows
    gp 2.3.3 Bezeichner
    gp 2.3.4 Reservierte Schlüsselwörter
    gp 2.3.5 Token
    gp 2.3.6 Semantik
  gp 2.4 Datentypen
    gp 2.4.1 Primitive Datentypen
    gp 2.4.2 Wahrheitswerte
    gp 2.4.3 Variablendeklarationen
    gp 2.4.4 Ganzzahlige Datentypen
    gp 2.4.5 Die Fließkommazahlen
    gp 2.4.6 Zeichen
    gp 2.4.7 Die Typanpassung (das Casting)
    gp 2.4.8 Lokale Variablen, Blöcke und Sichtbarkeit
    gp 2.4.9 Initialisierung von lokalen Variablen
  gp 2.5 Ausdrücke
    gp 2.5.1 Zuweisungsoperator und Verbundoperator
    gp 2.5.2 Präfix- oder Postfix-Inkrement und -Dekrement
    gp 2.5.3 Unäres Minus und Plus
    gp 2.5.4 Arithmetische Operatoren
    gp 2.5.5 Die relationalen Operatoren
    gp 2.5.6 Logische Operatoren
    gp 2.5.7 Reihenfolge und Rang der Operatoren in der Auswertungsreihenfolge
    gp 2.5.8 Was C(++)-Programmierer vermissen könnten
  gp 2.6 Bedingte Anweisungen oder Fallunterscheidungen
    gp 2.6.1 Die if-Anweisung
    gp 2.6.2 Die Alternative wählen mit einer if/else-Anweisung
    gp 2.6.3 Die switch-Anweisung bietet die Alternative
  gp 2.7 Schleifen
    gp 2.7.1 Die while-Schleife
    gp 2.7.2 Schleifenbedingungen und Vergleiche mit ==
    gp 2.7.3 Die do/while-Schleife
    gp 2.7.4 Die for-Schleife
    gp 2.7.5 Ausbruch planen mit break und Wiedereinstieg mit continue
    gp 2.7.6 break und continue mit Sprungmarken
  gp 2.8 Methoden einer Klasse
    gp 2.8.1 Bestandteil einer Funktion
    gp 2.8.2 Aufruf
    gp 2.8.3 Methoden ohne Parameter
    gp 2.8.4 Statische Methoden (Klassenmethoden)
    gp 2.8.5 Parameter und Wertübergabe
    gp 2.8.6 Methoden vorzeitig mit return beenden
    gp 2.8.7 Nicht erreichbarer Quellcode bei Funktionen
    gp 2.8.8 Rückgabewerte
    gp 2.8.9 Methoden überladen
    gp 2.8.10 Vorinitialisierte Parameter bei Funktionen
    gp 2.8.11 Finale lokale Variablen
    gp 2.8.12 Finale Referenzen in Objekten und das fehlende const
    gp 2.8.13 Rekursive Funktionen
    gp 2.8.14 Die Ackermann-Funktion
    gp 2.8.15 Die Türme von Hanoi
  gp 2.9 Weitere Operatoren
    gp 2.9.1 Bitoperationen
    gp 2.9.2 Vorzeichenlose Bytes in ein Integer und Char konvertieren
    gp 2.9.3 Variablen mit Xor vertauschen
    gp 2.9.4 Die Verschiebeoperatoren
    gp 2.9.5 Setzen, Löschen, Umdrehen und Testen von Bits
    gp 2.9.6 Der Bedingungsoperator
    gp 2.9.7 Überladenes Plus für Strings
  gp 2.10 Einfache Benutzereingaben


Galileo Computing

2.4 Datentypen  downtop

Java nutzt, wie es für imperative Programmiersprachen typisch ist, Variablen zum Ablegen von Daten. Eine Variable ist ein reservierter Speicherbereich und belegt eine feste Anzahl von Bytes. Alle Variablen (und auch Ausdrücke) haben einen Typ, der zur Übersetzungszeit bekannt ist. Der Typ wird auch Datentyp genannt, da eine Variable einen Datenwert, auch Datum genannt, hält. Für jeden Typ lässt sich die Speichergröße berechnen. Beispiele für Datentypen sind: Ganzzahlen, Fließkommazahlen, Wahrheitswerte, Zeichen und Objekte. Da jede Variable einen festen Datentyp hat und diesen nicht mehr ändern kann, zählt Java zu den strengtypisierten Sprachen. Der Datentyp erlaubt dem Übersetzer auch, die Daten im Speicher nach bestimmten Regeln zu behandeln. Wenn wir einen Speicherauszug lesen und dort die Bitinformationen 01011010, 11010010, 01010011, 10100010 finden, ist uns auch nicht klar, ob dies nun vier Buchstaben sind, eine Fließkommazahl oder eine Ganzzahl ist.

Die grobe Richtung

Die Datentypen in Java zerfallen in zwei Kategorien: primitive Typen und Referenztypen (auch Klassentypen). Die primitiven Typen sind die eingebauten Datentypen, die nicht als Objekte verwaltet werden. Referenztypen sind genau das, was noch übrig bleibt. Es gibt aber auch Sprachen, die keine primitiven Datentypen besitzen. Als Beispiel sei noch einmal auf das in der Fußnote angesprochene Smalltalk verwiesen.

Warum Sun sich für diese Teilung entschieden hat, lässt sich aus zwei Gründen erklären:

gp  Viele Programmierer kennen Syntax und Semantik von C(++) und anderen imperativen Programmiersprachen. Auf die neue Sprache Java zu wechseln fällt leicht, und das objektorientierte Denken aus C++ verhilft dazu, sich auf der Insel zurechtzufinden.
gp  Der andere Grund ist die Geschwindigkeit der häufig vorkommenden elementaren Rechenoperationen. Ein Compiler kann einerseits viele Optimierungen vornehmen, und das ist bei einer Sprache, die in der Industrie eingesetzt wird, ein wichtiges Kriterium. Die Klassen liefern andererseits die optimale Unterstützung für eine Wiederverwendung und skalierbare Produkte.

Wir werden uns im Folgenden erst mit primitiven Datentypen beschäftigen. Referenzen werden nur dann eingesetzt, wenn Objekte ins Spiel kommen. Dies dauert jedoch noch etwas.


Galileo Computing

2.4.1 Primitive Datentypen  downtop

In Java gibt es einige eingebaute Datentypen für ganze Zahlen, Gleitkommazahlen nach IEEE 754, Zeichen und Wahrheitswerte. (Strings werden bevorzugt behandelt, sind aber lediglich Verweise auf Objekte.) Die folgende Tabelle gibt darüber einen Überblick. Anschließend betrachten wir jeden Datentyp präziser.

Tabelle 2.4   Java-Datentypen und deren Wertebereiche
Schlüsselwort Länge in Bytes Belegung (Wertebereich)

boolean

1 true oder false

char

2 16-Bit Unicode Zeichen (0x0000...0xffff)

byte

1 -2^7 bis 2^7 – 1 (-128...127)

short

2 -2^15 bis 2^15 – 1 (-32768...32767)

int

4 -2^31 bis 2^31 – 1 (-2147483648...2147483647)

long

8 -2^63 bis 2^63 – 1 (-9223372036854775808...9223372036854775807)

float

4 1.40239846E-45f… 3.40282347E+38f

double

8 4.94065645841246544E-324... 1.79769131486231570E+308

Zwei wesentliche Punkte zeichnen die primitiven Datentypen aus:

gp  Alle Datentypen haben eine festgesetzte Länge, die sich unter keinen Umständen ändert. Der Nachteil, dass der Programmierer die Länge eines Datentyps nicht kennt, besteht in Java nicht. In den Sprachen C/C++ bleibt dies immer unsicher und die Umstellung auf 64-Bit-Maschinen bringt viele Probleme mit sich. Bei der Betrachtung der Auflistung fällt auf, dass char 16 Bit lang ist.
gp  Die numerischen Datentypen byte, short, int und long sind vorzeichenbehaftet, Fließkommazahlen sowieso. Dies ist leider nicht immer praktisch, aber wir müssen stets daran denken. Probleme gibt es, wenn wir einem Byte zum Beispiel den Wert 240 zuweisen wollen. Ein char ist im Prinzip ein vorzeichenloser Ganzzahltyp.

Galileo Computing

2.4.2 Wahrheitswerte  downtop

Der Datentyp boolean beschreibt einen Wahrheitswert, der entweder true oder false ist. Die Zeichenketten true und false sind reservierte Wörter und bilden so genannte Literale. Kein anderer Wert ist für Wahrheitswerte möglich.

Der boolesche Typ wird beispielsweise bei Bedingungen, Verzweigungen oder Schleifen benötigt.

Abbildung


Galileo Computing

2.4.3 Variablendeklarationen  downtop

Mit Variablen lassen sich Daten speichern, die vom Programm gelesen und geschrieben werden können. Um Variablen zu nutzen, müssen sie deklariert werden. Wir sprechen hier auch von der Definition einer Variablen. Die Schreibweise einer Variablendeklaration ist immer die gleiche: Hinter dem Typnamen folgt der Name der Variablen.

Typname Variablenname;

Ein Variablenname (der dann Bezeichner ist) kann alle Buchstaben und Ziffern des Unicode-Zeichensatzes beinhalten, mit der Ausnahme, dass am Anfang einer Zeichenkette keine Ziffer stehen darf. Ebenfalls darf der Variablenname mit keinem reservierten Schlüsselwort identisch sein.

Hinweis   Zwei Variablen ähnlicher Schreibweise, etwa counter und counters, führen schnell zur Verwirrung. Als Programmierer sollten wir uns konsistent an ein Namensschema halten.10 

Die Variablendeklaration ist eine Anweisung und wird daher mit einem Semikolon abgeschlossen.

Beispiel   Unicode-Sequenzen können vom Programmierer überall im Programm aufgenommen werden. Folgende Deklaration mit den Bezeichnernamen sind daher gleich:
double übelkübel;
double \u00FCbelk\u00FCbel;

Werden mehrere Variablen gleichen Typs bestimmt, so können diese mit einem Komma getrennt werden. Eine Deklaration kann in jeden Block geschrieben werden:

Typname Variablenname1[, Variablenname2, ... ]

Schreiben wir ein einfaches Programm, welches eine Wahrheitsvariable definiert und zuweist. Die Variablenbelegung erscheint zusätzlich auf dem Bildschirm.

Listing 2.3   FirstVariable.java

class FirstVariable
{
  public static void main( String args[] )
  {
    boolean trocken;

    trocken = true;
    System.out.print( "Ist die Socke trocken? " );
    System.out.println( trocken );
  }
}

Die Zeile trocken = true ist eine Zuweisung, die die Variable trocken mit einem Wert belegt. Sie ist ebenfalls eine Anweisung und wird mit einem Semikolon beendet. Steht auf der rechten Seite keine Variable, so steht dort ein Literal, eine Konstante, wie in unserem Fall true. Wir haben schon erwähnt, dass es für Wahrheitswerte nur die Literale true und false gibt.


Galileo Computing

2.4.4 Ganzzahlige Datentypen  downtop

Java stellt vier ganzzahlige Datentypen zur Verfügung: byte, short, int und long. Sie unterscheiden sich nur in der Länge, die jeweils 1, 2, 4 und 8 Byte umfasst. Die ganzzahligen Typen (lassen wir char einmal außen vor) sind in Java immer vorzeichenbehaftet und einen Modifizierer unsigned wie in C(++) gibt es nicht.

Beispiel   Variablendeklaration mit Wertinitialisierung
int schuhGröße;

int i = 1243, j = 01230, k = 0xcafebabe;

Den Variablen kann gleich bei der Definition ein Wert zugewiesen werden. Hinter einem Gleichheitszeichen wird der Wert geschrieben, der oft ein Literal ist. Eine Zuweisung gilt nur für immer genau eine Variable. Negative Zahlen werden durch Voran Stellen eines Minuszeichens gebildet. Ein Pluszeichen für positive Zeichen ist optional.

Die hexadezimalen und oktalen Zahlensysteme

Die Literale für Ganzzahlen lassen sich in drei unterschiedlichen Zahlensystemen angeben. Das natürlichste ist das Dezimalsystem, wie das Beispiel an der Variablen i zeigt. Die Literale bestehen aus den Ziffern »0« bis »9«. Zusätzlich existiert die Oktal- und Hexadezimalform, die die Zahlen zur Basis 8 und 16 schreiben. Ein oktaler Wert beginnt mit dem Präfix »0« und ein hexadezimaler Wert mit »0x«. Mit der Basis 8 werden nur die Ziffern »0« bis »7« für oktale Werte benötigt. Da zehn Ziffern für hexadezimale Zahlen nicht ausreichen, besteht eine Zahl zur Basis 16 zusätzlich aus den Buchstaben »a« bis »f« (beziehungsweise »A« bis »F«).

Der Datentyp long

Ganzzahlen doppelter Größe werden mit einem »l« oder »L« am Ende versehen.

Beispiel   Deklaration eines long mit angehängtem »L«
long l = 123456789098L, m = -1L, n = 0xC0B0L;

Betrachten wir folgende Zeile, so ist auf den ersten Blick kein Fehler zu erkennen:

System.out.println( 123456789012345 );

Der Übersetzungsvorgang fördert jedoch noch einmal zu Tage, dass alle Datentypen ohne explizite Größenangabe als int angenommen werden, dass heißt 32 Bit lang sind. Obige Zeile führt daher zu einem Compilerfehler, da die Zahl nicht im Wertebereich von –2147483648 bis 2147483647 liegt. Java reserviert also nicht so viele Bits wie benötigt und wählt nicht automatisch den passenden Wertebereich. Er muss ausdrücklich angegeben werden. Um die Zahl 123456789012345 gültig ausgeben zu lassen, müssen wir schreiben:

System.out.println( 123456789012345l );

Ersichtlich wird, dass ein kleines »l« sehr viel Ähnlichkeit mit der Ziffer Eins besitzt. Daher sollte bei Längenangaben immer ein großes »L« hinten angestellt werden.

System.out.println( 123456789012345L );

Allerdings ist das Compiler-Verhalten verwirrend, denn bei folgender Anweisung findet er auch automatisch die richtige Größe.

byte b = 12;

Galileo Computing

2.4.5 Die Fließkommazahlen  downtop

Java unterscheidet Fließkommazahlen einfacher Genauigkeit (float) und doppelter Genauigkeit (double). Die Datentypen sind im IEEE-754-Standard beschrieben und haben eine Länge von 4 Byte für float und 8 Byte für double.

Die Literale bestehen aus einem Vorkommateil, einem Dezimalpunkt (kein Komma) und einem Nachkommateil. Optional kann ein Exponent angegeben werden. Standardmäßig sind die Literale vom Typ double. Ein nachgestelltes»f« (oder »F«) zeigt an, dass es sich um ein float handelt. Vorkommateil und Exponent dürfen durch die Vorzeichen »+« oder »–« eingeleitet werden.

Beispiel   Gültige Zuweisungen für Fließkommazahlen vom Typ double und float:
double pi = 3.1415, klein = .001, x = 3.00e+8;
float y = 3.00E+8F;

Der Exponent kann entweder positiv oder negativ, muss aber eine Ganzzahl sein.


Galileo Computing

2.4.6 Zeichen  downtop

Der Datentyp char ist 2 Bytes groß und nimmt ein Unicode-Zeichen auf. Ein char ist nicht vorzeichenbehaftet. Die Literale für Zeichen werden in einfache Hochkommata gesetzt. Spracheinsteiger verwechseln häufig die einfachen Hochkommata mit den Anführungszeichen der Zeichenketten (Strings). Die einfache Merkregel: Ein Zeichen – ein Hochkomma, mehrere Zeichen – zwei Hochkommata (Gänsefüßchen).

Beispiel   Korrekte Hochkommata für Zeichen und Zeichenketten:
char   c = 'a';
String s = "Heut' schon gebeckert?";

Escape-Sequenzen/Fluchtsymbole

Für spezielle Zeichen stehen Escape-Sequenzen zur Verfügung, die so nicht direkt als Zeichen dargestellt werden können.

Tabelle 2.5   Escape-Sequenzen
Zeichen Bedeutung

\b

Rückschritt (Backspace)

\n

Zeilenschaltung (Newline)

\f

Seitenumbruch (Formfeed)

\r

Wagenrücklauf (Carriage return)

\t

Horizontaler Tabulator

\"

Doppeltes Anführungszeichen

\'

Einfaches Anführungszeichen

\\

Backslash

Beispiel   Zeichenvariablen mit Initialwerten und Sonderzeichen:
char          a = 'a',
    singlequote = '\'',
        newline = '\n',
Die Fluchtsymbole sind für Zeichenketten die gleichen. Auch dort können bestimmte Zeichen mit Escape-Sequenzen dargestellt werden.

Beispiel   String s = "Er fragte: \"Wer lispelt wie Katja Burkard?\"";


Galileo Computing

2.4.7 Die Typanpassung (das Castingdowntop

Möglicherweise kommt es vor, dass Datentypen konvertiert werden müssen. Dies nennt sich Typanpassung (engl. Typecast) oder auch casten. Java unterscheidet zwei Arten der Typanpassung:

gp  Automatische Typanpassung
Daten eines kleineren Datentyps werden automatisch dem größeren angepasst.
gp  Explizite Typanpassung
Ein größerer Typ kann einem kleineren Typ nur mit Verlust von Informationen zugewiesen werden.

Automatische Anpassung der Größe

Werte der Datentypen byte und short werden bei Rechenoperationen automatisch in den Datentyp int umgewandelt. Ist ein Operand vom Datentyp long, dann werden alle Operanden auf long erweitert. Wird aber short oder byte als Ergebnis verlangt, dann ist dieses durch einen expliziten Typecast anzugeben und nur die niederwertigen Bits des Ergebniswerts werden übergeben. Folgende Typumwandlungen sind ohne Informationsverlust möglich:

Tabelle 2.6   Zuweisungen ohne Informationsverlust
Von Typ in Typ
byte short, char, int, long, float, double
short int, long, float, double
char int, long, float, double
int long, float, double
long float, double
float double

Explizite Typanpassung

Der gewünschte Typ für eine Typanpassung wird vor den umzuwandelnden Datentyp geschrieben. Der gewollte Datentyp ist geklammert.

Beispiel   Umwandlung einer Fließkommazahl in eine Ganzzahl:
int n = (int) 3.1415;

Eine Typumwandlung hat eine sehr hohe Priorität. Daher muss der Ausdruck gegebenenfalls geklammert werden.

Beispiel   Die Zuweisung an n verfehlt das Ziel.
int n = (int) 1.0315 + 2.1;
int m = (int)(1.0315 + 2.1);       // das ist korrekt

Typumwandlung von Fließkomma- zu Ganzzahlen

Bei der expliziten Typumwandlung von double und float in einen Ganzzahltyp kann es selbstverständlich zum Verlust von Genauigkeit kommen sowie zur Einschränkung des Wertebereichs. Bei der Konvertierung von Fließkommazahlen verwendet Java eine Rundung gegen Null.

Beispiel   Zahlen, die bei der Konvertierung die Rundung nach Null aufzeigen.
double w = +12.34;
double x = +67.89;
double y = -12.34;
double z = -67.89;
System.out.println( (int) w );  // 12
System.out.println( (int) x );  // 67
System.out.println( (int) y );  // -12
System.out.println( (int) z );  // -67

Bei der Konvertierung eines größeren Ganzzahltyps in einen kleineren werden einfach die oberen Bits abgeschnitten. Eine Anpassung des Vorzeichens findet nicht statt.

int i = 123456789;
int j = -123456;
System.out.println( (short) i );  // -13035
System.out.println( (short) j );  // 7616

short und char

Ein short hat wie ein char eine Länge von 16 Bit. Doch diese Umwandlung ist nicht ohne ausdrückliche Konvertierung möglich. Das liegt am Vorzeichen von short. Zeichen sind per Definition immer ohne Vorzeichen. Würde ein char mit einem gesetzten höchstwertigen letzten Bit in ein short konvertiert, käme eine negative Zahl heraus. Ebenso wäre, wenn ein short eine negative Zahl bezeichnet, das oberste Bit im char gesetzt, was unerwünscht ist. Die ausdrückliche Umwandlung erzeugt immer nur positive Zahlen.

Der Verlust bei der Typumwandlung von char nach short tritt etwa bei der Han-Zeichenkodierung für chinesische, japanische oder koreanische Zeichen auf. Denn dort ist im Unicode das erste Bit gesetzt, welches bei der Umwandlung in ein short dem nicht gesetzten Vorzeichenbit weichen muss.

Typanpassungen von int und char

Die Methode printXXX() reagiert auf die Typen char und int, und eine Typumwandlung führt zu der gewünschten Ausgabe.

int c1 = 65;
char c2 = 'A';

System.out.println( c1 );            // 65
System.out.println( (int)c2 );       // 65
System.out.println( (char)c1 );      // A
System.out.println( c2 );            // A
System.out.println( (char)(c1+1) );  // B
System.out.println( c2+1 );          // 66

Einen Ganzzahlwert in einem int können wir als Zeichen ausgeben, genauso wie eine char-Variable als Zahlenwert. Wir sollten beachten, dass eine mathematische Operation auf char-Typen zu einem int führt.

Probleme bei Zuweisungen

Leider ist die Typanpassung nicht ganz so einleuchtend, wie folgendes Beispiel zeigt.

Listing 2.4   AutoConvert.java

public class AutoConvert
{
  public static void main( String args[] )
  {
    int   i1 = 1, i2 = 2, i3;
    long  l1 = 1, l2 = 2, l3;
    short s1 = 1, s2 = 2, s3;
    byte  b1 = 1, b2 = 2, b3;
    i3 = i1 + i2;              // das ist noch OK
    l3 = l1 + l2;
//    s3 = s1 + s2;              // Compilerfehler!
//    b3 = b1 + b2;
    s3 = (short) ( s1 + s2 );  // das ist wieder OK
    b3 = (byte)  ( b1 + b2 );
  }
}

Dies ist auf den ersten Blick paradox. Es ist nicht möglich, ohne explizite Typumwandlung zwei short- oder byte-Zahlen zu addieren. Das Verhalten des Übersetzers lässt sich mit der automatischen Anpassung erklären. Wenn Ganzzahl-Ausdrücke vom Typ kleiner int mit einem Operator verbunden werden, passt der Compiler eigenmächtig den Typ auf int an. Die Addition der beiden Zahlen arbeitet also nicht mit short- oder byte-Werten, sondern int-Werten. So werden auch Überläufe korrekt behandelt.

Bei der Zuweisung wird dies zum Problem. Denn dann steht auf der rechten Seite ein int und auf der linken Seite der kleinere Typ byte oder short. Nun muss der Compiler meckern, da Zahlen abgeschnitten werden könnten. Mit der ausdrücklichen Typumwandlung erzwingen wir diese Konvertierung und akzeptieren ein paar fehlende Bits. Diese Eigenart ist insofern verwunderlich, als dass doch auch ein int nur dann zu einem long erweitert wird, wenn einer der Operanden eines Ausdrucks vom Typ long ist.

Materialverlust durch Überläufe

Überläufe bei Berechnungen können zu schwer wiegenden Fehlern führen, so wie beim Absturz der Ariane 5 am 4. Juni 1996 genau 36.7 Sekunden nach dem Start. Die europäische Raumfahrtbehörde European Space Agency (ESA) startete von Französisch-Guyana aus eine unbemannte Rakete mit vier Satelliten an Bord, die 40 Sekunden nach dem Start explodierte. Glücklicherweise kamen keine Menschen ums Leben, doch der materielle Schaden belief sich auf etwa 500 Millionen US-Dollar. In dem Projekt steckten zusätzlich Entwicklungskosten von etwa 7 Milliarden US-Dollar. Grund für den Absturz war ein Rundungsfehler, der durch die Umwandlung einer 64-Bit-Fließkommazahl (die horizontale Geschwindigkeit) in eine vorzeichenbehaftete 16-Bit-Ganzzahl auftrat. Die Zahl war leider größer als 215  und die Umwandlung war nicht gesichert, da die Programmierer diesen Zahlenbereich nicht angenommen hatten. Die Konsequenz war, dass das Lenksystem zusammenbrach und die Selbstzerstörung ausgelöst wurde, da die Triebwerke abzubrechen drohten. Das wirklich Dumme an dieser Geschichte ist, dass die Software nicht unbedingt für den Flug notwendig war und nur den Startvorbereitungen diente. Im Fall einer Unterbrechung während des Countdowns hätte dann das Programm schnell abgebrochen werden können. Ungünstig war, dass der Programmteil unverändert durch Wiederverwendung per Copy-and-Paste aus der Ariane 4-Software kopiert worden war, die Ariane 5 aber schneller flog.


Galileo Computing

2.4.8 Lokale Variablen, Blöcke und Sichtbarkeit  downtop

In jedem Block und auch in jeder Klasse können Variablen deklariert werden. Globale Variablen, die für alle Funktionen und Klassen sichtbar sind, gibt es in Java nicht. (Eine globale Variable müsste in einer Klasse definiert werden, die dann alle Klassen übernehmen.)

Sichtbarkeit

Jede Variable hat einen Geltungsbereich (engl. Scope). Sie sind nur in dem Block gültig, in dem sie definiert wurden. In dem Block ist die Variable lokal.

Beispiel   Da ein Block immer mit geschweiften Klammern angegeben wird, erzeugen wir durch folgende Funktionen Blöcke, die einen weiteren inneren Block besitzen. Somit sind Blöcke ineinander geschachtelt.
void foo()
{
  int i;
  {
    int j;                // j gilt nur in dem Block
    j = 1;
  }
//  j = 2;                // Funktioniert auskommentiert nicht
}
void bar()
{
  int i, k;               // i hat mit oberem i nichts zu tun
  {
//    int k;              // Das würde nicht gehen!
  }
}

Zu jeder Zeit können Blöcke definiert werden. Außerhalb des Blocks sind deklarierte Variablen nicht sichtbar. Nach Abschluss des inneren Blocks, der j deklariert, ist ein Zugriff auf j nicht mehr möglich; auf i ist der Zugriff weiterhin erlaubt. Falls Objekte im Block angelegt wurden, wird der GC diese wieder freigeben, falls keine zusätzliche Referenz besteht.

Variablennamen können innerhalb eines Blocks nicht genauso gewählt werden wie lokale Variablennamen eines äußeren Blocks oder wie die Namen für die Parameter einer Funktion. Das zeigt zum Beispiel die Definition der Variablen k. Obwohl andere Programmiersprachen das erlauben, haben sich die Java-Sprachentwickler dagegen entschieden, um Fehlerquellen zu vermeiden.


Galileo Computing

2.4.9 Initialisierung von lokalen Variablen  toptop

Während Objektvariablen automatisch mit einem Null-Wert initialisiert werden, geschieht dies bei lokalen Variablen nicht. Das heißt, der Programmierer muss sich selbst um die Initialisierung kümmern.

Beispiel   Häufig passieren Fehler bei falsch angewendeten bedingten Anweisungen, wie das folgende Programmsegment demonstriert.
void test()
{
  int nene, williWurm;
  nene += 1;                   // Compilerfehler
  nene = 0; nene = nene + 1;
  if ( nene == 1 )
    williWurm = 2;
  williWurm = williWurm + 1;   // Compilerfehler
}

Die beiden lokalen Variablen nene und williWurm werden nicht automatisch mit Null initialisiert – so wie dies für Objektvariablen der Fall ist. So kommt es bei der Inkrementierung von nene zu einem Compilerfehler. Denn dazu ist erst ein Lesezugriff auf die Variable nötig, um anschließend den Wert 1 zu addieren. Der erste Zugriff muss aber eine Zuweisung sein. Das bedeutet, nene=0 ist in Ordnung. Den Fehler würden wir auch bekommen, wenn wir in System.out.println(nene) die Variablenbelegung auslesen würden.

Oftmals gibt es jedoch bei Zuweisungen in bedingten Anweisungen Probleme. Da williWurm nur nach der if-Abfrage auf den Wert 2 gesetzt wird, wäre nur unter der Bedingung nene gleich 2 ein Lesezugriff auf williWurm möglich. Da diese Variable jedoch sonst vorher nicht gesetzt wurde, ergäbe sich das oben angesprochene Problem.






1   Im Gegensatz dazu steht Smalltalk. In Smalltalk sind zuerst einmal alles Objekte und diese haben keinen Typ. Die Operationen werden erst zur Laufzeit an die Objekte gebunden.

2   In C(++) bedeutet Definition und Deklaration etwas Verschiedenes. In Java kennen wir diesen Unterschied nicht und betrachten daher beide Begriffe als gleichwertig.

3   Eine Software wie Mathematica warnt vor Variablen mit fast identischem Namen.

4   In Java bilden long und short einen eigenen Datentyp. Sie dienen nicht wie in C(++) als Modifizierer. Eine Deklaration wie long int ist also falsch.

5   LOGO verwendet für negative Exponenten den Buchstaben N anstelle des E. In Java bleibt das E mit einem folgenden unären Plus- oder Minus-Zeichen.

6   Die so genannten Objektvariablen oder Klassenvariablen, doch dazu später.





Copyright © Galileo Press GmbH 2003
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de