Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 
Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Objektorientierte Beziehungsfragen
7 Ausnahmen müssen sein
8 Äußere.innere Klassen
9 Besondere Typen der Java SE
10 Generics<T>
11 Lambda-Ausdrücke und funktionale Programmierung
12 Architektur, Design und angewandte Objektorientierung
13 Komponenten, JavaBeans und Module
14 Die Klassenbibliothek
15 Einführung in die nebenläufige Programmierung
16 Einführung in Datenstrukturen und Algorithmen
17 Einführung in grafische Oberflächen
18 Einführung in Dateien und Datenströme
19 Einführung ins Datenbankmanagement mit JDBC
20 Einführung in <XML>
21 Testen mit JUnit
22 Bits und Bytes und Mathematisches
23 Die Werkzeuge des JDK
A Java SE-Paketübersicht
Stichwortverzeichnis


Download:

- Beispielprogramme, ca. 35,4 MB


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 9 Besondere Typen der Java SE
Pfeil 9.1 Object ist die Mutter aller Klassen
Pfeil 9.1.1 Klassenobjekte
Pfeil 9.1.2 Objektidentifikation mit toString()
Pfeil 9.1.3 Objektgleichheit mit equals(…) und Identität
Pfeil 9.1.4 Klonen eines Objekts mit clone() *
Pfeil 9.1.5 Hashwerte über hashCode() liefern *
Pfeil 9.1.6 System.identityHashCode(…) und das Problem der nicht eindeutigen Objektverweise *
Pfeil 9.1.7 Aufräumen mit finalize() *
Pfeil 9.1.8 Synchronisation *
Pfeil 9.2 Die Utility-Klasse java.util.Objects
Pfeil 9.3 Vergleichen von Objekten
Pfeil 9.3.1 Natürlich geordnet oder nicht?
Pfeil 9.3.2 Die Schnittstelle Comparable
Pfeil 9.3.3 Die Schnittstelle Comparator
Pfeil 9.3.4 Rückgabewerte kodieren die Ordnung
Pfeil 9.3.5 Statische und Default-Methoden in Comparator
Pfeil 9.4 Wrapper-Klassen und Autoboxing
Pfeil 9.4.1 Wrapper-Objekte erzeugen
Pfeil 9.4.2 Konvertierungen in eine String-Repräsentation
Pfeil 9.4.3 Von einer String-Repräsentation parsen
Pfeil 9.4.4 Die Basisklasse Number für numerische Wrapper-Objekte
Pfeil 9.4.5 Vergleiche durchführen mit compare(…), compareTo(…), equals(…) und Hashwerten
Pfeil 9.4.6 Statische Reduzierungsmethoden in Wrapper-Klassen
Pfeil 9.4.7 Die Größe eines primitiven Typs in den Wrapper-Konstanten SIZE und BYTES
Pfeil 9.4.8 Behandeln von vorzeichenlosen Zahlen *
Pfeil 9.4.9 Die Klasse Integer
Pfeil 9.4.10 Die Klassen Double und Float für Fließkommazahlen
Pfeil 9.4.11 Die Long-Klasse
Pfeil 9.4.12 Die Boolean-Klasse
Pfeil 9.4.13 Autoboxing: Boxing und Unboxing
Pfeil 9.5 Iterator, Iterable *
Pfeil 9.5.1 Die Schnittstelle Iterator
Pfeil 9.5.2 Wer den Iterator liefert
Pfeil 9.5.3 Die Schnittstelle Iterable
Pfeil 9.5.4 Erweitertes for und Iterable
Pfeil 9.5.5 Interne Iteration
Pfeil 9.5.6 Einen eigenen Iterable implementieren *
Pfeil 9.6 Die Spezial-Oberklasse Enum
Pfeil 9.6.1 Methoden auf Enum-Objekten
Pfeil 9.6.2 Aufzählungen mit eigenen Methoden und Initialisierern *
Pfeil 9.6.3 enum mit eigenen Konstruktoren *
Pfeil 9.7 Zum Weiterlesen
 

Zum Seitenanfang

9.4Wrapper-Klassen und Autoboxing Zur vorigen ÜberschriftZur nächsten Überschrift

Die Klassenbibliothek bietet für jeden primitiven Datentyp wie int, double, char spezielle Klassen an. Diese so genannten Wrapper-Klassen (auch Ummantelungsklassen, Mantelklassen oder Envelope Classes genannt) erfüllen drei wichtige Aufgaben:

  • Wrapper-Klassen bieten statische Hilfsmethoden zur Konvertierung eines primitiven Datentyps in einen String (Formatierung) und vom String zurück in einen primitiven Datentyp (Parsen).

  • Die Datenstrukturen wie Listen und Mengen, die in Java Verwendung finden, können nur Referenzen aufnehmen. So stellt sich das Problem, wie primitive Datentypen diesen Containern hinzugefügt werden können. Wrapper-Objekte kapseln einen einfachen primitiven Wert in einem Objekt, sodass eine Referenz existiert, die etwa in einer vorgefertigten Datenstruktur gespeichert werden kann.

  • Der Wrapper-Typ ist wichtig bei Generics. Wenn etwa eine spezielle Function eine Ganzzahl auf eine Fließkommazahl abbildet, ist eine Implementierung mit Function<int,double> nicht korrekt – es muss Function<Integer,Double> heißen. Primitive Datentypen gibt es auch bei Generics nicht, es kommen immer die Wrapper-Typen zum Einsatz.

Es existieren Wrapper-Klassen zu allen primitiven Datentypen.

Wrapper-Klasse

Primitiver Typ

Byte

byte

Short

short

Integer

int

Long

long

Double

double

Float

float

Boolean

boolean

Character

char

Tabelle 9.2Wrapper-Klassen und primitive Datentypen

[»]Hinweis

