Ausgabe, Ausdrücke, Operatoren, Datentypen

1. Bildschirm-Ausgabe

Solange wir keine "Grafischen Oberflächen" programmieren können (und teils auch danach) ist die textuelle Ausgabe hauptsächliches Mittel um aus dem laufenden Programm Informationen aller Art bereitzustellen.

Java hat – wie vermutlich alle Programmiersprachen – eine Möglichkeit "eingebaut", um Textausgaben zu erzeugen.

Dazu ist folgendes Kommando (und Variationen davon) zu benutzen:
System.out.println("Der auszugebende Text");

Genaugenommen hat diese Methode die Signatur: public void System.out.println(String text).

An die Text-Ausgabe wird am Ende ein Zeilenschaltungszeichen gehängt, sodass die nächste Ausgabe wieder in einer neuen Zeile beginnen kann.

Wenn man allerdings mit einer späteren Ausgabe den Text direkt fortsetzen muss (also keine Zeilenschaltung haben möchte), verwendet man System.out.print("Der auszugebende Text"); – also OHNE das 'ln' am Ende.

Damit diese Ausgabemethode leistungsfähig wird, nutzen wir Javas Fähigkeiten, Texte zusammenzufügen – das führt uns zum Thema …​

2. Ausdrücke

Der als Parameter benötigte Text kann auf mehrere Arten vorliegen:

  • Text-Literal (z.B. "Ich bin ein Text-Literal")

  • Text-Variable (z.B.: String txt = "Hallo";)

  • Methode (z.B. getName()), die einen Text als Rückgabewert liefert – z.B. der "Getter" für eine Objektvariable evi einer Klasse Person: String name = evi.getName().

Sogar ein zusammengesetzter "Ausdruck" (Expression) aus den aufgezählten Möglichkeiten mit dem Textverknüpfungs-Operator '+' (wenn links davon ein Text vorliegt) ist erlaubt.
Das Zeichen '+' bildet (wenn mindestens auf einer Seite daven Text steht) sozusagen den "Klebstoff" (EN: glue), um Texte zusammenzufügen (EN: concatenate = verketten, verbinden, verknüpfen).

Man kann also schreiben: String name = "Evi"; String myText = "Hallo " + name;
Damit enthält die Variable myText: "Hallo Evi"

Analog mit Methodenaufruf: "Name: " + getName() erzeugt "Name: Evi" (wenn getName() den Rückgabewert "Evi" liefert).

Dies gilt ganz allgemein: überall wo ein Text, eine Zahl oder ein Wahrheitswert benötigt wird, kann dieser auf 4 Arten bereitgestellt werden:

  • als Literal (z.B. "Hallo", 273, false)

  • als Variable

  • als Rückgabewert einer Methode

  • als Ausdruck (Verknüpfung all dieser 4 mit Operatoren und Klammerung)

Die Operatoren haben eine Priorität. Bei Verkettung mit mehreren Operatoren ist die Reihenfolge von dieser Priorität bestimmt (siehe Nachfolgendes).

Neben System.out.println(…​) – Textausgabe mit nachfolgender Zeilenschaltung – gibt es auch System.out.print(…​) – ohne ln (für line = Zeile).

Damit bleibt die "Schreibmarke" (EN: Cursor) direkt nach dem letzten Zeichen stehen und es kann mit weiteren Ausgaben in der selben Zeile fortgesetzt werden.

3. Operatoren

3.1. Allgemeines

Operatoren (+, -, *, >, =, <, …​) nennt man Symbole, die für einen Vorgang (Operation) mit einem Ergebnis (Funktion) stehen. Zum Teil ist die Operation von den Datentypen abhängig, auf die der Operator angewendet wird.

Beispiel: der + Operator zwischen 2 ganzen Zahlen führt eine Addition der beiden Zahlen aus, zwischen denen er steht und stellt das Ergebnis für weitere Schritte bereit.

Wenn er aber als ersten Datentyp einen String vorfindet, fügt er in Java und manchen anderen Programmiersprachen den dahinter stehenden Wert als Text mit dem ersten Wert zusammen (auch wenn es eine Zahl oder ein Wahrheitswert ist). Englisch: Concatenate - verknüpfen, verketten.

Es gibt in Java und anderen Programmiersprachen mehrere Kategorien von Operatoren:

  • Text-Operator ( + …​ Zusammenfügen des vorderen Texts mit der textuellen Darstellung des nachfolgenden Wertes), Ergebnis: neuer Text

  • Arithmetische Operatoren ( +, -, *, /, % ), Ergebnis: Zahl

  • Relationale Operatoren ( <, <=, ==, >=, >, != ), Ergebnis: Boolean Wert

  • Logische Operatoren ( &&, ||, ! ), Ergebnis: Boolean Wert

