Logo Wissenstransfer Gerhard at CichnaDotCom

>> Wissensdatenbank / Objektorientiertes Programmieren

Java Sprachkonstrukte

Primitive Datentypen

Der Datentyp eines Attributes bestimmt, welche Informationen in einem Attribut abgelegt werden dürfen. Wird als Datentyp eines Attributes der Name einer Klasse angegeben, dürfen nur Objekte dieser Klasse als konkrete Werte diesem Attribut zugewiesen werden. Mit den sogenannten primitiven Datentypen gibt es in Java allerdings eine Menge von sehr einfachen Datentypen, die nicht durch eine eigene Klasse beschrieben werden.

Primitive Datentypen werden eingesetzt um Wahrheitswerte (true, false), ganze Zahlen (1, 12, 13131), Fließkommazahlen (1.123, 21234.1232) und einzelne Zeichen (t, w, f, d) zu speichern. Primitive Datentypen sind einfache Standard-Datentypen, die es auch in anderen Programmiersprachen gibt.

Übersicht über die primitiven Datentypen in Java:

Art der gespeicherten
Werte
Schlüsselwort
in Java
Beschreibung Beispiele
Wahrheitswerte boolean Kann entweder true oder false sein. Weitere Werte sind nicht erlaubt. true
false
Ganze Zahl byte 8Bit-Wertebreich von -128 bis 127 -123, 0,23, 120
short 16Bit-Wertebreich von -32.768 bis 32.767 -23000, 0, 13231
int 32Bit-Wertebreich von -2.147.483.648 bis 2.147.483.647 -12332123, 234,
1102379239
long 64Bit-Wertebreich von -9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807 -8347829790645,
13, 0, 34879,
789234789274
Fließkommazahl float 32Bit-Wertebreich von 1,40239846*10^-45 bis 3,40282347*10^38 1.87236f (Einfache Zahl mit Komma
und angehängtem "f")
-3.938e12f (-3.938*10^12 mit
angehängtem "f")
double 64Bit-Wertebreich von 4,94065645841246544*10^-324
bis 1,79769131486231570*10^308
1.87236d (Einfache Zahl mit Komma
und angehängtem "d")
-3.938e120d (-3.938*10^120 mit
angehängtem "d")
Schriftzeichen
(Character)
char Ein einzelnes Unicode-Zeichen. Umfasst neben Ziffern, Buchstaben
und Symbolen auch Steuerzeichen wie Leerzeichen, Tabulator oder
Zeilenumbruch.
'A' (= Buchstabe A)
'2' (= Ziffer 2)
'\n' (= Steuerzeichen Zeilenumbruch)

Für ganze Zahlen wird in der Regel der Datentyp int verwendet.

Ein weiterer häufig verwendeter Datentyp ist String. Er wird zur Speicherung von Zeichenketten verwendet. String ist kein primitiver Datentyp, sondern wird durch eine eigene Java-Klasse beschrieben. Dennoch kann String wie ein primitiver Datentyp Attributen zugewiesen werden.

Art der gespeicherten
Werte
Schlüsselwort
in Java
Beschreibung Beispiele
Zeichenketten String Wird zum Speichern von beliebig langen Zeichenketten verwendet. Ist
kein primitiver Datentyp. Die Klasse String bietet viele bereits vorhandene
Methoden für die Bearbeitung von Zeichenketten.
"online", "Herr Koch",
"Was nicht passt wird passend gemacht",
""

Variablen

Mit Variablen bietet Java die Möglichkeit, konkrete Werte im Speicher der Anwendung abzulegen. Damit können z.B. Ergebnisse von Berechnungen kurzzeitig gespeichert werden, die als Eingabeparameter für weitere Berechnungen benötigt werden.

