Jetzt anmelden...

Name
Passwort
Registrieren

Java Tutorial #3

Java Primitive Datentypen

Nachdem wir in den letzten Kapiteln einiges über Variablen gelernt haben, wollen wir uns nun die primitive Datentypen in Java genauer ansehen. Am Ende des Kapitels werden wir genau wissen, was primitive Datentypen sind und wie sie korrekt eingesetzt werden.

Kommentare [0]

Stefan 22.03.2018

Infos zum Artikel

Kategorie Java
Autor Stefan
Datum 22.03.2019

Thema in Kurzform

  • Datentypen für Ganzzahlen: byte, short, int, long
  • Datentypen für Fließkommawerte: float, double
  • Datentypen für Wahrheitswerte und Einzelzeichen: boolean, char

Ganz schön primitiv

Java kennt zwei ganz unterschiedliche Typarten: Objekttypen und primitive Typen. Im Alltag meiden wir für gewöhnlich die primitive Typen. In der Programmierung haben wir gerne mit ihnen zu tun. In diesem Kapitel wollen wir uns deshalb etwas näher mit ihnen beschäftigen.

Primitive Datentypen haben mit Objektorientierung zunächst einmal nichts zu tun; es sind ganz einfache Datentypen, die jeweils nur einen Wert speichern können. Sie sind ganz einfach und nicht komplex, ganz primitiv also.

In Java gibt es insgesamt 8 verschiedene primitive Datentypen, die sich darin unterscheiden, welche Werte sie speichern können. Ein Beispiel: Der primitive Datentyp short kann nur ganze Zahlen, die größer/gleich -32768 und kleiner/gleich 32767 sind, speichern. Um eine größere Ganzzahl zu speichern, müsste man auf einen anderen primitiven Typ zurückgreifen, wie z.B. Integer (int). Damit können wir Werte zwischen ca. -2 und +2 Millionen speichern. Solls noch größer sein? Dann bitte den Datentyp long nehmen. Oder ganz klein (-128 bis 127)? Dann eben byte.

Und was ist mit „Kommazahlen“? Eins vorweg: In der Programmierung werden „Kommazahlen“ nicht mit einem Komma, sondern einem Punkt(!) geschrieben. Also nicht 1,35, sondern 1.35. Fließkommazahlen können in den oben besprochenen Datentypen für Ganzzahlen nicht gespeichert werden. Wir benötigen stattdessen die primitiven Datentypen double oder float. Die können das.

Bleiben noch zwei übrig: char und boolean: Der Typ char kann ein Zeichen (z.B. 'A'), eine Ziffer oder Satzzeichen aufnehmen. Die einfachen Anführungszeichen ' sind übrigens verpflichtend!

Von besonderer Bedeutung schließlich (speziell bei Schleifenkonstruktionen) ist der Typ boolean. Der ist nun ganz anders als die bisher besprochenen Datentypen, denn er speichert keine Zahlen oder Zeichen, sondern Wahrheitswerte. Da es in der Logik nur zwei Wahrheitswerte gibt, nämlich wahr und falsch (nein: „Halbwahrheiten“ sind nicht zulässig!), kennt der boolean auch nur zwei mögliche Werte: true und false.

Die folgende Tabelle gibt uns noch einmal eine Übersicht aller primitiver Datentypen mit Ihrem jeweiligen Wertebereich:

Typ Speicher gültiger Wertebereich Beispiel
char 16 Bit Unicode-Wert 0...65.535 'A'
boolean 8 Bit true/ false true
byte 8 Bit Ganzzahl -128...127 127
short 16 Bit Ganzzahl -32.768...32.767 32767
int 32 Bit Ganzzahl -2.147.483.648...2.147.483.647 2147483647
long 64 Bit Ganzzahl -263...263-1 (sehr groß!) 9223372036854775807L
float 32 Bit Fließkomma +/-1,4E-45 ... +/-3,4E+38 123.45f
double 64 Bit Fließkomma +/-4,9E-324 ... +/-1,7E+308 123.45

