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


Java ist auch eine Insel (2. Aufl.) von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 1.4
Java ist auch eine Insel (2. Auflage)
gp Kapitel 11 Datenstrukturen und Algorithmen
  gp 11.1 Mit einem Iterator durch die Daten wandern
  gp 11.2 Dynamische Datenstrukturen
  gp 11.3 Die Klasse Vector
    gp 11.3.1 Vektoren erzeugen
    gp 11.3.2 Funktionen
    gp 11.3.3 Arbeitsweise des internen Arrays
    gp 11.3.4 Die Größe eines Felds
    gp 11.3.5 Eine Aufzählung und gleichzeitiges Verändern
  gp 11.4 Stack, der Stapel
    gp 11.4.1 Die Methoden von Stack
    gp 11.4.2 Ein Stack ist ein Vektor – aha!
  gp 11.5 Die Klasse Hashtable und assoziative Speicher
    gp 11.5.1 Ein Objekt der Klasse Hashtable erzeugen
    gp 11.5.2 Einfügen und Abfragen der Datenstruktur
    gp 11.5.3 Die Arbeitsweise einer Hashtabelle
    gp 11.5.4 Aufzählen der Elemente
    gp 11.5.5 Ausgabe der Hashtabelle und Gleichheitstest
    gp 11.5.6 Klonen
  gp 11.6 Die abstrakte Klasse Dictionary
    gp 11.6.1 Zugriff und Abfrage
    gp 11.6.2 Metainformationen
    gp 11.6.3 Iterationen über die Elemente
  gp 11.7 Die Properties-Klasse
    gp 11.7.1 Über die Klasse Properties
    gp 11.7.2 put(), get() und getProperties()
    gp 11.7.3 Eigenschaften ausgeben
    gp 11.7.4 Systemeigenschaften der Java-Umgebung
    gp 11.7.5 Browser-Version abfragen
    gp 11.7.6 Properties von der Konsole aus setzen
  gp 11.8 Windows-typische INI-Dateien
  gp 11.9 Queue, die Schlange
  gp 11.10 Die Collection-API
    gp 11.10.1 Die Schnittstelle Collection
    gp 11.10.2 Schnittstellen, die Collection erweitern, und Map
    gp 11.10.3 Abstrakte Basisklassen für Container
    gp 11.10.4 Konkrete Container-Klassen
    gp 11.10.5 Unterschiede zu den älteren Datenstrukturen und die Synchronisation
    gp 11.10.6 Das erste Programm mit Container-Klassen
    gp 11.10.7 Iteratoren
    gp 11.10.8 Der Comparator
    gp 11.10.9 toArray() von Collection verstehen – Chance für eine Falle erkennen
  gp 11.11 Listen
    gp 11.11.1 AbstractList
    gp 11.11.2 Optionale Methoden
    gp 11.11.3 ArrayList
    gp 11.11.4 LinkedList
  gp 11.12 Algorithmen
    gp 11.12.1 Datenmanipulation
    gp 11.12.2 Größten und kleinsten Wert einer Collection finden
    gp 11.12.3 Sortieren
    gp 11.12.4 Elemente in der Collection suchen
  gp 11.13 Typsichere Datenstrukturen
  gp 11.14 Die Klasse BitSet für Bitmengen
    gp 11.14.1 Ein BitSet anlegen und füllen
    gp 11.14.2 Mengenorientierte Operationen
    gp 11.14.3 Funktionsübersicht
    gp 11.14.4 Primzahlen in einem BitSet verwalten
  gp 11.15 Ein Design-Pattern durch Beobachten von Änderungen
    gp 11.15.1 Design-Pattern
    gp 11.15.2 Das Beobachter-Pattern (Observer/Observable)


Galileo Computing

11.7 Die Properties-Klasse  downtop

Unter älteren Windows-Versionen gibt es INI-Dateien, in denen ebenfalls Datenpaare aus Schlüssel und Wert abgelegt sind. Für die Windows-Umgebung ist beispielsweise win.ini eine wichtige Datei, in der Systemeinstellungen gesichert sind.