Operatoren benötigen je nach Art immer eine bestimmte Anzahl von Operanden (das 'Material', mit denen sie arbeiten, z.B. die Zahlen links und rechts vom +).

Das können Zahlen, Texte, Wahrheitswerte ( true / false ) etc. sein. Der arithmetische Operator '+' benötigt 2 Zahlen und liefert als Ergebnis die Summe dieser beiden Zahlen.

In der Mathematik hat sich eingebürgert, viele Operatoren zwischen die Operanden zu schreiben, z.B. 3 + 4. Das nennt man Infix-Notation.

Genauso wäre es möglich, folgende Schreibweise (Prefix-Schreibweise) zu wählen: + 3 4 oder 3 4 +` (Postfix-Notation). Man könnte das selbe auch als Funktion anschreiben: add(3, 4).

3.2. Rangfolge/Priorität:

In der Arithmetik ist festgelegt, dass Punktrechnung vor Strichrechnung angewendet wird: 5 + 3 * 4 wird nicht als (5+3)*4 [ergibt 32] berechnet, sondern als 5 + (3*4) [ergibt 17].

Oft enthalten Ausdrücke mehrere Operatoren und Operanden. Der Ausdruck wird gemäß der Priorität der Operatoren ausgewertet. bei gleicher Priorität erfolgt die Auswertung binärer Operatoren üblicherweise von links nach rechts (links-assoziativ).
Ausnahme: Zuweisungsoperator = und die Kombinationen +=, -=, *=, etc.. Auch der einzige ternäre Operator ?: und alle unären Operatoren (Vorzeichen, Inkrement, Negation, etc.) werden von rechts nach links ausgewertet ("rechts-assoziativ") Um die Reihenfolge bei Bedarf umstellen zu können, werden Klammern (in Java etc. ausschließlich runde Klammern) verwendet. Diese können auch verschachtelt werden.

Damit eine Programmiersprache (hier 'Java') eindeutig definierte, für den Programmierer nachvollziehbare Ergebnisse liefert, ist für alle Operatoren eine Priorität festgelegt. Diese lässt sich leicht in einer geordneten Tabelle darstellen (siehe weiter unten).

4. Details zu den Operatoren

4.1. String

Operanden: min. einer ist String, Rest egal, jeder Datentyp hat textuelle Repräsentation
Ergebnis: neuer Text (Objektreferenz darauf)

Einziger String-Operator: '+' (Concatenation Operator)
Zusammenfügen geschieht (wenn keine Klammern verwendet) sukzessive von links nach rechts

4.2. Arithmetische

Operanden: Zahlen
Ergebnis: Zahl (genauer Typ festgelegt durch den "größten" Typ eines der Elemente)

  • '+' plus, '-' minus (als Vorzeichen und für Differenzberechnung), '*' mal

  • Nichtganzzahl-'/' …​ wenn min. 1 Operand float oder double

  • Ganzzahl-'/' …​ wenn beide Operanden ganzahlig (int, long, short, byte). Der nicht-ganzzahlige Teil wird wegggelassen: 5/2 → 2 oder 2/3 → 0 oder 23 / 10 → 2 oder 20 / 10 → 2

  • '%' …​ Divisionsrest bzw. Modulo-Operator für Ganzzahl-Divisionen: 5 % 2 → 1 oder 2 % 3 → 2 oder 23 % 10 → 3 oder 20 % 10 → 0

4.3. Vergleich

Operanden: Zahlen; für '==', '!=' auch boolean und Objekte (auch Strings)
Ergebnis: Boolean Wert (true/false)

  • '==' Prüfung auf Gleichheit: 5 == 2 + 3true oder 5 == 4false

  • '!=' Prüfung auf Ungleichheit: 5 != 2 + 3false oder 5 != 4true

  • '<' Prüfung auf kleiner: 5 < 7true oder -1 < 0true oder 3 < 1false

  • '>' Prüfung auf größer: 5 > 7false oder 5 > 5false oder 3 > 1true

  • '<=' Prüfung auf kleiner oder gleich: 5 <= 7true oder 5 <= 5true oder 3 <= 1false

  • '>=' Prüfung auf größer oder gleich: 5 >= 7false oder 5 >= 5true oder 3 >= 1true

4.4. Boolsche (logische)

Operanden: Boolean Ergebnis: Boolean Wert (true/false)

  • '!' Logisches NOT (Umkehrung des Wahrheitswertes, nur ein einziger Operand):
            ! truefalse
            !falsetrue

  • '&&' Logisches AND (2 Operanden, "kleinlich"):
            false && falsefalse
            false && truefalse
            true && falsefalse
            true && truetrue

  • '||' Logisches OR (2 Operanden, "großzügig"):
            false || falsefalse
            false || truetrue
            true || falsetrue
            true || truetrue

4.5. Sonstige

  • Zuweisung: weist das Ergebnis des Ausdrucks auf der rechten Seite an die Variable auf der linken Seite zu.
    Ergebnis: der zugewiesene Wert. Man kann daher auch schreiben:
    int a = 2, b, c; c = b = a;
    zuerst erfolgt die Zuweisung von a auf b, das Ergebnis wird dann auch c zugewiesen. Am Ende haben alle 3 den Wert 2.

  • Klammerung: wertet den enthaltenen Ausdruck aus und liefert das Ergebnis.

  • Casting: Explizite Typumwandlung. Liefert den gewünschten Typ zurück.

  • einige weitere, die erst später besprochen werden (siehe auch nachstehende Prioritäten-Tabelle)

4.6. Operatoren-Priorität

Es gibt eine klar definierte Abarbeitungsreihenfolgen-Logik für zusammengesetzte Ausdrücke. Man verwendet dabei oft den Begriff Priorität oder Rang. Die "stärksten", am frühesten auszuwertenden Operatoren haben die höchste Priorität bzw. die kleinste Rang-Nummer, die zuletzt auszuwertenden die niedrigste.

Die höchste Priorität hat die Klammerung '()', z.B. 3*(4+5)
Die niedrigste Priorität haben die Zuweisungs-Operatoren ('=', '+=', '-=', '=', '=', '%=', '&=', '|='), d.h. die Zuweisung erfolgt immer erst nach vollständiger Auswertung der rechten Seite. Operatoren gleicher Priorität werden immer von links nach rechts ausgewertet.

Eine kompakte Darstellung der Prioritäten (höchste hat niedrigste Nummer):

Rang dazugehörige Operatoren

0

Klammerung '(…​)'

1

'++', '--', 'Vorzeichen +', 'Vorzeichen -', '~', '!', 'Casting mit (Typ)'

2

'*', '/', '%'

3

'+', '-' und 'String +'   Addition, Subtraktion, String-Zusammenfügen

4

'<<', '>>', '>>>'

5

'<', '<=', '>', >=, 'instanceof'

6

'=='', '!='

7 bis 11

'&', '^', '|', '&&', '||'

12

'? :' …​ 'true/false ? true-Zweig-Ausdruck : false-Zweig-Ausdruck '   liefert eines der Ausdruck-Ergebnisse zurück

13

'=', '*=', '/=', '%=', '+=', '-=', '<<=', '>>=', '>>>=', '&=', '^=', '|='     (Zuweisungen)

4.7. Erläuterungen, Tipps

Bei verketteten Rechnungen mit Divisionen besteht große Irrtums-Wahrscheinlichkeit, daher sollte ein zusammengesetzter Nenner IMMER in Klammern stehen (manchmal auch ein zusammengesetzter Zähler!)

Generell sollten außer in trivialen arithmetischen und boolschen Fällen Klammern zum Erzwingen und Evident-Machen der durchzuführenden Rechnung verwendet werden.

Ausnahme sind die unären Operatoren, die ALLE die nach der Klammerung höchsten Rang (niedrigste Nummer!) haben ('++', '--', 'Vorzeichen +', 'Vorzeichen -', '~', '!', Casten mit (Typ)).

5. Datentypen

5.1. "Primitive" Datentypen - Details und weitere

Die 8 grundlegendsten Datentypen haben eine technische Sonderstellung. Sie werden Primitive Datentypen genannt. Sie werden nachstehend vollständig mit ihren wichtigsten Eckdaten aufgelistet.

Was auffällt ist das Fehlen des Text-Datentyps String. In Java ist dieser kein primitiver Datentyp.

Die andere Kategorie von Datentypen sind die Objekt-Datentypen und String ist einer davon. Objekt-Datentypen sind leistungsfähiger – sie haben beispielsweise Methoden. Ihr Nachteil ist die etws aufwändigere Erzeugung und "Löschung". Dateninhalte aller Objektdatentypen sind letztlich in primitiven Datentypen bzw. "Arrays" davon abgelegt.

Die primitiven Datentypen von Java sind:

  • logisch (Werte true, false, Standardwert: false):
    boolean

  • Einzelzeichen (Ganzzahl, die den Unicode/UTF-16 Zeichencode enthält – man kann auch damit rechnen!):
    char (16 Bit, 0 …​ 216-1 = 65_535) oder in Hochkomma: z.B.: 'a'. Z.B. 'z' - 'a' liefert den Integer-Wert 26, 'a' + 1 liefert 'b'

  • ganzzahlig
    int (32 Bit, -231…​ 231-1 = -2_147_483_648 …​ 2_147_483_647), wird in Java als Standard-Typ für ganzzahlige Werte verwendet.
    long (64 Bit, -263…​ 263-1 = –9_223_372_036_854_775_808 … 9_223_372_036_854_775_807)
    short (16 Bit, -215…​ 215-1 = -32_768 …​ 32_767)
    byte (8 Bit, -27…​ 27-1 = -128 …​ 127 – oft auch für Arbeit "nahe an der Hardware")

  • Fließkomma
    float (32 Bit, 7 signifikante Stellen,, für Literale nachgestelltes f oder F nötig!, min./max. Werte: ca. ± 3,4×1038),
    double (64 Bit, ca. 17 signifikante Stellen, min./max. Werte: ca. ± 1,8×10308), wird in Java als Standard-Typ für nicht-ganzzahlige Werte verwendet.

Hinweis: Unterstrich '_' kann in Java für Zahlen als Tausender-Trenner - zur besseren Lesbarkeit - verwendet werden, z.B. 21_987 oder -7_654.32)

5.1.1. Typ-Umwandlung

5.1.1.1. Automatisch bei "Erweiterung"

Typ-Umwandlung kann automatisch erfolgen, wenn der neue Typ einen größeren Wertebereich hat. Das geht für:
byte → char, byte → short, char → int, short → int, int → long, long → float, float → double (und Kombinationen).

Der einzige primitive Datentyp ohne Typumwandlungsmöglichkeit ist boolean.

5.1.1.2. Manuell bei "Verengung"

In die Gegenrichtung kann (abhängig vom Wert) ein Problem auftreten:
Java macht keine Prüfung, sondern verweigert dafür die automatische Typumwandlung.

Hier muss man eine explizite Typ-Umwandlung "type cast" durchführen (und ist damit für die Problematik selbst verantwortlich).
Explizites Type Casting erfolgt durch Voranstellen des gewünschten Typs in runden Klammern – z.B.:
int anIntVar = 42; short aShortVar = (short) anIntVar;
Wenn "es sich nicht ausgeht", wird ein üblicherweise nicht gewollter Wert erzeugt.
Zum Probieren bzw. Beobachten des Problems:

    //  ...
    for (int i = 125; i < 260; i++) {
      byte bt = (byte) i;
      System.out.println("int: " + i + ", byte: " + bt);
    }
    //  ...

Es kann natürlich auch eine Umwandlung von float/double auf int erfolgen – dann wird er Nachkomma-Anteil einfach abgeschnitten.

Für echte Rundung auf die nächste ganze Zahl kann vor dem "Casten" einfach 0.5 dazugezählt werden, dann ergibt sich für Nachkomma-Werte ab 0.5 die nächsthöhere ganze Zahl:
3.51 + 0.5 = 4.01 → Abschneiden liefert 4
3.49 + 0.5 = 3.99 → Abschneiden liefert 3;

5.1.2. Wrapper-Klassen zu den primitiven Datentypen

5.2. Strings

Textuelle Inhalte sind extrem wichtig. Java stellt dafür keinen primitiven Datentyp bereit, sondern einen – besonders im Compiler unterstützten – Objekt-Datentyp: String.

Die besondere Unterstützung ist z.B. in der Syntax für String-Literale sichtbar: "Ein Text". Weiters werden String-Literale in einem speziellen "String-Pool" abgelegt, gleiche Texte werden nur ein einziges Mal abgelegt und immer der selbe Eintrag referenziert.
Weiters gibt es einen speziellen Operator, um Strings mit anderen Elementen zusammenzufügen (EN: concatenate): '+' – z.B.
"Hallo" + " Evi" → "Hallo Evi" oder
"Nummer " + 99 → "Nummer 99" oder
31 + ". Mai""31. Mai" oder
"Pi: " + 3.14f"Pi: 3.14"

String-Objekte sind übrigens nicht veränderbar (immutable). Für Änderungen wird immer ein neues String-Objekt erzeugt. Wir erzeugen ein String-Objekt, das von der Variablen hi referenziert wird: String hi = "Hallo";. Nach Ausführen von hi += " Evi"; wird also ein neues String-Objekt "Hallo Evi" erzeugt und der Variablen hi als neue Referenz zugewiesen. Im String-Pool liegen dann beide: "Hallo" und "Hallo Evi" (irgendwann wird "Hallo" automatisch wieder gelöscht, wenn keine Referenz mehr darauf zeigt – vom Garbage Collector, der zur JVM gehört).

5.3. Alle anderen Objekt-Datentypen

(Alles gilt auch für String-Objekte!)
Objekt-Datentypen werden als Referenz-Typen bezeichnet, weil sie nicht direkt den Wert enthalten, sondern nur eine Referenz (gewissermaßen die Adresse) auf ein Objekt im Hauptspeicher.