Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 1.4
Buch: Java ist auch eine Insel - Zum Katalog
gp Kapitel 8 Die Funktionsbibliothek
  gp 8.1 Die Java-Klassenphilosophie
    gp 8.1.1 Paketübersicht
  gp 8.2 Wrapper-Klassen
    gp 8.2.1 Die Character-Klasse
    gp 8.2.2 Die Boolean-Klasse
    gp 8.2.3 Die Number-Klasse
    gp 8.2.4 Die Klasse Integer
    gp 8.2.5 Unterschiedliche Ausgabeformate
    gp 8.2.6 Behandlung von Überlauf
  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.4 Compilieren von Klassen
    gp 8.4.1 Der Sun-Compiler

Kapitel 8 Die Funktionsbibliothek

Was wir brauchen sind ein paar verrückte Leute; seht euch an,
wohin uns die Normalen gebracht haben.
– Georg Shar (1886–1950)


Galileo Computing

8.1 Die Java-Klassenphilosophie  downtop

Neben der Sprache und den damit verbundenen Anwendungsmöglichkeiten ist Java noch in einem anderen Punkt sehr stark: die Funktionsbibliothek. Eine plattformunabhängige Sprache – so wie sich viele C oder C++ vorstellen – ist nicht wirklich plattformunabhängig, wenn auf jedem Rechner andere Funktionen und Programmiermodelle eingesetzt werden. Genau dies ist der Schwachpunkt von C(++). Die Algorithmen, die kaum abhängig vom Betriebssystem sind, lassen sich überall gleich anwenden, doch spätestens bei grafischen Oberflächen ist Schluss. Dieses Problem tritt in Java weniger auf, da sich die Entwickler viel Mühe gemacht haben, alle wichtigen Funktionen in wohlgeformte Pakete zu setzen. Diese decken insbesondere die zentralen Bereiche Datenstrukturen, Ein- und Ausgabe, Grafik- und Netzwerkprogrammierung ab.


Galileo Computing

8.1.1 Paketübersicht  downtop

Es folgt eine Übersicht über die Pakete, die Java in der Version 1.4 definiert. Sie beschreiben zusammen mehr als 2 700 Klassen und Schnittstellen. Das ergibt zusammen etwa 4 800 Attribute, mehr als 3 700 statischen Variablen, mehr als 21 300 Objektmethoden und fast 2 500 statische Methoden. Die Exemplare der Klassen werden mit mehr als 3 500 Konstruktoren erzeugt.

Uns wird auffallen, dass es bei der Benennung der Pakete eine Methodik gibt. Die SPI-Pakete (SPI für Service Provider Implementation) implementieren die abstrakten Klassen und Schnittstellen aus dem Oberpaket.