Für void, das kein Datentyp ist, existiert die Klasse Void. Sie deklariert nur die Konstante TYPE vom Typ Class<Void> und ist für Reflection (das Auslesen von Eigenschaften einer Klasse) interessanter.

In diesem Abschnitt wollen wir uns zunächst um das Erzeugen von Wrapper-Objekten kümmern, dann um Methoden, die in allen Wrapper-Klassen vorkommen, und schließlich die individuellen Methoden der einzelnen Wrapper-Klassen vorstellen. Der Klasse Character haben wir uns schon zu Beginn von Kapitel 4, »Der Umgang mit Zeichenketten«, gewidmet, als es um Zeichen und Zeichenketten ging.

Essenzielle Typbeziehungen der Wrapper-Klassen

Abbildung 9.5Essenzielle Typbeziehungen der Wrapper-Klassen

 

Zum Seitenanfang

9.4.1Wrapper-Objekte erzeugen Zur vorigen ÜberschriftZur nächsten Überschrift

Wrapper-Objekte lassen sich auf drei Arten aufbauen:

  • über statische valueOf(…)-Methoden, denen ein primitiver Ausdruck oder ein String übergeben wird

  • über Boxing: Aus einem primitiven Wert erzeugt der Compiler automatisch valueOf(…)-Methodenaufrufe, die das Wrapper-Objekt liefern.

  • über Konstruktoren der Wrapper-Klassen

[zB]Beispiel

Erzeuge einige Wrapper-Objekte:

Integer int1 = Integer.valueOf( "30" ); // valueOf()

Long lng1 = Long.valueOf( 0xC0B0L ); // valueOf()

Integer int2 = new Integer( 29 ); // Konstruktor

Long lng2 = new Long( 0xC0B0L ); // Konstruktor

Double dobl = new Double( 12.3 ); // Konstruktor

Boolean bool = true; // Boxing

Integer int3 = 42; // Boxing

Und die beste Möglichkeit zum Aufbauen von Wrapper-Objekten ist welche?

Nun gibt es also drei Möglichkeiten, an Wrapper-Objekte zu kommen. Boxing ist vom Schreibaufwand her gesehen die kürzeste und im Allgemeinen die beste, weil kompakteste Variante (Boxing ist allerdings nicht ganz unproblematisch, wie Abschnitt 9.4.13 zeigt). Da Boxing auf die valueOf(…)-Methoden zugreift, sind die beiden Varianten semantisch identisch und unterscheiden sich nur im Programmcode, aber nicht im Bytecode. Uns bleibt daher die Lösung »Konstruktor versus valueOf(…)«. Eine statische Methode zum Erzeugen von Objekten einzusetzen ist clever, da anders als ein Konstruktor eine statische Methode Objekte nicht immer neu erzeugen muss, sondern auch auf vorkonstruierte Objekte zurückgreifen kann. Und das ist genau das, was valueOf(…) bei den drei Klassen Byte, Short, Integer und Long macht: Stammen die Ganzzahlen aus dem Wertebereich –128 bis +127, so greift valueOf(…) auf vorbereitete Objekte aus einem Cache zurück. Das Ganze klappt natürlich nur, weil Aufrufer von valueOf(…) ein unveränderliches (engl. immutable) Objekt bekommen – ein Wrapper-Objekt kann nach dem Aufbau nicht verändert werden.

[»]Hinweis

In der Wrapper-Klasse Integer gibt es drei statische überladene Methoden, getInteger (String), getInteger(String, int), getInteger(String, Integer), die von Spracheinsteigern wegen der gleichen Rückgabe und Parameter schnell mit der valueOf(String)-Methode verwechselt werden können. Allerdings lesen die getInteger(String)-Methoden eine Umgebungsvariable aus und haben somit eine völlig andere Aufgabe als valueOf(String). In der Wrapper-Klasse Boolean gibt es mit getBoolean(String) Vergleichbares. Die anderen Wrapper-Klassen haben keine Methoden zum Auslesen einer Umgebungsvariablen.

Wrapper-Objekte sind immutable

Ist ein Wrapper-Objekt erst einmal erzeugt, lässt sich der im Wrapper-Objekt gespeicherte Wert nachträglich nicht mehr verändern. Um dies auch wirklich sicherzustellen, sind die konkreten Wrapper-Klassen allesamt final. Die Wrapper-Klassen sind nur als Ummantelung und nicht als vollständiger Datentyp gedacht. Da sich der Wert nicht mehr ändern lässt (er ist ja immutable), heißen Objekte mit dieser Eigenschaft auch Werte-Objekte. Wollen wir den Inhalt eines Integer-Objekts io zum Beispiel um eins erhöhen, so müssen wir ein neues Objekt referenzieren:

int i = 12;

Integer io = Integer.valueOf( i );

io = Integer.valueOf( io.intValue() + 1 );

i = io.intValue();

Die Variable io referenziert nun ein zweites Integer-Objekt, und der Wert des ersten io-Objekts mit 12 bleibt unangetastet. Das entspricht im Wesentlichen auch der Nutzung von String. Heißt es String s = "Co"; s = s + "ra";, wird der Referenzvariablen s auch nur ein neues Objekt zugewiesen, aber der String an sich ändert sich nicht.

 

Zum Seitenanfang

9.4.2Konvertierungen in eine String-Repräsentation Zur vorigen ÜberschriftZur nächsten Überschrift

Alle Wrapper-Klassen bieten statische toString(value)-Methoden zur Konvertierung des primitiven Elements in einen String an:

Listing 9.25com/tutego/insel/wrapper/WrapperToString.java, main()

String s1 = Integer.toString( 1234567891 ),

s2 = Long.toString( 123456789123L ),

s3 = Float.toString( 12.345678912f ),

s4 = Double.toString( 12.345678912 ),

