Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage |
<< | < | > | >> | API | Kapitel 16 - Utility-Klassen I |
Neben den bisher vorgestellten Datenstrukturen des Pakets java.util gibt es in java.lang eine Klasse System, die eine Reihe nützlicher Hilfsmittel zur Verfügung stellt. Die wichtigsten von ihnen sollen in den folgenden Abschnitten besprochen werden.
In Java gibt es keine Möglichkeit, direkt auf die Umgebungsvariablen eines Programms zuzugreifen. Ein solcher Zugriff wurde von den Java-Designern als nichtportabel angesehen und statt dessen durch das Konzept der Properties ersetzt. Properties sind Listen von Eigenschaften, die dem Programm vom Java-Laufzeitsystem zur Verfügung gestellt werden. Jede Eigenschaft besitzt einen Namen, unter dem auf sie zugegriffen werden kann. Das Java-Laufzeitsystem stellt standardmäßig die folgenden Properties zur Verfügung:
Property | Bedeutung |
java.version | Java-Versionsnummer |
java.vendor | Herstellerspezifische Zeichenkette |
java.vendor.url | URL (also ein Internet-Link) zum Hersteller |
java.home | Installationsverzeichnis |
java.class.version | Versionsnummer der Java-Klassenbibliothek |
java.class.path | Aktueller Klassenpfad |
os.name | Name des Betriebssystems |
os.arch | Betriebssystem-Architektur |
os.version | Versionsnummer des Betriebssystems |
file.separator | Trennzeichen für die Bestandteile eines Pfadnamens |
path.separator | Trennzeichen für die Laufwerksangabe eines Pfadnamens |
line.separator | Zeichenkette für Zeilenschaltung |
user.name | Name des angemeldeten Benutzers |
user.home | Home-Verzeichnis |
user.dir | Aktuelles Arbeitsverzeichnis |
java.vm.specification.version | Version der VM-Spezifikation |
java.vm.specification.vendor | Hersteller der VM-Spezifikation |
java.vm.specification.name | Bezeichnung der VM-Spezifikation |
java.vm.version | VM-Version |
java.vm.vendor | Hersteller der VM |
java.vm.name | Name der VM-Implementierung |
java.specification.version | Version der Spezifikation der Laufzeitumgebung |
java.specification.vendor | Hersteller der Spezifikation der Laufzeitumgebung |
java.specification.name | Bezeichnung der Spezifikation der Laufzeitumgebung |
Tabelle 16.2: Standard-Properties
Für den Zugriff auf diese Eigenschaften steht die Klasse Properties aus dem Paket java.util zur Verfügung. Sie bietet die Möglichkeit, Property-Listen zu erzeugen, mit Werten zu füllen und vorhandene Werte auszulesen. Die Klasse Properties ist eine Ableitung der Klasse Hashtable und stellt damit eine Tabelle von Schlüssel-/Wertepaaren dar.
Für den Zugriff auf einzelne Properties reicht meist die einfach zu bedienende Klassenmethode getProperty der Klasse System in java.lang aus:
public static String getProperty(String key) public static String getProperty(String key, String default) |
java.lang.System |
Die erste Variante liefert die Eigenschaft mit dem Namen key in Form einer Zeichenkette. Falls keine Eigenschaft mit diesem Namen gefunden wurde, wird null zurückgegeben. Die zweite Variante erlaubt die Übergabe eines Standardwertes. Der Unterschied zur ersten Variante besteht darin, daß nicht null, sondern der Standardwert zurückgegeben wird, wenn die gesuchte Eigenschaft nicht gefunden wurde.
Die Methode getProperties liefert das komplette Properties-Objekt mit den System-Properties:
public static Properties getProperties() |
java.lang.System |
Das folgende Programm gibt eine Liste aller System-Properties auf dem Bildschirm aus. Es verwendet dazu zunächst die Methode getProperties, um das System-Properties-Objekt zu beschaffen. Anschließend erzeugt es durch Aufruf von propertyNames einen Enumerator, mit dem alle Schlüsselwerte durchlaufen werden können und mit dem durch Aufruf von getProperty der zugehörige Wert ermittelt werden kann. Auf diese Weise listet das Programm alle verfügbaren System-Properties auf (das sind in der Regel sehr viel mehr als die plattformübergreifend spezifizierten):
001 /* Listing1604.java */ 002 003 import java.util.*; 004 005 public class Listing1604 006 { 007 public static void main(String[] args) 008 { 009 Properties sysprops = System.getProperties(); 010 Enumeration propnames = sysprops.propertyNames(); 011 while (propnames.hasMoreElements()) { 012 String propname = (String)propnames.nextElement(); 013 System.out.println( 014 propname + "=" + System.getProperty(propname) 015 ); 016 } 017 } 018 } |
Listing1604.java |
In den vorangegangenen Kapiteln wurde schon häufig der Aufruf System.out.println verwendet, um Daten auf die Standardausgabe bzw. in ein Debug-Fenster auszugeben. Diese Anweisung ruft die Methode println des Objekts out der Klasse System auf. Dabei ist out eine statische Variable vom Typ PrintStream, die beim Starten des Programms so initialisiert wird, daß ihre Ausgabe auf die Standardausgabe geleitet wird.
Analog zu out gibt es die statischen Variablen err und in. Dabei dient err zur Ausgabe von Fehlermeldungen, und in ist ein Standardeingabekanal, der dazu verwendet werden kann, Eingaben von der Tastatur zu lesen.
Mit Hilfe der Methoden setIn, setOut und setErr ist es sogar möglich, die Standardein- und -ausgabe aus dem Programm heraus umzuleiten:
public static void setIn(InputStream in) public static void setOut(PrintStream out) public static void setErr(PrintStream err) |
java.lang.System |
Die Verwendung dieser Klassenvariablen ist im Grunde genommen nicht konform mit dem Dialogkonzept einer GUI-Anwendung. Ihr Einsatz kann aber immer dann sinnvoll sein, wenn Java-Programme geschrieben werden sollen, die keine ausgefeilte Oberfläche benötigen. In diesem Fall sind sie ein nützliches Hilfsmittel, um einfache Ein-/Ausgaben ohne großen Aufwand realisieren zu können. Bereits in Kapitel 3 wurde gezeigt, wie man diese Routinen zur Ein- und Ausgabe verwenden kann. Weitere Informationen darüber sind in Kapitel 19 zu finden, das sich mit Byte-Streams beschäftigt. |
|
Auch Aufrufe der Methode System.exit sind uns schon begegnet:
public static void exit(int status) |
java.lang.System |
Mit System.exit wird das laufende Programm beendet. Der Aufrufparameter status dient als Fehleranzeige und wird als Exitcode an den Aufrufer des Programms zurückgegeben. Gemäß Konvention zeigt dabei ein Wert größer oder gleich 1 einen Fehler während der Programmausführung an, während 0 ein fehlerfreies Programmende signalisiert.
public static void gc() |
java.lang.System |
Ein Aufruf der Methode gc führt einen expliziten Aufruf des Garbage Collectors durch. Dieser sucht dann nach freiem Speicher und gibt diesen an das Laufzeitsystem zurück. Normalerweise ist ein Aufruf dieser Methode nicht erforderlich, denn der Garbage Collector läuft ständig als niedrig priorisierter Thread im Hintergrund. Der Aufruf von gc ist immer dann sinnvoll, wenn eine explizite Kontrolle über den Zeitpunkt der Speicherfreigabe gewünscht ist.
Die Methode currentTimeMillis liefert die Anzahl der Millisekunden, die zum Zeitpunkt des Aufrufs seit Mitternacht des 1.1.1970 vergangen sind:
public static long currentTimeMillis() |
java.lang.System |
Ob dabei tatsächlich eine Auflösung von einer Millisekunde erreicht wird, ist von der konkreten Java-Implementierung abhängig. In PC-basierten Java-Systemen orientiert sie sich meist an der Auflösung des System-Timers. Dieser wird 18,2 mal pro Sekunde aufgerufen, so daß die Auflösung damit bei etwa 55 ms. liegt. |
|
Mit dem folgenden Beispielprogramm kann die Auflösung des System-Timers ermittelt werden:
001 /* Listing1605.java */ 002 003 public class Listing1605 004 { 005 public static void main(String[] args) 006 { 007 long t1, t2; 008 int actres, sumres = 0, i = 0; 009 while (true) { 010 ++i; 011 t1 = System.currentTimeMillis(); 012 while (true) { 013 t2 = System.currentTimeMillis(); 014 if (t2 != t1) { 015 actres = (int)(t2 - t1); 016 break; 017 } 018 } 019 sumres += actres; 020 System.out.print("it="+i+", "); 021 System.out.print("actres="+actres+" msec., "); 022 System.out.print("avgres="+(sumres/i)+" msec."); 023 System.out.println(""); 024 try { 025 Thread.sleep(500); 026 } catch (InterruptedException e) { 027 //nichts 028 } 029 } 030 } 031 } |
Listing1605.java |
Das Programm bestimmt zunächst die aktuelle Systemzeit und merkt sich den Wert in der Variablen t1. Nun wird die Systemzeit in einer Schleife erneut so oft gemessen, bis sie sich geändert hat. Die Differenz zwischen beiden Werten wird als Auflösung des aktuellen Durchgangs angesehen und der Variablen actres zugewiesen.
Um eine größere Genauigkeit zu erzielen, führt das Programm die Bestimmung der Auflösung mit Hilfe der äußeren Schleife viele Male durch. Die dabei jeweils ermittelte Auflösung wird in der Variablen sumres addiert und durch Division durch die Anzahl der Schleifendurchläufe zur Ermittlung des gleitenden Durchschnitts verwendet. Das so errechnete Ergebnis pendelt sich tatsächlich bereits nach wenigen Durchläufen auf den vorausgesagten Wert von 55 ms. ein: |
|
it=1, actres=60 msec., avgres=60 msec. it=2, actres=50 msec., avgres=55 msec. it=3, actres=50 msec., avgres=53 msec. it=4, actres=50 msec., avgres=52 msec. it=5, actres=50 msec., avgres=52 msec. it=6, actres=50 msec., avgres=51 msec. ... it=65, actres=50 msec., avgres=55 msec. it=66, actres=50 msec., avgres=55 msec. it=67, actres=50 msec., avgres=55 msec. it=68, actres=60 msec., avgres=55 msec. it=69, actres=60 msec., avgres=55 msec. it=70, actres=60 msec., avgres=55 msec.
Interessanterweise bietet die Methode sleep der Klasse Thread (sie wird in Abschnitt 22.2.4 beschrieben) auf aktuellen SUN-JDKs unter Windows mit etwa 1 ms. eine wesentlich höhere Auflösung als currentTimeMillis. Diese Eigenschaft ist allerdings nicht dokumentiert und kann von Interpreter zu Interpreter sehr unterschiedlich sein. Selbst auf ein und derselben Java-Maschine kann es durch unterschiedliche Lastsituationen zu Abweichungen kommen. Das folgende Programm ermittelt die Auflösung von sleep: |
|
001 /* Listing1606.java */ 002 003 public class Listing1606 004 { 005 public static long testSleep(int millis) 006 { 007 final int MINDURATION = 3000; 008 int cnt = (millis >= MINDURATION ? 1 : MINDURATION/millis); 009 long start = System.currentTimeMillis(); 010 for (int i = 0; i < cnt; ++i) { 011 try { 012 Thread.sleep(millis); 013 } catch (InterruptedException e) { 014 } 015 } 016 long end = System.currentTimeMillis(); 017 return (end - start) / cnt; 018 } 019 020 public static void main(String[] args) 021 { 022 final int DATA[] = {345, 27, 1, 1962, 2, 8111, 6, 89, 864}; 023 for (int i = 0; i < DATA.length; ++i) { 024 System.out.println("Aufruf von sleep(" + DATA[i] + ")"); 025 long result = testSleep(DATA[i]); 026 System.out.print(" Ergebnis: " + result); 027 double prec = ((double)result / DATA[i] - 1.0) * 100; 028 System.out.println(" (" + (prec > 0 ? "+": "") + prec + " %)"); 029 } 030 } 031 } |
Listing1606.java |
Ein Aufruf unter dem JDK 1.4 auf einem Windows-98-System im Leerlauf
ergab folgendes Ergebnis:
Aufruf von sleep(345)
Ergebnis: 343 (-0.5797101449275366 %)
Aufruf von sleep(27)
Ergebnis: 27 (0.0 %)
Aufruf von sleep(1)
Ergebnis: 1 (0.0 %)
Aufruf von sleep(1962)
Ergebnis: 1920 (-2.1406727828746197 %)
Aufruf von sleep(2)
Ergebnis: 2 (0.0 %)
Aufruf von sleep(8111)
Ergebnis: 8080 (-0.3821970163974897 %)
Aufruf von sleep(6)
Ergebnis: 6 (0.0 %)
Aufruf von sleep(89)
Ergebnis: 89 (0.0 %)
Aufruf von sleep(864)
Ergebnis: 860 (-0.462962962962965 %)
Als letzte Methode der Klasse System soll arraycopy vorgestellt werden:
public static native void arraycopy( Object src, int src_position, Object dst, int dst_position, int length ) |
java.lang.System |
arraycopy kann dazu verwendet werden, Arrays oder Teile davon zu kopieren. Dabei können die Elemente sowohl innerhalb desselben Arrays als auch in ein anderes Array kopiert werden. Falls innerhalb desselben Arrays kopiert wird, dürfen sich Quell- und Zielbereich auch überlappen. Die Methode arbeitet sowohl mit elementaren als auch mit Objekttypen, Ziel- und Quellarray müssen lediglich zuweisungskompatibel sein. Da die Methode in C bzw. Assembler implementiert ist, arbeitet sie recht performant.
Als erste Argumente werden das Quellarray src und die Startposition src_position angegeben. Anschließend folgen das Zielarray dst und die Zielposition dst_position. Als letztes Argument wird die Länge length des zu kopierenden Bereichs angegeben. Falls die Argumente auf Elemente zeigen, die außerhalb des Arrays liegen, wird eine Ausnahme des Typs ArrayIndexOutOfBoundsException ausgelöst. Falls ein Element aufgrund eines Typfehlers nicht gespeichert werden kann, gibt es eine Ausnahme des Typs ArrayStoreException.
Das folgende Programm zeigt die Verwendung von arraycopy an einem einfachen Beispiel:
001 /* Listing1607.java */ 002 003 public class Listing1607 004 { 005 public static void main(String[] args) 006 { 007 int[] ar = {0,0,0,0,0,0,0,0,0,0}; 008 009 for (int i = 0; i < 10; ++i) { 010 System.arraycopy(ar,0,ar,1,9); 011 ar[0] = i; 012 } 013 System.out.print("ar = "); 014 for (int i = 0; i < 10; ++i) { 015 System.out.print(ar[i] + " "); 016 } 017 System.out.println(""); 018 } 019 } |
Listing1607.java |
Das Programm füllt ein 10-elementiges Array von Ganzzahlen, das
zunächst nur Nullen enthält, mit den Zahlen 0 bis 9. Dabei
wird jeweils durch Kopieren der ersten neun Elemente an die zweite
Position des Arrays an der ersten Position Platz gemacht, um dort
den Inhalt des fortlaufenden Schleifenzählers abzulegen. Nach
10 Durchläufen stehen somit die Zahlen 0 bis 9 verkehrt herum
im Array. Die Ausgabe des Programms ist:
ar = 9 8 7 6 5 4 3 2 1 0
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0.1 |
<< | < | > | >> | API | © 1998-2003 Guido Krüger, http://www.javabuch.de |