Vergleichbar mit einem Attribut muss zur Deklaration einer Variable der Datentyp und der Name einer Variable festgelegt werden. Abgeschlossen wird die Deklaration mit einem Semikolon ";". Anders als bei Attrbiuten können Variablen jedoch nur innerhalb des Methodenrumpfes verwendet werden. Daher benötigt sie keinen Sichtbarkeitsmodifikator. Nachdem Variablen deklariert worden sind, können sie innerhalb der Methode verwendet werden. Das bedeutet auch, dass ihr erst dann ein konkreter Wert zugewiesen werden kann, nachdem die Variable deklariert wurde.

Deklaration und Zuweisung von Variablen:
public void rechnerei() {
    int zahl1;
    byte zahl2;
    float zahl3;
    char einZeichen;
    String zeichenkette;

    zahl1 = 234;
    zahl2 = 123;
    zahl3 = 123.4f;
    einZeichen = 'E';
    zeichenkette = "Das ist eine Zeichenkette";
}

Im ersten Block werden Variablen deklariert (z.B. zahl1 vom Typ Integer), im zweiten Block werden den Variablen Werte zugewiesen (z.B. der Wert E der Variablen einZeichen). Die Zuweisung wird durch das Gleichheitszeichen "=" ausgedrückt. Links von "=" steht der Name der Variable, rechts von "=" der Wert, den die Variable erhalten soll.

Die Deklaration und Zuweisung von Variablen können auch innerhalb einer Anweisung erfolgen:
public void rechnerei() {
    int zahl1 = 234;
    byte zahl2 = 123;
    float zahl3 = 123.4f;
    char einZeichen = 'E';
    String zeichenkette = "Das ist eine Zeichenkette";

}

Operatoren und Ausdrücke

Operatoren ermöglichen das Rechnen und Verändern von in Variablen und Attributen gespeicherten Werten. Die Programmiersprache Java unterstützt dafür eine Menge arithmetischer und logischer Operatoren. Generell wird der Datentyp des Ergebnisses eines Operators durch den Operanden, der den größten Wertebereich hat, bestimmt.

Die wichtigsten arithmetischen Operatoren:

Ausgeführte Operation Operator Anwendbar auf die Datentypen Beispiel
Inkrement; erhöht eine
Variable um den Wert 1
++ Ganze Zahlen, Fließkommazahlen int a = 3;
a++;

float b = 3f;
b++;
Arithmetische Addition;
der Ergebnistyp der Berechnung
entspricht dem des Operanden mit
dem größten Wertebereich
(z.B. int + int = int,
int + long = long)
+ Ganze Zahlen, Fließkommazahlen int c, d, e;
c = 3;
d = 5;
e = c + d;

int x;
long y, z;
x = 3;
y = 4;
z = x + y;
Arithmetische Subtraktion - Ganze Zahlen, Fließkommazahlen int e = 3;
float f = 4;
float g;
g = e - f;
Arithmetische Multiplikation * Ganze Zahlen, Fließkommazahlen int c, d;
c = 3;
d = c * 4;
Arithmetische Division; berechnet
Quotienten aus Dividend und Divisor
/ Ganze Zahlen: Sind beide Operanden ganze Zahlen,
wird vom Ergebnis der Teil nach dem Komma abge-
schnitten.
Fließkommazahlen: Ist mindestens ein Operand
eine Fließkommazahl, wird das Ergebnis auch eine
Fließkommazahl und nicht gerundet.
int h = 4;
int i = 3;
int j;
j = h / i;

int o = 4;
double p = 3;
double q;
q = o / p;
Rest (auch: Restwert-Operator);
berechnet den Rest der arithmetischen
Division
% Ganze Zahlen, Fließkommazahlen int k = 11;
int l = 5;
int m;
m = k % l;

Im Unterschied zu arithmetischen Operatoren werten logische Operatoren Ausdrücke zu wahr (true) oder falsch (false) aus. Sie werden daher häufig zur Steuerung von Kontrollstrukturen eingesetzt. Der Ergebnistyp von logischen Operatoren ist immer boolean.

