Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
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 Boxing und Unboxing
gp 8.3 Ausführung von externen Programmen
gp 8.3.1 DOS-Programme aufrufen
gp 8.3.2 Die Windows-Registry verwenden
gp 8.3.3 Einen HTML-Browser unter Windows aufrufen
gp 8.4 Klassenlader (Class Loader)
gp 8.4.1 Woher die kleinen Klassen kommen
gp 8.4.2 Drei Typen von Klassenladern
gp 8.4.3 Der java.lang.ClassLoader
gp 8.4.4 Hot Deployment mit dem URL-ClassLoader
gp 8.4.5 Das jre/lib/endorsed-Verzeichnis
gp 8.4.6 Wie heißt die Klasse mit der Methode main()?
gp 8.5 Zeitmessung und Profiling
gp 8.6 Compilieren von Klassen
gp 8.6.1 Der Sun-Compiler


Galileo Computing

8.2 Wrapper-Klassendowntop

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.


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

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

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

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-Klassedowntop

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-Klassedowntop

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.

Systemeigenschaften auslesen

Eine ungewöhnliche Methode in der Boolean-Klasse ist getBoolean(). Die Methode sucht einen Eintrag in den Systemeigenschaften, und wenn sie ihn findet, wird versucht, diesen mittels toBoolean() in einen Wahrheitswert umzuwandeln. Entspricht der Wert dem String »true«, so ist das Ergebnis der Wert true, andernfalls, auch wenn kein Eintrag existiert, false.

public static boolean getBoolean( String name ) {
  return toBoolean( System.getProperty(name) );
}

class java.lang.Boolean
implements Serializable

gp static boolean getBoolean( String propName )
Liest eine Systemeigenschaft aus.

Galileo Computing

8.2.3 Die Basisklasse Number für numerische Wrapper-Objektedowntop

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. 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 Integerdowntop

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 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:


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

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


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 Überlaufdowntop

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_VALUE - a) )
    canAdd = false;
if ( a < 0 && b < 0 )
  if ( ! (b >= Integer.MIN_VALUE - a) )
    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 Ausgabeformatedowntop

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 Boxing und Unboxingtoptop

Eine Eigenschaft, die für die Version 1.5 angekündigt ist, ist das Boxing. (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 = new Integer(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. Die Schreibweise ist angenehm genau dann, wenn etwa in Datenstrukturen primitive Elemente abgelegt werden.

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

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.





Copyright (c) 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