The Preferences Class
by: Thomas Paul
Whenever we needed to store user preferences (such as the four most recently used files) we could use the Properties
class. The problem with the Properties class is that it makes assumptions about where user preferences should be
stored. The basic assumption is that the file system would be used to store this information but this raises several
issues. Where in the file system should this data be stored? In the application directory, perhaps? What if the
user did not have write access to that directory? What if the user was running on a network computer?
The Preferences API, which is new to Java 1.4, was written to solve these problems. The purpose of the API is to
allow for the easy storage of user preferences without the developer having to worry about where this information
will be stored. The API determines the appropriate place. For example, when running an application in Windows,
the preferences will be stored in the Windows registry.
The API itself is found in the java.util.prefs package and is very easy to use. All preferences are stored in a
tree-like structure with the data itself stored in nodes of the tree. The nodes contain name-value pairs with the
actual preference data. The API allows for two different types of preference data. System data is shared among
all users while User data is used by the current user only. In this way, multiple users on the same device can
have their own preferences.
Getting a Preferences Object
Before you do anything with preferences you must get a Preferences object. Preferences is an abstract class so
it can't be instantiated directly. Instead you have to run one of the static methods of the Preferences class that
will return a real Preferences object. There are two ways to do this. The first method is to manually define a
tree structure. The second is to allow your package structure to define the tree structure. The second options
should not be used if your class is not in a package since the node will be "unnamed" and shared by every
class not in a package.
In order to manually define a tree structure, we need to first get a Preferences object. The Preferences class
has a static method to get either the System or User root preferences object:
Preferences prefs = Preferences.userRoot();
Preferences prefs = Preferences.systemRoot();
|
Once we have our root Preferences object, we can then get our node Preferences object by specifying the node we
wish to use. We can combine the two requests into one statement:
Preferences prefs = Preferences.userRoot().node("com/javaranch/prefs");
Preferences prefs = Preferences.systemRoot().node("com/javaranch/prefs");
|
The second option is to use a Preferences method which will use the name of the package that a class is in to determine
the nodes of the tree.
Preferences prefs = Preferences.userNodeForPackage(PrefsTest.class);
Preferences prefs = Preferences.systemNodeForPackage(PrefsTest.class);
|
Note: The beta version of the Preferences API allowed you to specify an object (such as "this") as a
parameter but the final version of the API requires that a class object be specified.
The node created by this command will be determined by the PrefsTest package name. If PrefsTest is in package "com/tom/test",
then the node created will be the same as if we had specified:
Preferences.userRoot().node("com/tom/test");
|
Now that we have a Preferences object, we can use it to store and retrieve user preference information.
Retrieving and Storing Preferences Data
The Preferences class has several methods used to get and put data in the Preferences data store. You can use
only the following types of data:
- String
- boolean
- double
- float
- int
- long
- byte array
There is a get and put method for each type. The get format is:
type getType(String key, type defaultValue)
So for the boolean, you would code:
boolean getValue = getBoolean("StoredBoolean", false);
|
For a byte array you would use:
byte[] getValue = getByteArray("StoredBoolean", new byte[0]);
|
The method name for the String version does not use the type in the name so to get a string you would use this
form:
String getValue = get("StoredString", "");
|
The get methods require that a default value be supplied. If no entry is found in the Preferences data store, the
default value will be returned.
The put methods are very similar. The form of the put methods are:
putType(String key, type value)
Here's an example of using preferences to retrieve the user's preferred number of rows to display and the preferred
color to display the rows in for an application:
int numRows = prefs.getInt("Rows", 40);
String color = prefs.get("Color", "Blue");
|
If the user had not previously saved any preferences the defaults of 40 rows and the color blue will be used. After
the user changes the preferences we can store them easily:
prefs.putInt("Rows", numRows);
prefs.put("Color", userColor);
|
Importing and Exporting Preferences
It may become necessary to save the user's preferences into a file. The Preferences class has methods to copy the
preference data into an XML file. The exportNode method will take the specified node and copy it to the specified
OutputStream in XML format. The format of the exportNode method:
void exportNode(OutputStream stream);
To create an XML file from our preferences we would do something like this:
prefs.exportNode(stream);
|
The output of this command will look something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
<preferences EXTERNAL_XML_VERSION="1.0">
<root type="user">
<map />
<node name="com">
<map />
<node name="javaranch">
<map />
<node name="preferences">
<map>
<entry key="Rows" value="80" />
<entry key="Color" value="Red" />
</map>
</node>
</node>
</node>
</root>
</preferences>
|
You can read this into a Preferences object by using the importPreferences method:
void importPreferences(InputStream stream);
Sample Program
Here is a simple program you can use as a sample. When you run it the first time, it will display the default values
of 40, and blue. When you run it the second time, it will display the new values of 80 and red. In addition, the
program will create an XML file and write it to, "prefs.xml".
import java.util.prefs.*;
import java.io.*;
public class PrefsTest{
public static void main(String[] args) throws Exception {
Preferences prefs = Preferences.userRoot().node("com/javaranch/preferences");
int numRows = prefs.getInt("Rows", 40);
String color = prefs.get("Color", "Blue");
new PrefsTest().putPrefs(prefs);
System.out.println("Color:" + color);
System.out.println("Rows:" + numRows);
OutputStream stream = new FileOutputStream("prefs.xml");
prefs.exportNode(stream);
}
public void putPrefs(Preferences prefs) {
prefs.putInt("Rows", 80);
prefs.put("Color", "Red");
}
}
|
If you are running under Windows, after running this program you can check the Windows registry and see the changes
that were made. Look under the node, "HKEY_CURRENT_USER/Software/JavaSoft". There you should find
"Prefs\com\javaranch\preferences". At the lowest node, you should find the keys containing the
stored preferences.
Conclusion
The Preferences API has some other features which we haven't discussed. For example, you can set Listeners to listen
for node value changes. More information on this can be found on the Sun web site at: http://developer.java.sun.com/developer/technicalArticles/releases/preferences/
If you have been using the Properties class to store user preference data in your applications you may want to
start using the Preferences API. It is generally easier to use and it is much more portable.