Ausgeführte Operation Operator Anwendbar auf die Datentypen Beispiel
Logisches Komplement (Negation); ändert
den Wahrheitswert des Operanden
! boolean boolean b1;
boolean b2 = false;
b1 = !b2;
Logisches UND; liefert true, wenn beide
Operanden true sind
&& boolean boolean b3;
boolean b4 = true;
boolean b5 = true;
b3 = b4 && b5;
Logisches ODER; liefert true, wenn einer
der beiden Operanden true ist
|| boolean boolean b6;
boolean b7 = false;
boolean b8 = true;
b6 = b7 || b8;
Exklusiv-ODER; liefert true, wenn ein
Operand true und ein Operand false ist
^ boolean boolean b9;
boolean b10 = false;
boolean b11 = true;
b9 = b10 ^ b11;

Vergleichsoperatoren vergleichen Ausdrücke miteinander und liefern als Ergebnistyp boolean. Das heißt, sie liefern entweder wahr (true) oder falsch (false) zurück. Ebenso wie logische Operatoren werden Vergleichsoperatoren häufig zur Steuerung von Kontrollstrukturen eingesetzt.

Ausgeführte Operation Operator Anwendbar auf die Datentypen Beispiel
Gleich-
heit
Primitive Datentypen: liefert
true, wenn die Werte der
Operanden gleich sind
== Primitive Datentypen int z1, z2;
boolean e1;
z1 = 3;
z2 = 3;
e1 = z1 == z2;
Referenzdatentypen:
liefert true, wenn in
beiden Operanden die Referenz
auf dasselbe Objekt enthalten ist
Referenzdatentypen Kunde kunde1, kunde2;
boolean e2;
kunde1 = new Kunde();
kunde2 = kunde1;
e2 = kunde1 == kunde2;
Ungleich-
heit
Primitive Datentypen: liefert
true, wenn die Werte der
Operanden nicht gleich sind
!= Primitive Datentypen int z3, z4;
boolean e3;
z3 = 4;
z4 = 3;
e3 = z3 != z4;
Referenzdatentypen:
liefert true, wenn in
beiden Operanden unterschiedliche
Referenzen enthalten sind
Referenzdatentypen Kunde kunde3, kunde4;
boolean e4;
kunde3 = new Kunde();
kunde4 = new Kunde();
e4 = kunde3 != kunde4;
Kleiner als liefert true, wenn der Wert
des linken Operanden kleiner ist verglichen
mit dem Wert des rechten Operanden
< Ganze Zahlen,
Fließkommazahlen
int z5, z6;
boolean e5;
z5 = 4;
z6 = 5;
e5 = z5 < z6;
Kleiner gleich liefert true, wenn der Wert
des linken Operanden kleiner oder gleich
ist verglichen mit dem Wert des rechten
Operanden
<= Ganze Zahlen,
Fließkommazahlen
int z5, z6;
boolean e5;
z5 = 4;
z6 = 5;
e5 = z5 <= z6;
Größer als liefert true, wenn der Wert
des linken Operanden größer ist verglichen
mit dem Wert des rechten Operanden
> Ganze Zahlen,
Fließkommazahlen
int z7, z8;
boolean e6;
z7 = 6;
z8 = 5;
e6 = z7 > z8;
Größer gleich liefert true, wenn der Wert
des linken Operanden größer oder gleich
ist verglichen mit dem Wert des rechten
Operanden
>= Ganze Zahlen,
Fließkommazahlen
int z7, z8;
boolean e6;
z7 = 6;
z8 = 5;
e6 = z7 >= z8;
Typvergleich liefert true, wenn der Datentyp
des linken Operanden gleich dem gegebenen
Datentypen im rechten Operanden ist
instanceof Referenzdatentypen Kunde k1;
boolean e7;
k1 = new Kunde();
e7 = k1 instanceof Kunde;