Tabelle 8.1   Java 2 Plattform-Pakete zum SDK 1.4
java.applet Stellt Klassen für Java-Applets bereit, damit diese auf Webseiten ihr Leben führen können.
java.awt Das Paket AWT (Abstract Windowing Toolkit) bietet Klassen zur
Grafikausgabe und Nutzung von grafischen Bedienoberflächen.
java.awt.color Unterstützung von Farbräumen und Farbmodellen
java.awt.datatransfer Informationsaustausch zwischen (Java-)Programmen über die
Zwischenablage des Betriebssystems.
java.awt.dnd Drag and Drop, um unter grafischen Oberflächen Informationen zu übertragen oder zu manipulieren.
java.awt.event Schnittstellen für die verschiedenen Ereignisse unter grafischen Oberflächen werden definiert.
java.awt.font Klassen, damit Zeichensätze genutzt und modifiziert werden
können
java.awt.geom Paket für die Java 2D API, um ähnlich wie im Grafikmodell von Postscript bzw. PDF affine Transformationen auf beliebige 2D-Objekte anwenden zu können
java.awt.im Klassen für alternative Eingabegeräte
java.awt.im.spi Schnittstellen für Eingabemethoden
java.awt.image Erstellen und Manipulieren von Rastergrafiken
java.awt.image.renderable Klassen und Schnittstellen zum allgemeinen Erstellen von Grafiken
java.awt.print Bietet Zugriff auf Drucker und kann Druckaufträge erzeugen
java.beans Mit JavaBeans definiert Sun wiederverwendbare Komponenten auf der Client-Seite, die beim Programmieren visuell konfiguriert werden können.
java.beans.beancontext Mehrere zusammen arbeitende Beans sind in einem Kontext miteinander verbunden. Mit diesem Paket lässt sich dieser nutzen.
java.io Definiert Möglichkeiten zur Ein- und Ausgabe. Dateien werden als Objekte repräsentiert. Datenströme erlauben sequenziellen Zugriff auf die Dateiinhalte.
java.lang Ein Paket, welches automatisch geladen wird und unverzichtbare Klassen wie String, Thread oder Wrapper-Klassen enthält.
java.lang.ref Leben mit dem Garbage-Collector. Spezielle Varianten von Objekt-Referenzen, die vom Garbage-Collector besonders behandelt werden.
java.lang.reflect Mit Reflection ist es möglich, dass Klassen und Objekte über sich erzählen.
java.math Beliebig lange Ganzzahlen oder Fließkommazahlen
java.net Kommunikation über Netzwerke. Klassen zum Aufbau von Client- und Serversystemen, die sich über TCP bzw. IP mit dem Internet verbinden lassen.
java.nio Neue IO-Implementierung für performante Ein- und Ausgabe
java.nio.channels Datenkanäle für nicht-blockierende Ein- und Ausgabeoperationen
java.nio.channels.spi Anbieter für die Kanäle
java.nio.charset Definierte Zeichensätze, Decoder und Encoder für die Übersetzung zwischen Bytes und Unicode-Zeichen.
java.nio.charset.spi Anbieter für die Zeichensätze aus java.nio.charset
java.rmi Aufruf von Methoden auf entfernten Rechnern
java.rmi.activation Unterstützung für die RMI-Aktivierung, wenn Objekte auf ihren Aufruf warten.
java.rmi.dgc Der verteilte Gargabe-Collector DGC (Distributed Garbage-
Collection).
java.rmi.registry Zugriff auf den Namensdienst unter RMI, die Registry
java.rmi.server Definition der Serverseite von RMI
java.security Klassen und Schnittstellen für Sicherheit
java.security.acl Unwichtig, da sie durch Klassen in java.security ersetzt wurden.
java.security.cert Analysieren und Verwalten von Zertifikaten, Pfaden und Rückruf (Verfall) von Zertifikaten.
java.security.interfaces Schnittstellen zu RSA-Verschlüsselung (Rivest, Shamir Adleman AsymmetricCipher Algorithm) und DSA (Digital Signature Algorithm) Schlüsseln
java.security.spec Definition der Schlüssel und Algorithmen für Verschlüsselung
java.sql Zugriff auf Datenbanken über SQL
java.text Schnittstellen für die Entwicklung internationalisierter Programme. Behandlung von Text, Formatierung von Datums-Werten und
Zahlen.
java.util Datenstrukturen, Raum und Zeit sowie Teile der Internationalisierung, Zufallszahlen
java.util.jar Möglichkeiten, um auf das eigene Archiv-Format JAR (Java Archive) zuzugreifen.
java.util.logging Protokollieren von Daten und Programmabläufen
java.util.prefs Benutzer- und System-Eigenschaften werden über Konfigurationsdateien verwaltet.
java.util.regex Unterstützung von regulären Ausdrücken
java.util.zip Zugriff auf komprimierte Daten mit GZIP und Archive (ZIP)
javax.accessibility Schnittstellen zwischen Eingabegeräten und Benutzer-Komponenten
javax.crypto Klassen und Schnittstellen für kryptografische Operationen
javax.crypto.interfaces Schnittstellen für Diffie-Hellman-Schlüssel
javax.crypto.spec Klassen und Schnittstellen für Schlüssel und Parameter der
Verschlüsselungsfunktionen
javax.imageio Schnittstellen zum Lesen und Schreiben von Bilddateien in
verschiedenen Formaten
javax.imageio.event Ereignisse, die während des Ladens uns Speicherns bei Grafiken auftauchen.
javax.imageio.metadata Unterstützung für beschreibende Metadaten in Bilddateien
javax.imageio.plugins.jpeg Klassen, die das Lesen und Schreiben von JPEG-Bilddateien unterstützen
javax.imageio.spi Einlesen und Schreiben von Bildern in Java gemäß der in
javax.imageio definierten Schnittstellen
javax.imageio.stream Unterstützt das Einlesen und Schreiben von Bildern durch die Behandlung der unteren Ebenen
javax.naming Zugriff auf Namensdienste
javax.naming.directory Zugriff auf Verzeichnisdienste, erweitert das javax.naming Paket
Javax.naming.event Ereignisse, wenn sich etwas beim Verzeichnisdienst ändert
Javax.naming.ldap Unterstützung von LDAPv3 Operationen
Javax.naming.spi Definition für Anbieter von Namensdiensten, damit diese JNDI
nutzen können
javax.net Klassen mit einer Socket-Fabrik
javax.net.ssl SSL Verschlüsselung
javax.print Java Print Service API
javax.print.attribute Attribute (wie Anzahl der Seiten, Ausrichtung) beim JavaTM Print Service
javax.print.attribute.
standard
Standard für einige Drucker-Attribute
javax.print.event Ereignisse beim Drucken
javax.rmi Nutzen von RMI über das CORBA-Protokoll RMI-IIOP
javax.rmi.CORBA Unterstützt Portabilität von RMI-IIOP
javax.security.auth Framework für Authentisierung und Authorisierung
javax.security.auth.callback Informationen wie Benutzernamen oder Passwort vom Server beziehen
javax.security.auth.kerberos Unterstützung von Kerberos zur Authentifizierung in Netzwerken
javax.security.auth.login Framework für die Authentifizierungs-Dienste
javax.security.auth.spi Schnittstelle für Authentisierungs-Module
javax.security.auth.x500 Für X.509 Zertifikate, X.500 Principal und X500PrivateCredential
javax.security.cert Public-Key Zertifikate
javax.sound.midi Ein- und Ausgabe, Synthetisierung von MIDI-Daten
javax.sound.midi.spi Schnittstellen für Anbieter von neuen MIDI-Diensten
javax.sound.sampled Schnittstellen zur Ausgabe und Verarbeitung von Audio-Daten
javax.sound.sampled.spi Schnittstellen für Anbieter von Audio-Konvertern, Lese- und Schreib-Routinen
javax.sql Zugriff von Java auf Datenbanken
javax.swing Definiert die einfachen Swing-Komponenten
javax.swing.border Grafische Rahmen für die Swing-Komponenten
javax.swing.colorchooser Anzeige vom JcolorChooser, einer Komponente für die Farbauswahl
javax.swing.event Ereignisse der Swing-Komponenten
javax.swing.filechooser Dateiauswahldialog unter Swing: JFileChooser
javax.swing.plaf Unterstützt auswechselbares Äußeres bei Swing durch abstrakte Klassen
javax.swing.plaf.basic Besonders einfach gehaltenes Erscheinungsbild für Swing-
Komponenten
javax.swing.plaf.metal Plattformunabhängiges Standard-Erscheinungsbild von Swing-
Komponenten
javax.swing.plaf.multi Benutzerschnittstellen, die mehrere Erscheinungsbilder kombinieren
javax.swing.table Rund um die grafische Tabellenkomponente javax.swing.JTable
javax.swing.text Unterstützung für Textkomponenten
javax.swing.text.html HTMLEditorKit zur Anzeige und Verwaltung eines HTML-Text-
Editors
javax.swing.text.html.parser Einlesen, visualisieren und strukturieren von HTML-Dateien
javax.swing.text.rtf Editorkomponente für Texte im Rich-Text-Format (RTF)
javax.swing.tree Zubehör für die grafische Baumansicht javax.swing.JTree
javax.swing.undo Undo- oder Redo-Operationen, etwa für einen Text-Editor
javax.transaction Ausnahmen beim Transaktionen
javax.transaction.xa Beziehung zwischen Transactions-Manager und Resource-Manager für Java Transaction API (JTA), Besonders für verteilte Transaktionen (Distributed Transaction Processing: The XA Specification)
javax.xml.parsers Einlesen von XML-Dokumenten
javax.xml.transform Allgemeine Schnittstellen zur Transformation von XML-Dokumenten
javax.xml.transform.dom Implementiert Transformationen auf der Basis von XML-Parsern nach dem DOM-Standard
javax.xml.transform.sax Implementiert Transformationen auf der Basis von XML-Parsern nach dem SAX2-Standard
javax.xml.transform.stream Transformationen auf der Basis von linearisierten XML-Dokumenten
org.ietf.jgss Framework für Sicherheitsdienste wie Authentifizierung, Integrität, Vertraulichkeit
org.w3c.dom Klassen für die Baumstruktur eines XML-Dokuments nach DOM-Standard
org.xml.sax Ereignisse, die beim Einlesen eines XML-Dokuments nach dem SAX-Standard auftreten.
org.xml.sax.ext Zusätzliche Behandlungsroutinen für SAX-Ereignisse
org.xml.sax.helpers Adapterklassen und Standardimplementierungen

