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 2 und 7)
  • 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:

  1. entweder als anderer Ausdruck,
  2. als sog. Literal, das direkt einen Wert repräsentiert oder
  3. 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 = ca = (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 (true oder false) bei ^, && und ||
    • ein Wahrheitswert bei !
  • Ergebnis: eine Wahrheitswert (true oder false)
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: (&&) liefert true, wenn beide Operanden true sind.
  • ODER-Operator: (||) ergibt true, wenn mindestens einer der Operanden true ist.
  • NICHT-Operator: (!) kehrt den Wahrheitswert eines Ausdrucks um: Aus true wird false und 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 Ausdruck false ⇒ gesamter Ausdruck ist false
    • bei || ist ein Ausdruck true ⇒ gesamter Ausdruck ist true
  • & 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 (true oder false)
  • 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 == truea
  • a == false!a
  • a != true!a
  • a != falsea

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 (++a und --a) verändern den Wert der Variable und geben diesen zurück
  • Postfix-Operator (a++ und a--) 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 (true oder false)
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 = -Infinity
  • 0.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 (true oder false)
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
    statt a == b besser (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, longdouble, float
    • byteshort
    • shortint
    • intlong

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, floatbyte, short, int, long
    • longint
    • intshort

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 soll
  • WERT – 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.


Copyright © 2025 Thomas Smits