Besondere Prüfoperatoren sind die Prüfung auf Gleichheit (==) und die Prüfung auf Ungleichheit (!=). Denn bei diesen Operatoren bestimmen die Operanden wie verglichen wird. Dabei unterscheidet sich der Vergleich zwischen primitiven Datentypen und Referenzdatentypen grundsätzlich. Sind beide Operanden primitive Datentypen, so werden direkt die Werte der Operanden verglichen.
int z1, z2;
boolean e1;
z1 = 3;
z2 = 3;
e1 = z1 == z2;
System.out.println(e1);

In diesem Fall hat e1 den Wert true.

Für den Fall, dass Referenzdatentypen verglichen werden sollen, vergleicht der Operator "==" nicht den tatsächlichen Inhalt der zu vergleichenden Objekte. Es wird vielmehr nur verglichen, ob beide Operatoren dieselben Objekte referenzieren, das heißt, auf das gleiche Objekt "zeigen". Im untenstehenden Code wird k3 und k4 jeweils ein neu erzeugtes Objekt zugewiesen. In k3 ist die Referenz auf das erste Objekt "Kunde" gespeichert, in k4 die Referenz auf das zweite Objekt "Kunde". Der Vergleich auf "Referenzgleichheit" von k3 und k4 liefert damit den Wert false.
Kunde k3, k4;
boolean e1;
k3 = new Kunde();
k4 = new Kunde();
e1 = k3 == k4;
System.out.println(e1);

Im folgenden Codebeispiel wird der Variablen k4 kein neu erzeugtes Objekt mehr zugewiesen. Der Variablen k4 wird nun exakt der gleiche Wert zugewiesen, der in k3 gespeichert ist. Da der Wert von k3 eine Referenz auf ein Objekt vom Typ "Kunde" ist, hat k4 nach der Anweisung k4 = k3 genau dieselbe Referenz wie der Wert k3. Damit liefert die Operation k3 == k4 das Ergebnis true.
Kunde k3, k4;
boolean e1;
k3 = new Kunde();
k4 = k3;
e1 = k3 == k4;
System.out.println(e1);

Darüber hinaus gibt es noch einen Operator, der häufig verwendet wird, um Zeichenketten miteinander zu verbinden, das heißt, mehrere Zeichenketten zu einer neuen zusammenzuführen (String-Konkatenation).

Ausgeführte Operation Operator Anwendbar auf die Datentypen Beispiel
Verketten von Zeichen (Konkatenation);
Verketten von mehreren Zeichenketten zu einer
neuen Zeichenkette
+ String String s1, s2, s3, s4;
s1 = "Hallo";
s2 = " ";
s3 = "Welt!";
s4 = s1 + s2 + s3;

Kontrollstrukturen

Beim Ablauf eines Programms wird ein Methodenrumpf von oben nach unten durchgearbeitet. Bei der Implementierung von Algorithmen und Geschäftsregeln ist es jedoch häufig notwendig, konkrete Anweisungen nur beim Vorliegen ganz bestimmter Bedingungen durchzuführen. Weiterhin ist es häufig notwendig, dieselben Anweisungen hintereinander für eine Menge von Werten auszuführen.

Das bedingte oder kontrollierte wiederholte Ausführen von Anweisungen wird durch sogenannte Kontrollstrukturen ermöglicht. Die wichtigsten von Java unterstützten Kontrollstrukturen sind:

Die bedingte Verzweigung wird verwendet, um die konkrete Stelle, mit der das Programm fortgesetzt wird, anhand einer Bedingung zu prüfen.
if (Bedingung) {
    Anweisung1;
}
else {
    Anweisung2;
}