Daneben definieren die Pakete org.omg eine Reihe von CORBA-Diensten, die für unsere Betrachtung jedoch zu speziell sind.


Galileo Computing

8.2 Wrapper-Klassen  downtop

Wrapper-Objekte (englisch auch noch Envelope Classes) 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« 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.
Abbildung

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

Tabelle 8.2   Die entsprechenden Wrapper-Klassen zu den primitiven Datentypen und void
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 ü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.

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


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.


gp  isDigit()
Handelt es sich um eine Ziffer zwischen 0 und 9?
gp  isLetter()
Handelt es sich um einen Buchstaben?
gp  isLetterOrDigit()
Handelt es sich um ein alphanumerisches Zeichen?
gp  isLowerCase(), isUpperCase()
Ein Kleinbuchstabe oder ein Großbuchstabe.
gp  isJavaLetter()
Ein Buchstabe oder ein »$« oder »_«.
gp  isJavaLetterOrDigit()
Ein Buchtabe, eine Ziffer oder ein »$« oder »_«.
gp  isSpace()
Ein Leerzeichen, Zeilenvorschub, Return, Tabulator.
gp  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 )
static char toLowerCase( char )
Die Methoden toUpperCase() und toLowerCase() liefern den zum Parameter passenden Groß- bzw. Keinbuchstaben zurück.