s5 = Boolean.toString( true );

System.out.println( s1 ); // 1234567891

System.out.println( s2 ); // 123456789123

System.out.println( s3 ); // 12.345679

System.out.println( s4 ); // 12.345678912

System.out.println( s5 ); // true

Der String erscheint immer in der englisch geschriebenen Variante. So steht bei den Dezimalzahlen ein Punkt statt des uns vertrauten Kommas.

[»]Hinweis

Bei der Darstellung von Zahlen ist eine landestypische (länderspezifische) Formatierung sinnvoll. Das kann printf(…) leisten:

System.out.printf( "%f", 1000000. ); // 1000000,000000

System.out.printf( "%f", 1234.567 ); // 1234,567000

System.out.printf( "%,.3f", 1234.567 ); // 1.234,567

Der Formatspezifizierer für Fließkommazahlen ist %f. Die zusätzliche Angabe mit ,.3f im letzten Fall führt zum Tausenderpunkt und zu drei Nachkommastellen.

toString(…) als Objekt- und Klassenmethode

Liegt ein Wrapper-Objekt vor, so liefert die Objektmethode toString() die String-Repräsentation des Werts, den das Wrapper-Objekt speichert. Dass es gleichlautende statische Methoden toString(…) und eine Objektmethode toString() gibt, sollte uns nicht verwirren; während die Klassenmethode den Arbeitswert zur Konvertierung aus dem Argument zieht, nutzt die Objektmethode den gespeicherten Wert im Wrapper-Objekt.

Anweisungen, die ausschließlich zum Konvertieren über das Wrapper-Objekt gehen, wie new Integer(v).toString(), lassen sich problemlos umschreiben in Integer.toString(v). Zudem bietet sich auch die überladene statische Methode String.valueOf(v) an, die – eben weil sie überladen ist – für alle möglichen Datentypen deklariert ist (doch nutzt valueOf(v) intern auch nur WrapperKlasse.toString(v)).

 

Zum Seitenanfang

9.4.3Von einer String-Repräsentation parsen Zur vorigen ÜberschriftZur nächsten Überschrift

Die Wrapper-Klassen bieten statische parseXXX(…)-Methoden, die einen String in einen primitiven Datentyp konvertieren. Dem String kann ein Minus für negative Zahlen vorangestellt sein, auch ein Plus für positive Zahlen ist erlaubt. Die Methoden wurden in Abschnitt 4.7.2, »String-Inhalt in einen primitiven Wert konvertieren«, vorgestellt.

 

Zum Seitenanfang

9.4.4Die Basisklasse Number für numerische Wrapper-Objekte Zur vorigen ÜberschriftZur nächsten Überschrift

Alle numerischen Wrapper-Klassen können den gespeicherten Wert in einem beliebigen anderen numerischen Typ liefern. Die Methodennamen setzen sich – wie zum Beispiel doubleValue() und intValue() – aus dem Namen des gewünschten Typs und Value zusammen. Technisch gesehen überschreiben die Wrapper-Klassen Byte, Short, Integer, Long, Float und Double aus einer Klasse Number[ 190 ](Zusätzlich erweitern BigDecimal und BigInteger die Klasse Number und haben damit ebenfalls die xxxValue()-Methoden. AtomicInteger und AtomicLong erweitern ebenfalls Number, sind aber nicht immutable wie die restlichen Klassen. ) die xxxValue()-Methoden[ 191 ](Nur die Methoden byteValue() und shortValue() sind nicht abstrakt und müssen nicht überschrieben werden. Diese Methoden rufen intValue() auf und konvertieren den Wert über eine Typumwandlung auf byte und short. ).

abstract class java.lang.Number

implements Serializable
  • byte byteValue()

    Liefert den Wert der Zahl als byte.

  • abstract double doubleValue()

    Liefert den Wert der Zahl als double.

  • abstract float floatValue()

    Liefert den Wert der Zahl als float.

  • abstract int intValue()

    Liefert den Wert der Zahl als int.

  • abstract long longValue()

    Liefert den Wert der Zahl als long.

  • short shortValue()

    Liefert den Wert der Zahl als short.

[»]Hinweis

Wenn die Operandentypen beim Bedingungsoperator unterschiedlich sind, gibt es ganz automatisch eine Anpassung:

boolean b = true;

System.out.println( b ? 1 : 0.1 ); // 1.0

System.out.println( !b ? 1 : 0.1 ); // 0.1

Der Ergebnistyp ist double, sodass die Ganzzahl 1 als »1.0«, also als Fließkommazahl, ausgegeben wird. Die gleiche Anpassung nimmt der Compiler bei Wrapper-Typen vor, die er unboxt und konvertiert:

Integer i = 1;

Double d = 0.1;

System.out.println( b ? i : d ); // 1.0

System.out.println( !b ? i : d ); // 0.1

Während diese Ausgabe eigentlich klar ist, kann es zu einem Missverständnis kommen, wenn das Ergebnis nicht einfach ausgegeben, sondern als Verweis auf das resultierende Wrapper-Objekt zwischengespeichert wird. Da der Typ im Beispiel entweder Integer oder Double ist, kann der Ergebnistyp nur der Obertyp Number sein:

Number n1 = b ? i : d;

System.out.println( n1 ); // 1.0

System.out.println( n1 == i ); // false

Die Programmlogik und Ausgabe sind natürlich genauso wie vorher, doch Entwickler könnten annehmen, dass der Compiler keine Konvertierung durchführt, sondern entweder das originale Integer- oder das Double-Objekt referenziert; das macht er aber nicht. Die Variable n1 referenziert hier ein Integer-ungeboxtes-double-konvertiertes-Double-geboxtes Objekt, und so sind die Referenzen von n1 und i überhaupt nicht identisch. Wenn der Compiler hier wirklich die Originalobjekte zurückliefern soll, muss entweder das Integer- oder das Double-Objekt explizit auf Number gebracht werden, sodass damit das Unboxing ausgeschaltet wird und der Bedingungsoperator nur noch von beliebigen nicht zu interpretierenden Referenzen ausgeht:

