2.2 Datentypen und Wertebereiche

2.2 Datentypen und Wertebereiche

Die Typen die ein Entwickler in Java vewendet sind entweder primitive Datentypen oder komplexe Datentypen. Die beiden Kategorien unterscheiden sich in den folgenden Eigenschaften:

  Primitiver Datentyp Komplexer Datentyp (Javaklassen)
Operatoren viele hochoptimierte Operationen Nur Operatoren für Objekte (Vergleich, Referenzierung). Ansonsten Methoden der Klasse
Lebensdauer hängt vom umgebenden Block, bzw. Objekt ab liegt im Freispeicher. Lebensdauer hängt von der letzten Referenz auf Objekt ab
Speicherverbrauch konstant variabel
Speicherort im Prozedurblock oder im Objekt einer Klasse immer im Freispeicher (Heap)
Syntax immer klein geschrieben Systemklassen beginnen immer mit Großbuchstaben (Es ist guter Stil auch Benutzerklassen mit einem Großbuchstaben beginnen zu lassen).

Im nachfolgenden Diagramm wird die Klassifikation der wichtigsten Typen gezeigt:

Klassifikation der Javatypen

Die nächsten beiden Abschnitte behandeln die hier gezeigten Typen. Der dritte Abschnitt beschäftigt sich mit den Risiken von Zuweisungen zwischen Datentypen mit unterschiedlichen Wertebereichen.

Stefan Schneider Sun, 06/20/2010 - 16:43

2.2.1 Primitive Javatypen

2.2.1 Primitive Javatypen

Ganzzahlige Datentypen

Ganzzahlige Datentypen
Datentyp Bits Wertebereich Wertebereiche Konstante min. Konstante max.
byte 8=1 byte -27 bis 27-1 -128 bis +127

Byte.MIN_VALUE

Byte.MAX_VALUE

short 16=2 byte -215 bis 215-1 -32768 bis +32767

Short.MIN_VALUE

Short.MAX_VALUE

char 16=2 byte 0 bis 216-1 0 bis +65535 (Sonderfall!)

Character.MIN_VALUE

Character.MAX_VALUE

int 32=4 byte -231bis 231-1 -2147483648 bis +2147483647

Integer.MIN_VALUE

Integer.MAX_VALUE

long 64=8 byte -263 bis 263-1 −9,223,372,036,854,775,808 to +9,223,372,036,854,775,807

Long.MIN_VALUE

Long.MAX_VALUE

Die maximalen und minimalen Werte der Ganzzahltypen ergeben sich aus der Anzahl der Bits und der internen binären Präsentation. Dies ist am Beispiel des 8-Bit Typen Byte aufgeführt:

Java verwendet für die Darstellung negativer Ganzzahlen das Zweierkomplement. Der Vorteil des Zweierkomplement besteht in der einfacheren Implementierung von Arithmetikeinheiten in Prozessoren. Durch das Zweierkomplement kann das Vorzeichenbit bei Additionen und Subtraktion wie ein regulärers Bit des Wertebereichs behandelt werden.

Dies kann man gut am Beispiel der Zahl -1 erkennen. Addiert man zur Zahl -1 (Binärdarstellung 11111111) eine 1 (Binärdarstellung 00000001) so ergibt durch den Übertrag eine 0 (Binärdarstellung 00000000).

Die Anwendung zur Rechten ist in der Lage alle Ganzzahltypen in die Binärdarstellung umzuwandeln.

Das Vorzeichenbit wird in Rot dargestellt. Bei allen Typen die mehr als 16 Bit erfordern wird die Ausgabe nach 16 Bit umgebrochen. Das Bit mit der höchsten Ordnung wird zuerst ausgegeben. Das Bit mit der niedrigsten Ordnung wird am Ende ausgegeben.

Hinweis

Der Typ char ist ein Zahlentyp. Man muss jedoch genau ein beliebiges Zeichen (Buchstabe) im Eingabefenster eingeben. Der Typ char unterscheidet sich vom Typ short in der Benutzereingabe und im Wertebereich.

 Zum Herunterladen: BinaerIntApplet.jar

Starten Sie die Anwendung mit einem Doppelclick im Download Ordner oder öffen ein Terminal und führen den folgenden Befehl im Download Ordner aus:

java -jar BinaerIntApplet.jar

Es sollte ein Fenster erscheinen, dass ähnlich dem folgenden Fenster aussieht:

Screen shot BinaerIntApplet

Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole.

Fließkomma-Standarddatentypen