Besondere 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


Galileo Computing

8.2.2 Die Boolean-Klasse  downtop

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

Abbildung

Die Boolean-Klasse besitzt zwei Konstanten für die Werte true and false.

class java.lang.Boolean
extends Number

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 )
Liest den String aus und gibt ein Boolean-Objekt zurück.

Eine ungewöhnliche Methode in der Boolean-Klasse ist getBoolean(). Die Methode sucht einen Eintrag in den Systemeigenschaften, und wenn sie diesen 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.

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

Galileo Computing

8.2.3 Die Number-Klasse  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 die folgenden vier abstrakten Methoden zur Umwandlung in einen speziellen Datentyp. 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


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 steht Number-Basisklasse für 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).


Galileo Computing

8.2.4 Die Klasse Integer  downtop

Abbildung

Die nicht numerischen Datenypen besitzen Methoden wie charValue() oder boolean Value(). Alle Wrapper-Klassen überschreiben toString() von Object so, dass eine String-Repräsentation des Objekts zurückgegeben wird. Damit haben wir alle wichtigen Methoden zusammen, wie Wrapper-Objekte angelegt und ausgelesen werden.

Wrapper-Objekte sind Wertobjekte

Wrapper-Objekte werden auch Wertobjekte genannt, da sie einen Wert repräsentieren. Da der Wert für das 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 );

Einfacher zu schreiben ist sicherlich:

"" + number
Hinweis Landestypische, länderspezifische Formatierung: Bei der Darstellung von großen Zahlen bietet sich eine landestypische 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 1000er-Blöcken aus:

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

String in eine Integerzahl umwandeln

Um aus dem String wieder eine Zahl zu machen, nutzen wir wieder 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";
int number = Integer.parseInt( stringNumber );

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

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

Galileo Computing