Number n2 = b ? (Number) i : d; // oder Number n2 = b ? i : (Number) d;

System.out.println( n2 ); // 1

System.out.println( n2 == i ); // true
 

Zum Seitenanfang

9.4.5Vergleiche durchführen mit compare(…), compareTo(…), equals(…) und Hashwerten Zur vorigen ÜberschriftZur nächsten Überschrift

Haben wir zwei Ganzzahlen 1 und 2 vor uns, so ist es trivial zu sagen, dass 1 kleiner als 2 ist. Bei Fließkommazahlen ist das ein wenig komplizierter, da es hier »Sonderzahlen« wie Unendlich oder eine negative und positive 0 gibt. Damit das Vergleichen von zwei Werten immer nach dem gleichen Stil durchgeführt werden kann, gibt es zwei Sorten von Methoden in den Wrapper-Klassen, die uns sagen, ob zwei Werte kleiner, größer oder gleich sind:

  • Auf der einen Seite findet sich die Objektmethode compareTo(…) in den Wrapper-Klassen. Die Methode ist nicht zufällig in der Klasse, denn Wrapper-Klassen implementieren die Schnittstelle Comparable (wir haben die Schnittstelle schon am Anfang des Kapitels kurz vorgestellt).

  • Wrapper-Klassen besitzen zudem statische compare(x, y)-Methoden.

Die Rückgabe der Methoden ist ein int, und es kodiert, ob ein Wert größer, kleiner oder gleich ist.

[zB]Beispiel

Teste verschiedene Werte:

Listing 9.26com/tutego/insel/wrapper/CompareToDemo.java, main()

System.out.println( Integer.compare(1, 2) ); // –1

System.out.println( Integer.compare(1, 1) ); // 0

System.out.println( Integer.compare(2, 1) ); // 1



System.out.println( Double.compare(2.0, 2.1) ); // –1

System.out.println( Double.compare(Double.NaN, 0) ); // 1



System.out.println( Boolean.compare(true, false) ); // 1

System.out.println( Boolean.compare(false, true) ); // –1

Ein true ist »größer« als ein false.

Tabelle 9.3 fasst die Methoden der Wrapper-Klassen zusammen:

Klasse

Methode aus Comparable

Statische Methode compare(…)

Byte

int compareTo(Byte aByte)

int compare(byte x, byte y)

Short

int compareTo(Short aShort)

int compare(short x, short y)

Float

int compareTo(Float aFloat)

int compare(float f1, float f2)

Double

int compareTo(Double aDouble)

int compare(double d1, double d2)

Integer

int compareTo(Integer aInteger)

int compare(int x, int y)

Long

int compareTo(Long aLong)

int compare(long x, long y)

Character

int compareTo(Character aChar)

int compare(char x, char y)

Boolean

int compareTo(Boolean aBoolean)

int compare(boolean x, boolean y)

Tabelle 9.3Methoden der Wrapper-Klassen

Die Implementierung einer statischen Methode WrapperKlasse.compare(…) ist äquivalent zu WrapperKlasse.valueOf(x).compareTo(WrapperKlasse.valueOf(y)), nur ist zweite Variante langsamer und auch länger in der Schreibweise.

[»]Hinweis

Nur die genannten Wrapper-Klassen besitzen eine statische compare(…)-Methode. Es ist kein allgemeingültiges Muster, dass eine Klasse, wenn sie Number erweitert und Comparable implementiert, dann auch eine statische compare(…)-Methode hat. So erweitern zum Beispiel die Klassen BigInteger und BigDecimal die Oberklasse Number und implementieren Comparable, aber eine statische compare(…)-Methode bieten sie trotzdem nicht.

Gleichheitstest über equals(…)

Alle Wrapper-Klassen überschreiben aus der Basisklasse Object die Methode equals(…). So lässt sich testen, ob zwei Wrapper-Objekte den gleichen Wert haben, auch wenn die Wrapper-Objekte nicht identisch sind.

[zB]Beispiel

Die Ergebnisse einiger Gleichheitstests:

Boolean.TRUE.equals( Boolean.TRUE ) true

Integer.valueOf( 1 ).equals( Integer.valueOf( 1 ) ) true

Integer.valueOf( 1 ).equals( Integer.valueOf( 2 ) ) false

Integer.valueOf( 1 ).equals( Long.valueOf( 1 ) ) false

Integer.valueOf( 1 ).equals( 1L ) false

Es ist wichtig zu wissen, dass der Parametertyp von equals(…) immer Object ist, aber die Typen identisch sein müssen, da andernfalls automatisch der Vergleich falsch ergibt. Das zeigen das vorletzte und das letzte Beispiel. Die equals(…)-Methode aus den Zeilen 4 und 5 lehnt jeden Vergleich mit einem Nicht-Integer ab, und ein Long ist eben kein Integer. In der letzten Zeile kommt Boxing zum Einsatz, daher sieht der Programmcode kürzer aus, aber entspricht dem aus der vorletzten Zeile.

Die Objektmethode equals(…) der Wrapper-Klassen ist auch eine kurze Alternative zu wrapperObject.compareTo(anotherWrapperObject) == 0.

Ausblick

Dass die Wrapper-Klassen equals(…) implementieren, ist gut, denn so können Wrapper-Objekte problemlos in Datenstrukturen wie einer ArrayList untergebracht und wieder gefunden werden. Und dass Wrapper-Objekte auch Comparable sind, ist ebenfalls prima für Datenstrukturen wie TreeSet, die – ohne extern gegebene Comparator-Klassen für Vergleiche – eine natürliche Ordnung der Elemente erwarten.

