{"id":224,"date":"2007-09-20T00:00:00","date_gmt":"2007-09-20T00:00:00","guid":{"rendered":"http:\/\/www.strongd.net\/?p=224"},"modified":"2007-09-20T00:00:00","modified_gmt":"2007-09-20T00:00:00","slug":"Beyond Preferences API Basics","status":"publish","type":"post","link":"https:\/\/www.strongd.net\/?p=224","title":{"rendered":"Beyond Preferences API Basics"},"content":{"rendered":"<p><P>The Preferences API was first covered here shortly after it was introduced with the 1.4 version of the standard platform: the July 15, 2003 article, the <A href=\"http:\/\/communications1.sun.com\/r\/c\/r?2.1.3J1.2Vc.11Fua%5f.C4sP1g..H.EhMC.1nTk.DdHeEaf0\" target=_blank rel=nofollow>Preferences API<\/A>. <\/P><br \/>\n<P>That article described how to get and set user specific preferences. There is more to the Preferences API than just getting and setting user specific settings. There are system preferences, import and export preferences, and event notifications associated with preferences. There is even a way to provide your own custom location for storage of preferences. The first three options mentioned will be described here. Creating a custom preferences factory will be left to a later tip. <\/P><br \/>\n<P><B>System Preferences<\/B> <\/P><br \/>\n<P>The Preferences API provides for two separate sets of preferences. The first set is for the individual user, allows multiple users on the same machine to have different settings defined. These are called user preferences. Each user who shares the same machine can have his or her own unique set of values associated with a group of preferences. Something like this could be like a user password or starting directory. You don&#8217;t want every person on the same machine to have the same password and home directory. Well, I would hope you don&#8217;t want that. <\/P><br \/>\n<P>The other form of preferences is the system type. All users of a machine share the same set of system preferences. For instance, the location of an installed printer would typically be a system preference. You wouldn&#8217;t necessarily have a different set of printers installed for different users. Everyone running on one machine would know about all printers known by that machine. <\/P><br \/>\n<P>Another example of a system preference would be the high score of a game. There should only be one overall high score. That&#8217;s what a system preference would be used for. In the previous tip you saw how <CODE>userNodeForPackge()<\/CODE> &#8212; and subsequently <CODE>userRoot()<\/CODE> &#8212; was used to acquire the user&#8217;s preference node, the following example shows how to get the appropriate part of the system preferences tree with <CODE>systemNodeForPackage()<\/CODE> &#8212; or <CODE>systemRoot()<\/CODE> for the root. Other than the method call to get the right preference node, the API usage is identical. <\/P><br \/>\n<P>The example is a simple game, using the game term loosely here. It picks a random number from 0 to 99. If the number is higher than the previously saved number, it updates the &#8220;high score.&#8221; The example also shows the current high score. The Preferences API usage is rather simple. The example just gets the saved value with <CODE>getSavedHighScore()<\/CODE> , providing a default of -1 if no high score had been saved yet, and <CODE>updateHighScore(int value)<\/CODE> to store the new high score. The <CODE>HIGH_SCORE<\/CODE> key is a constant shared by the new Preferences API accesses. <\/P><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;int&nbsp;getSavedHighScore()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;Preferences&nbsp;systemNode&nbsp;=&nbsp;Preferences.systemNodeForPackage(High.class);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;systemNode.getInt(HIGH_SCORE,&nbsp;-1);<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;void&nbsp;updateHighScore(int&nbsp;value)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;Preferences&nbsp;systemNode&nbsp;=&nbsp;Preferences.systemNodeForPackage(High.class);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;systemNode.putInt(HIGH_SCORE,&nbsp;value);<\/CODE><BR><CODE>&nbsp;}<\/CODE><BR><BR>Here&#8217;s&nbsp;what&nbsp;the&nbsp;whole&nbsp;program&nbsp;looks&nbsp;like:<BR><BR><CODE>import&nbsp;java.util.*;<\/CODE><BR><CODE>import&nbsp;java.util.prefs.*;<\/CODE><BR><CODE>import&nbsp;javax.swing.*;<\/CODE><BR><CODE>import&nbsp;java.awt.*;<\/CODE><BR><CODE>import&nbsp;java.awt.event.*;<\/CODE><BR><BR><CODE>public&nbsp;class&nbsp;High&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;static&nbsp;JLabel&nbsp;highScore&nbsp;=&nbsp;new&nbsp;JLabel();<\/CODE><BR><CODE>&nbsp;&nbsp;static&nbsp;JLabel&nbsp;score&nbsp;=&nbsp;new&nbsp;JLabel();<\/CODE><BR><CODE>&nbsp;&nbsp;static&nbsp;Random&nbsp;random&nbsp;=&nbsp;new&nbsp;Random(new&nbsp;Date().getTime());<\/CODE><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;final&nbsp;String&nbsp;HIGH_SCORE&nbsp;=&nbsp;&#8220;High.highScore&#8221;;<\/CODE><BR><BR><CODE>&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main&nbsp;(String&nbsp;args[])&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;\/*&nbsp;&#8212;&nbsp;Uncomment&nbsp;these&nbsp;lines&nbsp;to&nbsp;clear&nbsp;saved&nbsp;score<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;Preferences&nbsp;systemNode&nbsp;=&nbsp;Preferences.systemNodeForPackage(High.class);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;systemNode.remove(HIGH_SCORE);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;*\/<\/CODE><BR><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;EventQueue.invokeLater(<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;Runnable()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JFrame&nbsp;frame&nbsp;=&nbsp;new&nbsp;JFrame(&#8220;High&nbsp;Score&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateHighScoreLabel(getSavedHighScore());<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.add(highScore,&nbsp;BorderLayout.NORTH);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.add(score,&nbsp;BorderLayout.CENTER);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JButton&nbsp;button&nbsp;=&nbsp;new&nbsp;JButton(&#8220;Play&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActionListener&nbsp;listener&nbsp;=&nbsp;new&nbsp;ActionListener()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;next&nbsp;=&nbsp;random.nextInt(100);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;score.setText(Integer.toString(next));<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;old&nbsp;=&nbsp;getSavedHighScore();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(next&nbsp;&gt;&nbsp;old)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateHighScore(next);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateHighScoreLabel(next);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;button.addActionListener(listener);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.add(button,&nbsp;BorderLayout.SOUTH);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setSize(200,&nbsp;200);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setVisible(true);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;);<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;void&nbsp;updateHighScoreLabel(int&nbsp;value)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(value&nbsp;==&nbsp;-1)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highScore.setText(&#8220;&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highScore.setText(Integer.toString(value));<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;int&nbsp;getSavedHighScore()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;Preferences&nbsp;systemNode&nbsp;=&nbsp;Preferences.systemNodeForPackage(High.class);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;systemNode.getInt(HIGH_SCORE,&nbsp;-1);<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;void&nbsp;updateHighScore(int&nbsp;value)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;Preferences&nbsp;systemNode&nbsp;=&nbsp;Preferences.systemNodeForPackage(High.class);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;systemNode.putInt(HIGH_SCORE,&nbsp;value);<\/CODE><BR><CODE>&nbsp;}<\/CODE><BR><CODE>}<\/CODE><BR><br \/>\n<P>And, here&#8217;s what the screen looks like after a few runs. The 61 score is not apt to be your high score, but it certainly could be. <\/P><BR><IMG alt=\"\" src=\"http:\/\/a676.g.akamaitech.net\/f\/676\/773\/60m\/images.delivery.net\/cm50content\/18646\/090081018087295d\/high.png\"><BR><br \/>\n<P>You can try running the application as different users to see that they all share the same high score. <\/P><br \/>\n<P><B>Import and Export<\/B> <\/P><br \/>\n<P>In the event that you wish to transfer preferences from one user to another or from one system to another, you can export the preferences from that one user\/system, and then import them to the other side. When preferences are exported, they are exported into an XML formatted document whose DTD is specified by <CODE>http:\/\/java.sun.com\/dtd\/preferences.dtd<\/CODE> , though you don&#8217;t really need to know that. You can export either a whole subtree with the <CODE>exportSubtree()<\/CODE> method or just a single node with the <CODE>exportNode()<\/CODE> method. Both methods accept an <CODE>OutputStream<\/CODE> argument to specify where to store things. The XML document will be UTF-8 character encoded. Importing of the data then happens via the <CODE>importPreferences()<\/CODE> method, which takes an <CODE>InputStream<\/CODE> argument. From an API perspective, there is no difference in importing a system node\/tree or a user node. <\/P><br \/>\n<P>Adding a few lines of code to the previous example will export the newly updated high score to the file high.xml. Much of the added code is responsible for launching a new thread to save the file and for handling exceptions. There are only three lines to export the single node: <\/P><CODE>&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;runner&nbsp;=&nbsp;new&nbsp;Thread(new&nbsp;Runnable()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileOutputStream&nbsp;fis&nbsp;=&nbsp;new&nbsp;FileOutputStream(&#8220;high.xml&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;systemNode.exportNode(fis);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fis.close();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;});<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;runner.start();<\/CODE><BR><BR>When&nbsp;exported,&nbsp;the&nbsp;file&nbsp;will&nbsp;look&nbsp;something&nbsp;like&nbsp;the&nbsp;following:<BR><BR><CODE>&lt;?xml&nbsp;version=&#8221;1.0&#8243;&nbsp;encoding=&#8221;UTF-8&#8243;&nbsp;standalone=&#8221;no&#8221;?&gt;<\/CODE><BR><CODE>&lt;!DOCTYPE&nbsp;preferences&nbsp;SYSTEM&nbsp;&#8220;http:\/\/java.sun.com\/dtd\/preferences.dtd&#8221;&gt;<\/CODE><BR><CODE>&lt;preferences&nbsp;EXTERNAL_XML_VERSION=&#8221;1.0&#8243;&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&lt;root&nbsp;type=&#8221;system&#8221;&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&lt;map\/&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&lt;node&nbsp;name=&#8221;&lt;unnamed&gt;&#8221;&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;map&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry&nbsp;key=&#8221;High.highScore&#8221;&nbsp;value=&#8221;95&#8243;\/&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;\/map&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&lt;\/node&gt;<\/CODE><BR><CODE>&nbsp;&nbsp;&lt;\/root&gt;<\/CODE><BR><CODE>&lt;\/preferences&gt;<\/CODE><BR><br \/>\n<P>Notice the root element has a type attribute that says &#8220;<CODE>system<\/CODE> &#8220;. This states the type of node it is. The node also has a name attribute valued at &#8220;<CODE>&lt;unnamed&gt;<\/CODE> &#8220;. Since the <CODE>High<\/CODE> class was not placed in a package, you get to work in the unnamed system node area. The entry attribute provide the current high score value, 95 in the example here, though your value could differ. <\/P><br \/>\n<P>While we won&#8217;t include any import code in the example here, the way to import is just a static method call on Preferences, passing in the appropriate input stream: <\/P><CODE>&nbsp;&nbsp;FileInputStream&nbsp;fis&nbsp;=&nbsp;new&nbsp;FileInputStream(&#8220;high.xml&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;Preferences.importPreferences(fis);<\/CODE><BR><CODE>&nbsp;&nbsp;fis.close();<\/CODE><BR><br \/>\n<P>Since the XML file includes information about whether the preferences are system or user type, the import call doesn&#8217;t have to explicitly include this bit of information. Besides the typical <CODE>IOExceptions<\/CODE> that can happen, the import call will throw an <CODE>InvalidPreferencesFormatException<\/CODE> if the file format is invalid. Exporting can also throw a <CODE>BackingStoreException<\/CODE> if the data to export can&#8217;t be read correctly from the backing store. <\/P><br \/>\n<P>Event Notifications <\/P><br \/>\n<P>The original version of the <CODE>High<\/CODE> game updated the high score preference, then explicitly made a call to update the label on the screen. A better way to perform this action would be to add a listener to the preferences node, then a value change can automatically trigger the label to update its value. That way, if the high score is ever updated from multiple places, you won&#8217;t need to remember to add code to update the label after saving the updated value. <\/P><br \/>\n<P>The two lines: <\/P><CODE>&nbsp;&nbsp;updateHighScore(next);<\/CODE><BR><CODE>&nbsp;&nbsp;updateHighScoreLabel(next);<\/CODE><BR><BR>can&nbsp;become&nbsp;one&nbsp;with&nbsp;the&nbsp;addition&nbsp;of&nbsp;the&nbsp;right&nbsp;listeners.<BR><BR><CODE>&nbsp;&nbsp;updateHighScore(next);<\/CODE><BR><br \/>\n<P>There is a <CODE>PreferenceChangeListener<\/CODE> and its associated <CODE>PreferenceChangeEvent<\/CODE> for just such a task. The listener will be notified for all changes to the associated node, so you need to check for which key-value pair was modified, as shown here. <\/P><CODE>&nbsp;&nbsp;&nbsp;&nbsp;PreferenceChangeListener&nbsp;changeListener&nbsp;=<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;PreferenceChangeListener()&nbsp;{<\/CODE><BR><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;preferenceChange(PreferenceChangeEvent&nbsp;e)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(HIGH_SCORE.equals(e.getKey()))&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;newValue&nbsp;=&nbsp;e.getNewValue();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;value&nbsp;=&nbsp;Integer.valueOf(newValue);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateHighScoreLabel(value);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;};<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;systemNode.addPreferenceChangeListener(changeListener);<\/CODE><BR><br \/>\n<P>The <CODE>PreferenceChangeEvent<\/CODE> has three important properties: the key, new new value, and the node itself. The new value doesn&#8217;t have all the convenience methods of Preferences though. For example, you can&#8217;t retrieve the value as an int. Instead you must manually convert the value yourself. Here&#8217;s what the modified <CODE>High<\/CODE> class looks like: <\/P><CODE>import&nbsp;java.awt.*;<\/CODE><BR><CODE>import&nbsp;java.awt.event.*;<\/CODE><BR><CODE>import&nbsp;java.io.*;<\/CODE><BR><CODE>import&nbsp;java.util.*;<\/CODE><BR><CODE>import&nbsp;java.util.prefs.*;<\/CODE><BR><CODE>import&nbsp;javax.swing.*;<\/CODE><BR><BR><CODE>public&nbsp;class&nbsp;High&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;static&nbsp;JLabel&nbsp;highScore&nbsp;=&nbsp;new&nbsp;JLabel();<\/CODE><BR><CODE>&nbsp;&nbsp;static&nbsp;JLabel&nbsp;score&nbsp;=&nbsp;new&nbsp;JLabel();<\/CODE><BR><CODE>&nbsp;&nbsp;static&nbsp;Random&nbsp;random&nbsp;=&nbsp;new&nbsp;Random(new&nbsp;Date().getTime());<\/CODE><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;final&nbsp;String&nbsp;HIGH_SCORE&nbsp;=&nbsp;&#8220;High.highScore&#8221;;<\/CODE><BR><CODE>&nbsp;&nbsp;static&nbsp;Preferences&nbsp;systemNode&nbsp;=<\/CODE><BR><CODE>&nbsp;&nbsp;Preferences.systemNodeForPackage(High.class);<\/CODE><BR><BR><CODE>&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main&nbsp;(String&nbsp;args[])&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;\/*&nbsp;&#8212;&nbsp;Uncomment&nbsp;these&nbsp;lines&nbsp;to&nbsp;clear&nbsp;saved&nbsp;score<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;systemNode.remove(HIGH_SCORE);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;*\/<\/CODE><BR><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;PreferenceChangeListener&nbsp;changeListener&nbsp;=<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;PreferenceChangeListener()&nbsp;{<\/CODE><BR><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;preferenceChange(PreferenceChangeEvent&nbsp;e)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(HIGH_SCORE.equals(e.getKey()))&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;newValue&nbsp;=&nbsp;e.getNewValue();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;value&nbsp;=&nbsp;Integer.valueOf(newValue);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateHighScoreLabel(value);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;};<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;systemNode.addPreferenceChangeListener(changeListener);<\/CODE><BR><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;EventQueue.invokeLater(<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;Runnable()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JFrame&nbsp;frame&nbsp;=&nbsp;new&nbsp;JFrame(&#8220;High&nbsp;Score&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateHighScoreLabel(getSavedHighScore());<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.add(highScore,&nbsp;BorderLayout.NORTH);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.add(score,&nbsp;BorderLayout.CENTER);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JButton&nbsp;button&nbsp;=&nbsp;new&nbsp;JButton(&#8220;Play&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActionListener&nbsp;listener&nbsp;=&nbsp;new&nbsp;ActionListener()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;next&nbsp;=&nbsp;random.nextInt(100);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;score.setText(Integer.toString(next));<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;old&nbsp;=&nbsp;getSavedHighScore();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(next&nbsp;&gt;&nbsp;old)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateHighScore(next);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;button.addActionListener(listener);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.add(button,&nbsp;BorderLayout.SOUTH);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setSize(200,&nbsp;200);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.setVisible(true);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;);<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;void&nbsp;updateHighScoreLabel(int&nbsp;value)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(value&nbsp;==&nbsp;-1)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highScore.setText(&#8220;&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highScore.setText(Integer.toString(value));<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;int&nbsp;getSavedHighScore()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;systemNode.getInt(HIGH_SCORE,&nbsp;-1);<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><BR><CODE>&nbsp;&nbsp;private&nbsp;static&nbsp;void&nbsp;updateHighScore(int&nbsp;value)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;systemNode.putInt(HIGH_SCORE,&nbsp;value);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;Save&nbsp;XML&nbsp;in&nbsp;separate&nbsp;thread<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;runner&nbsp;=&nbsp;new&nbsp;Thread(new&nbsp;Runnable()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileOutputStream&nbsp;fis&nbsp;=&nbsp;new&nbsp;FileOutputStream(&#8220;high.xml&#8221;);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;systemNode.exportNode(fis);<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fis.close();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;});<\/CODE><BR><CODE>&nbsp;&nbsp;&nbsp;&nbsp;runner.start();<\/CODE><BR><CODE>&nbsp;&nbsp;}<\/CODE><BR><CODE>}<\/CODE><BR><br \/>\n<P>In addition to the <CODE>PreferenceChangeListener\/Event<\/CODE> class pair, there is a <CODE>NodeChangeListener<\/CODE> and <CODE>NodeChangeEvent<\/CODE> combo for notification of preference changes. However, these are for notification nodes additions and removals, not changing values of specific nodes. Of course, if you are writing something like a Preferences viewer, clearly you&#8217;d want to know if\/when nodes appear and disappear so these classes may be of interest, too. <\/P><br \/>\n<P>The whole Preferences API can be quite handy to store data beyond the life of your application without having to rely on a database system. For more information on the API, see the article <A href=\"http:\/\/communications1.sun.com\/r\/c\/r?2.1.3J1.2Vc.11Fua%5f.C4sP1g..H.EhMG.1nTk.DdTGEbD0\" target=_blank rel=nofollow>Sir, What is Your Preference?<\/A><\/P><br \/>\n<DIV><\/DIV><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Preferences API was first covered here shortly after it was introduced with the 1.4 version of the standard platform: the July 15, 2003 article, the Preferences API. That article described how to get and set user specific preferences. There is more to the Preferences API than just getting and setting user specific settings. There &hellip; <a href=\"https:\/\/www.strongd.net\/?p=224\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Beyond Preferences API Basics<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-224","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/posts\/224","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=224"}],"version-history":[{"count":0,"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/posts\/224\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=224"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=224"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=224"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}