Operatoren
Ausdruck
Variablen an sich sind nutzlos, solange man mit den Werten, die darin gespeichert sind, nicht rechnen kann. Daher benötigt man, wie in der Mathematik Operatoren, die es erlauben aus bekannten Werten, neue Werte zu berechnen.
Ein Ausdruck (expression) verknüpft Operanden mithilfe eines Operators
- Operand (operand) ist der Wert (Variable oder Literal) der verknüpft werden soll
- Operator (operator) legt die Art der Verknüpfung fest (z. B. Addition mit
+)
19 + 7
vermoegenVonBillGates + 1000000
Die Berechnung eines neuen Wertes erfolgt mithilfe eines Ausdrucks, z. B. 2 + 7. Ein Ausdruck besteht aus
- Operanden die Werte miteinander verknüpfen (im Beispiel
2und7) - Operatoren welche die Art der Verknüpfung festlegen (im Beispiel
+)
Arten von Operatoren
Drei Arten von Operatoren
- Unäre Operatoren haben nur einen Operanden
- Syntax:
op Operand - Beispiel: Negation einer Zahl (
a = -b) - Binäre Operatoren verknüpfen zwei Operanden
- Syntax:
Operand1 op Operand2 - Beispiel: Multiplikation zweier Zahlen (
a = b * c) - Ternäre Operatoren verknüpfen drei Operanden (selten)
- Syntax:
Operand1 op Operand2 op Operande3 - Beispiel: bedingter Ausdruck (
x = a > b ? a : b)
Man kann Operatoren danach klassifizieren, wie viele Operanden sie haben. Am häufigsten sind Operatoren mit zwei Operanden (binäre Operatoren). Deutlich seltener sind solche mit nur einem Operanden (unäre Operatoren). Mit drei Operanden (ternärer Operator) existiert in Java nur ein einziger Operator, der sogenannte Fragezeichen-Operator, der später noch behandelt werden wird.
Ausdrücke können selbst wieder Ausdrücke enthalten. Dieses Vorgehen ist aus der Mathematik bekannt, wo man auch beliebige Ausdrücke verschachteln kann, z. B. \( ((3 + 5) \cdot 7) + 6 \). In diesem Beispiel liegen drei Ausdrücke vor, nämlich \( A_1 = (3 + 5) \), \( A_2 = A_1 \cdot 7 \) und \( A_3 = A_2 + 6 \), wobei jeweils der vorhergehende Ausdruck Teil des nächsten ist.
Die Reihenfolge der Ausführung kann man durch Klammern (siehe nächsten Abschnitt) steuern.
Operanden kommen auf drei Wegen in einen Ausdruck:
- entweder als anderer Ausdruck,
- als sog. Literal, das direkt einen Wert repräsentiert oder
- als Variable bei der deren Wert in den Ausdruck eingeht.
Im ersten Fall wird der innere Ausdruck ausgewertet und das Ergebnis geht dann in den Gesamtausdruck ein. Im zweiten Fall steht das Ergebnis schon fest und im dritten Fall wird der in der Variable gespeicherte Wert in den Ausdruck eingesetzt.
Rangfolge der Operatoren
- Rangfolge (priority) legt fest, welche Operatoren zuerst ausgewertet werden (vgl. „Punkt vor Strichrechnung“ in der Mathematik)
- Assoziativität legt fest, in welcher Richtung Operatoren mit gleicher Rangfolge ausgewertet werden
- links-assoziativ: Auswertung erfolgt von Links nach Rechts
3 + 5 + 6⇔(3 + 5) + 6 - rechts assoziativ: Auswertung erfolgt von Rechts nach Links
a = b = c⇔a = (b = c) - Durch Klammern kann die Rangfolge geändert werden
(2 + 2) * 2 = 8 - Im Zweifelsfall sollte man lieber Klammern setzen, als sich auf die Rangfolge zu verlassen:
2 + (2 * 2)
Da häufig mehrere Operatoren aufeinandertreffen, muss man festlegen, welche Operatoren zuerst ausgewertet werden. Diese Reihenfolge wird durch die Rangfolge der Operatoren eindeutig bestimmt, d. h. für jeden Operator ist festgelegt, vor welchen anderen Operatoren er ausgewertet wird.
Wenn ein Ausdruck mehrere Operatoren mit identischer Rangfolge enthält, wird die Richtung der Ausführung durch die Assoziativität bestimmt.
Zum Beispiel sind Multiplikations- und Modulo-Operator linksassoziativ. Daher sind die beiden folgenden Anweisungen austauschbar:
int result = 5 * 70 % 6; // ergibt 2
int result = (5 * 70) % 6; // ergibt 2
Nicht aber
int result = 5 * (70 % 6); // ergibt 20
Rechtsassoziativ sind zum Beispiel die Vorzeichenoperatoren (+, -) oder die Zuweisung. Hier wird der Ausdruck von rechts nach links ausgewertet.
int summe;
int result = summe = 12; // summe ist 12, result ist 12
Logische Operatoren
- Logische Operatoren verknüpfen Wahrheitswerte miteinander
- Eingabe
- zwei Wahrheitswerte (
trueoderfalse) bei^,&&und|| - ein Wahrheitswert bei
! - Ergebnis: eine Wahrheitswert (
trueoderfalse)
| Operator | Bedeutung | Beispiel | Ergebnis |
|---|---|---|---|
^ | exclusiv oder | true ^ true | false |
&& | und | true && false | false |
|| | oder | true || false | true |
! | nicht | !false | true |
In Java dienen logische Operatoren dazu, Bedingungen zu verknüpfen oder logische Ausdrücke auszuwerten, die einen Wahrheitswert (true oder false) liefern. Die wichtigsten logischen Operatoren sind:
- UND-Operator: (
&&) lieferttrue, wenn beide Operandentruesind. - ODER-Operator: (
||) ergibttrue, wenn mindestens einer der Operandentrueist. - NICHT-Operator: (
!) kehrt den Wahrheitswert eines Ausdrucks um: Austruewirdfalseund umgekehrt.
Diese Operatoren werden typischerweise in if-Bedingungen verwendet, um komplexere logische Prüfungen durchzuführen, etwa: if (a > 0 && b < 10) prüft, ob a größer als 0 und b kleiner als 10 ist.
Kurzschlussoperatoren
&& und || sind sogenannte Kurzschlussoperatoren
&&und||brechen die Auswertung ab, sobald das Ergebnis feststeht- bei
&&ist ein Ausdruckfalse⇒ gesamter Ausdruck istfalse - bei
||ist ein Ausdrucktrue⇒ gesamter Ausdruck isttrue &und|werten den gesamten Ausdruck aus und berechnen dann erst das Ergebnis- Normalerweise braucht man
&und|nicht und sollte immer&&und||verwenden
Es gibt in Java zwei Arten von logischen Operatoren für Und und Oder, die normalen | und & und die sogenannten Kurzschlussoperatoren || und &&. Bei den normalen Operatoren werden alle (Teil-)Ausdrücke ausgewertet und erst dann wird der logische Operator angewandt. Bei den Kurzschlussoperatoren wird die Auswertung streng von links nach rechts durchgeführt und sie wird abgebrochen, sobald der logische Wert des Gesamtausdrucks feststeht.
Da ein Abbruch der Auswertung in nahezu allen Fällen im Interesse der Programmierer:innen liegt, werden die normalen logischen Operatoren in dieser Vorlesung nicht verwendet und es kommen immer die Kurzschlussoperatoren zum Einsatz
Logische Vergleichsoperatoren
Logische Vergleichsoperatoren verknüpfen Wahrheitswerte miteinander
- Entsprechen den bekannten Operatoren aus der Mathematik
- Eingabe: zwei Wahrheitswerte
- Ergebnis: eine Wahrheitswert (
trueoderfalse) - meist überflüssig
| Operator | Bedeutung | Beispiel | Ergebnis |
|---|---|---|---|
== | gleich | true == false | false |
!= | ungleich | true != false | true |
Man kann Wahrheitswerte auch über Vergleichsoperatoren miteinander in Beziehung setzen, d. h. auf den Wert der Variable testen. Da die Eingabe zwei boolesche Ausdrücke sind und das Ergebnis ein boolescher Wert, braucht man die Operatoren nur selten, denn:
a == true⇔aa == false⇔!aa != true⇔!aa != false⇔a
Rangfolge der logischen Operatoren
| Rang | Operator | Assoziativität |
|---|---|---|
| 1. | ! | R |
| 2. | == | L |
| 2. | != | L |
| 3. | && | L |
| 4. | || | L |
| 5. | = | R |
Beispiel
true && false == false || true && true
(true && (false == false)) || (true && true)
Arithmetische Operatoren für ganze Zahlen
- Arithmetische Operatoren verknüpfen Zahlen miteinander
(entsprechen den bekannten Operatoren aus der Mathematik) - Eingabe: zwei ganze Zahlen (
byte,short,int,long) - Ergebnis: eine ganze Zahl (
int,long) - Division durch 0 führt zu einem Laufzeitfehler (
5 / 0)
| Operator | Bedeutung | Beispiel | Ergebnis |
|---|---|---|---|
+ | Addition | 39 + 3 | 42 |
- | Subtraktion | 26 - 3 | 23 |
* | Multiplikation | 19 * 7 | 133 |
/ | Division | 19 / 7 | 2 |
% | Modulo | 19 % 7 | 5 |
Die arithmetischen Operatoren für ganze Zahlen sind alle bereits aus der Mathematik bekannt, werden nur teilweise durch andere Zeichen repräsentiert.
Der Modulo-Operator (%) kommt in der Schulmathematik selten zum Einsatz, hat in der Informatik aber eine überragende Rolle, da viele Problemlösungen auf Module-Arithmetik beruhen. Details hierzu werden in anderen Veranstaltungen im Rahmen von Hashtabellen und ähnlichen Datenstrukturen behandelt werden.
Zusammengefasste Operatoren
Zusammengefasste Operatoren
Zuweisung und Rechnung können zusammengefasst werden
| Operator | Bedeutung |
|---|---|
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 |
Aus C hat Java die Eigenschaft geerbt, dass man den Zuweisungsoperator mit den arithmetischen Operatoren kombinieren kann. Die so entstehenden Konstrukte sind häufig sehr unübersichtlich und bringen wenig für das Programm. Daher kann man die kombinierten Operatoren im Allgemeinen getrost ignorieren. Da aber andere Programmierer:innen sie benutzen könnten, sollte man sie zumindest kennen, um den Quelltext verstehen zu können.
Inkrement und Dekrement-Operator
Für die sehr häufige Operation des Erhöhens und Erniedrigens einer Zahl um den Wert 1 gibt es spezielle Operatoren (Inkrement-Operator und Dekrement-Operator)
- Präfix-Operator (
++aund--a) verändern den Wert der Variable und geben diesen zurück - Postfix-Operator (
a++unda--) geben den Wert der Variable zurück und verändern sie erst danach
| Operator | Bedeutung |
|---|---|
a++ | a = a + 1 |
++a | a = a + 1 |
a-- | a = a - 1 |
--a | a = a - 1 |
Eine der häufigsten arithmetischen Operationen ist das Erhöhen oder Erniedrigen einer ganzen Zahl um den Wert Eins. Daher gibt es für diesen Fall zwei (genaugenommen vier) Operatoren, nämlich ++ und --, die auf eine Variable (nicht auf einen Wert) angewendet werden können.
Eine für Einsteiger schwer verständliche Eigenschaft der Operatoren ist, welchen Wert der Ausdruck selbst zurückliefert. In jedem Fall wird die Variable erhöht oder erniedrigt. Von der Position des Operators (vor oder hinter der Variable) hängt aber ab, welchen Wert der Ausdruck zurückgibt. Steht der Operator vor der Variable, wird zuerst die Variable verändert und dann wird das Ergebnis zurückgeben. Steht der dahinter, wird erst der Wert der Variable zurückgegeben und dann der Wert erhöht.
Kommt der Operator nicht in einer Zuweisung vor, ist es egal, welche Form man verwendet. Es hat sich aber bei den meisten Programmierer:innen eingebürgert, dann die Postfix-Schreibweise (a++) zu verwenden.
int a = 7;
int b = a++;
System.out.println("a=" + a + ", b=" + b); // a=8, b=7
int a = 7;
int b = a--;
System.out.println("a=" + a + ", b=" + b); // a=6, b=7
int a = 7;
int b = ++a;
System.out.println("a=" + a + ", b=" + b); // a=8, b=8
int a = 7;
int b = --a;
System.out.println("a=" + a + ", b=" + b); // a=6, b=6
Vergleichsoperatoren für ganze Zahlen
- Numerische Vergleichsoperatoren verknüpfen Zahlen miteinander
- Entsprechen den bekannten Operatoren aus er Mathematik
- Eingabe: zwei ganze Zahlen
- Ergebnis: eine Wahrheitswert (
trueoderfalse)
| Operator | Bedeutung | Beispiel | Ergebnis |
|---|---|---|---|
< | kleiner | 8 < 9 | true |
> | größer | 9 > 4 | true |
<= | kleiner gleich | 7 <= 3 | false |
>= | größer gleich | 7 >= 3 | true |
== | gleich | 6 == 5 | false |
!= | ungleich | 6 != 5 | true |
Arithmetische Operatoren für Fließkommazahlen
- Arithmetische Operatoren verknüpfen Zahlen miteinander
- Eingabe: zwei Zahlen (davon mindestens eine Fließkommazahl)
- Ergebnis: eine Fließkommazahl
| Operator | Bedeutung | Beispiel | Ergebnis |
|---|---|---|---|
+ | Addition | 39.1 + 3.3 | 42.4 |
- | Subtraktion | 26.0 - 3 | 23.0 |
* | Multiplikation | 19.3 * 7.8 | 150.54 |
/ | Division | 37.41 / 4.3 | 8.7 |
% | Modulo | 19.0 % 7 | 5.0 |
Zuweisung und Rechnung können auch bei Fließkommazahlen zusammengefasst werden (häufig unübersichtlich)
| Operator | Bedeutung |
|---|---|
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++ | a = a + 1 |
++a | a = a + 1 |
a-- | a = a - 1 |
--a | a = a - 1 |
Besonderheit bei Fließkommazahlen
Division durch 0 ist bei Fließkommazahlen erlaubt. Ergebnis Infinity und NaN (not a number)
Beispiele
2 / 0.0 = +Infinity-2.0 / 0 = -Infinity0.0 / 0.0 = NaN
Vergleichsoperatoren für Fließkommazahlen
- Identisch zu den Operatoren für Ganzzahlen
- Entsprechen den bekannten Operatoren aus er Mathematik
- Eingabe: zwei Fließkommazahlen
- Ergebnis: eine Wahrheitswert (
trueoderfalse)
| Operator | Bedeutung | Beispiel | Ergebnis |
|---|---|---|---|
< | kleiner | 8.0 < 9.0 | true |
> | größer | 9.0 > 4.0 | true |
<= | kleiner gleich | 7.0 <= 3.0 | false |
>= | größer gleich | 7.0 >= 3.0 | true |
== | gleich | 6.0 == 5.0 | false |
!= | ungleich | 6.0 != 5.0 | true |
Vergleich zweier Fließkommazahlen mit ==
- Fließkommazahlen sind mit Ungenauigkeit behaftet
- Vergleich mit
==schlägt häufig wegen Rundungsfehlern fehl - daher besser mit Epsilon-Umgebung vergleichen
statta == bbesser(a + epsilon > b) && (a - epsilon < b)
Ein direkter Vergleich von Fließkommazahlen mit == ist in Java (und auch in anderen Programmiersprachen) problematisch, weil Fließkommazahlen intern nicht exakt dargestellt werden können. Durch Rundungsfehler bei Berechnungen kann es passieren, dass zwei Werte, die mathematisch gleich sein sollten, sich in einer der letzten Nachkommastelle minimal unterscheiden und der Vergleich mit == deshalb false ergibt.
Stattdessen verwendet man eine Epsilon-Umgebung, also einen kleinen Toleranzwert, um zu prüfen, ob zwei Zahlen nahe genug beieinanderliegen.
double a = 3.0;
double b = 0.1;
double c = a * b; // -> 0.30000000000000004
// schlägt wegen Rundungsfehler fehl
System.out.println(c == 0.3); // -> false
// besser
System.out.println((c - 0.00001 < 0.3) && (c + 0.00001 > 0.3)); // -> true
Konvertierung von Datentypen
In Java unterscheidet man zwischen impliziter und expliziter Konvertierung (auch Typumwandlung oder Type Casting genannt), wenn Daten von einem Datentyp in einen anderen überführt werden.
- Implizite Konvertierung – kleinere Datentypen werden automatisch in größere Datentypen konvertiert (kein Verlust an Genauigkeit)
- passiert automatisch
byte,short,int,long→double,floatbyte→shortshort→intint→long
Eine implizite Konvertierung (auch widening cast genannt) erfolgt automatisch durch den Compiler, wenn der Zieltyp größer ist als der Ausgangstyp. Zum Beispiel kann ein int-Wert ohne Probleme in einen long oder double-Wert umgewandelt werden, da dabei keine Information verloren geht.
- Explitize Konvertierung – Genauigkeit geht verloren, wenn größerer in kleineren Datentyp konvertiert wird
- muss explizit über Cast angefordert werden
double,float→byte,short,int,longlong→intint→short
Eine explizite Konvertierung (auch narrowing cast genannt) ist notwendig, wenn der Zieltyp kleiner oder weniger genau ist als der Ausgangstyp. Hier kann es zu Informationsverlust kommen, weshalb man die Umwandlung bewusst durch ein sogenanntes Casting anfordern muss.
Cast-Operator
Cast-Operator führt eine explizite Konvertierung durch
- Syntax:
(DATENTYP) WERT DATENTYP– Datentyp in den konvertiert werden sollWERT– Wert, der konvertiert wird
double d = 3.141;
int i = (int) d; // -> 3
long j = 7000000000L;
int k = (int) j; // -> -1589934592
Der Cast-Operator in Java dient dazu, eine explizite Typumwandlung vorzunehmen, also einen Wert bewusst von einem Datentyp in einen anderen zu überführen, wenn dies nicht automatisch durch den Compiler möglich ist. Dies ist insbesondere dann notwendig, wenn bei der Umwandlung Informationsverlust auftreten kann, zum Beispiel bei der Zuweisung einem double an einen int.
Der Cast-Operator hat die Form eines vorangestellten Zieltyps in runden Klammern.