Hashwerte

Der Hashwert eines Objekts bildet den Zustand auf eine kompakte Ganzzahl ab. Haben zwei Objekte ungleiche Hashwerte, so müssen auch die Objekte ungleich sein (mindestens, wenn die Berechnung korrekt ist). Zur Bestimmung des Hashwerts deklariert jede Klasse über die Oberklasse java.lang.Object die Methode int hashCode(). Alle Wrapper-Klassen überschreiben diese Methode. Zudem gibt es statische Methoden, sodass sich leicht der Hashwert berechnen lässt, ohne extra ein Wrapper-Objekt zu bilden.

Klasse

Klassenmethode

Objektmethode

Boolean

static int hashCode(boolean value)

int hashCode()

Byte

static int hashCode(byte value)

int hashCode()

Short

static int hashCode(short value)

int hashCode()

Integer

static int hashCode(int value)

int hashCode()

Long

static int hashCode(long value)

int hashCode()

Float

static int hashCode(float value)

int hashCode()

Double

static int hashCode(double value)

int hashCode()

Character

static int hashCode(char value)

int hashCode()

Tabelle 9.4Statische Methode hashCode(…) und Objektmethoden im Vergleich

Zur Berechnung von Hashwerten und deren Bedeutung für Datenstrukturen gleich mehr in Abschnitt 9.1.5, »Hashwerte über hashCode() liefern *«.

 

Zum Seitenanfang

9.4.6Statische Reduzierungsmethoden in Wrapper-Klassen Zur vorigen ÜberschriftZur nächsten Überschrift

In den numerischen Wrapper-Klassen Integer, Long, Float und Double gibt es drei Methoden sum(), max() und min(), die genau das machen, was der Methodenname verspricht.

final class java.lang.Integer|Long|Float|Double

extends Number

implements Comparable<Integer>
  • static Typ sum(Typ a, Typ b)

    Bildet die Summe zweier Werte und liefert diese zurück. Entspricht einem einfachen a + b. Die Angabe Typ steht dabei für den entsprechenden primitiven Typ byte, short, int, long, float oder double, etwa in int sum(int a, int b).

  • static Typ min(Typ a, Typ b)

    Liefert das Minimum der zwei Zahlen.

  • static Typ max(Typ a, Typ b)

    Liefert das Maximum der zwei Zahlen.

Des Weiteren bieten die Boolean-Klassen drei Methoden für drei boolesche Operationen:

final class java.lang.Boolean

implements Comparable<Boolean>, Serializable
  • static boolean logicalAnd(boolean a, boolean b)

    Liefert a && b.

  • static boolean logicalOr(boolean a, boolean b)

    Liefert a || b.

  • static boolean logicalXor(boolean a, boolean b)

    Liefert a ^ b.

Die Methoden sind für sich genommen nicht spannend. Für die Summe (Addition) tut es genauso gut der +-Operator – er steckt sowieso hinter den sum(…)-Methoden –, und so wird keiner auf die Idee kommen, i = Integer.sum(i, 1) statt i++ zu schreiben. Für das Maximum/Minimum bietet die Math-Klasse auch schon entsprechende Methoden min(a, b)/max(a, b). Der Grund für alle genannten Methoden ist vielmehr, dass sie im Zusammenhang mit funktionaler Programmierung interessant sind und zwei Werte auf einen Wert reduzieren.

 

Zum Seitenanfang

9.4.7Die Größe eines primitiven Typs in den Wrapper-Konstanten SIZE und BYTES Zur vorigen ÜberschriftZur nächsten Überschrift

Alle Wrapper-Klassen besitzen eine int-Konstante SIZE für die Anzahl der Bits und eine int-Konstante BYTES, die mit der Größe des zugehörigen primitiven Datentyps belegt ist.

Wrapper-Klasse

Belegung SIZE

Belegung BYTES

Byte

8

1

Short

16

2

Integer

32

4

Long

64

8

Float

32

4

Double

64

8

Character

16

2

Tabelle 9.5BYTES- und SIZE-Konstanten in den Wrapper-Klassen

Jedem Entwickler sind die Belegungen im Grunde klar, vielmehr sind die Konstanten für Tools gedacht.

 

Zum Seitenanfang

9.4.8Behandeln von vorzeichenlosen Zahlen * Zur vorigen ÜberschriftZur nächsten Überschrift

Alle Ganzzahldatentypen (char ausgenommen) sind immer vorzeichenbehaftet, und Java wird wohl nie neue Datentypen für eine Unterscheidung einführen, wie es etwa C(++) bietet. Entwickler müssen damit leben, dass ein Byte immer zwischen –128 und +127 liegt und nicht zwischen 0 und 255. Doch wenn die Sprache keine neuen Typen einführt, kann hilft doch die Java-API mit Methoden in den Wrapper-Klassen.

toUnsignedXXX(…)-Methoden

Der erste Typ von Methoden interpretiert ein Bit-Muster als vorzeichenlose Zahl. Ist zum Beispiel ein byte mit dem Bit-Muster 11111111 belegt, so steht dies nicht für 255, sondern für –1. Die statischen Konvertierungsmethoden toUnsignedXXX(…) der Wrapper-Klassen helfen dabei, das Bit-Muster ohne Vorzeichenbit zu interpretieren, wobei ein Wechsel zu einem nächsthöheren Datentyp nötig ist – das ist einleuchtend, denn im Fall von 11111111 kann ein byte wegen der Beschränkung auf –127 bis 128 die Zahl 255 nicht aufnehmen, int aber schon.

Byte

static int toUnsignedInt(byte x)

static long toUnsignedLong(byte x)

Short

static int toUnsignedInt(short x)