Wertebereiche der primitiven Datentypen

Wie wir oben schon erfahren haben, gibt es für die verschiedenen Datentypen unterschiedlich gültige Wertbereiche. Wir wollen nun einige Variablen mit primitiven Typen deklarieren und initialisieren und bewegen uns dabei zunächst einmal in den gültigen Wertebereichen. Das folgende Code-Beispiel ist daher voll und ganz korrekt:

char zeichen = 'F';
boolean bool = true;
byte zahlMini = 80;
int zahlNormal = 1500000;
double zahlMitKomma = 3.49;

Es ist übrigens ohne Weiteres möglich, eine char-Variable mit einer Ganzzahl von 0 bis 65.535 zu initialisieren. Der angegebene Zahlen-Wert greift dann auf die Unicode-Zeichen-Tabelle zurück und weist unserer char-Variable das entsprechende Zeichen zu, wie wir im folgenden Code sehen:

char zeichen1 = 82; //Zeichen: 'R'
char zeichen2 = 174; //Zeichen : '®'
char zeichen3 = 198; //Zeichen: 'Æ'

Nun verlassen wir einmal die gültigen Wertebereiche und überfordern unsere Variablen:

int z1 = 1.2; // Fehler! Typ nicht kompatabil!
short z2 = 1921222; // Fehler! Typ nicht kompatabil!
int z3 = 9f; // Fehler! Typ nicht kompatabil!

Zu den erzeugten Fehlern im Einzelnen:

Zeile 1: Eine int-Variable kann nur Ganzzahlen speichern. 1.2 ist eine Fließkommazahl und "passt" daher nicht in die int-Variable.

Zeile 2: Eine short-Variable hat einen gültigen Wertebereich bis maximal 32.767. Wir sind also deutlich darüber: 1921222 "passt" nicht in eine Variable vom Typ short.

Zeile 3: 9f bedeutet float-Wert (Kleines-f nach der Zahl). 9.0 (Fließkomma) passt aber niemals in eine int-Variable.

Ganz gemein sind auch folgende häufig gemachten Fehler:

long z4 = 5432423433300; // Fehler! Integer-Zahl zu groß!
float z5 = 4535234343500; // Fehler! Integer-Zahl zu groß!

Warum gibt es hier einen Compilerfehler? Müssten die beiden Zahlen auf der rechten Seite nicht locker in die long- bzw. float-Variable (z4 bzw. z5) passen? Theoretisch schon, aber hier gilt es zu beachten, dass die Zahlen 5432423433300 und 4535234343500 zuerst als int-Werte interpretiert werden und int-Werte - Sie haben es erfasst! - haben einen maximalen Wertebereich von 2147483647. Es spielt hier also keine Rolle, dass es sich bei den Variablen um long- bzw. float-Typen handelt. Der Compiler liest hier von rechts nach links, erkennt eine für Integer zu große Zahl und meckert.

Wir können das Problem einfach lösen, indem wir dem Compiler mitteilen, dass es sich bei diesen großen Zahlen nicht um int-Werte (wie er es denkt), sondern explizit um Werte vom Datentyp long- bzw. float handelt. Hierzu schreiben wir hinter die jeweilige Zahl den Buchstaben L (für long) bzw. f (für float).

long z4 = 5432423433300L; //läuft!
float z5 = 4535234343500f; //läuft!

So wie Ganzzahlen zunächst standardmäßig als int-Werte interpretiert werden, werden Fließkommazahlen vom Compiler automatisch als Werte vom Datentyp double gelesen. Damit ist klar, warum der folgende Code einen Fehler erzeugt:

float z6 = 30.5; //Nix gut!

