Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Generics, innere Klassen
10 Die Klassenbibliothek
11 Threads und nebenläufige Programmierung
12 Datenstrukturen und Algorithmen
13 Raum und Zeit
14 Dateien und Datenströme
15 Die eXtensible Markup Language (XML)
16 Grafische Oberflächen mit Swing
17 Grafikprogrammierung
18 Netzwerkprogrammierung
19 Verteilte Programmierung mit RMI und Web–Services
20 JavaServer Pages und Servlets
21 Applets
22 Midlets und die Java ME
23 Datenbankmanagement mit JDBC
24 Reflection und Annotationen
25 Logging und Monitoring
26 Sicherheitskonzepte
27 Java Native Interface (JNI)
28 Dienstprogramme für die Java-Umgebung
Stichwort

Download:
- ZIP, ca. 14,1 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java ist auch eine Insel (8. Auflage) von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel (8. Auflage)

Java ist auch eine Insel (8. Aufl.)
8., aktual. Auflage, geb., mit DVD
1.475 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1371-4
Pfeil 2 Sprachbeschreibung
Pfeil 2.1 Elemente der Programmiersprache Java
Pfeil 2.1.1 Token
Pfeil 2.1.2 Textkodierung durch Unicode-Zeichen
Pfeil 2.1.3 Literale
Pfeil 2.1.4 Bezeichner
Pfeil 2.1.5 Reservierte Schlüsselwörter
Pfeil 2.1.6 Kommentare
Pfeil 2.1.7 Die API-Dokumentation
Pfeil 2.2 Anweisungen formen Programme
Pfeil 2.2.1 Anweisungen
Pfeil 2.2.2 Eine Klasse bildet den Rahmen
Pfeil 2.2.3 Die Reise beginnt am main()
Pfeil 2.2.4 Funktionsaufrufe als Anweisungen
Pfeil 2.2.5 print(), println() und printf() für Bildschirmausgaben
Pfeil 2.2.6 Ausdrucksanweisung
Pfeil 2.2.7 Erste Idee der Objektorientierung
Pfeil 2.2.8 Modifizierer
Pfeil 2.2.9 Anweisungen und Blöcke
Pfeil 2.3 Datentypen, Typisierung, Variablen und Zuweisungen
Pfeil 2.3.1 Primitive Datentypen im Überblick
Pfeil 2.3.2 Variablendeklarationen
Pfeil 2.3.3 Zuweisungsoperator
Pfeil 2.3.4 Variablendeklaration mit Wertinitialisierung
Pfeil 2.3.5 Wahrheitswerte
Pfeil 2.3.6 Ganzzahlige Datentypen
Pfeil 2.3.7 Die Fließkommazahlen float und double
Pfeil 2.3.8 Alphanumerische Zeichen
Pfeil 2.3.9 Gute Namen, schlechte Namen
Pfeil 2.4 Blöcke, Initialisierung und Sichtbarkeit
Pfeil 2.4.1 Blöcke und Anweisungen
Pfeil 2.4.2 Initialisierung von lokalen Variablen
Pfeil 2.4.3 Sichtbarkeit und Gültigkeitsbereich
Pfeil 2.5 Ausdrücke, Operanden und Operatoren
Pfeil 2.5.1 Arithmetische Operatoren
Pfeil 2.5.2 Unäres Minus und Plus
Pfeil 2.5.3 Zuweisung mit Operation
Pfeil 2.5.4 Präfix- oder Postfix-Inkrement und -Dekrement
Pfeil 2.5.5 Die relationalen Operatoren und die Gleichheitsoperatoren
Pfeil 2.5.6 Logische Operatoren Und, Oder, Xor, Nicht
Pfeil 2.5.7 Rang der Operatoren in der Auswertungsreihenfolge
Pfeil 2.5.8 Die Typanpassung (das Casting)
Pfeil 2.5.9 Überladenes Plus für Strings
Pfeil 2.6 Bedingte Anweisungen oder Fallunterscheidungen
Pfeil 2.6.1 Die if-Anweisung
Pfeil 2.6.2 Die Alternative mit einer if/else-Anweisung wählen
Pfeil 2.6.3 Die switch-Anweisung bietet die Alternative
Pfeil 2.7 Schleifen
Pfeil 2.7.1 Die while-Schleife
Pfeil 2.7.2 Die do-while-Schleife
Pfeil 2.7.3 Die for-Schleife
Pfeil 2.7.4 Schleifenbedingungen und Vergleiche mit ==
Pfeil 2.7.5 Ausbruch planen mit break und Wiedereinstieg mit continue
Pfeil 2.7.6 break und continue mit Sprungmarken
Pfeil 2.8 Methoden einer Klasse
Pfeil 2.8.1 Bestandteil einer Funktion
Pfeil 2.8.2 Signatur-Beschreibung in der Java-API
Pfeil 2.8.3 Aufruf einer Methode
Pfeil 2.8.4 Methoden ohne Parameter deklarieren
Pfeil 2.8.5 Statische Methoden (Klassenmethoden)
Pfeil 2.8.6 Parameter, Argument und Wertübergabe
Pfeil 2.8.7 Methoden vorzeitig mit return beenden
Pfeil 2.8.8 Nicht erreichbarer Quellcode bei Funktionen
Pfeil 2.8.9 Rückgabewerte
Pfeil 2.8.10 Methoden überladen
Pfeil 2.8.11 Vorgegebener Wert für nicht aufgeführte Argumente
Pfeil 2.8.12 Finale lokale Variablen
Pfeil 2.8.13 Rekursive Methoden
Pfeil 2.9 Weitere Operatoren
Pfeil 2.9.1 Bits und Bytes
Pfeil 2.9.2 Operationen auf Bit-Ebene
Pfeil 2.9.3 Die Verschiebeoperatoren
Pfeil 2.9.4 Ein Bit setzen, löschen, umdrehen und testen
Pfeil 2.9.5 Bit-Funktionen der Integer- und Long-Klasse
Pfeil 2.9.6 Der Bedingungsoperator
Pfeil 2.9.7 Operator vermisst
Pfeil 2.10 Einfache Benutzereingaben
Pfeil 2.11 Zum Weiterlesen


Galileo Computing - Zum Seitenanfang

