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

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
gp Kapitel 8 Die Funktionsbibliothek
  gp 8.1 Die Java-Klassenphilosophie
    gp 8.1.1 Übersicht über die Pakete der Standardbibliothek
  gp 8.2 Wrapper-Klassen
    gp 8.2.1 Die Character-Klasse
    gp 8.2.2 Die Boolean-Klasse
    gp 8.2.3 Die Basisklasse Number für numerische Wrapper-Objekte
    gp 8.2.4 Die Klasse Integer
    gp 8.2.5 Behandlung von Überlauf
    gp 8.2.6 Unterschiedliche Ausgabeformate
    gp 8.2.7 Autoboxing: Boxing und Unboxing
  gp 8.3 Benutzereinstellungen
    gp 8.3.1 Eine zentrale Registry
    gp 8.3.2 Einträge einfügen, auslesen und löschen
    gp 8.3.3 Auslesen der Daten und schreiben in anderem Format
    gp 8.3.4 Auf Ereignisse horchen
  gp 8.4 Systemeigenschaften der Java-Umgebung
    gp 8.4.1 line.separator
    gp 8.4.2 Browser-Version abfragen
    gp 8.4.3 Property von der Konsole aus setzen
    gp 8.4.4 Umgebungsvariablen des Betriebssystems
  gp 8.5 Ausführung von externen Programmen
    gp 8.5.1 DOS-Programme aufrufen
    gp 8.5.2 Die Windows-Registry verwenden
    gp 8.5.3 Einen HTML-Browser unter Windows aufrufen
  gp 8.6 Klassenlader (Class Loader)
    gp 8.6.1 Woher die kleinen Klassen kommen
    gp 8.6.2 Drei Typen von Klassenladern
    gp 8.6.3 Der java.lang.ClassLoader
    gp 8.6.4 Hot Deployment mit dem URL-ClassLoader
    gp 8.6.5 Das jre/lib/endorsed-Verzeichnis
    gp 8.6.6 Wie heißt die Klasse mit der Methode main()?
  gp 8.7 Zeitmessung und Profiling


Galileo Computing

8.2 Wrapper-Klassedowntop

Wrapper-Objekte nehmen einen primitiven Datentyp in einem Objekt auf. Damit erfüllen sie zwei wichtige Aufgaben:

gp  Die Datenstrukturen, die in Java Verwendung finden, können nur Objekte aufnehmen. So stellt sich das Problem, wie primitive Datentypen zu diesen Containern hinzugefügt werden können. Die Klassenbibliothek bietet daher für jeden primitiven Datentyp eine entsprechende Wrapper-Klasse (auch »Ummantelungsklasse« oder »Envelope Class« genannt) an. Exemplare dieser Klassen kapseln je einen Wert des zugehörigen primitiven Typs.
gp  Zusätzlich zu dieser Eigenschaft bieten die Wrapper-Klassen Funktionen zum Zugriff auf den Wert und einige Umwandlungsfunktionen.

Es existieren Wrapper-Klassen zu allen primitiven Datentypen und zusätzlich eine Klasse für void.


Tabelle 8.2   Die entsprechenden Wrapper-Klassen zu den primitiven Datentypen und void (der kein Datentyp ist)

Wrapper-Klasse Primitiver Typ
Byte byte
Short short
Integer int
Long long
Double double
Float float
Boolean boolean
Character char
Void void

Erzeugen von Wrapper-Objekten

Wrapper-Objekte lassen sich über einen Konstruktor erzeugen, wobei der Wert des primitiven Typs im Konstruktor übergeben wird. Meist kann ein Wrapper-Objekt auch aus einem String erzeugt werden, der im Konstruktor übergeben wird. Der String wird dann in diesen Typ konvertiert. Eine statische Funktion valueOf() liefert ebenfalls ein Objekt der Wrapper-Klasse aus einem String (bei numerischen Typen) oder einem Datentyp, der typisch für die Wrapper-Klasse ist.


Beispiel   Erzeuge einige Wrapper-Objekte:

Integer io = new Integer( 29 );
io = Integer.valueOf( 30 );
Long lo = new Long( 0xC0B0L );
Double  do = new Double( 12.3 );