static long toUnsignedLong(short x)

Integer

static long toUnsignedLong(int x)

Tabelle 9.6toUnsignedXXX(…)-Methoden in Byte, Short, Integer

Naheliegenderweise bietet Long so eine Methode nicht, denn es gibt keinen eingebauten Datentyp größer als long.

toUnsignedString(…) und parseUnsignedString(…)

Was Long jedoch bietet, und auch Integer, sind Umwandlungen in String-Repräsentationen und auch Parse-Methoden:

Integer

static String toUnsignedString(int i)

static String toUnsignedString(int i, int radix)

static int parseUnsignedInt(String s)

static int parseUnsignedInt(String s, int radix)

Long

static String toUnsignedString(long i)

static String toUnsignedString(long i, int radix)

static long parseUnsignedLong(String s)

static long parseUnsignedLong(String s, int radix)

Tabelle 9.7String-Repräsentationen von vorzeichenlosen Zahlen erstellen und parsen

Die parseXXX(…)-Methoden lösen bei falschem Format eine NumberFormatException aus.

Vergleich, Division und Rest

Es gibt eine dritte Gruppe von Methoden, die eher mathematisch sind, und zwar statische Methoden zum Vergleichen, Dividieren und zur Restwertbildung:

Integer

static int compareUnsigned(int x, int y)

static int divideUnsigned(int dividend, int divisor)

static int remainderUnsigned(int dividend, int divisor)

Long

static int compareUnsigned(long x, long y)

static long divideUnsigned(long dividend, long divisor)

static long remainderUnsigned(long dividend, long divisor)

Tabelle 9.8Mathematische Methoden

 

Zum Seitenanfang

9.4.9Die Klasse Integer Zur vorigen ÜberschriftZur nächsten Überschrift

Die Klasse Integer kapselt den Wert einer Ganzzahl vom Typ int in einem Objekt und bietet

  • Konstanten,

  • statische Methoden zur Konvertierung in einen String und zurück

  • sowie weitere Hilfsmethoden mathematischer Natur.

Um aus dem String eine Zahl zu machen, nutzen wir Integer.parseInt(String).

[zB]Beispiel

Konvertiere die Ganzzahl 38.317, die als String vorliegt, in eine Ganzzahl:

String number = "38317";

int integer = 0;

try {

integer = Integer.parseInt( number );

}

catch ( NumberFormatException e ) {

System.err.println( "Fehler beim Konvertieren von " + number );

}

System.out.println( integer );

Die NumberFormatException ist eine nicht geprüfte Exception – Genaueres dazu liefert Kapitel 7, »Ausnahmen müssen sein« –, muss also nicht zwingend in einem try-Block stehen.

Die statische Methode Integer.parseInt(String) konvertiert einen String in int, und die Umkehrmethode Integer.toString(int) liefert einen String. Weitere Varianten mit unterschiedlicher Basis wurden schon in Abschnitt 4.7.2, »String-Inhalt in einen primitiven Wert konvertieren«, vorgestellt.

final class java.lang.Integer

extends Number

implements Comparable<Integer>
  • static int parseInt(String s)

    Erzeugt aus der Zeichenkette die entsprechende Zahl. Die Basis ist 10.

  • static int parseInt(String s, int radix)

    Erzeugt die Zahl mit der gegebenen Basis.

  • static String toString(int i)

    Konvertiert die Ganzzahl in einen String und liefert sie zurück.

parseInt(…) erlaubt keine länderspezifischen Tausendertrennzeichen, etwa in Deutschland den Punkt oder im angelsächsischen Raum das Komma.

 

Zum Seitenanfang

9.4.10Die Klassen Double und Float für Fließkommazahlen Zur vorigen ÜberschriftZur nächsten Überschrift

Die Klassen Double und Float haben wie die anderen Wrapper-Klassen eine Doppelfunktionalität: Sie kapseln zum einen eine Fließkommazahl als Objekt und bieten zum anderen statische Utility-Methoden. Wir kommen in Kapitel 22, »Bits und Bytes und Mathematisches«, noch genauer auf die mathematischen Objekt- und Klassenmethoden zurück.

 

Zum Seitenanfang

9.4.11Die Long-Klasse Zur vorigen ÜberschriftZur nächsten Überschrift

Integer und Long sind im Prinzip API-gleich, nur ist der kleinere Datentyp int durch long ersetzt.

 

Zum Seitenanfang

9.4.12Die Boolean-Klasse Zur vorigen ÜberschriftZur nächsten Überschrift

Die Klasse Boolean kapselt den Datentyp boolean. Sie deklariert als Konstanten zwei Boolean-Objekte: TRUE und FALSE.

final class java.lang.Boolean

implements Serializable, Comparable<Boolean>
  • static final Boolean FALSE

  • static final Boolean TRUE

    Konstanten für Wahrheitswerte

  • Boolean(boolean value)

    Erzeugt ein neues Boolean-Objekt. Dieser Konstruktor sollte nicht verwendet werden, stattdessen sollten Boolean.TRUE oder Boolean.FALSE eingesetzt werden. Boolean-Objekte sind immutable, und ein new Boolean(value) ist unnötig.

  • Boolean(String s)

    Parst den String und liefert ein neues Boolean-Objekt zurück.

  • static Boolean valueOf(String s)

    Parst den String und gibt die Wrapper-Typen Boolean.TRUE oder Boolean.FALSE zurück. Die statische Methode hat gegenüber dem Konstruktor Boolean(boolean) den Vorteil, dass sie immer das gleiche immutable Wahr- oder Falsch-Objekt (Boolean.TRUE oder Boolean.FALSE) zurückgibt, anstatt neue Objekte zu erzeugen. Daher ist es selten nötig, den Konstruktor aufzurufen und immer neue Boolean-Objekte aufzubauen.

  • public static boolean parseBoolean(String s)

    Parst den String und liefert entweder true oder false.

