Variable können an fast jeder Stelle des Quelltextes deklariert werden. Obwohl dies nicht nötig ist, sollten solche Deklarationen wegen der Übersichtlichkeit am Anfang eines Blockes stehen. Eine Deklaration besteht aus dem Typ der Variablen, gefolgt von derem Namen. Die Anweisung wird noch von einem Semikolon abgeschlossen.
public static void main(String[] args) {
int a; // eine Variable a vom Typ int
}
Es können mehrere Variablen auf einmal deklariert werden, indem man sie durch Kommata abtrennt. Diese Variablen haben dann alle den angegebenen Typ.
int b, c;
Alle diese Variablen haben noch keinen konkreten Wert. Wenn man sie benutzt,
ohne ihnen vorher einen Wert zuzuweisen, dann gibt es schon vom Compiler einen
Fehler (z.B. variable c might not have been initialised
). Eine
Zuweisung besteht aus dem Namen der Variablen, einem Gleichheitszeichen und
dem Ausdruck, der ausgewertet und zugewiesen wird. Ein Ausdruck ist entweder
eine Konstante, eine andere Variable gleichen Typs, das Ergebnis eines
Methodenaufrufs oder eine Verkettung von Ausdrücken mit Operatoren.
a = 7;
b = 3;
c = a / b;
Da der Compiler auch nicht der intelligenteste ist, und eine fehlende Initialisierung nur in Spezialfällen erkennt, sollte grundsätzlich zur Fehlervermeidung ein Anfangwert zugewiesen werden.
int a = 7;
int c, b = 3; // c hat noch keinen Wert
c = a / b;
Hierbei wird nur der Variablen b
ein Wert zugewiesen.
c
ist nach der zweiten Zeile noch unbelegt. Um keine Verwirrung
zu stiften, sollte man eine derartige Deklaration vermeiden.
Es stehen die üblichen Operatoren zur Verfügung, wobei Punkt- vor Strichrechnung gilt.
Operator | Bedeutung |
+ | Addition |
- | Subtraktion |
* | Multiplikation |
/ | Division |
Wenn der Operator auf zwei Variablen gleichen Typs angewendet werden, dann hat
auch das Ergebnis diesen Typ. Falls die beiden Werte einen unterschiedlichen
Typ haben, wird der Typ des Ergebnises der grössere der beiden sein. Da
im obigen Beispiel beide Variablen a
und b
ganzzahlig sind, ist auch das Ergebnis ganzzahlig vom Typ int
.
Die Anweisung
System.out.println(c);
ergibt also 2
und nicht 2.333333
. Selbst wenn
c
den Typ double
hat, wird die Operation
a / b
ganzzahlig berechnet. Um die Berechnung mit Kommazahlen
durchzuführen muss mindestens eine der beiden Zahlen einen Gleitkommatyp
haben.
double d = 7 / 3.0;
d = 7.0 / 3;
d = a / (double) b;
Die ersten beiden Varianten funktionieren identisch. Die letzte Variante kann
für Variablen verwendet werden. Dabei wird der Wert von b
vor der Division in eine Gleitkommazahl umgewandelt, was dann auch zur
Durchführung der Gleitkommadivision führt. Eine solche Typumwandlung
nennt man Casting (Lektion 04). Der Wert von
d
ist in jedem Fall 2.3333333333333335
. Das sind die
17 Stellen des Typs double
. Der Rundungsfehler der letzten Ziffer
liegt daran, dass diese Zahl keine exakte endliche Darstellung im Dualsystem
besitzt (schon gar keine, die in die 64 bit von double
passt).
Die Berechnung 1 / 0
mit ganzen Zahlen führt zu einer
ArithmeticException
, d.h. das Programm stürzt ab (zu
Exceptions später mehr). Bei Gleitkommazahlen ist das nicht so. Es gibt
drei spezielle Werte von Gleitkommazahlen:
Double.POSITIVE_INFINITY = 1.0 / 0.0;
Double.NEGATIVE_INFINITY = -1.0 / 0.0;
Double.NaN = 0.0d / 0.0;
Dabei steht NaN
für "not a number". Bei dieser Konstante
gibt es noch eine Kuriosität, die man beachten sollte. Der Test auf
Gleichheit mit dieser Konstante liefert immer false
.
double x = 0.0 / 0.0;
System.out.println("x == x ? " + (x == x));
Interessanterweise liefert das die Ausgabe
x == x ? false
Dieses Verhalten kann damit begründet werden, das der Vergleich mit einer Zahl, die keine ist, nicht wahr sen kann.
Für ganze Zahlen gibt es noch weitere Operationen.
Operator | Bezeichnung | Bedeutung |
% | modulo | Rest der ganzzahligen Division |
& | and | bitweise und-Verknüpfung |
| | or | bitweise oder-Verknüpfung |
^ | xor | bitweise exklusiv-oder-Verknüpfung |
! | not | bitweise Negation (unärer Operator) |
<< | shift left | bitweise nach links schieben |
>> | shift right | bitweise nach rechts schieben Vorzeichenbit bleibt erhalten |
>>> | shift right | bitweise nach rechts schieben Vorzeichenbit wird mit verschoben |
Bei bitweise Verschiebungen ist das Vorzeichen problematisch, da dieses ja im höchsten bit der Zahl abgelegt wird.
System.out.println(1 << 30); // ergibt 1073741824
System.out.println(1 << 31); // ergibt -2147483648
System.out.println(1 << 32); // ergibt 1
Der shift left Operator ist also zyklisch und verschiebt bei positiven Zahlen
auch über das Vorzeichenbit. Bei negativen Zahlen hingegen wird das
Vorzeichenbit geschützt. Für den shift right Operator gibt es zwei
Varianten. -1
besteht in der Binärdarstellung nur aus
Einsen, wobei das erste bzw. höchste Bit das Vorzeichen darstellt.
Ausdruck | dezimal | binär |
x | -12345678 | 11111000101001000011001011101011 |
x >> 1 | -61728395 | 11111100010100100001100101110101 |
x >>> 1 | 2085755253 | 01111100010100100001100101110101 |
Dabei entspricht x >> 1
einfach der Division durch
2
, also x / 2
, wodurch das Vorzeichen erhalten
bleibt. Wohingegen x >>> 1
alle bits inclusive Vorzeichen
verschiebt. Dies ist dann nützlich, wenn man die Zahl als Bit-Map
verwendet.
Die üblichen Vergleichsrelationen stehen auch in Java zur Verfügung.
Operator | Bedeutung |
> | grösser |
>= | grösser oder gleich |
< | kleiner |
<= | kleiner oder gleich |
== | gleich |
Es ist zu beachten, dass sich das Symbol für den Vergleich
==
vom Symbol für eine Zuweisung =
unterscheidet.
Theoretisch arbeiten logische Verknüpfungen von Boolschen Werten genau wie die bitweisen Operationen. Es gibt aber trotzdem eigene Operatoren dafür in Java.
Operator | Bedeutung |
&& | logische und-Verknüpfung |
|| | logische oder-Verknüpfung |
! | logische Negation (unärer Operator) |
Vergleichsoperatoren haben eine höhere Priorität als logische Verknüpfungen. Deshalb sind die folgenden beiden Ausdrücke gleichwertig:
a >= 0 && a <= 10
(a >= 0) && (a <= 10)
Für eine bessere Lesbarkeit und um Verwechselungen zu vermeiden, sollte die zweite Variante verwendet werden.