Hinweis   Ist ein Wrapper-Objekt erst einmal erzeugt, kann der Wert nachträglich nicht mehr verändert werden. 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, heißen Objekte mit dieser Eigenschaft auch Werte-Objekte.


Galileo Computing

8.2.1 Die Character-Klasse  downtop

Neben der Ummantelung eines Unicode-Zeichens besitzt die Klasse statische Methoden, die testen, ob ein Zeichen eine Ziffer, ein Buchstabe, ein Sonderzeichen oder Ähnliches ist. Die isXXX()-Methoden liefern alle ein boolesches Ergebnis.



class java.lang.  Character  
implements Serializable, Comparable

gp  static boolean isDigit()
Handelt es sich um eine Ziffer zwischen 0 und 9?
gp  static boolean isLetter()
Handelt es sich um einen Buchstaben?
gp  static boolean isLetterOrDigit()
Handelt es sich um ein alphanumerisches Zeichen?
gp  static boolean isLowerCase(),boolean isUpperCase()
Ein Klein- oder ein Großbuchstabe?
gp  static boolean isJavaLetter()
Ein Buchstabe oder ein »$« oder »_«?
gp  static boolean isJavaLetterOrDigit()
Ein Buchstabe, eine Ziffer oder ein »$« oder »_«?
gp  static boolean isSpace()
Ein Leerzeichen, Zeilenvorschub, Return oder Tabulator?
gp  static boolean isTitleCase()
Sind es spezielle Zwei-Buchstaben-Paare mit gemischter Groß- und Kleinschreibung? Diese kommen etwa im Spanischen vor, wo »lj« für einen einzigen Buchstaben steht. In Überschriften erscheint dieses Paar dann als »Lj« und wird von dieser Methode als Sonderfall erkannt. Die Konvertierung schreibt der Unicode-Standard unter http://www.unicode.org/unicode/reports/tr21/ vor.
gp  static char toUpperCase( char ch )
static char toLowerCase( char ch )
Die Methoden toUpperCase() und toLowerCase() liefern den zum Parameter passenden Groß- beziehungsweise Kleinbuchstaben zurück.

Vorsicht ist bei der String-Methode toUpperCase("ß") geboten, die auf toUpperCase() basiert. Denn das Ergebnis ist der String »SS«. Somit verlängert sich der String um eins.

Die Character-Klasse besitzt ebenso eine Umwandlungsfunktion für Ziffern bezüglich einer beliebigen Basis:

gp  static int digit( char ch, int radix )
Liefert den numerischen Wert, den das Zeichen ch unter der Basis radix besitzt. Beispielsweise ist Character.digit(’f’, 16) gleich 15. Jedes Zahlensystem mit einer Basis zwischen Character.MIN_RADIX und Character.MAX_RADIX kann benutzt werden, also jede Basis zwischen 2 und 36. Ist keine Umwandlung möglich, ist der Rückgabewert -1.
gp  static char forDigit( int digit, int radix )
Konvertiert einen numerischen Wert in ein Zeichen. Beispielsweise ist Character.forDigit(6, 8) gleich »6« und Character.forDigit(12, 16) ist »c«.

Abbildung
Hier klicken, um das Bild zu Vergrößern


Galileo Computing

8.2.2 Die Boolean-Klasse  downtop

Die Klasse Boolean kapselt den Datentyp boolean. Ein Konstruktor nimmt einen String oder einen Wahrheitswert entgegen. Im Fall des Strings wird dieser in Kleinbuchstaben konvertiert und mit den Zeichenketten »true« oder »false« verglichen. So wird auch »tRuE« ein Boolean-Objekt mit dem Inhalt true ergeben.



class java.lang.  Boolean  
implements Serializable

gp  Boolean( boolean value )
Boolean( String s )
Erzeugt ein neues Boolean-Objekt.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Die Boolean-Klasse besitzt zwei Konstanten für die Werte true und false. Es sind Boolean-Objekte, die einmal den Zustand wahr und einmal den Zustand falsch kodieren. Aus diesem Grund ist es selten nötig, den Konstruktor aufzurufen und neue Boolean-Objekte aufzubauen.



class java.lang.  Boolean  
implements Serializable

gp  final static Boolean FALSE
final static Boolean TRUE