Der Konstruktor Boolean(String name) bzw. die beiden statischen Methoden valueOf(String name) und parseBoolean(String name) nehmen Strings entgegen und führen im JDK den Test name != null && name.equalsIgnoreCase("true") durch. Das heißt zum einen, dass die Groß-/Kleinschreibung unwichtig ist, und zum anderen, dass Dinge wie » false « (mit Leerzeichen), »falsch« oder »Ostereier« automatisch false ergeben, wobei »TRUE« oder »True« dann true liefern.

[+]Tipp

Würde jeder Entwickler ausschließlich die Konstanten Boolean.TRUE und Boolean.FALSE nutzen, so wären bei lediglich zwei Objekten Vergleiche mit == bzw. != in Ordnung. Da es aber einen Konstruktor für Boolean-Objekte gibt – und es ist durchaus diskussionswürdig, warum es überhaupt Konstruktoren für Wrapper-Klassen gibt –, ist die sicherste Variante ein boolean1.equals(boolean2). Wir können eben nicht wissen, ob eine Bibliotheksmethode wie Boolean isNice() auf die zwei Konstanten zurückgreift oder immer wieder neue Boolean-Objekte aufbaut.

 

Zum Seitenanfang

9.4.13Autoboxing: Boxing und Unboxing Zur vorigen ÜberschriftZur nächsten Überschrift

Autoboxing ist eine Eigenschaft von Java, bei der primitive Datentypen und Wrapper-Objekte bei Bedarf ineinander umgewandelt werden. Ein Beispiel:

int primInt = 4711;

Integer wrapInt = primInt; // steht für wrapInt = Integer.valueOf(primInt) (1)

primInt = wrapInt; // steht für primInt = wrapInt.intValue() (2)

Die Anweisung in (1) nennt sich Boxing und erstellt automatisch ein Wrapper-Objekt, sofern erforderlich. Schreibweise (2) ist das Unboxing und steht für das Beziehen des Elements aus dem Wrapper-Objekt. Das bedeutet: Überall dort, wo der Compiler ein primitives Element erwartet, aber ein Wrapper-Objekt vorhanden ist, entnimmt er den Wert mit einer passenden xxxValue(…)-Methode dem Wrapper. Der Compiler wird den Code automatisch generieren, vom Autoboxing ist später im Bytecode nichts mehr zu sehen.

Autoboxing von int/Integer

Abbildung 9.6Autoboxing von int/Integer

Die Operatoren ++, -- *

Der Compiler konvertiert nach festen Regeln, und auch die Operatoren ++, -- sind erlaubt:

Integer i = 12;

i = i + 1; // (1)

i++; // (2)

System.out.println( i ); // 14

Wichtig ist, dass weder (1) noch (2) das Original-Integer-Objekt mit der 12 ändern (alle Wrapper-Objekte sind immutable), sondern i nur andere Integer-Objekte für 13 und 14 referenziert.

Boxing für dynamische Datenstrukturen (Ausblick)

Angenehm ist das Autoboxing, wenn in Datenstrukturen »primitive« Elemente abgelegt werden sollen:

ArrayList<Double> list = new ArrayList<Double>();

list.add( Math.sin(Math.PI / 4) ); // Boxing bei boolean add(Double)

double d = list.get( 0 ); // Unboxing bei Double get(int)

Leider ist es so, dass der Typ der Liste tatsächlich mit dem Wrapper-Typ Double festgelegt werden muss und nicht mit dem Primitivtyp double. Aber vielleicht ändert sich das ja noch irgendwann. Das Autoboxing macht es jedenfalls etwas erträglicher.

Keine Konvertierung der null-Referenz zu 0

Beim Unboxing führt der Compiler bzw. die Laufzeitumgebung keine Konvertierung von null in 0 durch. Mit anderen Worten: Bei der folgenden versuchten Zuweisung gibt es keinen Compilerfehler, aber zur Laufzeit eine NullPointerException:

int n = (Integer) null; // inline java.lang.NullPointerException zur Laufzeit

[»]Hinweis

In switch-Blöcken sind int, Aufzählungen und Strings als Typen erlaubt. Bei Ganzzahlen führt der Compiler automatisch Konvertierungen und Unboxing in int durch. Beim Unboxing gibt es aber die Gefahr einer NullPointerException:

Integer integer = null;

switch ( integer ) { } // inline NullPointerException zur Laufzeit

Autoboxing bei Arrays?

Da primitive Datentypen und Wrapper-Objekte durch Autoboxing automatisch konvertiert werden, fällt im Alltag der Unterschied nicht so auf. Bei Arrays ist der Unterschied jedoch augenfällig, und hier kann Java keine automatische Konvertierung durchführen. Denn auch wenn zum Beispiel char und Character automatisch ineinander umgewandelt werden, so sind Arrays nicht konvertierbar. Eine Array-Initialisierung der Art

Character[] chars = { 'S', 'h', 'a' };

enthält zwar rechts dreimal Boxing von char in Character, und eine automatische Umwandlung auf der Ebene der Elemente ist gültig, sodass

char first = chars[ 0 ];

natürlich gilt, aber die Array-Objekte lassen sich nicht ineinander überführen. Folgendes ist nicht korrekt:

char[] sha = chars; // inline Compilerfehler!