Die Bedingung muss dabei ein Ausdruck sein, der zu true oder false ausgewertet werden kann. Die Anweisung1 wird nur dann ausgeführt, wenn die Bedingung true ist. In diesem Fall wird Anweisung2 nicht ausgeführt. Anweisung2 wird hingegen nur dann ausgeführt, wenn die Bedingung false ist und damit Anweisung1 nicht ausgeführt wurde. Der else-Teil in bedingten Verzweigungen ist optional.
Anwendungsfall bedingte Verzweigung
Bei einer bedingten Verzweigung werden entweder die Anweisungen im "if-Block" ausgeführt oder die Anweisungen im "else-Block". Niemals jedoch beide Blöcke.

Beispiel einer einfachen bedingten Verzweigung:
Code-Beispiel bedingte Verzweigung

In einer erweiterten if-else-Verzweigung wird nicht nur eine Bedingung geprüft, bevor der else-Block abgearbeitet wird, sondern mehrere sich gegenseitig ausschließende Bedingungen. Die Struktur einer erweiterten if-else-Verzweigung in Java sieht wie folgt aus:
if (Bedingung1) {
    Anweisung1;
}
else if (Bedingung2) {
    Anweisung2;
} else {
    Anweisung3;
}

Auch bei einer erweiterten if-else-Vezweigung wird jeweils nur ein Block abgearbeitet.

Neben den Verzweigungen sind die sogenannten Schleifen eine weitere wichtige Kontrollstruktur. Schleifen ermöglichen die mehrfache Ausführung von gleichen Anweisungen hintereinander. Die Anzahl der Schleifendurchläufe wird von einer Einhaltung einer Schleifenbedingung (auch: Laufbedingung, Abbruchbedingung) bestimmt. Grundsätzlich können drei verschiedene Schleifenarten unterschieden werden:

Die while-Schleife prüft zuerst die Schleifenbedingung. Wenn die Bedingung erfüllt ist, werden die Anweisungen der Schleife ausgeführt. Andernfalls werden die Anweisungen der Schleife übersprungen. Nach der Ausführung der Anweisungen der Schleife wird erneut die Bedingung geprüft. Wird diese zu false ausgewertet, ist die Schleife beendet. Bei einer Auswertung der Bedingung zu true werden die Anweisungen der Schleife erneut durchlaufen. Die Struktur einer while-Schleife sieht folgendermaßen aus:
while (Bedingung) {
    Anweisungen;
}

Die while-Schleife wird auch kopfgesteuerte Schleife genannt, denn bereits vor dem ersten Ablauf wird die Bedingung bereits geprüft. Wird sie dabei zu false ausgewertet, wird die komplette Schleife übersprungen.
While-Schleife

Die do-while-Schleife ist eine fußgesteuerte while-Schleife. Die Anweisungen der Schleife werden in jedem Fall mindestens einmal ausgeführt. Erst dann wird die Schleifenbedingung geprüft. Wenn die Bedingung erfüllt ist, werden die Anweisungen der Schleife wiederholt ausgeführt. Andernfalls wird die Schleife beendet. Die Struktur einer do-while-Schleife sieht folgendermaßen aus:
do {
    Anweisungen;
} while (Bedingung);

Do-While-Schleife

Im Unterschied zur while-Schleife und zur do-while-Schleife sind bei der for-Schleife die Initialisierung, Bedingungsprüfung und das Ändern der Zählvariablen Elemente des Schleifenkopfes. Genauso wie die while-Schleife ist die for-Schleife eine kopfgesteuerte Schleife, das heißt, vor dem erstmaligen Ausführen der Schleife wird geprüft, ob die Bedingung erfüllt ist.

Die Struktur einer for-Schleife kann folgendermaßen dargestellt werden:
for (Initialisierung; Bedingung; Schleifenfortschaltung) {
    Anweisungen;
}

Das Ablaufschema einer for-Schleife kann wie folgt beschrieben werden:

  1. Ausführen der Anweisung der Initialisierung
  2. Prüfung der Bedingung
  3. Auswerten der Bedingung
    1. Wenn Bedingung true: Ausführung aller Anweisungen der for-Schleife
    2. Wenn Bedingung false: Abbruch und keine Ausführung der Anweisungen
  4. Ausführen der Anweisung der Schleifenfortschaltung
  5. Weiter mit Punkt 2 (Prüfung der Bedingung)