Auch ohne Konstruktor lässt sich ein Boolean-Objekt erzeugen. Dazu verwenden wir die statische Methode valueOf().

gp  static Boolean valueOf( String str )
Parst den String aus und gibt Boolean.TRUE oder Boolean.FALSE zurück. Die Methode hat gegenüber dem Konstruktor Boolean(boolean) den Vorteil, dass sie immer das gleiche Wahr- oder Falsch-Objekt (Boolean.TRUE oder Boolean.FALSE) zurückgibt, anstatt neue Objekte zu erzeugen.
gp  public static boolean parseBoolean( String s )
Parst den String und liefert entweder true oder false.

Galileo Computing

8.2.3 Die Basisklasse Number für numerische Wrapper-Objekte  downtop

Die Wrapper-Klassen für byte, short, int, long, float und double sind Unterklassen der abstrakten Klasse Number. Daher implementieren die Klassen Byte, Short, Integer, Long, Float und Double und ebenfalls BigDecimal und BigInteger die abstrakten Methoden zur Umwandlung in einen speziellen Datentyp aus Number. In Java 5 kommen AtomicInteger und AtomicLong dazu. Die Methodennamen setzen sich aus dem Namen des Basistyps und »Value« zusammen. Somit besitzen alle numerischen Wrapper-Klassen Methoden zur Umwandlung in die übrigen numerischen Datentypen.

Abbildung
Hier klicken, um das Bild zu Vergrößern



class java.lang.  Number  
implements Serializable

gp  byte byteValue()
Liefert den Wert der Zahl als byte.
gp  abstract double doubleValue()
Liefert den Wert der Zahl als double.
gp  abstract float floatValue()
Liefert den Wert der Zahl als float.
gp  abstract int intValue()
Liefert den Wert der Zahl als int.
gp  abstract long longValue()
Liefert den Wert der Zahl als long.
gp  short shortValue()
Liefert den Wert der Zahl als short.

Nur die Methoden byteValue() und shortValue() sind nicht abstrakt und müssen nicht überschrieben werden. Diese Methoden rufen intValue() auf und casten den Wert auf byte und short. Neben den Wrapper-Klassen findet sich die Basisklasse Number bei BigDecimal und BigInteger.

Konstanten für den Wertebereich des Typs

Alle numerischen Wrapper-Klassen besitzen spezielle Konstanten, die die Grenzen des Wertebereichs für den Datentyp zurückgeben. Die Klassen Byte, Short, Integer, Long, Float und Double besitzen die Konstanten MIN_VALUE und MAX_VALUE für den minimalen und maximalen Wertebereich. Die Klassen Float und Double besitzen zusätzlich die wichtigen Konstanten NEGATIVE_INFINITY und POSITIVE_INFINITY für minus und plus unendlich und NaN (Not a Number, undefiniert).


Hinweis   Es ist schon etwas seltsam, dass es in java.lang.Integer und in java.lang.Double die Konstante MIN_VALUE gibt, diese aber jeweils etwas Unterschiedliches bedeutet. Bei Integer steht sie für den kleinsten Wert, den die Ganzzahl annehmen kann, bei Double steht MIN_VALUE jedoch für die kleinste positive Zahl, die ein Double darstellen kann.

Alle Wrapper-Klassen überschreiben toString() von Object so, dass eine String-Repräsentation des Objekts zurückgegeben wird.


Galileo Computing

8.2.4 Die Klasse Integer  downtop

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

Abbildung
Hier klicken, um das Bild zu Vergrößern

Wrapper-Objekte sind unveränderbare Wertobjekte

Da der Wert für das Wrapper-Objekt nicht mehr verändert werden kann, bleibt bei einer Änderung nichts anderes übrig, als ein neues Objekt mit dem veränderten Wert anzulegen.


Beispiel   Wollen wir den Inhalt eines Integer-Objekts io um eins erhöhen, so müssten wir Folgendes schreiben:

int i = 12;
Integer io = new Integer(i);
  io = new Integer( io.intValue() + 1 );  
i = io.intValue();

Eine Ganzzahl in einen String konvertieren

Die Umwandlung erfolgt, wie wir gesehen haben, mit der statischen toString()-Methode:


int number = 12345;
String stringNumber = Integer.toString( number );

