• JDK1.4下实现访问WIN32注册表

    2004-07-15

    Tag:

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://javan.blogbus.com/logs/268381.html

    (通过 JSR 10)为 Java 1.4 增加的 java.util.prefs 包,通过提供对特定于实现的注册表(例如,Windows 平台上的 Windows 注册表)的访问能力,使您能够操作用户首选项数据和配置数据。

    您是不是曾经需要保存程序的配置数据但却不知应将数据存储在哪里? 虽然您可以使用属性文件或资源束获取这些信息,但 Java 平台从未指定过用于存储这些文件的标准位置。JSR 10 出现后一切都变了,它为 Java 1.4 API 提供增加的 java.util.prefs 包。存储机制是特定于实现的细节,但程序员不必知道,也不必操心。对于 Windows 平台,它的位置是在“Windows 注册表”。 您虽然不能够对注册表自由控制,但您的确可以通过一个公共根节点访问所有的应用程序。

    开始
    命名很恰当的 Preferences 类提供操作首选项的基本框架。这个类提供了一系列静态和抽象方法来操作两套首选项(其中一套是用户首选项,另一套是系统首选项)中的其中一套。 使用静态方法,您会得到一个特定于平台的实现,就象 WindowsPreferences 类; 然后您可以使用由这个特定于平台的实现实现的抽象方法来做这项工作。

    用包将程序的首选项分组是个好习惯,可以避免与其它应用程序的命名冲突。 当您查找 Preferences 对象时,只需传递包的名称。在使用非静态方法时, 您可以传递对自身的引用(this),程序将为您确定查找的是哪个包,如清单 1 所示。

    清单 1. 从非静态方法获取 Preferences 对象

    
    Preferences userPrefs = Preferences.userNodeForPackage(this);
    Preferences sysPrefs = Preferences.systemNodeForPackage(this);
    

    但是,如果您使用的是静态方法,您就必须得到根节点并自己提供包,如清单 2 所示。

    清单 2. 从静态方法获取 Preferences 对象

    
    Preferences userPrefs = Preferences.userRoot().node("/net/zukowski/ibm");
    Preferences sysPrefs = Preferences.systemRoot().node("/net/zukowski/ibm");
    

    有了进行操作的节点后,您就可以轻松地设置、获取、除去和转储设置选项。只要把 Preferences 对象当作一个大的键 — 值散列表(这个表把树形结构中的键组织起来)。可它不是“集合框架”( Collections Framework)的部件,)。

    写数据
    我们将从讨论如何存储首选项开始。Preferences 类提供一系列 put() 方法,如下所示,用于存储值。除支持基本的字符串之外,您还可以存储布尔值、双精度数、浮点数、整型数、长整型数和字节数组(考虑序列化)。助手方法采用适当的数据类型并执行必要的转换以便将数据存储为字符串。

    • put(String key, String value)
    • putBoolean(String key, boolean value)
    • putByteArray(String key, byte value[])
    • putDouble(String key, double value)
    • putFloat(String key, float value)
    • putInt(String key, int value)
    • putLong(String key, long value)

    所有的 put() 方法都返回一个 void。如果存储机制不可用,将抛出一个BackingStoreException

    注意:一个特定首选项的键长度被限制为 Preferences.MAX_KEY_LENGTH(80)个字符,而它的值被限制为 Preferences.MAX_VALUE_LENGTH(8192)个字符。

    读数据
    可通过下面所示的一系列 get() 方法获取特定的首选项。与写数据相似,每种受支持的数据类型,都有自己的与众不同的方法。但与获取数据时不同的是,在备用存储不可用,或有些东西尚未保存时您必须提供缺省值。这要求您确保自己的程序至少要有合理的缺省设置选项。

    • get(String key, String default)
    • getBoolean(String key, boolean default)
    • getByteArray(String key, byte default[])
    • getDouble(String key, double default)
    • getFloat(String key, float default)
    • getInt(String key, int default)
    • getLong(String key, long default)

    如果您对首选项名称不确定,您可以用 keys() 方法查找一列与节点相关联的键。这个方法返回节点的 String[]。 除获取和存储个别首选项以及获取一列关键字之外,您还可以用 clear()remove()removeNode() 除去节点和值。

    转储数据
    如果您想在系统提供的备用存储器之外保存和恢复首选项,您可以在 XML 格式的文档中执行这些操作。您可以用 exportNode() 导出一个节点或用 exportSubtree() 导出整个子树。信息以 UTF-8 格式存储。然后,当您想恢复信息时,可使用 importPreferences() 方法。

    侦听
    “好奇心会害死一只猫”,但如果您对弄清除首选项何时改变很感兴趣,您可以注册一个 NodeChangeListenerPreferenceChangeListener,而不考虑随之而来的后果。NodeChangeListener 负责通知您节点被添加和除去的时间, 而 PreferenceChangeListener 告诉您值的变化。这些都紧跟着基本 JavaBeans 组件事件用 add/removeNodeChangeListener(NodeChangeListener)add/removePreferenceChangeListener() 方法处理结构之后发生。基本上,您先实现侦听器,然后注册侦听器,这样您会发现将来的变化。

    完整的示例
    真的就这些。清单 3 为您提供了一个完整的示例来试验新功能。程序运行后会自己清除,所以如果您想在注册表中找到值,请注释掉程序尾部的清除代码。

    清单 3. 完整的示例

    
    package net.zukowski.ibm;
    
    import java.io.*;
    import java.util.prefs.*;
    
    public class Prefs {
      public static void main(String args[]) {
        String denominations[] = 
          {"One", "Two", "Five", "Ten", "Twenty"};
        String pictures[] = 
          {"Washington", "Jefferson", "Lincoln", "Hamilton", "Jackson"};
    
        NodeChangeListener nodeChangeListener = 
          new NodeChangeListener() {
            public void childAdded(NodeChangeEvent event) {
              Preferences parent = event.getParent();
              Preferences child  = event.getChild();
              System.out.println(parent.name() + " has a new child " +
                child.name());
            }
            public void childRemoved(NodeChangeEvent event) {
              Preferences parent = event.getParent();
              Preferences child  = event.getChild();
              System.out.println(parent.name() + " lost a child " +
                child.name());
            }
          };
    
        PreferenceChangeListener preferenceChangeListener = 
          new PreferenceChangeListener() {
            public void preferenceChange(PreferenceChangeEvent event) {
              String key = event.getKey();
              String value = event.getNewValue();
              Preferences node = event.getNode();
              System.out.println(node.name() + " now has a value of " + 
                value + " for " + key);
            }
          };
    
        // Look up user root
        Preferences prefs = 
          Preferences.userRoot().node("/net/zukowski/ibm");
    
        // Add listeners
        prefs.addNodeChangeListener(nodeChangeListener);
        prefs.addPreferenceChangeListener(preferenceChangeListener);
    
        // Save a bunch of key-value pairs
        for (int i=0, n=denominations.length; i < n; i++) {
          prefs.put(denominations[i], pictures[i]);
        }
    
        // Display all the entries
        try {
          String keys[] = prefs.keys();
          for (int i=0, n=keys.length; i < n; i++) {
            System.out.println(keys[i] + ": " + prefs.get(keys[i], "Unknown"));
          }
        } catch (BackingStoreException e) {
          System.err.println("Unable to read backing store: " + e);
        }
    
        // Create child
        Preferences child = Preferences.userRoot().node("/net/zukowski/ibm/foo");
    
        // Save to XML file
        try {
          FileOutputStream fos = new FileOutputStream("prefs.out");
          prefs.exportNode(fos);
        } catch (Exception e) {
          System.err.println("Unable to export nodes: " + e);
        }
    
        // Clean up
        try {
          prefs.removeNode();
        } catch (BackingStoreException e) {
          System.err.println("Unable to access backing store: " + e);
        }
    
      }
    }
    

    我们经常需要将我们的程序中的设定,如窗口位置,开启过的文件,用户的选项设定等数据记录下来,以做便用户下一次开启程序能继续使用这些数据。
    以前我们通常的做法是使用Properties类,它提供以下方法:


    void load(InputStream inStream)
    void store(OutputStream out, String header)
    String getProperty(String key, String defaultValue)
    String getProperty(String key)


    这些方法让我们很容易的存取设定数据.
    另外的办法是使用ResourceBundle这个类来储存设定数据,甚至有些程序作者使用一个自定结构的文件来储存设定数据。
    但不管怎样,最让程序作者头痛的是:我该将这些数据保存在哪?

    现在好了,JDK1.4为我们提供的java.util.prefs包,里面有一个Preferences类,能让以上的工作变得极其轻松!
    写VB程序的人常常用SaveSetting函数和getSettging来存取注册表中的用户设定数据。而Java的Preferences类也提供了类似的机制。
    Preferences类在不同的平台中有不同的实现方式。而在Windows平台中,Preferences是将数据保存在注册表中的,而在其它平台中的实现我就不得而知了(我对Linux等系统都不太了解).
    但不管怎样,都是通过相同的接口来使用的,程序作者可以不管实现细节。

    建立Preferences对象


    为了区分不同的应用程序的参数项,在建立Preferences时要指定一个节点路径。
    Preferences是一个抽象类,提供了一系列静态方法和抽象方法来操作参数项:
    抽象方法:


    Preferences userData = Preferences.userNodeForPackage(this);
    Preferences sysData = Preferences.systemNodeForPackage(this);


    这两个方法是从指定的物件所在的包(package)返回一个节点路径,如this是javax.swing.JComponent,则返回/javax/swing
    静态方法:


    Preferences userData = Preferences.userRoot().node("/com/sunway/spc");
    Preferences sysData = Preferences.systemRoot().node("/com/sunway/spc");


    以上每种方式提供了两套操作方法。其中一套是用户参数项,另一套是系统参数项。
    在Windows平台中,用户参数项在注册表中的根节点是
    HKEY_CURRENT_USER\Software\JavaSoft\Prefs
    系统参数项在注册表中的根节点是
    HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs
    而我们指定的节点路径是位于这些根节点之下的.

    如何读写数据


    Preferences提供了多种类型数据的读写方法。
    先来看写(put)的方法,


    put(String key, String value)
    putBoolean(String key, boolean value)
    putByteArray(String key, byte value[])
    putDouble(String key, double value)
    putFloat(String key, float value)
    putInt(String key, int value)
    putLong(String key, long value)


    下面的是读(get)的方法,


    get(String key, String default)
    getBoolean(String key, boolean default)
    getByteArray(String key, byte default[])
    getDouble(String key, double default)
    getFloat(String key, float default)
    getInt(String key, int default)
    getLong(String key, long default)


    注意,每一种get方法的第二个参数需要我们为它指定缺省的参数。
    除了以上方式之外,Preferences还允许我们将它的数据导出到一个XML文件保存,


    void exportNode(OutputStream os)
    void exportSubtree(OutputStream os)


    我们可以导出一个节点,或是导出整个子节点树.

    Preferences的一个演示
    以上的讲解足可以写一个示例来看看Preferences是如何为我们工作的,看看下面的例子:


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

    public class PrefsDemo {
    public static void main(String args[])
    {
    String keys[] =
    {"sunway","copyright","author"};
    String values[] =
    {"sunway technology company","copyright 2002","turbochen@163.com"};

    /* 建立一个位于user root下的/com/sunway/spc节点参数项*/
    Preferences prefsdemo =
    Preferences.userRoot().node("/com/sunway/spc");

    /* 储存参数项*/
    for (int i=0 ; i < keys.length; i++)
    {
    prefsdemo.put(keys[i], values[i]);
    }

    /* 导出到XML文件 */
    try
    {
    FileOutputStream fos = new FileOutputStream("prefsdemo.xml");
    prefsdemo.exportNode(fos);
    } catch (Exception e)
    {
    System.err.println("Cannot export nodes: " + e);
    }

    /* 去掉注释可以清除注册表中的参数项*/
    /*
    try
    {
    prefsdemo.removeNode();
    } catch (BackingStoreException e)
    {
    } */


    }
    }


    以上介绍了Preferences功能,要了解更多Preferences的方法,请查阅JDK文档java.util.prefs.Preferences


    收藏到:Del.icio.us