Beispiel   Ein Ausschnitt aus der Datei win.ini
[Desktop]
Wallpaper=(None)
TileWallpaper=1
WallpaperStyle=0
Pattern=(None)
...

[colors]
Scrollbar=224 224 224
Background=0 128 128
ActiveTitle=0 0 128

Es finden sich in der Datei mit einem eindeutigen Schlüsselwort verbundene Zeichenketten. Die INI-Datei kann einfach mit einem beliebigen ASCII-Editor angepasst werden und ist somit leicht für Änderungen zugänglich. In Java gibt es einen ähnlichen Mechanismus. Die Klasse Properties erlaubt, solche Wertepaare aufzubauen und dadurch die Daten aus dem Programmtext herauszuziehen und in eine Art INI-Datei zu legen, die eine Änderung der Werte bequem ohne Neukompilierung möglich macht. Leider kann eine Eigenschaften-Datei nicht segmentiert werden. Hierarchisch benannte Properties wie Desktop. Wallpaper sind eine interessante Alternative, doch tauchen sie quer gewürfelt in der Datei auf.


Galileo Computing

11.7.1 Über die Klasse Properties  downtop

Die Properties-Klasse ist eine Erweiterung von Hashtable, da die Speicherung der Einstellungsdaten in dieser Datenstruktur erfolgt. Ein Properties-Objekt erweitert die Hashtabelle um die Möglichkeit, die Schlüssel-Wert-Paare in einem festgelegten Format aus einer Textdatei beziehungsweise einem Datenstrom zu laden und wieder zu speichern. Außerdem kommt noch eine Hierarchie von Hashtabellen hinzu; jedes Properties-Exemplar hat ein Eltern-Properties-Objekt, das gegebenenfalls automatisch durchsucht wird. Und gerade weil Hashtable erweitert wird, sind auch alle Methoden der Klasse Hashtable auf ein Properties-Objekt anwendbar.

Beispiel   Das nachfolgende Programm erzeugt zwei Properties-Objekte. Eine davon ist eine Standard-Liste und die andere soll eine benutzerdefinierte sein, die anfänglich alle Einstellungen von der Standard-Liste übernimmt. Dieses Übernehmen ist durch einen speziellen Konstruktor möglich, denn beim Erzeugen der benutzerdefinierten Liste wird eine andere Liste einfach im Konstruktor übergeben.

Listing 11.4   TestProperties.java

import java.util.Properties;

class TestProperties
{
  public static void main( String args[] )
  {
    Properties defaultProperties = new Properties(),
              userProperties = new Properties(defaultProperties);

    defaultProperties.setProperty( "User", "Standard" );
    defaultProperties.setProperty( "Version", "" + 0.02f );
    userProperties.setProperty( "User", "Ulli Schnulli" );
    userProperties.setProperty( "MagCleverUndSmart", "" + true );

    System.out.println( "Default Properties:" );
    defaultProperties.list( System.out );

    System.out.println( "\nUser Properties:" );
    userProperties.list( System.out );

    System.out.println( "Property 'User' is '" +
                        userProperties.getProperty("User")+"'");
    }
}

Testen wir das Programm, so fällt ein Zusammenhang besonders auf: Obwohl wir zunächst das Default- und User-Objekt erzeugen, werden die später zum Default-Objekt hinzugefügten Daten an das User-Objekt weitergereicht. Nachträglich dem Default-Objekt zugeordnete Werte kommen also auch zum Aufrufer zurück. Die Implementierung zeigt: Zuerst durchsucht ein Property-Exemplar die eigene, in ihm enthaltene Hashtabelle. Liefert diese keinen Eintrag oder keinen Wert vom Typ String, so wird das im Konstruktoraufruf angegebene Property-Objekt durchsucht. Auf diese Weise lassen sich mehrstufige Hierarchien von Property-Verzeichnissen konstruieren. Damit ergibt sich die Ausgabe:

Default Properties:
-- listing properties --
Version=0.02
User=Standard

User Properties:
-- listing properties --
MagCleverUndSmart=true
Version=0.02
User=Ulli Schnulli
Property 'User' is 'Ulli Schnulli'