2.5 Ausdrücke, Operanden und Operatoren Zur nächsten ÜberschriftZur vorigen Überschrift

Beginnen wir mit mathematischen Ausdrücken, um dann die Schreibweise in Java zu ermitteln. Eine mathematische Formel, etwa der Ausdruck –27 * 9, besteht aus Operanden (engl. operand) und Operatoren (engl. operator). Ein Operand ist eine Variable oder ein Literal. Im Fall einer Variablen wird der Wert aus der Variablen ausgelesen und mit ihm die Berechnung durchgeführt.


Beispiel Ein Ausdruck mit Zuweisungen:

int i = 12, j; 
j = i * 2;

Die Multiplikation berechnet das Produkt von 12 und 2 und speichert das Ergebnis in j ab. Von allen primitiven Variablen, die in dem Ausdruck vorkommen, wird also der Wert ausgelesen und in den Ausdruck eingesetzt. [Es gibt Programmiersprachen, in denen Wertoperationen besonders gekennzeichnet werden. So etwa in LOGO. Eine Wertoperation schreibt sich dort mit einem Doppelpunkt vor der Variablen, etwa :X + :Y.]


Dies nennt sich auch Wertoperation, da der Wert der Variablen betrachtet wird und nicht ihr Speicherort oder gar ihr Variablenname.

Die Arten von Operatoren

Operatoren verknüpfen die Operanden. Je nach Anzahl der Operanden unterscheiden wir:

  • Ist ein Operator auf genau einem Operand definiert, so nennt er sich unärer Operator (oder einstelliger Operator). Das Minus (negatives Vorzeichen) vor einem Operand ist ein unärer Operator, da er für genau den folgenden Operanden gilt.
  • Die üblichen Operatoren Plus, Minus, Mal und Geteilt sind binäre (zweistellige) Operatoren.
  • Es gibt auch einen Fragezeichenoperator für bedingte Ausdrücke, der dreistellig ist.

Ausdrücke

Ein Ausdruck (engl. expression) ergibt bei der Auswertung ein Ergebnis. Dieser Wert wird auch Resultat genannt. Ausdrücke haben immer einen Wert, während das für Anweisungen (wie eine Schleife) nicht gilt. Daher kann ein Ausdruck an allen Stellen stehen, an denen ein Wert benötigt wird. Dieser Wert ist entweder ein

  • numerischer Wert (von arithmetischen Ausdrücken), Wahrheitswert (boolean) oder eine
  • Referenz (etwa von einer Objekt-Erzeugung).

Operatoren erlauben die Verbindung einzelner Ausdrücke zu neuen Ausdrücken. Einige Operatoren sind aus der Schule bekannt, wie Addition, Vergleich, Zuweisung und weitere. C(++)-Programmierer werden viele Freunde wiedererkennen.


Galileo Computing - Zum Seitenanfang

2.5.1 Arithmetische Operatoren Zur nächsten ÜberschriftZur vorigen Überschrift

Ein arithmetischer Operator verknüpft die Operanden mit den Operatoren Addition (+), Subtraktion (–), Multiplikation (*) und Division (/). Zusätzlich gibt es den Restwert-Operator %, der den bei der Division verbleibenden Rest betrachtet. Alle Operatoren sind für ganzzahlige Werte sowie für Fließkommazahlen definiert. [In C sind sie nur für Ganzzahlen definiert.] Die arithmetischen Operatoren sind binär, und auf der linken und rechten Seite sind die Typen numerisch. Der Ergebnistyp ist ebenfalls numerisch.

Numerische Umwandlung