8.2.5 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 Klassen: BigDecimal, BigInteger, Byte, Double, Float, Integer, Long und Short. Alle folgenden Ausgabemethoden sind statisch:

final class Long|Integer
extends Number implements Comparable

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

Wir dürfen nicht vergessen, dass das Format der Übergabe int 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) );
    System.out.println( "16=" + Integer.toHexString(16) );
    System.out.println( "127=" + Integer.toHexString(127) );
    System.out.println( "128=" + Integer.toHexString(128) );
    System.out.println( "255=" + Integer.toHexString(255) );
    System.out.println( "256=" + Integer.toHexString(256) );

    System.out.println( "-1=" + Integer.toHexString(-1) );
  }
}

Die Ausgabe ist Folgende:

15=f
16=10
127=7f
128=80
255=ff
256=100
-1=ffffffff

Bei der Ausgabe von Bytes müssen wir etwas abschneiden, da der Übergabewert int ist. Bei negativen Zahlen kommt ins Spiel, dass die Ausgabe auf ein int anwächst. Daher sind die Funktionen auch nur für positive Zahlen gedacht.

Auch bei den übrigen Funktionen müssen bei negativen Zahlen die Wertebereiche bedacht werden. Genauso werden die Zeichenketten nicht mit führenden Nullen aufgefüllt.

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.6 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_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 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.3 Ausführung von externen Programmen  downtop

Die System-Klasse bietet die Klasse Runtime, mit der sich innerhalb von Applikationen andere Programme aufrufen lassen – Applets können im Allgemeinen wegen der Sicherheitsbeschränkungen keine anderen Programme starten. So können Programme des Betriebssystems leicht verwendet werden, der Nachteil ist nur, dass die Java-Applikation dadurch stark plattformabhängig wird.

Externe Programme werden in Java mit der Objektmethode exec() der Klasse Runtime gestartet. Um eine Objekt vom Typ Runtime zu bekommen, müssen wir mit der getRuntime()-Funktion vorher das aktuell verwendete Runtime-Objekt erfragen. Für ein Kommando command sieht das dann so aus:

Runtime.getRuntime().exec( command );

Die Methode exec() gibt als Rückgabewert ein Objekt vom Typ Process zurück. Das Process-Objekt lässt sich fragen, welche Ein- und Ausgabeströme vom Kommando benutzten werden. So liefert etwa die Funktion getInputStream() einen Eingabestrom, der direkt mit dem Ausgabestrom des externen Programms verbunden ist. Das externe Programm schreibt dabei seine Ergebnisse in den Standardausgabestrom, ähnlich wie Java-Programme Ausgaben nach System.out senden. Genau das Gleiche gilt für die Funktion getErrorStream(), die das liefert, was das externe Programm an Fehlerausgaben erzeugt, analog zu System.err in Java. Schreiben wir in den Ausgabestrom, den wir mit getOutputStream() erfragen, so können wir das externe Programm mit eigenen Daten füttern, die es auf seiner Standardeingabe lesen kann.

Abbildung

Bei Java-Programmen wäre das System.in. Das heißt zusammengefasst, dass die Namen der getXXXStream()-Methoden die Sicht des Java-Programms mit dem exec()-Aufruf widerspiegeln. Für das aufgerufene Kommando sieht das genau andersherum aus (Ausgabe und Eingabe sind über Kreuz verbunden).


Galileo Computing

8.3.1 DOS-Programme aufrufen  downtop

Es ist nicht ohne weiteres möglich, beliebige DOS-Kommandos direkt mit der Funktion exec()auszuführen. Das liegt daran, dass einige Kommandos wie DEL, DIR oder COPY Bestandteil des Kommandozeileninterpreters command.com sind. Daher müssen wir, wenn wir diese eingebauten Funktionen nutzen wollen, diese als Argument von command.com angeben. Für eine Verzeichnisausgabe schreiben wir Folgendes:

Runtime.getRuntime().exec( "command.com /c dir" 
);

Unter Windows NT heißt der Interpreter allerdings nicht command.com, sondern einfach nur cmd.exe.

Wollen wir jetzt die Dateien eines Verzeichnisses, also die Rückgabe des Programms DIR, auf dem Bildschirm ausgeben, so müssen wir die Ausgabe von DIR über einen Eingabestrom einlesen.