Die Zahlendarstellung der Fließkommazahlen geschieht nach der Norm IEEE 754 getrennt nach Bits für Vorzeichen (V), Mantisse und Exponent mit unterschiedlicherAnzahl von Bits abhängig vom Typ nach der Regel:

z= (-1)V*Mantisse*2Exponent

Fließkomma-Standarddatentypen
Datentyp Bits V
(bits)
Mantisse
(bits)
Exponent
(bits)
Zahlenbereich Dezimalstellen in Mantisse
float 32=4 Byte 1 23 8 ≈-3.4*1038 bis +3,4*1038 7
double 64=8 Byte 1 52 11 ≈-1.7*10308 bis +1.7*10308 15

Die Minimal- und Maximalwerte als Konstanten können über die Attribute MIN_VALUE und MAX_VALUE der Klassen Float und Double abgerufen werden.

Die Berechnung der dezimalen Werte ist für den menschlichen Betrachter nicht so einfach wie die Umwandlung von Ganzzahlen.

  • Der Exponent ist um 127 verschoben um auch negative Zahlen darstellen zu können.
  • Bei der Mantisse wurde im Diagramm eine symbolische (rote) Eins eingefügt, die nicht Teil der 32 Bit Muster ist. Jede Mantisse muss mit einer führenden 1 beginnen damit sie normalisiert ist. Da dieser Wert immer konstant ist muss man ihn nicht extra im 32 bit Wort speichern.

Der IEEE 754 Standard ist recht anschaulich in Wikipedia beschrieben.

Eine 32 Bit Fließkommazahl berechnet sich nach IEEE 754 wie folgt:

= (-1)Vorzeichen*2(Exponent-127)*Mantisse

Für den 32 Bit Typ float ergibt sich so nach dem Standard IEEE 754 das folgende Bitmuster für verschiedene Werte:

 

Das Applet zur Rechten ist in der Lage 32 Bit Fließkommazahlen in die Binärdarstellung umzuwandeln.