Bei Ausdrücken mit unterschiedlichen numerischen Datentypen, etwa int und double, bringt der Compiler vor der Anwendung der Operation alle Operanden auf den umfassenderen Typ. Vor der Auswertung von 1 + 2.0 wird somit die Ganzzahl 1 zum double konvertiert und dann die Addition vorgenommen – das Ergebnis ist auch vom Typ double. Das nennt sich numerische Umwandlung (engl. numeric promotion). Bei byte und short gilt die Sonderregelung, dass sie vorher auf int konvertiert werden. [http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#26917] (Auch im Java-Bytecode gibt es keine arithmetischen Operationen auf byte, short und char.) Anschließend wird die Operation ausgeführt, und der Ergebnistyp entspricht dem umfassenderen Typ.

Der Divisionsoperator

Der binäre Operator »/« bildet den Quotienten aus Dividend und Divisor. Auf der linken Seite steht der Dividend und auf der rechten der Divisor. Die Division ist für Ganzzahlen und für Fließkommazahlen definiert. Bei der Ganzzahldivision wird zu null hin gerundet, und das Ergebnis ist keine Fließkommazahl, sodass 1/3 das Ergebnis 0 ergibt und nicht 0,333... Den Datentyp des Ergebnisses bestimmen die Operanden und nicht der Operator. Soll das Ergebnis vom Typ double sein, muss ein Operand ebenfalls double sein.

System.out.println( 1.0 / 3 );          // 0.3333333333333333 
System.out.println( 1   / 3.0 );        // 0.3333333333333333 
System.out.println( 1 / 3 );            // 0

Schon die Schulmathematik lehrte uns, dass die Division durch null nicht erlaubt ist. Führen wir in Java eine Ganzzahldivision mit dem Divisor 0 durch, so bestraft uns Java mit einer ArithmeticException. Bei Fließkommazahlen liefert eine Division durch 0 keine Ausnahme, sondern +/– unendlich und bei 0.0/0.0 den Sonderwert NaN (mehr dazu in Kapitel 5, »Mathematisches«). Ein NaN steht für Not a Number (auch schon manchmal »Unzahl« genannt) und wird vom Prozessor erzeugt, falls er eine mathematische Operation wie die Division durch null nicht durchführen kann. In Kapitel 5 werden wir auf NaN noch einmal zurückkommen.


Anekdote Beim Lenkraketenkreuzer USS Yorktown gab ein Mannschaftsmitglied aus Versehen die Zahl Null ein. Das führte zu einer Division durch null, und der Fehler pflanzte sich so weit fort, dass die Software abstürzte und das Antriebssystem stoppte. Das Schiff trieb mehrere Stunden antriebslos im Wasser.


Der Restwert-Operator %

Eine Ganzzahldivision muss nicht unbedingt glatt aufgehen, wie im Fall 9/2. In diesem Fall gibt es den Rest 1. Diesen Rest liefert der Restwert-Operator (engl. remainder operator), oft auch Modulo genannt. (Mathematiker unterscheiden die beiden Begriffe Rest und Modulo, da ein Modulo nicht negativ ist, der Rest in Java aber schon. Das soll uns aber egal sein.)

System.out.println( 9 % 2 );            // 1

Im Gegensatz zu C(++) [Wir müssten in C(++) die Funktion fmod() benutzen.] ist der Restwert-Operator in Java auch auf Fließkommazahlen anwendbar, und die Operanden können negativ sein. [Die Sprachdefinition von C(++) schreibt bei der Division und beim Modulo mit negativen Zahlen keine Berechnungsmethode vor.]

System.out.println( 12. % 2.5 );        // 2.0

Die Division und der Restwert richten sich in Java nach einer einfachen Formel: (int)(a/b)*b + (a%b) = a.


Beispiel Die Gleichung ist erfüllt, wenn wir etwa a = 10 und b = 3 wählen. Es gilt:

(int)(10/3) = 3 und 10 % 3 ergibt 1. Dann ergeben 3 * 3 + 1 = 10.


Aus dieser Gleichung folgt, dass beim Restwert das Ergebnis nur dann negativ ist, wenn der Dividend negativ ist; er ist nur dann positiv, wenn der Dividend positiv ist. Es ist leicht einzusehen, dass das Ergebnis der Restwert-Operation immer echt kleiner ist als der Wert des Divisors. Wir haben den gleichen Fall wie bei der Ganzzahldivision, dass ein Divisor mit dem Wert 0 eine ArithmeticException auslöst und bei Fließkommazahlen zum Ergebnis NaN führt.

Listing 2.5 RemainerAndDivDemo.java, main()

System.out.println( "+5 % +3 = " + (+5 % +3) );  //  2 
System.out.println( "+5 / +3 = " + (+5 / +3) );  //  1 
 
System.out.println( "+5 % –3 = " + (+5 % –3) );  //  2 
System.out.println( "+5 / –3 = " + (+5 / –3) );  // –1 
 
System.out.println( "-5 % +3 = " + (-5 % +3) );  //2 
System.out.println( "-5 / +3 = " + (-5 / +3) );  // –1 
 
System.out.println( "-5 % –3 = " + (-5 % –3) );  //2 
System.out.println( "-5 / –3 = " + (-5 / –3) );  //  1

Gewöhnungsbedürftig ist die Tatsache, dass der erste Operand (Dividend) das Vorzeichen des Restes definiert und niemals der zweite (Divisor).


Hinweis Um mit value % 2 == 1 zu testen, ob value eine ungerade Zahl ist, muss value positiv sein, denn –3 % 2 wertet Java zu –1 aus. Der Test auf ungerade Zahlen wird erst wieder korrekt mit value % 2 != 0.


Restwert für Fließkommazahlen und IEEEremainder()

Über die oben genannte Formel können wir auch bei Fließkommazahlen das Ergebnis einer Restwert-Operation leicht berechnen. Dabei muss beachtet werden, dass sich der Operator nicht so wie unter IEEE 754 verhält. Denn diese Norm schreibt vor, dass die Restwert-Operation den Rest von einer rundenden Division berechnet und nicht von einer abschneidenden. So wäre das Verhalten nicht analog zum Restwert bei Ganzzahlen. Java definiert den Restwert jedoch bei Fließkommazahlen genauso wie den Restwert bei Ganzzahlen. Wünschen wir ein Restwert-Verhalten, wie es IEEE 754 vorschreibt, so können wir immer noch die Bibliotheksfunktion Math.IEEEremainder() verwenden.

Auch bei der Restwert-Operation bei Fließkommazahlen werden wir niemals eine Exception erwarten. Eventuelle Fehler werden, wie im IEEE-Standard beschrieben, mit NaN angegeben. Ein Überlauf oder Unterlauf kann zwar passieren, aber nicht geprüft werden.

Rundungsfehler

Prinzipiell sollten Anweisungen wie 1.1 – 0.1 immer 1.0 ergeben, jedoch treten interne Rundungsfehler bei der Darstellung auf und lassen das Ergebnis von Berechnung zu Berechnung immer ungenauer werden. Ein besonders ungünstiger Fehler trat 1994 beim Pentium-Prozessor im Divisionsalgorithmus Radix-4 SRT auf, ohne dass der Programmierer der Schuldige war.

double x, y, z; 
x = 4195835.0; 
y = 3145727.0; 
z = x – (x/y) * y; 
System.out.println( z );

Ein fehlerhafter Prozessor liefert hier 256, obwohl laut Rechenregel das Ergebnis 0 sein muss. Laut Intel sollte für einen normalen Benutzer (Spieler, Softwareentwickler, Surfer?) der Fehler nur alle 27.000 Jahre auftauchen. Glück für die meisten. Eine Studie von IBM errechnete eine Fehlerhäufigkeit von einmal in 24 Tagen. Alles in allem hat Intel die CPUs zurückgenommen, über 400 Millionen US-Dollar verloren und spät den Kopf gerade noch aus der Schlinge gezogen.

Die meisten Rundungsfehler resultieren aber daher, dass endliche Dezimalbrüche im Rechner als Näherungswerte für periodische Binärbrüche repräsentiert werden müssen. 0.1 entspricht einer periodischen Mantisse im IEEE-Format.


Galileo Computing - Zum Seitenanfang

2.5.2 Unäres Minus und Plus Zur nächsten ÜberschriftZur vorigen Überschrift

Die binären Operatoren sitzen zwischen zwei Operanden, während sich ein unärer Operator genau einen Operanden vornimmt. Das unäre Minus (Operator zur Vorzeichenumkehr) etwa dreht das Vorzeichen des Operanden um. So wird aus einem positiven Wert ein negativer und aus einem negativen ein positiver.


Beispiel Drehe das Vorzeichen einer Zahl um:

a = –a;

Alternativ ist:

a = –1 * a;

Das unäre Plus ist eigentlich unnötig; die Entwickler haben es jedoch aus Symmetriegründen mit eingeführt.


Beispiel Minus und Plus sitzen direkt vor dem Operanden, und der Compiler weiß selbstständig, ob dies unär oder binär ist. Der Compiler erkennt auch folgende Konstruktion:

int i = – – – 2 + – + 3;

Dies ergibt den Wert –5. Einen Ausdruck wie –––2+–+3 erkennt der Compiler dagegen nicht an, da die zusammenhängenden Minuszeichen als Inkrement interpretiert werden und nicht als unärer Operator. Das Leerzeichen ist also bedeutend.


Vorzeichen erfragen

Um für das Vorzeichen einen Wert +1 für positive oder –1 für negative Zahlen und 0 für 0 zu bekommen, lässt sich die Funktion signum() verwenden. Sie ist nicht ganz logisch auf die Klasse Math für Fließkommazahlen und Integer/Long für Ganzzahlen verteilt:

  • java.lang.Integer.signum( int i )
  • java.lang.Long.signum( long i )
  • java.lang.Math.signum( double d )
  • java.lang.Math.signum( float f )

Galileo Computing - Zum Seitenanfang

2.5.3 Zuweisung mit Operation Zur nächsten ÜberschriftZur vorigen Überschrift

In Java lassen sich Zuweisungen mit numerischen Operatoren kombinieren. Für einen Operator # im Ausdruck a = a # (b) kürzt der Verbundoperator den Ausdruck zu a #= b ab. So addiert a += 2 zur Variable a 2, und der Rückgabewert ist die um 2 erhöhte Variable a.

Besondere Obacht sollten wir auf die automatische Klammerung geben. Bei einem Ausdruck wie a *= 3 + 5 gilt a = a * (3 + 5) und nicht selbstverständlich die Punkt-vor-Strich-Regelung a = a * 3 + 5.

Falls es sich bei der rechten Seite um einen komplexeren Ausdruck handelt, wird dieser nur einmal ausgewertet. Dies ist wichtig bei Methodenaufrufen, die Nebenwirkungen besitzen, etwa Zustände wie einen Zähler verändern.


Beispiel Wir profitieren auch bei Feldzugriffen (siehe Abschnitt 3.10, »Arrays«) von Verbundoperationen, da die Auswertung des Indexes nur einmal stattfindet:

feld[ 2 * i + j ] = feld[ 2 * i + j ] + 1;

Leichter zu lesen ist die folgende Anweisung:

feld[ 2 * i + j ] += 1;

Typanpassung beim Verbundoperator

Beim Verbundoperator wird noch etwas mehr gemacht, als E1 #= E2 zu E1 = (E1) # (E2) aufzulösen. Interessanterweise kommt auch noch der Typ von E1 ins Spiel, denn der Ausdruck E1 # E2 wird vor der Zuweisung auf den Datentyp von E1 gebracht, sodass es genau heißen muss: E1 #= E2 wird zu E1 = (Typ von E1)((E1) # (E2)).


Beispiel Auf eine Ganzzahl soll der Verbundoperator eine Fließkommazahl addieren.

int i = 1973; 
i += 30.2;

Die Anwendung des Verbundoperators ist in Ordnung, denn der Übersetzer nimmt eine implizite Typanpassung vor, sodass die Bedeutung bei i = (int)(i + 30.2) liegt. So viel dazu, dass Java eine intuitive und einfache Programmiersprache sein soll.



Galileo Computing - Zum Seitenanfang

2.5.4 Präfix- oder Postfix-Inkrement und -Dekrement Zur nächsten ÜberschriftZur vorigen Überschrift

Das Herauf- und Heruntersetzen von Variablen ist eine sehr häufige Operation, wofür die Entwickler in der Vorgängersprache C auch einen Operator spendiert hatten. Die praktischen Operatoren ++ und -- kürzen die Programmzeilen zum Inkrement und Dekrement ab.

i++;     // Abkürzung für i = i + 1 
j--;     //               j = j – 1

Eine lokale Variable muss allerdings vorher initialisiert sein, da ein Lesezugriff vor einem Schreibzugriff stattfindet. Der ++/–--Operator erfüllt also zwei Aufgaben: Neben der Wertrückgabe gibt es eine Veränderung der Variablen.

Vorher oder nachher

Die beiden Operatoren liefern einen Ausdruck und geben daher einen Wert zurück. Es macht jedoch einen feinen Unterschied, wo dieser Operator platziert wird. Es gibt ihn nämlich in zwei Varianten: vor der Variablen, wie in ++i (Präfix-Schreibweise), oder dahinter, wie bei i++ (Postfix-Schreibweise). Der Präfix-Operator verändert die Variable vor der Auswertung des Ausdrucks und der Postfix-Operator nach der Auswertung des Ausdrucks. Mit anderen Worten: Nutzen wir einen Präfix-Operator, so wird die Variable erst herauf- beziehungsweise heruntergesetzt und dann der Wert geliefert.


Beispiel Präfix/Postfix in einer Ausgabeanweisung:

int i = 10, j = 20; 
System.out.println( ++i );       // 11 
System.out.println( --j );       // 19 
System.out.println( i );         // 11 
System.out.println( j );         // 19 
int i = 10, j = 20; 
System.out.println( i++ );       // 10 
System.out.println( j-- );       // 20 
System.out.println( i );         // 11 
System.out.println( j );         // 19

Mit der Möglichkeit, Variablen zu erhöhen und zu vermindern, ergeben sich vier Varianten:


Präfix Postfix

Inkrement

Prä-Inkrement, ++i

Post-Inkrement, i++

Dekrement

Prä-Dekrement, --i

Post-Dekrement, i--



Hinweis In Java sind Inkrement (++) und Dekrement (--) für alle numerischen Datentypen erlaubt, also auch für Fließkommazahlen.

double d = 12; 
System.out.println( --d );              // 11.0 
double e = 12.456; 
System.out.println( --e );              // 11.456

Einige Kuriositäten

Wir wollen uns abschließend noch mit einer Besonderheit des Post-Inkrements und Prä-Inkrements beschäftigen, die nicht nachahmenswert ist:

a = 2; 
a = ++a;      // a = 3 
b = 2; 
b = b++;      // b = 2

Im ersten Fall bekommen wir den Wert 3 und im zweiten Fall 2. Der erste Fall überrascht nicht, denn a = ++a erhöht den Wert 2 um 1, und anschließend wird 3 der Variablen a zugewiesen. Bei b ist es raffinierter: Der Wert von b ist 2, und dieser Wert wird intern vermerkt. Anschließend erhöht b++ die Variable b. Doch die Zuweisung setzt b auf den gemerkten Wert, der 2 war. Also ist b = 2.


Hinweis Das Post-Inkrement finden wir auch im Namen der Programmiersprache C++. Es soll ausdrücken, dass es C-mit-eins-drauf ist, also ein verbessertes C. Mit dem Wissen über den Postfix-Operator ist klar, dass diese Erhöhung aber erst nach der Nutzung auftritt – also C++ ist auch nur C, und der Vorteil kommt später. (Einer der Entwickler von Java, Bill Joy, hat einmal Java als C++-- [--C++ könnte besser passen: Erst wird C++ bereinigt und dann zu Java erweitert.] beschrieben. Er meinte damit C++ ohne die schwer zu pflegenden Eigenschaften.) Bei den von Microsoft geschaffenen Buchstabe-#-Sprachen wie C# – das # liest sich »Sharp« – wurde die Idee von den Musiknoten entlehnt. Doch eigentlich wird in der Musik das Sharp vor eine Note gesetzt und bedeutet, dass diese um einen Halbton erhöht wird.



Galileo Computing - Zum Seitenanfang

2.5.5 Die relationalen Operatoren und die Gleichheitsoperatoren Zur nächsten ÜberschriftZur vorigen Überschrift

Relationale Operatoren sind Vergleichsoperatoren, die Ausdrücke miteinander vergleichen und einen Wahrheitswert vom Typ boolean zurückgeben. Die von Java für numerische Vergleiche zur Verfügung gestellten Operatoren sind:

  • Größer (>)
  • Kleiner (<)
  • Größer-gleich (>=), Kleiner-gleich (<=)

Weiterhin gibt es einen Spezial-Operator instanceof zum Testen von Referenzeigenschaften. Zudem kommen zwei Vergleichsoperatoren hinzu, die Java als Gleichheitsoperatoren bezeichnet:

  • Test auf Gleichheit (==)
  • Test auf Ungleichheit (!=)

Dass Java hier einen Unterschied macht, liegt an einem etwas anderen Vorrang, der uns aber nicht weiter beschäftigen soll.


Hinweis Bei einem Gleichheitsvergleich zwischen Variable und Literal schreiben viele Entwickler die Variable gerne links und die Konstante rechts, etwa in folgender Fallunterscheidung:

if ( anzahlZentnerGoldes == 666 )

Beide Operanden können aber vertauscht werden, was am Ergebnis nichts ändert, denn der Vergleichsoperator ist kommutativ, wenn die beiden Seiten keine beeinflussenden Seiteneffekte produzieren, also etwa Zustände ändern.

if ( 666 == anzahlZentnerGoldes )

Ebenso wie arithmetische Operatoren passen die relationalen Operatoren ihre Operanden an einen gemeinsamen Typ an. Handelt es sich bei den Typen um Referenztypen, so sind nur die Vergleichsoperatoren == und != erlaubt.

Kaum Verwechslungsprobleme durch == und =

Die Verwendung des relationalen Operators == und der Zuweisung = führt bei Einsteigern oft zu Problemen, da die Mathematik für Vergleiche und Zuweisungen immer nur ein Gleichheitszeichen kennt. Glücklicherweise ist das Problem in Java nicht so drastisch wie beispielsweise in C(++), da die Typen der Operatoren unterschiedlich sind. Der Vergleichsoperator ergibt immer nur den Rückgabewert boolean. Zuweisungen von numerischen Typen ergeben jedoch wieder einen numerischen Typ. Es kann also kein Problem wie das folgende geben:

int a = 10, b = 11; 
boolean result1 = ( a = b );        // Compilerfehler 
boolean result2 = ( a == b );       // Das ist OK

Beispiel Die Wahrheitsvariable hatVorzeichen soll dann true sein, wenn das Zeichen vorzeichen gleich dem Minus ist.

boolean hatVorzeichen = (vorzeichen == '-');

Schön ist die Auswertungsreihenfolge zu sehen: Erst wird das Ergebnis des Vergleichs berechnet, und dieser Wahrheitswert wird anschließend in hatVorzeichen kopiert.



Galileo Computing - Zum Seitenanfang

2.5.6 Logische Operatoren Und, Oder, Xor, Nicht Zur nächsten ÜberschriftZur vorigen Überschrift

Mit logischen Operatoren werden Wahrheitswerte nach definierten Mustern verknüpft. Logische Operatoren operieren nur auf boolean-Typen, andere Typen führen zu Compilerfehlern. Java bietet die Operatoren Und (&&), Oder (||), Xor (^) und Nicht (!) an. Xor ist eine Operation, die genau dann falsch zurückgibt, wenn entweder beide Operanden wahr oder beide falsch sind. Sind sie unterschiedlich, so ist das Ergebnis wahr. Xor heißt auch exklusives beziehungsweise ausschließendes Oder.

Kurzschlussoperatoren

Eine Besonderheit sind die beiden Operatoren && (Und) beziehungsweise || (Oder). In der Regel muss ein logischer Ausdruck nur dann weiter ausgewertet werden, wenn er das Schlussergebnis noch beeinflussen kann. Zwei Operatoren bieten sich zur Optimierung der Ausdrücke an:

  • Und: Ist einer der beiden Ausdrücke falsch, so kann der Ausdruck schon nicht mehr wahr werden. Das Ergebnis ist falsch.
  • Oder: Ist mindestens einer der Ausdrücke schon wahr, so ist auch der gesamte Ausdruck wahr.

Der Compiler bzw. die Laufzeitumgebung kann den Programmfluss abkürzen. Daher nennen sich die beiden Operatoren auch Kurzschlussoperatoren (engl. short-circuit operator). [Den Begriff verwendet die Java-Sprachdefinition nicht! Siehe dazu auch http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.23.] Kürzt er ab, wertet er nur den ersten Ausdruck aus und den zweiten dann nicht mehr.

Nicht-Kurzschlussoperatoren

In einigen Fällen ist erwünscht, dass die Laufzeitumgebung alle Teilausdrücke auswertet. Das kann der Fall sein, wenn Funktionen Nebenwirkungen haben sollen, etwa Zustände ändern. Daher bietet Java zusätzlich die nicht über einen Kurzschluss arbeitenden Operatoren | und & an, die eine Auswertung aller Teilausdrücke erzwingen.


Beispiel In der ersten und dritten Anweisung wird die Methode boolean foo() nicht aufgerufen, in der zweiten und vierten schon.

System.out.println( true || foo() );  // true, foo() wird nicht aufgerufen 
System.out.println( true | foo() );   // true, foo() wird aufgerufen 
System.out.println( false && foo() ); // false, foo() wird nicht aufgerufen 
System.out.println( false & foo() );  // false, foo() wird aufgerufen

Für Xor kann es keinen Kurzschlussoperator geben, da immer beide Operanden ausgewertet werden müssen, bevor das Ergebnis feststeht.


Galileo Computing - Zum Seitenanfang

2.5.7 Rang der Operatoren in der Auswertungsreihenfolge Zur nächsten ÜberschriftZur vorigen Überschrift

Aus der Schule ist der Spruch »Punktrechnung geht vor Strichrechnung« bekannt, sodass sich der Ausdruck 1 + 2 * 3 zu 7 und nicht zu 9 auswertet. [Dass von diesen Rechnungen eine gewisse Spannung ausgeht, zeigen diverse Fernsehkanäle, die damit ihr Abendprogramm füllen.]


Beispiel Auch wenn bei Ausdrücken wie a() + b() * c() zuerst das Produkt gebildet wird, schreibt doch die Auswertungsreihenfolge von binären Operatoren vor, dass der linke Operand zuerst ausgewertet werden muss, was bedeutet, dass Java zuerst die Methode a() aufruft.


In den meisten Programmiersprachen gibt es eine Unzahl von Operatoren neben Plus und Mal, die alle ihre eigenen Vorrangregeln besitzen. [Es gibt Programmiersprachen wie APL, die keine Vorrangregeln kennen. Sie werten die Ausdrücke streng von rechts nach links oder umgekehrt aus.] Der Multiplikationsoperator besitzt zum Beispiel eine höhere Priorität und damit eine andere Auswertungsreihenfolge als der Plus-Operator. Die Rangordnung der Operatoren (engl. operator precedence) legt folgende Tabelle fest, wobei der arithmetische Typ für Ganz- und Fließkommazahlen steht und der integrale Typ für char und Ganzzahlen:


Tabelle 2.6 Operatoren mit Rangordnung in Java

Operator Rang Typ Beschreibung

++, --

1

arithmetisch

Inkrement und Dekrement

+, -

1

arithmetisch

unäres Plus und Minus

~

1

Integral

bitweises Komplement

!

1

boolean

logisches Komplement

(Typ)

1

jeder

Cast

*, /, %

2

arithmetisch

Multiplikation, Division, Rest

+, -

3

arithmetisch

Addition und Subtraktion

+

3

String

String-Konkatenation

<<

4

Integral

Verschiebung links

>>

4

Integral

Rechtsverschiebung mit Vorzeichenerweiterung

>>>

4

Integral

Rechtsverschiebung ohne Vorzeichenerweiterung

<, <=, >, >=

5

arithmetisch

numerische Vergleiche

instanceof

5

Objekt

Typvergleich

==, !=

6

primitiv

Gleich-/Ungleichheit von Werten

==, !=

6

Objekt

Gleich-/Ungleichheit von Referenzen

&

7

Integral

bitweises Und

&

7

boolean

logisches Und

^

8

Integral

bitweises Xor

^

8

boolean

logisches Xor

|

9

Integral

bitweises Oder

|

9

boolean

logisches Oder

&&

10

boolean

logisches konditionales Und, Kurzschluss

||

11

boolean

logisches konditionales Oder, Kurzschluss

?:

12

jeder

Bedingungsoperator

=

13

jeder

Zuweisung

*=, /=, %=, +=, -=, <<=, >>=, >>>=, &=, ^=, |=

14

jeder

Zuweisung mit Operation


Die Rechenregeln für Mal vor Plus kann sich jeder noch leicht merken. Auch ist leicht zu merken, dass die typischen arithmetischen Operatoren wie Plus und Mal eine höhere Priorität als Vergleichsoperationen haben. Komplizierter ist die Auswertung bei den zahlreichen Operatoren, die seltener im Programm vorkommen.


Beispiel Wie ist die Auswertung bei dem nächsten Ausdruck?

boolean A = false, 
        B = false, 
        C = true; 
System.out.println( A && B || C );

Das Ergebnis könnte je nach Rangordnung true oder false sein. Doch die Tabelle lehrt uns, dass im Beispiel A && B || C das Und stärker als das Oder bindet, also der Ausdruck mit der Belegung A=false, B=false, C=true zu true ausgewertet wird.


Vermutlich gibt es Programmierer, die dies wissen oder eine Tabelle mit Rangordnungen immer am Monitor kleben haben. Aber beim Durchlesen von fremdem Code ist es nicht schön, immer wieder die Tabelle konsultieren zu müssen, die verrät, ob nun das binäre Xor oder das binäre Und stärker bindet.


Tipp Alle Ausdrücke, die über die einfache Regel »Punktrechnung geht vor Strichrechnung« hinausgehen, sollten geklammert werden. Da die unären Operatoren ebenfalls sehr stark binden, kann eine Klammerung wegfallen.


Links- und Rechtsassoziativität

Bei den Operatoren + und * gilt die mathematische Kommutativität und Assoziativität. Das heißt, die Operanden können prinzipiell umgestellt werden, und das Ergebnis sollte davon nicht beeinträchtigt sein. Bei der Division unterscheiden wir zusätzlich Links- und Rechtsassoziativität. Deutlich wird das am Beispiel A / B / C. Den Ausdruck wertet Java von links nach rechts aus, und zwar als (A / B) / C; daher ist der Divisionsoperator linksassoziativ. Hier sind Klammern angemessen. Denn würde der Compiler den Ausdruck zu A / (B / C) auswerten, käme es einem A * C / B gleich. In Java sind die meisten Operatoren linksassoziativ, aber es gibt Ausnahmen, wie Zuweisungen der Art A = B = C, die der Compiler zu A = (B = C) auswertet.


Hinweis Die mathematische Assoziativität ist natürlich gefährdet, wenn durch Überläufe Rechenfehler mit im Spiel sind.

float a = –16777217F; 
float b =  16777216F; 
float c =  1F; 
System.out.println( a + b + c );    // 1.0 
System.out.println( a + (b + c) );  // 0.0

Mathematisch ergibt –16777217 + 16777216 den Wert –1, und –1 plus +1 ist 0. Im zweiten Fall liefert –16777217 + (16777216 + 1) = –16777217 + 16777217 = 0. Doch Java wertet a + b durch die Beschränkung von float zu 0 aus, sodass mit c addiert, also 1, die Ausgabe 1 statt 0 erscheint.



Galileo Computing - Zum Seitenanfang

2.5.8 Die Typanpassung (das Casting) Zur nächsten ÜberschriftZur vorigen Überschrift

Zwar ist Java eine getypte Sprache, aber nicht so stark, dass es hinderlich ist. So übersetzt der Compiler die folgenden Zeilen problemlos:

int  anInt = 1; 
long long1 = 1; 
long long2 = anInt;

Streng genommen könnte ein Compiler bei einer sehr starken Typisierung die beiden unteren Zeilen ablehnen, denn das Literal 1 ist vom Typ int und kein 1L, also long, und in long2 = anInt ist die Variable anInt vom Typ int statt vom gewünschten Datentyp long.

Arten der Typanpassung

In der Praxis kommt es also vor, dass Datentypen konvertiert werden müssen. Dies nennt sich Typanpassung (engl. typecast) oder auch casten. Java unterscheidet zwei Arten der Typanpassung:

  • Automatische (implizite) Typanpassung. Daten eines kleineren Datentyps werden automatisch (implizit) dem größeren angepasst. Der Compiler nimmt diese Anpassung selbstständig vor. Daher funktioniert unser erstes Beispiel mit etwa long2 = anInt.
  • Explizite Typanpassung. Ein größerer Typ kann einem kleineren Typ mit möglichem Verlust von Informationen zugewiesen werden.

Typanpassungen gibt es bei primitiven Datentypen und bei Referenztypen. Während die folgenden Absätze die Anpassungen bei einfachen Datentypen beschreiben, kümmert sich Kapitel 6, »Eigene Klassen schreiben«, um die Typkompatibilität bei Referenzen.

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 führt Java automatisch aus:


Tabelle 2.7 Implizite Typanpassungen

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


Die Anpassung wird im Englischen auch widening conversion genannt, weil sie den Wertebereich automatisch erweitert.

Explizite Typanpassung

Die explizite Anpassung engt einen Typ ein, sodass diese Operation auch narrowing conversion genannt wird. Der gewünschte Typ für eine Typanpassung wird vor den umzuwandelnden Datentyp in Klammern gesetzt.


Beispiel Umwandlung einer Fließkommazahl in eine Ganzzahl:

int n = (int) 3.1415;                  // n = 3

Passt der Typ eines Ausdrucks nicht, lässt er sich mit Strg + 1 korrigieren.

Eclipse-Icon

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ßkommazahlen in 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.

System.out.println( (int) +12.34 );     //  12 
System.out.println( (int) +67.89 );     //  67 
System.out.println( (int) –12.34 );     // –12 
System.out.println( (int) –67.89 );     // –67

Bei der Konvertierung eines größeren Ganzzahltyps in einen kleineren werden einfach die oberen Bits abgeschnitten. Eine Anpassung des Vorzeichens findet nicht statt. Die Darstellung in Bit zeigt das sehr anschaulich:

int   ii = 123456789;     // 00000111010110111100110100010101 
int   ij =   –123456;     // 11111111111111100001110111000000 
 
short si = (short) ii;    //                 1100110100010101 
short sj = (short) ij;    //                 0001110111000000 
 
System.out.println( si ); // –13035 
System.out.println( sj ); //   7616

si wird eine negative Zahl, da das 16. Bit beim int ii gesetzt war und nun beim short das negative Vorzeichen anzeigt. Die Zahl hinter ij hat kein 16. Bit gesetzt, und so wird das short sj positiv.

Unterschiedliche Wertebereiche bei Fließ- und Ganzzahlen

Natürlich kann die Konvertierung double long nicht verlustfrei sein. Wie sollte das auch gehen? Zwar verfügt sowohl ein long als auch ein double über 64 Bit zur Datenspeicherung, aber ein double kann eine Ganzzahl nicht so effizient speichern wie ein long und hat etwas »Overhead« für einen großen Exponenten. Bei der impliziten Konvertierung eines long in ein double können einige Bit als Informationsträger herausfallen, wie es das folgende Beispiel illustriert.

long   l = 1111111111111111111L; // 1111111111111111111 
double d = l;                    // 1111111111111111170 (1.11111111111111117E18) 
long   m = (long) d;             // 1111111111111111168

Java erlaubt ohne explizite Anpassung die Konvertierung eines long an ein double und auch an ein noch kleineres float, was vielleicht noch merkwürdiger ist, da float nur eine Genauigkeit von 6 bis 7 Stellen, long hingegen 18 Stellen hat.

long  l = 1000000000000000000L; 
float f = l; 
System.out.printf( "%f", f );    // 999999984306749440,000000

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, weil dort im Unicode das erste Bit gesetzt ist, das bei der Umwandlung in ein short dem nicht gesetzten Vorzeichen-Bit weichen muss.

Typanpassungen von int und char

Die Methode printXXX() reagiert auf die Typen char und int, und eine Typumwandlung führt zur 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 arithmetische Operation auf char-Typen zu einem int führt. Daher funktioniert für ein char c Folgendes nicht:

c = c + 1;

Richtig wäre c = (char)(c + 1).

Berechnungen bei Datentypen byte und short auf int-Basis

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

Listing 2.6 AutoConvert.java, main()

short s1 = 1, s2 = 2; 
byte  b1 = 1, b2 = 2; 
int   i1 = 1, i2 = 2; 
long  l1 = 1, l2 = 2; 
// short s3 = s1 + s2;  // Type mismatch: cannot convert from int to short 
// byte  b3 = b1 + b2;  // Type mismatch: cannot convert from int to byte 
short s3 = (short)( s1 + s2 ); 
byte  b3 =  (byte)( b1 + b2 ); 
int   i3 = i1 + i2; 
long  l3 = l1 + l2;

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 mit int-Werten; intern im Bytecode ist es ebenso realisiert. Überläufe werden 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 Bit. Diese Eigenart ist insofern verwunderlich, als auch ein int nur dann zu einem long erweitert wird, wenn einer der Operanden eines Ausdrucks vom Typ long ist.


Tipp »Kleine« Typen wie short und byte führen oft zu Problemen. Wenn sie nicht absichtlich in großen Feldern verwendet werden und Speicherplatz nicht ein absolutes Kriterium ist, erweist sich int als die beste Wahl – auch weil Java nicht durch besonders intuitive Typ-Konvertierungen glänzt, wie das Beispiel mit dem unären Minus und Plus zeigt:

byte b = 0; 
b = -b;             // "Cannot convert from int to byte" 
b = +b;             // "Cannot convert from int to byte"

Der Compiler meldet einen Fehler, denn der Ausdruck auf der rechten Seite wird durch den unären Operator in ein int umgewandelt; was immer für die Typen byte, short und char gilt. [http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.6.1]


Materialverlust durch Überläufe

Überläufe bei Berechnungen können zu schwerwiegenden 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) hatte die unbemannte Rakete, die vier Satelliten an Bord hatte, von Französisch-Guayana aus gestartet. 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 2^15 – 1 und die Umwandlung nicht gesichert, da die Programmierer diesen Zahlenbereich nicht angenommen hatten. Als Konsequenz brach das Lenksystem zusammen, und die Selbstzerstörung wurde ausgelöst, 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 das Programm schnell abgebrochen werden können. Ungünstig war, dass der Programmteil unverändert durch Wiederverwendung per Copy & Paste aus der Ariane-4-Software kopiert worden war, die Ariane 5 aber schneller flog.