Galileo Computing

11.7.2 put(), get() und getProperties()  downtop

Ein weiterer Augenmerk ist auf die put()-Funktion zu legen. Sie gibt es in der Klasse Properties nicht, denn put() wird von Hashtable geerbt. Wir sollten sie jedoch nicht verwenden, da es über sie möglich ist, Objekte einzufügen, die nicht vom Typ String sind. Properties von Hashtable abzuleiten ist übrigens ein genauso fragwürdiges Beispiel für den Einsatz von Vererbung (wie Stack als Unterklasse von Vector).

Das gleiche Argument könnte für get() gelten, doch zwei Dinge sprechen dagegen. Zunächst einmal, dass wir beim get() aus einem Hashtable-Objekt immer ein Object-Objekt bekommen, und daher meistens einen Cast brauchen, und zweitens durchsucht diese Methode lediglich den Inhalt des angesprochenen Properties-Exemplars. getProperties() arbeitet da etwas anders. Nicht nur, dass der Rückgabewert automatisch ein String ist, sondern auch, dass nachgeschachtelte Properties-Objekte mit durchsucht werden, die zum Beispiel Standardwerte speichern.

class java.util.Properties
extends Hashtable

gp  Properties()
Erzeugt ein leeres Properties-Objekt ohne Schlüssel und Werte.
gp  Properties( Properties defaults )
Erzeugt ein leeres Properties-Objekt, das bei Anfragen auch auf die Einträge in dem übergebenen Properties-Objekt zurückgreift.
gp  String getProperty( String key )
Sucht in den Properties nach der Zeichenkette key als Schlüssel und liefert den zugehörigen Wert. Durchsucht auch nachgeschaltete Properties-Objekte.
gp  String getProperty( String key, String default )
Sucht in den Properties nach der Zeichenkette key als Schlüssel und liefert den zugehörigen Wert. Ist der Schlüssel nicht vorhanden, wird der String default zurückgegeben.
gp  Object setProperty( String key, String value )
Trägt Schlüssel und Wert im Properties-Exemplar ein. Existiert der Schlüssel schon, wird er überschrieben. Mitunter verdeckt der Schlüssel den Wert der Property in der übergeordneten Property.

Galileo Computing

11.7.3 Eigenschaften ausgeben  downtop

Eine kleine Hilfe zum Testen bietet die Bibliothek durch eine list()-Methode. Sie wandert einfach durch die Daten eines Properties-Exemplars und gibt sie auf einem PrintStream oder PrintWriter aus. Das sind Datenströme, denen wir uns näher im Eingabe- und Ausgabekapitel widmen wollen. Eine Ausgabe auf dem Bildschirm erhalten wir mit list(System.out).

Die Paare folgen einer Kopfzeile

-- listing properties --

und haben die Form:

Schlüssel=Wert

Umfasst der Wert mehr als 40 Zeichen, wird die Ausgabe mit »...« abgekürzt.

class java.util.Properties
extends Hashtable

gp  void list( PrintStream out )
Listet die Properties auf dem PrintStream aus.
gp  void list( PrintWriter out )
Listet die Propierties auf dem PrintWriter aus.

Galileo Computing

11.7.4 Systemeigenschaften der Java-Umgebung  downtop

In einem weiteren Beispiel erzeugen wir ein Properties-Objekt, welches direkt die System-Java-Properties widerspiegelt. Nach dem Hinzufügen eines Eintrags schreiben wir diese Werte in eine Datei, lesen sie wieder und geben sie mit der list()-Methode aus. Eine umfangreiche Liste von Systemeigenschaften bietet die Webseite http://www.tolstoy.com/samizdat/sysprops.html.

Listing 11.5   SaveProperties.java

import java.io.*;
import java.util.*;

