Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 16 - Utility-Klassen I

## 16.3 Die Klasse System

- 16.3 Die Klasse System
  - 16.3.1 System-Properties
  - 16.3.2 in, err und out
  - 16.3.3 exit
  - 16.3.4 gc
  - 16.3.5 currentTimeMillis
  - 16.3.6 arraycopy

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.

### 16.3.1 System-Properties

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

Listing 16.4: Ausgeben der System-Properties

### 16.3.2 in, err und out

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

### 16.3.3 exit

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.

### 16.3.4 gc

    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.

### 16.3.5 currentTimeMillis

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

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

Listing 16.5: Die Auflösung des System-Timers bestimmen

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

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

    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

Listing 16.6: Die Auflösung von Thread.sleep bestimmen

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 %)

### 16.3.6 arraycopy

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

Listing 16.7: Verwendung von System.arraycopy

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
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de