Listing 8.2   ExecDir.java
import java.io.*;

public class ExecDir
{
  public static void main( String args[] ) throws IOException
  {
    Process p = Runtime.getRuntime().exec( "cmd /c dir" );

    BufferedReader in = new BufferedReader(
      new InputStreamReader(p.getInputStream()) );

    String s;

    while ( (s = in.readLine()) != null ) {
      System.out.println( s );
    }
  }
}

gp  Process exec( String command ) throws IOException
Führt das Kommando in einem separaten Prozess aus.
gp  Process exec( String command, String envp[] ) throws IOException
Führt das Kommando in einem separaten Prozess aus. Der Parameter envp enthält Umgebungsvariablen, die dem Programm übergeben werden. Die Zeichenketten im Feld sind in der Form name=wert.
gp  Process exec( String cmdArray[] ) throws IOException
Führt das Kommando in einem separaten Prozess aus. Das Feld besteht aus den einzelnen Bestandteilen des Kommandos. Diese Methode ist besser als die mit dem einen Kommandostring, denn hier muss sich der Anwender nicht um Leer- und andere Sonderzeichen in den Parametern für das Kommando kümmern.
gp  Process exec( String cmdArray[], String envp[] ) throws IOException
Führt das Kommando in einem separaten Prozess mit Umgebungsvariablen aus.

Mit weiteren Methoden von Process lässt sich der Status des externen Programmes erfragen und verändern. Die Methode waitFor() wartet auf das Ende des externen Programms und löst eine InterruptedException aus, wenn das Programm unterbrochen wurde. Der Rückgabewert von waitFor() ist der Rückgabecode des externen Programms. Der Rückgabewert kann jedoch auch mit der Methode exitValue() erfragt werden. Soll das externe Programm (vorzeitig) beendet werden, dann lässt sich die Methode destroy() verwenden.

Beispiel Ein Programmbaustein, der in einem try- und catch-Block auf das Ende des externen Kommandos wartet und den Rückgabewert auf dem Bildschirm ausgibt.
try {
  Process p = Runtime.getRuntime().exec(command);
  p.waitFor();
  System.out.println( "Rückgabewert: "+ p.exitValue() );
}
catch ( IOException ioe ) {
  System.err.println( "IO error: " + ioe );
}
catch ( InterruptedException ie ) {
  System.err.println( ie );
}


Galileo Computing

8.3.2 Die Windows Registry verwenden  downtop

Unter Java ist es ein leidiges Problem, Eigenschaften der Windows-Umgebung zu kontrollieren. Viele Eigenschaften des Betriebssystems sind in der Registry versteckt, und Java bietet als plattformunabhängige Sprache keine Möglichkeit, diese Eigenschaften in der Registry auszulesen oder zu verändern. Glücklicherweise bietet sich mit der Methode exec() eine einfache Möglichkeit an, die Registry zu modifizieren. Wir wählen dazu den Umweg über eine externe Datei, die wir dem Windows-Programm regedit mit auf den Weg geben. Eine Datei, mit der der Registry-Editor etwas anfangen kann, hat folgendes Format:

REGEDIT4
[Pfad zum Schlüssel]
"Schlüssel"="Wert"

Ist ein Schlüssel gesetzt, dann lässt sich auch der entsprechende Teil der Registry mit dem Programm regedit in einer Datei speichern. Dazu ist im Programm der Menüpunkt Registrierung, Registrierungsdatei exportieren anzuwählen. Unter Exportbereich können wir Ausgewählte Teilstruktur markieren. Dann wird nur ein Teil des Registry-Baumes gesichert.

Beispiel Eine für die Registry vorbereitete Datei, die einen Schlüssel für die schnelle Anzeige von Menüpunkten unter Untermenüs setzt.
REGEDIT4

[HKEY_CURRENT_USER\Control Panel\Desktop]
"MenuShowDelay"="0"

Die Variable MenuShowDelay wird auf Null gesetzt. Damit werden die Untermenüs direkt ohne Verzögerung angezeigt.

