11.7 Die Properties-Klasse
 
Die Klasse Properties ist eine Sonderform der Assoziativspeicher, die Schlüssel/Wertepaare aus Strings verwaltet. Sie können später in einer Datei gespeichert und wieder ausgelesen werden. Auf diese Weise lassen sich zum Beispiel Zeichenketten aus dem Programmtext herausziehen und externalisieren, so dass auch ohne Neuübersetzung die Werte bequem verändert werden können.
11.7.1 Properties setzen und lesen
 
Um ein Properties-Objekt zu füllen, wird die Funktion setProperty() eingesetzt. Die Argumente sind zwei Zeichenketten, der Schlüssel und der Wert. Um später wieder an den Schlüssel zu kommen, wird getProperty() mit dem Schlüssel aufgerufen, und liefert dann – wenn beide Zeichenketten vorher verbunden wurden – den Wert.
Properties props = new Properties(),
props.setProperty( "User", "King Karl" );
props.setProperty( "Version", "" + 0.02 );
System.out.println( props.getProperty("User") ); // King Karl
System.out.println( props.getProperty("Passwort") ); // null
11.7.2 Properties verketten
 
Wenn ein Properties-Objekt einen Schlüssel nicht findet, dann kann es sich an ein anderes Properties-Objekt wenden, so dass eine Liste von Quellen entsteht. Das Eltern-Properties-Objekt, wird einfach im Konstruktor übergeben.
Beispiel Das nachfolgende Programm erzeugt zwei Properties-Objekte. Eine davon ist eine Standardliste und die andere soll eine benutzerdefinierte sein, die anfänglich alle Einstellungen von der Standardliste übernimmt.
|
Listing 11.5
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 die Weiterleitung 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 Hash-Tabelle. 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. Über list() 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’
class java.util. Properties
extends Hashtable<Object,Object>
|
|
Properties()
Erzeugt ein leeres Properties-Objekt ohne Schlüssel und Werte. |
|
Properties( Properties defaults )
Erzeugt ein leeres Properties-Objekt, das bei Anfragen auch auf die Einträge in dem übergebenen Properties-Objekt zurückgreift. |
|
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. |
|
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. |
|
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. |
11.7.3 Eigenschaften ausgeben
 
Die list()-Methode wandert durch die Daten eines Properties-Exemplars und schreibt sie in einen PrintStream oder PrintWriter. 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). Schlüssel und Werte werden von einem Gleichheitszeichen getrennt. Die Ausgabe über list() ist gekürzt, denn ist ein Wert länger als 40 Zeichen, so wird er abgekürzt. Den Paaren geht eine Kopfzeile der Art -- listing properties -- voran. Es ist wichtig zu verstehen, dass durch die Art der Speicherung (ein Assoziativspeicher auf Basis des Hashings) die Ausgabe unsortiert erfolgt.
class java.util. Properties
extends Hashtable<Object,Object>
|
|
void list( PrintStream out )
Listet die Properties auf dem PrintStream aus. |
|
void list( PrintWriter out )
Listet die Properties auf dem PrintWriter aus. |
11.7.4 Hierarchische Eigenschaften
 
Leider kann eine Eigenschaften-Datei nicht segmentiert werden, wie etwa alte Windows INI-Dateien dies machen. Die Alternative ist, hierarchisch benannte Eigenschaften erzeugen, in dem eine Zeichenkette vor jedem Schlüssel gesetzt wird. Um zum Beispiel einen Schlüssel User einmal unter Private und einmal unter Public zu halten, lässt sich die Eigenschaft Private.User und Public.User einsetzen. Doch leider tauchen sie dann gespeichert quer gewürfelt in der Datei auf, da ein Assoziativspeicher keine Sortierung besitzt.
11.7.5 Properties speichern
 
Während die list()-Funktion nur für Testausgaben gedacht ist, dient store() zum Speichern und load() zum Laden eines Properties-Objektes in einer ASCII-Datei, die Schlüssel und Werte mit einem Gleichheitszeichen trennt.
Das folgende Beispiel initialisiert ein Properties-Objekt mit den Systemeigenschaften und fügt dann einen Wert hinzu. Anschließend werden die Daten persistent gemacht, wieder gelesen und mit der list()-Methode auf dem Bildschirm ausgeben.
Listing 11.6
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<Object,Object>
|
|
void load( InputStream inStream )
Lädt eine Properties-Liste aus einem Eingabestrom. |
|
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. |
|
void Enumeration<?> propertyNames()
Liefert eine Enumeration von allen Schlüsseln in der Property-Liste inklusive der Standardwerte aus nachgeschalteten Properties. |
Properties im XML-Format speichern
Seit Java 5 können die Properties auch im XML-Format gespeichert und geladen werden. Zum Speichern dient die Funktion storeXML() und zum Laden loadFromXML(). Die XML-Dateien haben ein spezielles Format, wie es ein Einzeiler wie System.getProperties().storeToXML(System.out, ""); zeigt.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment></comment>
<entry key="java.runtime.name">Java(TM) 2 Runtime Environment, Standard Edition
</entry>
<entry key="sun.boot.library.path">C:\Programme\j2sdk1.5.0\jre\bin</entry>
<entry key="java.vm.version">1.5.0-beta-b32c</entry>
<entry key="java.vm.vendor">Sun Microsystems Inc.</entry>
<entry key="java.vendor.url">http://java.sun.com/</entry>
<entry key="path.separator">;</entry>
...
<entry key="sun.desktop">windows</entry>
<entry key="sun.cpu.isalist">pentium i486 i386</entry>
</properties>
Die Methode loadFormXML() liest aus einem InputStream und löst im Fall eines fehlerhaften Dateiformats eine InvalidPropertiesFormatException aus. Beim Speichern kann so ein Fehler natürlich nicht auftreten. Und genauso wie bei store() ein OutputStream mit einem Kommentar gespeichert wird, so tut das auch storeToXML(). Die Funktion ist mit einem zusätzlichen Parameter überladen, der eine XML-Kodierung erlaubt. Ist der Wert nicht gesetzt, so ist die Standardkodierung UTF-8.
11.7.6 Über die Beziehung Properties und Hashtable
 
Die Properties-Klasse ist eine Erweiterung von Hashtable, da die Speicherung der Einstellungsdaten in dieser Datenstruktur erfolgt. Ein Properties-Objekt erweitert die Hash-Tabelle um die Möglichkeit, die Schlüssel-Werte-Paare in einem festgelegten Format aus einer Textdatei beziehungsweise einem Datenstrom zu laden und wieder zu speichern. Doch gerade weil Hashtable erweitert wird, sind auch alle Methoden der Klasse Hashtable auf ein Properties-Objekt anwendbar. Das macht nicht für alle Methoden Sinn und ist auch nicht in jedem Fall problemlos. Dass Properties eine Unterklasse von Hashtable ist, ist ähnlich fragwürdig wie die Vererbungsbeziehung Stack als Unterklasse von Vector. So ist ein 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 auch nicht verwenden, da es über sie möglich ist, Objekte einzufügen, die nicht vom Typ String sind. 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 eine Typanpassung benötigen, 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 nachgeschaltete Properties-Objekte mit durchsucht werden, die zum Beispiel Standardwerte speichern.
|