Die Knöpfe auf der rechten Seite erlauben die Eingabe von Extremwerten wie

  • NaN: Not a Number (z,Bsp das Ergebnis der Wurzel von -1
  • positiv, negativ unendlich
  • Größter Wert
  • Kleinster Wert der noch größer als Null ist

 

 

 Zum Herunterladen: BinaerFloatApplet.jar

Starten Sie die Anwendung mit einem Doppelclick im Download Ordner oder öffen ein Terminal und führen den folgenden Befehl im Download Ordner aus:

java -jar BinaerFloatApplet.jar

 Es sollte ein Fenster erscheinen, dass ähnlich dem folgenden Fenster aussieht:

Screenshot BinaerFloatApplet

Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole

Wahrheitswerte

Wahrheitswerte
Datentyp Bits Wertebereich Werte Konstante
boolean 8 (in der Oracle VM) wahr oder falsch true,false Boolean.FALSE, Boolean.TRUE

Die Anzahl der allokierten Bits hängt von der Implementierung ab (Siehe Spezifikation).

Wichtig: Alle Vergleiche (z.Bsp. (a<b) ) haben als Ergebnis einen boolschen Wert!

Zeichen

Java behandelt einzelne Zeichen intern als ganze Zahlen. Man kann auf den Typ char alle Operationen anwenden die auch für Zahlen erlaubt sind. Der wesentliche Unterschied zum Typ short besteht in der Eingabe und Ausgabe, sowie im Wertebereich. Hier werden lexikalische Zeichen ein- oder ausgegeben. 

Wichtiger Sonderfall: Der Typ char benutzt 16 Bit zum kodieren wie auch der Typ short. Die Wertebereiche unterscheiden sich jedoch. Der Typ char kodiert nur positive Werte. Im Englischen wird ein solcher Typ "unsigned" gennannt. Es ist ein Typ ohne Vorzeichenbit.

Zeichen
Datentyp Bits Wertebereich Werte  Kommentar 
char 16 UC='\u0000' bis '\uffff' 16 Bit Unicode 4.0 Basic Multilingual Plane (BMP)   "supplementary character" Unterstützung seit JDK 1.5

 

Stefan Schneider Sat, 10/29/2011 - 14:46

Anonymous (not verified)

Sun, 12/16/2012 - 13:21

Wieso werden für den Datentyp boolean 8 Bits benötigt? Eigentlich würde doch 1 Bit ausreichen.

Sie haben recht.
Alle Prozessoren auf denen man Java ausführen kann haben Registergrößen zum Rechnen von 32 oder 64 Bit.
Selbst alte oder einfache Prozessoren haben Registergrößen von 8 oder 16 Bit.
Das Laden und Speichern eines beliebigen Datums aus dem Speicher erfolgt auch immer in Blöcken die ein Vielfaches von 8 Bit (1 Byte) sind.
Die Prozessoren werden nicht effektiver wenn Sie auf kleineren Datenstrukturen arbeiten müssen.
Noch schlimmer:
Wenn man 8 Boolean in ein einzelnes Byte stecken würde, könnte es sein, dass an acht Stellen mit den acht Variablen gleichzeitig gearbeitet werden müsste. 7 Ausführungseinheiten müssten dann immer auf die achte warten die gerade das entsprechende Byte in ihrem Register bearbeitet.
Platzverschwendung: Boolean werden nicht so häufig wie andere Typen verwendet. Es macht bei den heutigen Hauptspeichergrößen für die meissten Anwendungen keinen Unterschied beim Hauptspeicherverbrauch.

2.2.2 Komplexe Javatypen

2.2.2 Komplexe Javatypen

Diese Datentypen werden mit Hilfe von Javaklassen implementiert. Die so erzeugten Datenstrukturen sind Javaobjekte und haben einen anderen Lebenszyklus als primitive Typen die in einer Javaklasse oder einem Programmierblock benutzt werden.

Komplexe Datentypen (Javaklassen) haben einen variablegroßen Speicherplatzbedarf. Sie werden auf dem Javafreispeicher (Heap) angelegt.

Zeichenketten

Zeichenketten sind in Java kein vordefinierter primitiver Typ. Zeichenketten werden im Paket java.lang mit Hilfe der Klasse String implementiert. Die Klasse String kann jedoch ohne eine spezielle Deklaration wie ein primitiver Typ verwendet werden.

Zeichenketten (Strings) sind in Java nicht modifizierbar. Bei jeder Zuweisung wird eine neue Datenstruktur angelegt. Bei primitiven Typen werden die Werte an der gleichen Stelle überschrieben.

Aufzählungstypen

Aufzählungstypen sind seit JDK 5.0 Bestandteil der Sprache.

Definition
Aufzählungstypen
Aufzählungstypen in Java haben einen Wertebereich der aus einer geordneten Menge von Konstanten besteht. Der aktuelle Wert einer Variablen besteht aus einem der Aufzählungskonstanten

Aufzählungstypen können nur als Bestandteil einer Klasse deklariert werden. Sie können nicht wie die anderen Basistypen innerhalb eines Blocks deklariert werden. Die Syntax einer Deklaration in einer Klasse ist die Folgende:

Syntax eines Aufzählungstyps

Beispiel einer Deklaration in einer Klasse:

class  AufzaehlungsDemo {
   enum Kartenfarbe {KARO, HERZ, PIK, KREUZ}
   enum Wochentag {MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG}
}

Bei der Benutzung von Aufzählungstypen kann nur eine zuvor deklarierte Aufzählungskonstante benutzt werden.

Beispiel der Benutzung in einer Klassenmethode der zugehörigen Klasse:

Wochentag heute = Wochentag.MITTWOCH;
Kartenfarbe dame = Kartenfarbe.HERZ;

Aufzählungstypen wurden wie Referenzvariablen (die erst später vorgestellt werden) implementiert.

Referenzen

Referenztypen erlauben es auf andere Objekte zu referenzieren. Sie werden im Abschnitt 7 behandelt.

Übergroße Zahlen (Big Decimal)

Die Java BigDecimal Klassen erlauben das Rechnen mit einer beliebigen Präzision. BigDecimal Klassen werden im Rahmen dieser Einführung nicht behandelt.

Stefan Schneider Sat, 10/29/2011 - 14:48

2.2.3 Typkonversion bei Zuweisung verschiedener Typen

2.2.3 Typkonversion bei Zuweisung verschiedener Typen

Die Sprache Java und ihre (Lautzeitumgebung) sind typsicher. Typsicher bedeutet, dass bei Zuweisungen für Variablen der Typ der Variable und der zugewiesene Typ eines Wertes geprüft werden und nur sichere Zuweisungen erlaubt werden. Der Übersetzer (javac) erkennt dies schon beim Parsen (lesen) des Quellcodes und meldet dies als Fehler.

Beispiel: Der Wertebereich einer Variable vom Type short endet bei 65535. Der Typ int hat jedoch einen Wertebereich der bis 4294967295 reicht. Im folgenden Fall kann es also zu einem Überlauf kommen:

short a;
int b;
....
a = b;

Der Übersetzer wird einen Fehler melden, da es hier zu einem Überlauf/Unterlauf aufgrund der unterschiedlichen Wertebereiche kommen kann.

Die Zuweisungskompatibilität kann man als Teilmengen mit dem Symbol ⊃ darstellen. In Java gilt die folgenden Beziehung:

doublefloatlongintshort/char byte

  • Alle Zuweisungen von bytedouble sind erlaubt. Java führt hier eine implizite Konvertierung (impliziter Cast) durch, weil es eine sichere Operation ist.
  • Alle Zuweisung von double ➔ byte werden vom Übersetzer nicht akzeptiert.

Bei Berechnungen vor der Zuweisung kommen eventuell verschieden Typen in Operationen (z.Bsp.) Addition vor.

Hier verfährt Java nach der folgenden Regel:

  • Der jeweils kleinere Operandentyp wird vor der Operation in den größeren konvertiert.
  • Die Operanden werden jedoch zumindest in den Typ int konvertiert
  • Das Ergebnis hat den gleichen Typ wie die beiden Operanden (nach Konvertierung)

Explizite Typkonvertierungen(Casts)

Um Zuweisungen zwischen inkompatiblen Typen zu erzwingen kann man den Zieltyp in runden Klammern dem Ausdruck voran stellen. Diesen Operator nennt man Castoperator (cast im englischen: gießen, betonieren).

Beispiel

short a;
int b;
....
a = (short)b; 

Hiermit erzwingt man Operationen die unsicher sein können! Es kann zu Überläufen in Wertebereichen oder Präzisionsverlusten kommen kann. Der Entwickler übernimmt hier die Verantwortung und überstimmt den Übersetzer. Der Castoperator sollte daher nur wenn nötig eingesetzt werden.

Das Applet zur Rechten ist in der Lage einen beliebigen Ganzzahltypen auf einen beliebigen anderen Ganzzahltypen zu zuweisen.

Die Variable y enthält den Eingabewert. Den Typ von y kann man in der rechten Spalte wählen.

Der Wert von y wird mit Hilfe einer Typkonversion (Cast) auf x zugewiesen. Den Typ von x kann man links wählen.

Nach der Wahl der Typen und des Eingabewerts kann die Zuweisung mit dem "Enter" Button gestartet werden.

Fragen:

  • Was geschieht bei der Typkonversion mit negativen Werten (siehe rotes Vorzeichenbit)?
  • Was geschieht bei der Zuweisung von großen Typen auf kleine Typen? Was geschieht mit den Vorzeichen?
  • Was geschieht wenn Zeichen (Typ char) auf andere Typen zugewiesen werden?

Zum Testen bitte runterladen: BinaerCastApplet.jar

Starten Sie die Anwendung mit einem Doppelclick im Download Ordner oder öffen ein Terminal und führen den folgenden Befehl im Download Ordner aus:

java -jar BinaerCastApplet.jar

Es sollte ein Fenster erscheinen welches wie folgt aussieht: 

Screenshot BinearCastApplet

 

Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole

Stefan Schneider Sat, 10/29/2011 - 15:25

Anonymous (not verified)

Sun, 12/16/2012 - 13:33

Laut "double ⊃ float ⊃ long ⊃int ⊃ short/char ⊃ byte" ist die Zuweisungen von Long-Literalen auf eine Float-Variable vom Compiler akzeptiert. Aber kommt es hier nicht zu Genauigkeitsverlusten, da long 64 Bit Genauigkeit hat und float nur 32 Bit. Und wenn ja, wieso akzeptiert der Compiler das dann trotzdem?

Korrekte Beobachtung. Ein long Typ kann Werte noch ohne Rundungsfehler korrekt darstellen wo ein float Typ schon im Bereich >1 also 20, 21, 22 etc.

Das ganze ist (wahrscheinlich) erlaubt da

  1. das Zahlenintervall des Typs float größer als das des Typ long ist. Es gibt also keinen Überlauf an den Grenzen der Zahlenintervalle
  2. Wer mit dem Typ float arbeitet muss wissen, das hier immer gerundet werden kann.

Anonymous (not verified)

Sun, 12/16/2012 - 13:41

Wenn ich in dem Applett auf der linken Seite den Typ byte wähle und auf der rechten Seite den Typ int. Dann müsste doch oben eigentlich in blauer Schrift die Zuweisung "byte x=(byte) y" stehen, oder? Jedenfalls steht dort "byte x=(int) y"; das würde doch gar nicht funktionieren, denn dann würde ich ja immer noch ein Int-Literal einer Byte-Variablen zuweisen.