class SaveProperties
{
  public static void main( String args[] )
  {
   String filename = "properties.txt";
    try
    {
      FileOutputStream propOutFile =
         new FileOutputStream( filename );

      Properties p1 = new Properties( System.getProperties() );

      p1.setProperty( "MeinNameIst", "Forest Gump" );
      p1.store( propOutFile, "Eine Insel mit zwei Bergen" );

      FileInputStream propInFile =
        new FileInputStream( filename );

      Properties p2 = new Properties();
      p2.load( propInFile );

      p2.list( System.out );
     }
     catch ( FileNotFoundException e ) {
       System.err.println( "Can't find " + filename );
     }
     catch ( IOException e ) {
       System.err.println( "I/O failed." );
     }
  }
}
class java.util.Properties
extends Hashtable

gp  void load( InputStream inStream )
Lädt eine Properties-Liste aus einem Eingabestrom.
gp  void store( OutputStream out, String header )
Speichert die Properties-Liste mit Hilfe des Ausgabestroms ab. Am Kopf der Datei wird eine Kennung geschrieben, die im zweiten Argument steht. Die Kennung darf auch null sein.
gp  void Enumeration propertyNames()
Liefert eine Enumeration von allen Schlüsseln in der Property-Liste inklusive der Standardwerte aus nachgeschalteten Properties.

Galileo Computing

11.7.5 Browser-Version abfragen  downtop

Eine besondere Eigenschaft bezeichnet die Zeichenkette browser.version. Ob es sich bei dem Browser um einen Internet Explorer oder um einen Netscape Communicator handelt, können wir aus der Variablen java.vendor auslesen.

String prop = System.getProperty( "java.vendor" );
boolean ie = prop.indexOf("Microsoft Corp.") >= 0;

Bei der Anweisung System.getProperty(key) ist das Properties-Objekt gut versteckt. In einer etwas längeren Schreibweise ist das etwa:

Properties prop = System.getProperties();
String s = prop.getProperty( "property" );

Galileo Computing

11.7.6 Properties von der Konsole aus setzen  toptop

Eigenschaften lassen sich auch bei Programmstart von der Konsole aus setzen. Dies ist praktisch für eine Eigenschaft, die beispielsweise das Verhalten des Programms steuert oder das Programm konfiguriert. In der Kommandozeile werden mit »-D« der Name der Eigenschaft und ihr Wert angegeben. Viele Entwicklungsumgebungen erlauben es, diese in einem Fenster zu setzen. Die Informationen tauchen nicht bei der Argument-Liste in der main()-Methode auf, da sie vor dem Namen der Klasse stehen und bereits von der Java-Laufzeitumgebung verarbeitet werden.

Um die Eigenschaften auszulesen, gibt es zwei Möglichkeiten. Eine davon ist überraschend.

Listing 11.6   SetProperty.java

class SetProperty
{
  static public void main( String args[] )
  {
    boolean debug = false;

    String prop = System.getProperty( "DEBUG" );

    // Erster Weg

    if ( prop != null )
      debug = Boolean.valueOf(prop).booleanValue();

     if ( debug )
       System.out.println( "Wir dürfen debuggen" );

    // Zweiter Weg

    System.out.println( Boolean.getBoolean("DEBUG") );
 }
}

Auf der Konsole folgt die Ausgabe

$ java -DDEBUG=true SetProperty
Wir dürfen debuggen
true

Wir bekommen über getProperty() einen String zurück, der den Wert anzeigt. Falls es überhaupt keine Eigenschaft mit diesem Namen gibt, erhalten wir stattdessen null. So wissen wir auch, ob dieser Wert überhaupt gesetzt wurde. Im Fall von Wahrheitswerten gibt es die Sonderfunktion getBoolean() in der Klasse Boolean, die aus der Eigenschaften-Liste der Klasse System eine Eigenschaft mit dem angegebenen Namen heraussucht. Es erstaunt, diese Funktion in der Wrapper-Klasse Boolean anzutreffen. Der einzige Vorteil gegenüber der Lösung mit der valueOf()-Methode ist, dass die Groß-/Kleinschreibung beim Wert keine Rolle spielt. Dies erkaufen wir mit dem Nachteil, dass wir bei einem false nicht unterscheiden können, ob es die Eigenschaft nicht gibt oder ob sie mit dem Wert false belegt ist.





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