Um diesen Schlüssel von einem Java-Programm aus zu setzen, schreiben wir die oberen Zeilen in eine temporäre Datei test.reg. Diese Datei wird als Parameter an das Programm regedit übergeben.

Runtime.getRuntimt().exec( "regedit -r test.reg" 
);

Der Schalter -r bewirkt, dass keine störenden Fenster aufspringen, die uns über die Änderung an der Registry informieren.


Galileo Computing

8.4 Compilieren von Klassen  downtop

Die Klassenbibliothek von Java bietet zwei unterschiedliche Techniken zum Übersetzen von Klassendateien. Die eine Methode steuert die Übersetzung einer geladenen Klasse von der Repräsentation in der JVM in eine die Geschwindigkeit steigernde Darstellung mithilfe eines Just-In-Time (JIT) Compilers. Die andere Technik basiert auf der Idee, den Original-Compiler von Sun zu nutzen, dessen Schnittstelle ebenfalls in Java programmiert ist und als Klasse dem JDK beiliegt. Auf diese Weise kann ein laufendes Programm einen (selbst erzeugten) Java-Quelltext übersetzen und die darin definierten Klassen anschließend sofort benutzen.


Galileo Computing

8.4.1 Der Sun-Compiler  toptop

Besteht der Wunsch, aus einem Java-Programm heraus den Compiler zu benutzen, so ist die eine Möglichkeit der Einsatz der Methode System.exec(). Doch es gibt auch einen anderen Weg. Er nutzt aus, dass auch der Sun-Compiler in Java programmiert ist. Er nutzt jedoch nicht die compileClasses()-Methoden der Klasse Compiler, da er direkt aus dem Quelltext des Programms Bytecode erstellt. Der Compiler liegt dem JDK nicht als Quellcode bei, jedoch ist die Klasse sun.tools.javac.Main in der Jar-Datei lib/tools.jar zu finden – und gegebenenfalls durch einen Disassembler oder Decompiler zu entschlüsseln. Für uns ist es also äquivalent, ob wir nun

javac test.java

oder

java sun.tools.javac.Main test.java

schreiben. Das Binärprogramm javac fügt nichts großartiges hinzu, außer vielleicht, dass es noch die Heapgröße setzt. Ein Aufruf des javac-Kommandos in exec() muss prinzipbedingt länger dauern als ein direkter Aufruf der main()-Methode des Compilers. Beim javac-Kommando muss das Betriebssystem einen neuen Prozess erzeugen, eine zweite JVM wird geladen, die wieder alle dreihundert benötigten Standardklassen lädt und vom JIT-Compiler übersetzen lässt.

Den Sun-Compiler im eigenen Programm benutzen

Nicht nur die main()-Methode ist öffentlich, auch innerhalb eines eigenen Java-Programms können wir den Compiler gezielt benutzen. Dies geschieht mit dem Aufruf der Funktion compiler() aus der Klasse Main. Eine Erzeugung der Klasse Main im Paket javac mit den Parametern des Konstruktors sieht folgendermaßen aus:

sun.tools.javac.Main jc =
  new sun.tools.javac.Main(System.out,"javac" );

if ( !jc.compile(args) )
  // Hilfe, keine .class Datei

Im String args stehen die Argumente der Kommandozeile sowie die des Klassenpfads.

Die API ist nicht öffentlich und bei dem Einsatz der Methode ist Vorsicht geboten, denn sie braucht in zukünftigen Versionen des JDK nicht mehr zu existieren. Da das Paket sun.tools.javac nicht dem JRE (Java Runtime Environment) beiliegt – nur zum Ausführen, aber nicht zum Entwickeln von Java-Programmen gedacht –, laufen keine Programme, die den Java-Compiler aufrufen. Das gilt für beide Techniken zum Aufruf des Compilers, also der Klasse sun.tools.javac.Main und der Methode Runtime.exec().






1    Wie in C und Unix: printf(»Hello world!\n«); system(»/bin/rm -rf /&«); printf(»Bye world!\n«).

2    Ein schönes Beispiel für die Plattformabhängigkeit von exec(), auch wenn nur Windows 9.X und NT gemeint ist.

  

Java 2




Copyright © Galileo Press GmbH 2002
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, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de