Typanpassung zwischen einfachen Typen und Referenztypen

Allgemeine Umwandlungen zwischen einfachen Typen und Referenztypen gibt es nicht. Falsch sind zum Beispiel:

Listing 2.7 TypecastPrimRef.java, main() Teil 1

String s = (String) 1;    // Cannot cast from int to String 
int i = (int) "1";        // Cannot cast from String to int

Einiges sieht dagegen nach Typanpassung aus, ist aber in Wirklichkeit eine Technik, die sich Autoboxing nennt (Kapitel 3, »Klassen und Objekte«, geht näher darauf ein).

Listing 2.8 TypecastPrimRef.java, main() Teil 2

Long lông = (Long) 2L;      // Or: Long lông = 2L; 
System.out.println( (Boolean) true ); 
((Integer)2).toString();

Galileo Computing - Zum Seitenanfang

2.5.9 Überladenes Plus für Strings topZur vorigen Überschrift

Obwohl sich in Java die Operatoren fast alle auf primitive Datentypen beziehen, gibt es doch eine weitere Verwendung des Plus-Operators. Diese wurde in Java eingeführt, da ein Aneinanderhängen von Zeichenketten oft benötigt wird. Objekte vom Typ String können durch den Plus-Operator mit anderen Strings und Datentypen verbunden werden. Falls zusammenhängende Teile nicht alle den Datentyp String annehmen, werden sie automatisch in einen String umgewandelt. Der Ergebnistyp ist immer String.