Die Typen char[] und Character[] sind also zwei völlig unterschiedliche Typen, und eine Überführung ist nicht möglich (von den Problemen mit null-Referenzen einmal ganz abgesehen). So muss in der Praxis zwischen den unterschiedlichen Typen konvertiert werden, und bedauerlicherweise bietet die Java-Standardbibliothek hierfür keine Methoden.[ 192 ](Die Lücke füllt etwa die Open-Source-Bibliothek Apache Commons Lang (http://commons.apache.org/proper/commons-lang) mit der Klasse ArrayUtils, die mit toObject(…) und toPrimitive(…) die Konvertierungen durchführt.)

Mehr Probleme als Lösungen durch Autoboxing? (Teil 1) *

Mit dem Autoboxing ist eine Reihe von Unregelmäßigkeiten verbunden, die der Programmierer beachten muss, um Fehler zu vermeiden. Eine Unregelmäßigkeit hängt mit dem Unboxing zusammen, das der Compiler immer dann vornimmt, wenn ein Ausdruck einen primitiven Wert erwartet. Wenn kein primitives Element erwartet wird, wird auch kein Unboxing vorgenommen:

Listing 9.27com/tutego/insel/wrapper/Autoboxing.java, main(), Teil 1

Integer i1 = new Integer( 1 );

Integer i2 = new Integer( 1 );



System.out.println( i1 >= i2 ); // true

System.out.println( i1 <= i2 ); // true

System.out.println( i1 == i2 ); // false

System.out.println( i1 == i2-0 ); // true

Der Vergleich mit == ist ein Referenzvergleich, und es findet kein Unboxing auf primitive Werte statt, sodass es auf einen Vergleich von primitiven Werten hinausliefe. Wenn der Compiler zwei Referenzvariablen vor sich sieht, gibt es für Unboxing keinen Grund, auch wenn sie Wrapper-Typen sind. Daher muss bei zwei unterschiedlichen Integer-Objekten dieser Identitätsvergleich immer falsch sein. Das ist natürlich problematisch, da die alte mathematische Regel »aus i <= j und i >= j folgt automatisch i == j« nicht mehr gilt. Wenn es die unterschiedlichen Integer-Objekte für gleiche Werte nicht gäbe, bestünde dieses Problem nicht. Mit dem Minus-Null-Trick erreichen wir ein Unboxing, sodass wieder zwei Zahlen verglichen werden. Alternativ hilft eine explizite Typumwandlung in int oder ein Zugriff auf die Objektmethode intValue() zum bewussten Auspacken.

Mehr Probleme als Lösungen durch Autoboxing? (Teil 2) *

Es ist interessant zu wissen, was nun genau passiert, wenn das Boxing eine Zahl in ein Wrapper-Objekt umwandelt. In dem Moment wird nicht der Konstruktor aufgerufen, sondern die statische valueOf(…)-Methode:

Listing 9.28com/tutego/insel/wrapper/Autoboxing.java, main(), Teil 2

Integer n1 = new Integer( 10 );

Integer n2 = Integer.valueOf( 10 );

Integer n3 = 10; // Integer.valueOf( 10 );

Integer n4 = 10; // Integer.valueOf( 10 );

System.out.println( n1 == n2 ); // false

System.out.println( n2 == n3 ); // true

System.out.println( n1 == n3 ); // false

System.out.println( n3 == n4 ); // true

In dem Beispiel vergleichen wir viermal die Identität der Wrapper-Objekte. Es fällt auf, dass einige Vergleiche falsch sind, was kein Wunder ist und durch new schnell erklärt ist, denn hier legt die Laufzeitumgebung immer neue Objekte an. Auffällig sind die identischen Objekte (n2, n3, n4), die beim Boxing durch das dahinterliegenden valueOf(int) entstehen. Das Rätsel löst sich nur mit einem Blick in die von Oracle gewählte Implementierung auf, dass nämlich über Boxing gebildete Integer-Objekte einem Pool entstammen. Mit dem Pool versucht das JDK, das Problem mit dem == zu lösen, dass ausgewählte Wrapper-Objekte wirklich identisch sind, und == sich bei Wrapper-Referenzen so verhält wir == bei primitiven Werten.

Da nicht beliebig viele Wrapper-Objekte aus einem Pool kommen können, gilt die Identität der über Boxing gebildeten Objekte nur in einem ausgewählten Wertebereich zwischen –128 und +127, also dem Wertebereich eines Bytes:

Listing 9.29com/tutego/insel/wrapper/Autoboxing.java, main(), Teil 3

Integer j1 = 2;

Integer j2 = 2;

System.out.println( j1 == j2 ); // true

Integer k1 = 127;

Integer k2 = 127;

System.out.println( k1 == k2 ); // true

Integer l1 = 128;

Integer l2 = 128;

System.out.println( l1 == l2 ); // false

Integer m1 = 1000;

Integer m2 = 1000;

System.out.println( m1 == m2 ); // false

Wir betonten bereits, dass auch bei Wrapper-Objekten der Vergleich mit == immer ein Referenzvergleich ist. Da 2 und 127 im Wertebereich zwischen –128 und +127 liegen, entstammen die entsprechenden Integer-Objekte dem Pool. Das gilt für 128 und 1.000 nicht; sie sind immer neue Objekte. Damit ergibt auch der ==-Vergleich false.

Abschlussfrage

Welche Ausgabe kommt auf den Bildschirm? Ändert sich etwas, wenn i und j auf 222 stehen?

Integer i = 1, j = 1;

boolean b = (i <= j && j <= i && i != j);

System.out.println( b );

 


Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
 Zum Katalog
Zum Katalog: Java ist auch eine Insel Java ist auch eine Insel

Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?

Ihre Meinung



 Buchempfehlungen
Zum Katalog: Java ist auch eine Insel

Java ist auch eine Insel




Zum Katalog: Java SE 9-Standard-Bibliothek

Java SE 9-Standard-Bibliothek




Zum Katalog: Professionell entwickeln mit Java EE 8

Professionell entwickeln mit Java EE 8




Zum Katalog: Entwurfsmuster

Entwurfsmuster




Zum Katalog: IT-Projektmanagement

IT-Projektmanagement




 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2017

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de