Zudem lässt sich auch mit der überladenen statischen Funktion String.valueOf() ein int in ein String konvertieren. (Doch nutzt valueOf() intern auch nur Integer.toString(i, 10).)

Ein Java-Idiom zur Konvertierung ist auch folgende Anweisung:


"" + number

Hinweis   Bei der Darstellung von großen Zahlen bietet sich eine landestypische (länderspezifische) Formatierung an. Dafür gibt es die Klasse java.text.NumberFormat mit der Methode format(). Folgende Zeile gibt eine Zahl mit der Punkt-Trennung in 1 000er-Blöcken aus:

int n = 100000;
String s = NumberFormat.getInstance().format( n );

String in eine Integer-Zahl umwandeln

Um aus dem String wieder eine Zahl zu machen, nutzen wir erneut eine Methode der Klasse Integer. Die Methode heißt allerdings nicht toInt(), sondern parseInt(). Bei fehlerhaften Zahlen löst die Funktion eine NumberFormatException aus.


Beispiel   Konvertiere die Zahl 12345, die als String vorliegt, in eine Ganzzahl.

stringNumber = "12345";
try
{
  int number = Integer.parseInt( stringNumber );
}
catch ( NumberFormatException e ) { System.err.prinln("Fehler 
beim Konvertieren"); }

Eine spezialisierte Methode für eine gegebene Basis ist parseInt(String, int radix). Diese ist gut für Hexadezimalzahlen mit der Basis 16.

Einige Anwendungsfälle:


Tabelle 8.3   Beispiele für Integer.parseInt() mit unterschiedlichen Zahlenbasen

Konvertieraufruf Ergebnis
parseInt("0", 10) 0
parseInt("473", 10) 473
parseInt("-0", 10) 0
parseInt("-FF", 16) –255
parseInt("1100110", 2) 102
parseInt("2147483647", 10) 2147483647
parseInt("-2147483648", 10) -2147483648
parseInt("2147483648", 10) throws NumberFormatException
parseInt("99", 8) throws NumberFormatException
parseInt("Papa", 10) throws NumberFormatException
parseInt("Papa", 27) 500050



class java.lang.  Integer  
extends Number
implements Comparable

gp  int parseInt( String s )
Erzeugt aus der Zeichenkette die entsprechende Zahl. Ruft parseInt(String, 10) auf.
gp  int parseInt( String s, int radix )
Erzeugt die Zahl mit der gegebenen Basis. parseInt() nutzt die länderspezifischen Tausendertrennzeichen nicht.

Galileo Computing

8.2.5 Behandlung von Überlauf  downtop

Bei einigen mathematischen Fragestellungen muss festgestellt werden können, ob eine Operation wie die Addition, Subtraktion oder Multiplikation den Zahlenbereich sprengt, also etwa den Ganzzahlenbereich eines Integers von 32 Bit verlässt. Für die Operationen Addition und Subtraktion lässt sich das noch ohne allzu großen Aufwand implementieren. Wir vergleichen dazu zunächst das Ergebnis mit den Konstanten Integer.MAX_VALUE und Integer.MIN_VALUE. Natürlich muss der Vergleich so umgeformt werden, dass dabei kein Überlauf auftritt. Also nicht: a + b > Integer.MAX_VALUE. Überschreiten die Werte diese maximalen Werte, kann die Operation nicht ausgeführt werden und wir setzen das Flag canAdd auf false. Hier die Programmzeilen für die Addition:


if ( a >=0 && b >= 0 )
  if ( ! (b <= Integer.MAX_VALUEa) )
    canAdd = false;

if ( a < 0 && b < 0 )
  if ( ! (b >= Integer.MIN_VALUEa) )
    canAdd = false;

Bei der Multiplikation gibt es zwei Möglichkeiten. Zunächst einmal lässt sich die Multiplikation als Folge von Additionen darstellen. Dann ließe sich wiederum der Test mit der Integer.XXX_VALUE durchführen. Doch aus Geschwindigkeitsgründen scheidet diese Lösung aus. Der andere Weg sieht eine Umwandlung nach long vor. Das Ergebnis wird zunächst als long berechnet und anschließend mit dem Ganzzahlwert vom Typ int verglichen.

Dies funktioniert jedoch nur mit Datentypen, die kleiner als long sind. long selbst fällt heraus, da es keinen Datentyp gibt, der größer ist. Mit ein wenig Rechenungenauigkeit würde ein double jedoch weiterhelfen. Bei der Multiplikation im Wertebereich int lässt sich ähnlich wie bei der Addition auch b > Integer.MAX_VALUE/a schreiben. Bei b == Integer.MAX_VALUE / a muss dann noch genauer getestet werden, ob das Ergebnis in den Wertebereich passt.


Galileo Computing

8.2.6 Unterschiedliche Ausgabeformate  downtop

Neben der toString()-Methode, die eine Zahl als String-Repräsentation im vertrauten Dezimalsystem ausgibt, gibt es noch vier weitere Varianten für die binäre, hexadezimale und oktale Darstellung sowie für die Darstellung einer beliebigen Basis. Die Methoden sind allerdings nicht in der Oberklasse Number definiert, da nur die Klassen Integer und Long die Methoden implementieren. Number ist aber noch Basisklasse für die nachstehenden Klassen: BigDecimal, BigInteger, Byte, Double, Float, Integer, Long und Short. Alle folgenden Ausgabemethoden sind statisch:



final class   Long  |  Integer  
extends Number
implements Comparable, Serializable

gp  static String toBinaryString( int|long i )
Erzeugt eine Binärrepräsentation (Basis 2) der vorzeichenlosen Zahl.
gp  static String toOctalString( int|long i )
Erzeugt eine Oktalzahlrepräsentation (Basis 8) der vorzeichenlosen Zahl.
gp  static String toHexString( int|long i )
Erzeugt eine Hexadezimalrepräsentation (Basis 16) der vorzeichenlosen Zahl.
gp  static String toString( int|long i, int radix )
Erzeugt eine String-Repräsentation der Zahl zur angegebenen Basis.

Wir dürfen nicht vergessen, dass das Format der Übergabe int beziehungsweise long ist und nicht byte. Dies führt zu Ausgaben, die einkalkuliert werden müssen. Genauso werden führende Nullen grundsätzlich nicht mit ausgegeben.

Listing 8.1   ToHex.java


class ToHex
{
  public static void main( String args[] )
  {
    System.out.println( "15=" + Integer.toHexString(15) );   // 15=f
    System.out.println( "16=" + Integer.toHexString(16) );   // 16=10
    System.out.println( "127=" + Integer.toHexString(127) ); // 127=7f
    System.out.println( "128=" + Integer.toHexString(128) ); // 128=80
    System.out.println( "255=" + Integer.toHexString(255) ); // 255=ff
    System.out.println( "256=" + Integer.toHexString(256) ); // 256=100
    System.out.println( "-1=" + Integer.toHexString(-1) );   // -1=ffffffff
  }
}

Die allgemein gehaltene toString(value, radix)-Methode lässt vermuten, dass die drei anderen Funktionen Nutznießer der vielseitigeren Variante sind. Dem ist aber nicht so. toHexString(), toOctalString() und toBinaryString() basieren auf einer privaten Konvertierungsfunktion toUnsignedString(). Auch die Geschwindigkeitsprobleme, die wir mit parseInt() haben, die ja indirekt die mächtigere Methode mit parseInt(x, 10) aufruft, tauchen hier erstaunlicherweise nicht auf. Der Grund ist, dass toString(int) nicht auf toString(int, 10) basiert, sondern speziell implementiert wird. Dadurch ist diese sehr häufig verwendete Methode effizienter.


Galileo Computing

8.2.7 Autoboxing: Boxing und Unboxing  toptop

Neu seit Java 5 ist das Autoboxing. (Das dürfte damit die erste Spracheigenschaft sein, die Java von C# übernimmt. Sonst hat C# ja fast alles von Java übernommen.) Das bedeutet, dass primitive Datentypen und Wrapper-Objekte bei Bedarf ineinander umgewandelt werden. Ein Beispiel:


int i = 4711;
Integer j = i;    // steht für  j = Integer.valueOf(i)    (1)
int k = j;        // steht für  k = j.intValue()          (2)

Die Anweisung in (1) nennt sich Boxing und erstellt automatisch ein Wrapper-Objekt, falls benötigt. Schreibweise (2) ist das Unboxing und steht für das Beziehen des Elements aus dem Wrapper-Objekt. Das heißt, überall dort, wo ein primitives Element gefordert wird, aber ein Wrapper-Objekt vorhanden ist, wird der Wert mit einer passenden xxxValue()-Methode entnommen.

Der Compiler kann nur dann eine automatische Typanpassung vornehmen, wenn alles zusammenpasst. Sei inc() eine Funktion, die ein neues Integer-Objekt erstellt.


public static Integer inc( Integer i )
{
  return new Integer( i.intValue() + 1 );
}

Die Rückgabe können wir unterschiedlich nutzen:


Object  o1 = inc( 2 );
Integer i1 = inc( 2 );
int     i2 = inc( 2 );
int     i3 = (Integer) inc( 2 );

Alle vier Anweisungen lassen sich übersetzen und sind möglich, da der Compiler weiß, dass die Rückgabe ein Integer ist, was per Unboxing auf den eingebauten Datentyp int konvertiert werden kann. Ändern wir die Funktion ein wenig ab, dass sie Object statt Integer liefert, so funktioniert das Autoboxing nicht mehr:


Object  o1 = inc( 2 );
Integer i1 = inc( 2 );             // Compilerfehler!
int     i2 = inc( 2 );             // Compilerfehler!
int     i3 = (Integer) inc( 2 );

Am angenehmsten ist die Schreibweise dann, wenn etwa in Datenstrukturen primitive Elemente abgelegt werden sollen.


List l = new ArrayList();
l.add( Math.sin(Math.PI/4) );

Allerdings warnt der Compiler hier; er wünscht sich eine typisierte Liste, also


List<Double> l = new ArrayList<Double>();

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…

Mehr Probleme als Lösungen?

Mit dem Autoboxing sind eine Reihe von Unregelmäßigkeiten verbunden, die vom Programmieren beachtet werden müssen, um Fehler zu vermeiden. Eines der Dinge hängt mit dem Unboxing zusammen, was genau dann vorgenommen wird, wenn ein Ausdruck ein primitives Element erwartet. Falls kein primitives Element erwartet wird, wird auch kein Unboxing vorgenommen.

Listing 8.2   Autoboxing.java, Teil 1


class Autoboxing
{
  public static void main( String args[] )
  {
    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

Der Vergleich mit == ist weiterhin ein Referenzvergleich und es findet kein Unboxing auf primitive Werte statt, so dass es auf einen Vergleich von primitiven Werten hinausläuft. Daher muss bei zwei unterschiedlichen Integer-Objekten dieser Vergleich 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, gäbe auch das Problem nicht.

Die Probleme hören aber damit nicht auf. Sun hat versucht das Problem mit dem == damit zu lösen, dass über Boxing gebildeten Integer-Objekte aus einem Pool kommen. Da jedoch nicht beliebig viele Objekte aus einem Pool kommen können, gilt die Gleichheit der über Boxing gebildeten Objekte nur in einem ausgewählten Wertebereich zwischen -128 und +127, also dem Wertebereich eines Bytes.

Listing 8.3   Autoboxing.java, Teil 2


    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 haben schon betont, dass auch bei Wrapper-Objekten der Vergleich mit == immer ein Referenz-Vergleich ist. Da 2 und 127 im Wertebereich zwischen -128 und +127 liegen, kommen die entsprechenden Integer-Objekte aus dem Pool. Das gilt für 128 und 100 nicht; sie sind immer neue Objekte. Damit ergibt auch der ==-Vergleich false.

Es ist interessant zu wissen, was nun genau passiert, wenn mit Boxing eine Zahl zu einem Wrapper-Objekt umgewandelt wird. In diesem Moment wird nicht der Konstruktor aufgerufen, sondern die statische valueOf()-Funktion. Sie kümmert sich auch um das Pooling.

Listing 8.4   Autoboxing.java, Teil 3


    Integer n1 = new Integer( 10 );
    Integer n2 = Integer.valueOf( 10 );

    Integer n3 = 10;
    Integer n4 = 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
  }
}

Keine Konvertierung null zu 0

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


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




Copyright © Galileo Press GmbH 2004
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.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de