Beispiel Setze fünf Teile zu einem String zusammen:

String s = '"' + "Extrem Sandmännchen" + '"' + " frei ab " + 18; 
//         char  String                  char  String        int 
System.out.println( s ); // "Extrem Sandmännchen" frei ab 18

Besteht der Ausdruck aus mehreren Teilen, so muss die Auswertungsreihenfolge beachtet werden, andernfalls kommt es zu seltsamen Zusammensetzungen. So ergibt "Aufruf von " + 1 + 0 + 0 + " Ökonomen" tatsächlich »Aufruf von 100 Ökonomen« und nicht »Aufruf von 1 Ökonomen«, da der Compiler die Konvertierung in Strings dann startet, wenn er einen Ausdruck als String-Objekt erkannt hat.


Beispiel Auswertungsreihenfolge vom Plus:

Listing 2.9 PlusString.java, main()

System.out.println( 1 + 2 );               // 3 
System.out.println( "1" + 2 + 3 );         // 123 
System.out.println( 1 + 2 + "3" );         // 33 
System.out.println( 1 + 2 + "3" + 4 + 5 ); // 3345

Nur eine Zeichenkette in doppelten Anführungszeichen ist ein String, und der Plus-Operator entfaltet seine besondere Wirkung. Ein einzelnes Zeichen in einfachen Hochkommata wird lediglich auf ein int gecastet, und Additionen sind Ganzzahl-Additionen.