Dabei wird deutlich, dass im Anweisungsblock keine Steueranweisungen der Schleife, wie zum Beispiel das Ändern der Zählvariablen, implementiert sind:
For-Schleife

Kontrollstrukturen können in ihren Anweisungsblöcken selbst wieder Kontrollstrukturen enthalten. Daher können auch Schleifen ineinander geschachtelt werden.

Pakete und Sichtbarkeitsmodifikatoren

Zur logischen Strukturierung der Klassen innerhalb eines Projektes sowie zur Aufteilung der Aufgaben im Entwicklungsteam wird die Menge aller Klassen in verschiedene Pakete unterteilt. Klassen mit ähnlichen Funktionen und Klassen, die stark voneinander abhängen, werden gleichen Paketen zugeordnet. Die Zuordnung zu Paketen hat Auswirkungen auf den Speicherort der Klasse und auf die Zugriffsberechtigungen auf Methoden anderer Klassen.

Pakete lassen sich mit Verzeichnissen des Dateisystems eines Computers vergleichen. Verzeichnisse dienen als Mittel zur Strukturierung von Dateien und speichern selber keine Informationen. Jede Datei wird in genau einem Verzeichnis gespeichert. Verzeichnisse können Unterverzeichnisse enthalten. Dateien können jedoch keine weiteren Dateien oder Verzeichnisse enthalten. Genau so verhält es sich mit Paketen und Klassen anstelle von Verzeichnissen und Dateien.

Pakete

Die erste Quelltextzeile in einer Java-Klasse enthält immer die Angabe des Pakets, in dem sich die Klasse befindet.
package onlineshop.abwicklung;

public class Bestellung {
...
}

Der Speicherort der Datei ergibt sich aus dem Paket einer Klasse. Dabei wird für jedes Paket eines Programms ein Verzeichnis angelegt. Die Datei mit der Java-Klasse muss sich in dem Verzeichnis des Pakets befinden. Für die Klasse "Kunde" im Paket "onlineshop.nutzer" muss es eine Datei "onlineshop/nutzer/Kunde.java" geben.

Klassennamen müssen innerhalb eines Pakets eindeutig sein. Der für die Java-Laufzeitumgebung vollständige Name einer Klasse (der sogenannte "qualifizierte Name") ergibt sich aus dem Paket der Klasse und dem Namen der Klasse.

Mit Hilfe der Sichtbarkeitsmodifikatoren wird die Sichtbarkeit von Klassen, Attributen und Methoden und damit Zugriffsmöglichkeiten von anderen Klassen festgelegt. In Java gibt es folgende vier Sichtbarkeitsmodifikatoren, die auf die Elemente Klasse, Attribut und Methode angewendet werden können:

Sichtbarkeitsmodifikator Beschreibung Anwendbar auf Beispiel
public Das Element ist für alle Klassen des Programms
sichtbar.
Klassen, Attribute, Methoden public class Bestellung {}
public void berechneSumme() {}
(nichts) Das Element ist nur im gleichen Paket sichtbar. Klassen, Attribute, Methoden class Bestellung {}
void berechneSumme() {}
protected Das Element ist nur im gleichen Paket oder
abgeleiteten Klassen sichtbar.
Attribute, Methoden protected void berechneSumme() {}
protected int anzahlArtikel;
private Das Element ist nur für Elemente der gleichen
Klasse sichtbar.
Attribute, Methoden private void berechneSumme() {}
private int anzahlArtikel;

Obwohl Java dem Entwickler die Möglichkeit gibt, die Sichtbarkeit von Attributen so festzulegen, dass ein direkter Zugriff durch andere Klassen erfolgen kann, sollten Attribute außer in gut begründeten Ausnahmefällen immer mit der Sichtbarkeit "private" versehen werden.