Der Fehler kommt deshalb zustande, weil 30.5 ist eine Fließkommazahl und der Compiler diese, wie gesagt, stets als double-Wert interpretiert. Wir sehen auf die Wertetabelle und machen uns bewusst, dass double (64Bit) niemals in float (32Bit) passt. Die Lösung ist auch hier, der Fließkommazahl ein kleines float-f zu spendieren:

float z6 = 30.5f; //läuft!

Die Kompatibilität der Datentypen ist ein zentrales Thema für die Beherrschung der Java-Programmierung. Aber unter uns: Den genauen Wertebereich der Datentypen muss man für >99% der Arbeit mit Java nicht genau wissen. Es genügt völlig, sich die jeweilige Kompatibilität zu merken. Folgende Grafik hilft dabei:

Java primitive Datentypen Schaubild erklärt

Datentypen casten

Nehmen wir einmal folgenden Fall an: Wir haben zwei verschiedene Variablen. Die erste Variable – wir nennen sie einmal var1 - ist vom Typ double und hält den Fließkommawert 30.95. Die zweite Variable namens var2 ist vom Typ int. Nun soll der Wert von var1 der Variable var2 zugewiesen werden. Wir versuchen es:

double var1 = 30.95;
int var2 = var1;     //Typ inkompatibel!

Nein, so funktioniert das nicht! Eine Fließkomma-Zahl wie 30.95 passt (wir wiederholen es gerne noch einmal) nicht ohne Weiteres in eine Variable eines Ganzzahlen-Typs (byte, short, int, long). Der Code kompiliert nicht. Wenn wir aber bereit für Kompromisse sind, können wir die Zuweisung an var1 dennoch vollziehen. Hierzu setzen wir die Typumwandlung, das sogenannte casting, ein: Aus einem double-Wert machen wir einen int-Wert und dieser passt - oh Wunder! - perfekt in die int-Variable var2. Auf die Nachkommastellen von 30.95 müssen wir dann zwar verzichten, aber hey: Man kann nicht alles haben. Das Casting läuft dann folgendermaßen ab:

double var1 = 30.95;
int var2 = (int) var1;     // hier wird gecasted!
System.out.println(var2);  // 30

Casten primitiver Typen ist simpel: Wir verwenden hierzu den Cast-Operator, indem wir einfach vor den zu castenden Wert (bzw. der Variable, die ihn trägt) den gewünschten Datentyp in Klammern schreiben.

Aber seien Sie vorsichtig, dass Sie sich beim Casten nicht die Finger verbrennen: Wenn Sie nämlich von einem größeren Typ in einen kleineren casten und der initialisierte Wert des größeren Typs passt nicht in den neuen kleineren Typ, kommt es zu einem Ergebnis, das Sie in die Tonne kloppen können:

long gross = 50000;
short klein = (short) gross;
System.out.println(klein);   //Neuer Wert: -15536 *wtf?

50000 passt nicht in short, daher der für uns unsinnige negative Wert von -15536 (dieser kommt zustande, weil Java Zahlen intern auf bestimmte Art und Weise ablegt). In eine int-Variable dagegen würde 50000 locker passen, weshalb ein casting von long mit dem Wert 50000 in den niedrigen Datentyp int problemlos funktioniert:

long gross = 50000;
int mittel = (int) gross;
System.out.println(mittel);   //Neuer Wert mittel: 50000 *schön!

implizites Casten

In den bisher besprochenen Casting-Beispielen handelt es sich um explizites Casting, denn wir müssen(!) ausdrücklich (explizit) den Cast-Operator (neuer Datentyp in Klammern) verwenden; dies immer dann, wenn wir von einem größeren Datentyp in einen kleineren Umwandeln wollen. Im umgekehrten Fall (kleiner Datentyp in größeren umwandeln) wird der Casting-Operator nicht benötigt, das Casting läuft automatisch ab. Man spricht von implizitem Casting. Dies ist etwa im folgenden Beispiel der Fall:

int zahlMittel = 1000;
long zahlLang = zahlMittel;

