3.7 Die Properties-Klasse
Die Klasse Properties ist eine Sonderform der Assoziativspeicher, bei der Schlüssel-Werte-Paare immer vom Typ String sind. Da sich die Einträge in einer Datei speichern und wieder auslesen lassen, können auf diese Weise fest verdrahtete Zeichenketten aus dem Programmtext externalisiert werden, sodass sich die Werte auch ohne Neuübersetzung bequem verändern lassen.
3.7.1 Properties setzen und lesen
Die Methode setProperty(String, String) fügt dem Properties-Objekt ein Schlüssel-Werte-Paar hinzu. Um später wieder an den Wert zu kommen, wird getProperty(String) 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
3.7.2 Properties verketten
Properties-Objekte lassen sich hierarchisch verbinden, sodass im Fall einer erfolglosen Suche nach einem Schlüssel das Properties-Objekt die Anfrage an ein übergeordnetes Properties-Objekt weiterleitet; das Eltern-Properties-Objekt wird einfach im Konstruktor übergeben:
Listing 3.21: com/tutego/insel/util/map/PropertiesDemo.java. main()
Properties defaultProperties = new Properties(),
userProperties = new Properties( defaultProperties );
Die Zeilen erzeugen zwei Properties-Objekte. Obwohl am Anfang beide leer sind, werden doch die in defaultProperties hinzugefügten Einträge auch in userProperties sichtbar sein. Im Folgenden ist abzulesen, wie userProperties einen Eintrag überschreibt:
defaultProperties.setProperty( "User", "C.Ullenboom" );
defaultProperties.setProperty( "Password", "(nicht gesetzt)" );
userProperties.setProperty( "Password", "SagIchNet" );
Zuerst durchsucht ein Property-Exemplar die eigene Datenstruktur. Liefert diese Property 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. Ein list() auf die defaultProperties beziehungsweise userProperties ergibt folgende Ausgabe:
-- listing properties --
Password=(nicht gesetzt)
User=C.Ullenboom
-- listing properties --
Password=SagIchNet
User=C.Ullenboom
Zusammenfassung der Methoden
class java.util.Properties |
- 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 übergeordnete 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. - void Enumeration<?> propertyNames()
Liefert eine Enumeration aller Schlüssel in der Properties-Liste inklusive der Standardwerte aus übergeordneten Properties.
3.7.3 Hierarchische Eigenschaften
Leider kann eine Eigenschaften-Datei nicht segmentiert werden, wie etwa alte Windows-INI-Dateien dies machen. Die Alternative besteht darin, hierarchisch benannte Eigenschaften zu erzeugen, indem eine Zeichenkette vor jeden 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 nach dem Speichern durcheinandergewürfelt in der Datei auf, weil der Assoziativspeicher keine Sortierung besitzt (Properties basiert nicht auf TreeMap).
3.7.4 Eigenschaften auf der Konsole 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 trennt ein Gleichheitszeichen. Die Ausgabe über list() ist gekürzt, denn ist ein Wert länger als 40 Zeichen, 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 |
- void list(PrintStream out)
Listet die Properties auf dem PrintStream aus. - void list(PrintWriter out)
Listet die Properties auf dem PrintWriter aus.
3.7.5 Properties laden und speichern
Während die list()-Methode nur für Testausgaben gedacht ist, dient store() zum Speichern und load() zum Laden eines Properties-Objekts 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 macht store() die Daten persistent, load() liest sie wieder, und list() gibt die Eigenschaften auf dem Bildschirm aus:
Listing 3.22: com/tutego/insel/util/map/SaveProperties.java, main()
Writer writer = null;
Reader reader = null;
try
{
writer = new FileWriter( "properties.txt" );
Properties prop1 = new Properties( System.getProperties() );
prop1.setProperty( "MeinNameIst", "Forrest Gump" );
prop1.store( writer, "Eine Insel mit zwei Bergen" );
reader = new FileReader( "properties.txt" );
Properties prop2 = new Properties();
prop2.load( reader );
prop2.list( System.out );
}
catch ( IOException e )
{
e.printStackTrace();
}
finally
{
try { if ( writer != null ) writer.close(); }
catch ( IOException e ) { e.printStackTrace(); }
try { if ( reader != null ) reader.close(); }
catch ( IOException e ) { e.printStackTrace(); }
}
Properties im XML-Format speichern
Die Properties-Klasse kann die Eigenschaften im XML-Format speichern und laden. Zum Speichern dient die Methode storeXML(), und zum Laden dient loadFromXML(). Die XML-Dateien haben ein spezielles Format, wie es der Einzeiler 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="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 loadFromXML() 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, macht das auch storeToXML(). Die Methode ist mit einem zusätzlichen Parameter überladen, der eine XML-Kodierung erlaubt. Ist der Wert nicht gesetzt, so ist die Standardkodierung UTF-8.
class java.util.Properties |
- void store(OutputStream out, String header)
Speichert die Properties-Liste mithilfe des Ausgabestroms ab. Am Kopf der Datei wird eine Kennung geschrieben, die im zweiten Argument steht. Die Kennung darf null sein. - void load(InputStream inStream)
Lädt eine Properties-Liste aus einem Eingabestrom. - void storeToXML(OutputStream os, String comment, String encoding) throws IOException
Speichert die Properties im XML-Format. comment kann null sein, wenn ein Kommentar erwünschst ist. encoding steht für die Zeichenkodierung, etwa »Latin-1« oder »UTF-8«. - void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException
Liest Properites im XML-Format von einem Eingabestrom ein.
3.7.6 Klassenbeziehungen: Properties und Hashtable *
Die Properties-Klasse ist eine Erweiterung von Hashtable, weil 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 ergibt 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 von Stack als Unterklasse von Vector. So ist ein Augenmerk auf die put()-Methode 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 sprechen zwei Dinge dagegen: zum einen, dass wir beim get() aus einem Hashtable-Objekt immer ein Object-Objekt bekommen und daher meistens eine Typanpassung benötigen; und zum anderen durchsucht diese Methode lediglich den Inhalt des angesprochenen Properties-Exemplars. getProperties() arbeitet da etwas anders. Nicht nur ist der Rückgabewert automatisch ein String, sondern getProperties() durchsucht auch übergeordnete Properties-Objekte mit, die zum Beispiel Standardwerte speichern.
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.