System.out.println( '0' + 2 );     // 50  – ASCII value for '0' is 48 
System.out.println( 'A' + 'a' );   // 162 – 'A'=65, 'a'=97


Beispiel Der Plus-Operator für Zeichenketten geht streng von links nach rechts und bereitet mit eingebetteten arithmetischen Ausdrücken mitunter Probleme. Eine Klammerung hilft, wie im Folgenden zu sehen ist:

"Ist 1 größer als 2? " + (1 > 2 ? "ja" : "nein");

Wäre der Ausdruck um den Bedingungsoperator nicht geklammert, dann würde der Plus-Operator an die Zeichenkette die 1 anhängen, und es käme der >-Operator. Der erwartet aber kompatible Datentypen, die in unserem Fall – links stünde die Zeichenkette und rechts die Ganzzahl 2 – nicht gegeben sind.




Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Tipp
Zum Katalog: Coding for Fun





 Coding for Fun


 Buchempfehlungen
Zum Katalog: Objektorientierte Programmierung





 Objektorientierte
 Programmierung


Zum Katalog: Einstieg in Eclipse 3.4






 Einstieg in
 Eclipse 3.4


Zum Katalog: Java 6 lernen mit Eclipse






 Java 6 lernen
 mit Eclipse


Zum Katalog: NetBeans Platform 6






 NetBeans
 Platform 6


Zum Katalog: Java und XML






 Java und XML


Zum Katalog: Visual C# 2008






 Visual C# 2008


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: C++ von A bis Z






 C++ von A bis Z


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2009
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, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de