Numerische Promotion

Welchen Datentyp hat das Ergebnis von 7.38 (float) geteilt durch 2 (int)? Was ist der Datentyp des Ergebnisses aus 4 (byte) multipliziert mit 10 (byte)?

Da wir in Java immer wieder Zahlen unterschiedlicher Datentypen miteinander verrechnen, ist es wichtig, die Prinzipien zu kennen, die uns bei der Beantwortung solcher Fragen helfen. Hier sollte man sich die vier Gesetze einprägen, die als numerische Promotion bezeichnet werden. Für arithmetische Operation in Java lauten diese wie folgt:

  • Wenn zwei zu verrechnende Werte (Operanden) unterschiedliche Datentypen haben, befördert Java automatisch den kleineren Wert zum Datentyp des größeren.
  • Wenn einer der beiden Operanden eine Ganzzahl und der andere eine Fließkommazahl ist, wird Java den ganzzahligen Wert automatisch zum passenden Fließkommawert befördern.
  • Die kleinen Datentypen byte, short und char werden bei arithmetischen Operationen grundsätzlich zuerst immer zum Datentyp int befördert.
  • Nachdem alle Anpassungen erfolgten und die Operanden denselben Datentyp haben, hat der Ergebniswert denselben Datentyp wie die beförderten Operanden.

Nun können wir die anfangs gestellten Fragen auch beantworten:

Der Ergebnisdatentyp von 7.38(float) / 2 (int) ist float (3.69): Es greift zuerst Regel Nr. 2, danach Regel 4.

Der Ergebnisdatentyp von 4 (byte) * 10 (byte) ist int (40): Es greift zunächst Regel Nr. 3, dann Regel 4.

Lassen Sie uns zur Übung noch einige weitere Beispiele anschauen. Denken Sie mit: Welcher Datentyp ist das Ergebnis folgender arithemtischer Operationen?

Beispiel 1

int x = 1;
long y = 33;

Was ist der Ergebnisdatentyp folgender arithemtischer Operation?

x * y;

Lösung: long!
Erklärung: int wird zu long (Regel 1), anschließend folgt Regel 4.

Beispiel 2

short x = 14;
short y = 3;

Was ist der Ergebnisdatentyp folgender arithemtischer Operation?

x / y;

Lösung: int!
Erklärung: beide shorts werden zu int (Regel 3), dann folgt Regel 4.

Beispiel 3

short x = 14;
float y = 13;
double z = 30;

Was ist der Ergebnisdatentyp folgender arithemtischer Operation?

x * y / z;

Lösung: double!
Erklärung: Short wird bei arithemtischen Operationen grundsätzlich zu int, was bei x * y sogleich auch geschieht (Regel 3); anschließend wird x, das nun einen int-Wert hat, durch die Multiplikation mit dem float-Wert von y selbst zu einem float befördert (Regel 2). Da das Teilergebnis der Multiplikation x * y (float!) schließlich durch z (double!) geteilt wird, wird der float-Wert nun schließlich zum höheren double angehoben (Regel 1). Der Ergebnisdatentyp am Ende ist dann automatisch ebenfalls vom Typ double (Regel 4).

Kommentar schreiben

Nur angemeldete Benutzer können kommentieren.

Alle Kommentare

Es gibt bislang noch keine Kommentare zu diesem Thema.

Primitive Datentypen

Am Ende des Kapitels werden Sie wissen, wie Primitive Datentypen korrekt eingesetzt werden.

Scope von Variablen

Wie lange lebt eine Variable eigentlich?

Pass-by-value in Java

wie genau der Vorgang der Parameterübergabe bei Methoden abläuft, klären wir hier.

FALCONBYTE.NET

Handmade with 🖤️

© 2018, 2019 Stefan E. Heller

Impressum | Datenschutz

facebook programmieren lernen twitter programmieren lernen youtube programmieren lernen