描述了Java中幾個關(guān)鍵的實用程序庫。這些庫包括: ● Java日志(Java logging):一個強大的日志系統(tǒng),它對于工作于該領(lǐng)域的終端用戶、開發(fā)人員和其他相關(guān)人員能否獲得有意義的錯誤消息來說至關(guān)重要。 ● 正則表達式(Regular expression):一個強大的“縮微語言”,可以用來按照各種方式處理字符串,例如搜索匹配一個特定模式的子字符串。 ● Java首選項(Java preference):一種存儲和獲取系統(tǒng)和用戶定義的配置選項的方法。每一個庫都設(shè)計為可以靈活使用。熟悉這些庫對于在Java中開發(fā)解決方案是非常重要的。作為一個開發(fā)人員,掌握的工具越多,就意味著裝備越好。1.4.1 Java日志
Java擁有一個設(shè)計良好的類集,用于通過日志系統(tǒng)來控制、格式化以及發(fā)布消息。對于一個程序來說,記錄錯誤和狀態(tài)消息顯得非常重要。有許多人可以從日志消息中獲益,包括開發(fā)人員、測試人員、終端用戶,以及工作在該領(lǐng)域卻沒有源代碼來對程序進行疑難處理的人員。在一個程序中提供大量高質(zhì)量的日志消息至關(guān)重要,從狀態(tài)更新到錯誤條件(例如在捕獲某種異常時)。通過使用日志系統(tǒng),就有可能在不查看源代碼的情況下,觀察到程序正在干什么,而且最重要的是,可以向下追蹤錯誤條件一直到程序的一個特定部分。日志系統(tǒng)的價值是顯然的,特別是在大系統(tǒng)中,一個具備很少或者沒有日志消息的偶見錯誤需要花費數(shù)天或者更長時間來向下追蹤。java.util.logging中的日志系統(tǒng)功能非常強大,包括了滿足以下條件的日志消息的優(yōu)先級處理方法:只有特定記錄器感興趣的消息才被記錄,并且消息可以輸出到Handler對象能夠處理的任意源。日志目標(biāo)文件的例子包括文件、數(shù)據(jù)庫和輸出流。仔細觀看圖1-1可以看到整個日志系統(tǒng)的概覽。

圖1-1特定的Logger對象確實是層次結(jié)構(gòu)的,并且盡管沒有強制,但它還是能夠反映類層次結(jié)構(gòu)。當(dāng)一個Logger接收一個日志消息時,該消息也將自動被傳送到Logger的父對象。根記錄器被命名為" "(空字符串),并且沒有父對象。每一個其他的Logger通常都被命名為諸如java.util或者java.util.ArrayList之類的內(nèi)容,以反映包/類層次結(jié)構(gòu)。從樹結(jié)構(gòu)向下的Logger對象的名稱都用點號來分隔。因此,java.util是java.util.ArrayList的父Logger??梢詫⒂涗浧髅麨槿我庾址?,但是需要用點號分隔約定來幫助清晰命名。日志系統(tǒng)的最簡單用法是創(chuàng)建一個Logger,并使用日志系統(tǒng)的所有系統(tǒng)默認設(shè)置(在一個屬性文件中定義)。下面的示例使用一個被稱為SimpleFormatter的格式化類來輸出日志消息,該類向日志消息中添加了時間/日期/來源信息:import java.util.logging.*;public class BasicLoggingExample { public static void main(String args[]) { Logger logger = Logger.getLogger("BasicLoggingExample"); logger.log(Level. INFO, "Test of logging system"); }}下面的內(nèi)容是BasicLoggingExample的輸出:Feb 22, 2004 4:07:06 PM BasicLoggingExample mainINFO: Test of logging system1.日志管理器
一個特定應(yīng)用程序的整個日志系統(tǒng)都由LogManager類的一個實例來控制。此實例在Log- Manager初始化期間創(chuàng)建。LogManager包括了擁有所有指定Logger對象的層次式名字空間。LogManager也包括了日志系統(tǒng)配置中的Handlers以及其他對象所使用的日志控制屬性。這些配置屬性保存在lib/logging.properties文件中,該文件位于JRE安裝路徑中。有兩個系統(tǒng)屬性可以用來初始化帶有不同屬性的日志系統(tǒng)。第一種方法是覆蓋屬性java.util. logging.config.file,并且將完整路徑規(guī)定為自身的logging.properties版本。另一個屬性java.util.logging.config.class用來指出自身的LogManager。這種自定義LogManager負責(zé)讀入其配置信息。如果這兩個屬性都沒有被配置,那么Java將默認使用JRE目錄下的logging. properties文件。查看下表1-11中的有關(guān)屬性,這些屬性可以在此文件中的LogManager上設(shè)置。同時也可以在此文件中為Loggers和Handles指定屬性。本節(jié)后面將介紹這些屬性。
表1-11
屬性關(guān)鍵字
屬性值
Handlers
逗號分隔的Handler類的列表。每個處理程序必須位于系統(tǒng)類路徑中的某個位置
.level
設(shè)置特定Logger的最小級別。
level前面必須帶有一個特定Logger的完整路徑。一個獨立的點號設(shè)置了根記錄器的級別
(1)LogManager類LogManager類可以通過大量配置方法來配置日志系統(tǒng)的當(dāng)前實例,追蹤記錄器并提供對這些記錄器的訪問,同時處理某些日志事件。這些方法在表1-12到表1-14中列出。(2)配置表1-12中列出的方法涉及存儲和獲取LogManager中的配置信息。表1-12
方法
描述
String getProperty(String name)
返回與一個指定日志屬性相對應(yīng)的值
void readConfiguration()
使用與啟動時相同的過程重新加載該配置。如果控制初始化的系統(tǒng)屬性尚未改變,那么在啟動時讀過的相同文件將在這里再次被讀出
void readConfiguration(InputStream ins)
從InputStream讀取格式為java.util.Properties的配置信息
void reset()
重新設(shè)置日志系統(tǒng)。所有Handlers都被關(guān)閉和移除,并且除了根級別之外的所有記錄器級別都被設(shè)置為null。根記錄器的級別被設(shè)置為Level.INFO
(3)記錄器控制表1-13中列出的方法涉及存儲、獲取和管理單個Logger引用。這些是LogManager類上最常用的方法。表1-13
方法
描述
static LogManager getLogManager()
返回LogManager對象的惟一實例
boolean addLogger(Logger logger)
如果傳入的Logger沒有注冊過(它的名稱尚不在列表中),則返回true。注冊記錄器。
如果Logger對象的名稱已經(jīng)存在于已注冊記錄器列表中,則返回false
Logger getLogger(String name)
返回一個被命名為“name”的Logger對象的引用;如果沒有發(fā)現(xiàn)記錄器,則返回null
Enumeration getLoggerNames()
返回一個包含了所有當(dāng)前已注冊記錄器名稱列表的Enumeration
(4)事件表1-14中列出的方法用于,在LogManager上的屬性被修改時,添加和移除應(yīng)該被通知的偵聽器的引用。表1-14
方法
描述
void addPropertyChangeListener
(PropertyChangeListener 1)
向一個偵聽器列表添加一個屬性更改偵聽器,偵聽器希望在屬性發(fā)生變化時會得到通知??梢远啻翁砑酉嗤膫陕犉?div style="height:15px;">
void removePropertyChangeListener
(PropertyChangeListener 1)
在偵聽器列表中移除一個屬性更改偵聽器
2.Logger類
客戶端代碼使用Logger類的一個實例來記錄一個消息。所有的日志消息以及每個記錄器都具有相關(guān)聯(lián)的級別。如果日志消息的級別等同于或者高于記錄器的級別,那么消息將會被處理。否則,記錄器將會丟棄日志消息。測試是否丟棄日志消息的代價并不大,此操作在日志系統(tǒng)的入口(Logger類)處就可以完成。這些級別是在Level類中定義的??梢圆榭幢?-15中的一個完整級別列表。表1-15
記錄器級別
描述
SEVERE
最高日志級別。它具有最高的優(yōu)先級
WARNING
比SEVERE低一個級別。表示是一個需要注意但不很嚴重的警告消息
INFO
比SEVERE低兩個級別。表示一種信息消息
CONFIG
比SEVERE低三個級別。表示與配置相關(guān)的輸出
FINE
比SEVERE低四個級別。表示程序追蹤信息
續(xù)表
記錄器級別
描述
FINER
比SEVERE低五個級別。表示程序追蹤信息
FINEST
最低日志級別。它具有最低優(yōu)先級
ALL
使得系統(tǒng)將記錄所有消息的特殊級別
OFF
使得系統(tǒng)將不記錄消息的特殊級別(完全關(guān)閉日志功能)
(1)記錄器方法Logger是利用日志系統(tǒng)的代碼中所使用的主要類。有方法可以用于獲得一個命名的或者匿名的記錄器,配置該記錄器并獲取有關(guān)該記錄器的信息,以及記錄消息。(2)獲得一個記錄器表1-16中給出的方法用于獲取一個Logger的句柄。這些是靜態(tài)方法,提供了無需通過LogManager即可獲得Logger的一種便捷途徑。表1-16
方法
描述
static Logger getAnonymousLogger() static Logger getAnonymousLogger (String resourceBundleName)
創(chuàng)建一個匿名記錄器,該記錄器可以免除標(biāo)準安全檢查,用于applet中。匿名記錄器不在LogManager名稱空間中注冊,但是將根記錄器("")作為一個父記錄器,繼承了根記錄器的級別和處理程序。同時也可以指定資源綁定用于日志消息的本地化
static Logger getLogger(String name)
static Logger getLogger(String name, String resourceBundleName)
返回一個來自LogManager名稱空間的命名記錄器;如果沒有發(fā)現(xiàn)記錄器,則創(chuàng)建并且返回一個新的命名記錄器。同時也可以指定資源綁定用于日志消息的本地化
(3)配置一個Logger對象表1-17中的方法用于配置一個Logger對象??梢蕴砑雍鸵瞥幚沓绦?,設(shè)置此Logger對象上的日志級別,設(shè)置它的父對象,以及選擇日志消息是否應(yīng)該向記錄器層傳遞。表1-17
方法
描述
void addHandler(Handler handler)
向記錄器添加一個Handler。可以添加多個處理程序。同時也要注意,根記錄器配置為具有一個默認的Handlers集
void removeHandler(Handler
handler)
移除此記錄器處理程序列表上的一個指定處理程序。如果沒有發(fā)現(xiàn)該處理程序,則此方法正常地返回
void setLevel(Level newLevel)
設(shè)置此記錄器將會使用的日志級別。低于記錄器值的消息級別將會被自動丟棄。如果傳入null,則該級別將從此記錄器的父記錄器繼承
void setParent(Logger parent)
設(shè)置此記錄器的父記錄器。它不應(yīng)該由應(yīng)用程序代碼所調(diào)用,因為它只能由日志系統(tǒng)使用
續(xù)表
方法
描述
void setUseParentHandlers (boolean useParentHandlers)
如果日志消息應(yīng)該被傳遞給它們的父記錄器,則指定true;如果阻止日志消息傳遞給它們的父記錄器,則指定false
Filter getFilter()
返回用于此記錄器的過濾程序,如果沒有過濾程序關(guān)聯(lián)將可能返回null
Handler[] getHandlers()
返回一個與此記錄器關(guān)聯(lián)的所有處理程序的數(shù)組
Level getLevel()
返回分配給此記錄器的日志級別,如果返回null,則表示將會使用父記錄器的日志級別
String getName()
返回此記錄器的名稱;如果這是一個匿名記錄器,則返回null
Logger getParent()
返回當(dāng)前記錄器的最近父記錄器;如果當(dāng)前記錄器是根記錄器,則返回null
ResourceBundle getResourceBundle()
返回與此記錄器關(guān)聯(lián)的ResourceBundle。資源綁定用來本地化日志消息。如果返回null,則將使用父記錄器的資源綁定
String getResourceBundleName()
返回此記錄器用于本地化的資源綁定名稱;如果資源綁定是繼承自該記錄器的父記錄器,則返回null
boolean getUseParentHandlers()
如果日志消息被傳遞給該記錄器的父記錄器,則返回true;如果日志消息沒有傳遞到該層級,則返回false
(4)記錄消息表1-18中給出的方法都使用一個Logger來實際記錄一個消息。這里為在每個日志級別上記錄消息以及進入、退出方法和拋出異常提供了便捷方法。還提供了額外的方法以使用資源綁定來本地化日志消息。
表1-18
方法
描述
void config(String msg)
Logger類包含了大量用于記錄消息的便捷方法。為快速記錄一個指定級別的消息,為每個日志級別定義了一個方法
void fine(String msg)
void finer(String msg)
void finest(String msg)
void info(String msg)
void severe(String msg)
void warning(String msg)
void entering(String sourceClass,
String sourceMethod)
首次調(diào)用一個方法時記錄一個消息。變體形式對方法使用一個參數(shù),或者一個參數(shù)數(shù)組,來提供方法調(diào)用的更詳細的追蹤信息。除其他有關(guān)方法調(diào)用的信息之外,記錄的消息還有ENTRY。日志級別是Level.FINER
void entering(String sourceClass,
String sourceMethod, Object param1)
void entering(String sourceClass,
String sourceMethod, Object params [])
續(xù)表
方法
描述
void exiting(String sourceClass, String sourceMethod)
在一個方法將要返回時記錄一個消息。日志消息包括RETURN,日志級別是Level.FINER。源類和源方法也都被記錄。
void exiting(String sourceClass, String sourceMethod, Object result)
boolean isLoggable(Level level)
檢查某個級別是否將被記錄。如果它將被記錄,則返回true;否則,返回false
void log(Level level, String msg)
一般標(biāo)準的記錄便捷方法。各種變體可以指定日志的一個參數(shù)或者參數(shù)數(shù)組,或者Throwable信息。這個信息被放置在LogRecord對象中并且被送到日志系統(tǒng)中,最后一個變體使用了一個LogRecord對象
void log(Level level, String msg, Object param1)
void log(Level level, String msg, Object [] params)
void log(Level level, String msg, Throw- able thrown)
void log(LogRecord record)
void logp(Level level, String source Class, String sourceMethod, String msg)
這些記錄方法采用了源類以及源方法名稱,同時還采用了其他信息。所有這些都被放置到一個LogRecord對象中并且被發(fā)送到系統(tǒng)中
void logp(Level level, String source Class, String sourceMethod, String msg, Object param1)
void logp(Level level, String source Class, String sourceMethod, String msg, Object[] params)
void logp(Level level, String source Class, String sourceMethod, String msg, Throwable thrown)
void logrb(Level level, String source Class, String sourceMethod, String bundle Name, String msg)
這些方法用于指定資源綁定以及其他信息。資源綁定將被用于本地化日志消息
void logrb(Level level, String source Class, String sourceMethod, String bundle Name, String msg, Object param1)
void logrb(Level level, String source Class, String sourceMethod, String bundle Name, String msg, Object[] params)
void logrb(Level level, String source Class, String sourceMethod, String bundle Name, String msg, Throwable thrown)
void throwing(String sourceClass, String sourceMethod, Throwable thrown)
記錄一個拋出的消息。日志級別是Level.FINER。日志記錄的消息被設(shè)置為THROW,并且thrown的內(nèi)容被放入日志記錄的thrown屬性中,而不是放入日志記錄的消息中
3.LogRecord類
LogRecord類封裝了一個日志消息,將消息在整個日志系統(tǒng)中傳送。Handler和Formatter使用LogRecord來獲取有關(guān)處理消息的更多信息(例如消息發(fā)送的時間以及日志級別等)。如果一個日志系統(tǒng)的客戶端擁有一個對LogRecord對象的引用,則該對象在它被傳送到日志系統(tǒng)中之后應(yīng)該不再被使用。(1)LogRecord方法LogRecord包含了大量方法用于檢查和操作日志記錄上的屬性,例如消息來源、日志記錄的級別,它是什么時候被發(fā)送到系統(tǒng)中的,以及任意相關(guān)聯(lián)的資源綁定。表1-19中列出了這些方法。
表1-19
方法
描述
Level getLevel()
返回日志記錄的級別
String getMessage()
返回日志消息在格式化/本地化之前的未格式化版本
long getMillis()
返回創(chuàng)建日志記錄所需的時間(以毫秒為單位)
Object[] getParameters()
返回日志記錄的參數(shù)數(shù)組;如果沒有設(shè)置參數(shù),則返回null
long getSequenceNumber()
返回日志記錄的序列號。在日志記錄的構(gòu)造方法中分配序列號來為每一個日志記錄創(chuàng)建一個惟一的序號
Throwable getThrown()
返回與此日志記錄關(guān)聯(lián)的Throwable,例如Exception(在一個異常被記錄的情況下)。如果沒有設(shè)置Throwable,則返回null
String getLoggerName()
返回記錄器的名稱,如果它是匿名記錄器,那么該名稱就可能為null
String getSourceClassName()
獲取可能已經(jīng)記錄消息的類的名稱。此信息可以被顯式指定,或者可以從棧追蹤中推斷得到,因此可能不準確
String getSourceMethodName()
獲取可能已經(jīng)記錄消息的方法的名稱。此信息可以被顯式指定,或者可以從棧追蹤推斷得到,因此可能不準確
int getThreadID
返回發(fā)起日志消息的線程的標(biāo)識符。這是在Java VM中的ID
(2)設(shè)置有關(guān)消息來源的信息表1-20中的方法用于設(shè)置日志消息的來源信息,例如關(guān)聯(lián)的異常、記錄消息的類和方法,以及發(fā)起線程的ID等。
表1-20
方法
描述
void setSourceClassName(String source ClassName)
設(shè)置日志消息所來自的類的名稱
void setSourceMethodName(String source MethodName)
設(shè)置日志消息所來自的方法的名稱
void setThreadID(int threadID)
設(shè)置日志消息所來自的線程的標(biāo)識符
void setThrown(Throwable thrown)
設(shè)置與日志消息相關(guān)聯(lián)的Throwable,可能為null
(3)資源綁定方法表1-21中的方法用于獲取和配置與日志消息一起使用的資源綁定。資源綁定用于本地化日志消息。
表1-21
方法
描述
ResourceBundle getResourceBundle()
返回與記錄器關(guān)聯(lián)的ResourceBundle,它用于本地化日志消息。如果沒有關(guān)聯(lián)的ResourceBundle,則有可能返回null
String getResourceBundleName()
返回用于本地化日志消息的資源綁定的名稱。如果日志消息不可本地化,則返回null(沒有定義資源綁定)
void setResourceBundle(Resource Bundle bundle)
設(shè)置資源綁定以用于本地化日志消息
void setResourceBundleName (String name)
設(shè)置資源綁定的名稱以用于本地化日志消息
(4)設(shè)置有關(guān)消息的信息表1-22中的方法配置日志消息自身??梢耘渲玫呐c日志消息相關(guān)聯(lián)的一些信息包括它的級別、消息的內(nèi)容以及消息被發(fā)送的時間。
表1-22
方法
描述
void setLevel(Level level)
設(shè)置日志消息的級別
void setLoggerName(String name)
設(shè)置發(fā)送此消息的記錄器的名稱,該名稱可能為null
void setMessage(String message)
設(shè)置格式化/本地化之前消息的內(nèi)容
void setMillis(long millis)
設(shè)置日志消息的時間(以毫秒為單位),從1970年開始記錄
void setParameters(Object [] parameters)
設(shè)置日志消息的參數(shù)
void setSequenceNumber(long seq)
設(shè)置日志消息的序列號。此方法不常被調(diào)用,因為構(gòu)造函數(shù)為每一個日志消息分配了一個惟一的序號
4.Level類
Level類定義日志級別的完整集合,并且該類的對象還代表一個特定的日志級別,該級別可被記錄器、處理程序等使用。如果需要,可以對該類進行子類化,然后定義自己的自定義級別,只要它們與現(xiàn)有的日志級別不沖突即可。(1)日志級別表1-23中的日志級別在Level類中有定義。表1-23
日志級別
描述
OFF
初始化為Integer.MAX_VALUE的特殊值。它將關(guān)閉日志功能
SEVERE
意味著嚴重失敗。初始化為1 000
續(xù)表
日志級別
描述
WARNING
意味著存在潛在問題。初始化為900
INFO
一般信息。初始化為800
CONFIG
意味著用于調(diào)試的有用消息。初始化為700
FINE
意味著最少量的追蹤信息。初始化為500
FINER
更詳細的追蹤信息。初始化為400
FINEST
最詳細級別的追蹤信息。初始化為300
ALL
特殊值。記錄所有消息。初始化為Integer.MIN_VALUE
(2)Level方法Level類定義了設(shè)置和獲取一個特定日志級別的方法。可以使用級別的數(shù)字和文本版本。見表1-24。表1-24
方法
描述
static Level parse(string name)
返回一個代表被傳入的級別名稱的Level對象。name字符串可以是日志級別之一,例如SEVERE或者CONFIG。Integer.MIN_VALUE和Integer.MAX_VALUE之間的任意數(shù)字都能夠被傳入(作為一個字符串)。如果該數(shù)字代表一個存在的級別值,則返回該級別。否則,返回一個與傳入數(shù)字值對應(yīng)的新Level。任意無效名稱或者數(shù)字都將導(dǎo)致拋出一個IllegalArgumentException。如果名稱為null,則拋出一個Null PointerException
boolean equals(Object ox)
如果被傳入的對象與當(dāng)前類級別相同,則返回true
String getLocalized Name()
返回當(dāng)前級別名稱的本地化版本,如果沒有本地化版本,則返回非本地化版本
String getName()
返回當(dāng)前級別名稱的非本地化版本
String getResourceBundle Name()
返回級別的本地化資源綁定名稱,如果沒有定義本地化資源綁定,則返回null
int hashCode()
返回一個基于級別值的哈希碼
int intValue()
返回當(dāng)前級別的整數(shù)值
String toString()
返回當(dāng)前級別的非本地化名稱
5.Handler類
Handler類用于接收日志消息,然后向一個外部目的地發(fā)布這些消息。目的地可能是存儲器、文件、數(shù)據(jù)庫、TCP/IP流,或者可以存儲日志消息的任意個位置。如同記錄器一樣,處理程序具有關(guān)聯(lián)的級別。低于處理程序上的級別的日志消息將被丟棄。每個特定Handler實例都具有自己的屬性,并且通常都配置在logging.properties文件中。下一節(jié)將討論java.util.logging包中的各種處理程序。創(chuàng)建一個自定義的處理程序是很簡單的,因為只需要實現(xiàn)close()、flush()和publish(LogRecord record)就可以了。(1)Handler方法Handler類定義了三種抽象的方法,它們需要繼承類中的指定行為。Handler類的其他方法用于處理消息編碼、過濾程序、格式化程序和錯誤處理程序。(2)關(guān)鍵抽象方法在開發(fā)一個自定義處理程序的時候,必須要覆蓋三個抽象方法。如表1-25所示。表1-25
方法
描述
abstract void close()
該方法應(yīng)該執(zhí)行一個flush(),然后釋放處理程序所使用的任意資源。在調(diào)用close()之后,不應(yīng)該再使用Handler
abstract void flush()
刷新任意緩存的輸出以確保它被保存到相關(guān)聯(lián)的資源中
abstract void publish(LogRecord record)
通過記錄器轉(zhuǎn)發(fā)一個日志消息,然后將它寫入相關(guān)聯(lián)的資源。消息應(yīng)該被格式化(使用Formatter)和本地化
(3)設(shè)置和獲取有關(guān)處理程序的信息表1-26中所列出的方法用于獲取有關(guān)處理程序的信息,例如它的編碼、相關(guān)聯(lián)的錯誤管理器、過濾程序、格式化程序和級別,以及設(shè)置配置信息。表1-26
方法
描述
String getEncoding()
返回字符編碼的名稱。如果名稱為null,則應(yīng)該使用默認編碼
ErrorManager getErrorManager()
返回與此處理程序相關(guān)聯(lián)的ErrorManager
Filter getFilter()
返回與此處理程序相關(guān)聯(lián)的Filter,它可能為null
Formatter getFormatter()
返回與此處理程序相關(guān)聯(lián)的Formatter,它可能為null
Leve lgetLevel()
返回此處理程序的級別。低于此級別的日志消息被丟棄
boolean isLoggable(LogRecord record)
如果傳入的LogRecord將由此處理程序記錄,則返回true。這項檢查包括將記錄級別同處理程序級別進行比較、測試過濾程序(如果它已經(jīng)被定義了)以及任意其他在處理程序中已定義的檢查
void setEncoding(String encoding)
將編碼設(shè)置為一種指定的字符編碼。如果傳入null,則使用默認平臺編碼
void setErrorManager
(Error Manager em)
設(shè)置處理程序的一個ErrorManager。如果在處理期間發(fā)生任意錯誤,則調(diào)用Error Manager的error方法
void setFilter(Filter new Filter)
設(shè)置一個自定義的過濾程序,它在調(diào)用publish方法時決定是丟棄或是保持一個日志消息
void setFormatter(Formatter newFormatter)
設(shè)置一個Formatter,用于在傳向處理程序的日志消息被寫入目的地之前對日志消息執(zhí)行自定義的格式化
void setLevel(Level newLevel)
設(shè)置處事程序的級別閾值。低于此級別的日志消息將自動被丟棄
(4)主要處理程序java.util.logging包包括了大量預(yù)定義處理程序來向通用目的地寫入日志消息。這些類包括ConsoleHandler、FileHandler、MemoryHandler、SocketHandler和StreamHandler。這些類提供Handler類中抽象方法的一個特定實現(xiàn)。表1-27中的所有屬性關(guān)鍵字名稱在實際的屬性文件中都被加上java.util.logging前綴。StreamHandler主要作為所有處理程序的一個基類,這些處理程序?qū)⑷罩鞠懭肽硞€Output Stream。StreamHandler的子類包括ConsoleHandler、FileHandler和SocketHandler。大量流處理代碼被內(nèi)置于此類中。有關(guān)StreamHandler的屬性列表,請參見表1-27。表1-27
屬性名稱
描述
默認值
StreamHandler.level
處理程序的日志級別
Level.INFO
StreamHandler.filter
要使用的過濾程序
未定義
StreamHandler.formatter
要使用的格式化程序
java.util.logging.SimpleFormatter
StreamHandler.encoding
要使用的字符集編碼
默認平臺編碼
表1-28中的方法在StreamHandler類中被定義/實現(xiàn)。表1-28
方法
描述
void close()
Formatter的head字符串將被寫入(若尚未寫入的話),tail字符串在流關(guān)閉之前寫入
void flush()
向流中寫入任意被緩沖的輸出(刷新流)
boolean isLoggable(LogRecord record)
執(zhí)行針對level和filter的標(biāo)準檢查,但是,如果沒有輸出流打開或者被傳入的記錄為null,則還是返回false
void publish(LogRecord record)
如果傳入的記錄是可記錄的,那么將調(diào)用Formatter來格式化該日志消息,然后將該消息寫入輸出流
void setEncoding(String encoding)
設(shè)置日志消息要使用的字符編碼。傳入null以使用當(dāng)前平臺的默認字符編碼
protected void setOutputStream(Output Stream out)
設(shè)置一個要使用的OutputStream。如果已經(jīng)打開了一個OutputStream,則它將被刷新并關(guān)閉。接著新的OutputStream將打開
ConsoleHandler向System.err寫入日志消息。它子類化了StreamHandler,但是覆蓋了close(),以便只執(zhí)行一個刷新,因此System.err流不會關(guān)閉。所使用的默認格式化程序是SimpleFormatter。表1-29描述了可以在ConsoleHandler的logging.properties文件中定義的屬性。表1-29
屬性名稱
描述
默認值
ConsoleHandler.level
處理程序的日志級別
Level.INFO
ConsoleHandler.filter
要使用的過濾程序
未定義
ConsoleHandler.formatter
要使用的格式化程序
java.util.logging.SimpleFormatter
ConsoleHandler.encoding
要使用的字符集編碼
默認平臺編碼
SocketHandler通過一個指定的TCP端口向網(wǎng)絡(luò)中寫入日志消息。表1-30中列出了Socket Handler所使用的屬性。默認的構(gòu)造方法使用已定義的屬性,第二個構(gòu)造方法允許通過Socket Handler(String host , int port)對主機和端口進行規(guī)格說明。Close()方法刷新并且關(guān)閉輸出流,publish()方法在每個記錄被寫入之后刷新該流。
表1-30
屬性名稱
描述
默認值
SocketHandler.level
處理程序的日志級別
Level.INFO
SocketHandler. filter
要使用的過濾程序
未定義
SocketHandler. formatter
要使用的格式化程序
java.util.logging.XMLFormatter
SocketHandler. encoding
要使用的字符集編碼
默認平臺編碼
SocketHandler. host
連接到的目標(biāo)主機名稱
未定義
SocketHandler.port
要使用的目標(biāo)TCP端口
未定義
FileHandler可以寫入到單個文件,或者在每個文件達到指定的最大長度時向文件的一個循環(huán)集寫入。序列中的下一個序號被添加到每個正在循環(huán)使用的文件名稱尾端,除非在別處指定了一個generation(序列)模式。FileHandler的屬性列在表1-31中。表1-31
屬性名稱
描述
默認值
FileHandler.level
處理程序的日志級別
Level.INFO
FileHandler.filter
要使用的過濾程序
未定義
FileHandler.formatter
要使用的格式化程序
java.util.logging.XMLFormatter
FileHandler.encoding
要使用的字符集編碼
默認平臺編碼
FileHandler.limit
指定寫入一個文件的最大字節(jié)數(shù)的一個近似值。0意味著沒有限制
0
FileHandler.count
指定有多少輸出文件要循環(huán)通過
1
FileHandler.pattern
用來生成輸出文件名的模式
%h/java%u.log
FileHandler.append
布爾值,指定是否附加到一個已經(jīng)存在的文件中,或者重寫該文件
false
FileHandler類支持文件名模式,允許路徑(例如用戶的主目錄或系統(tǒng)的臨時目錄)的替代。正斜杠(/)用作目錄分隔符,并且這對Unix和Windows機器都有效。同時受支持的是能夠在循環(huán)日志文件時指定,生成序號位于文件名中的何處。這些模式每個前面都加上百分號(%)。要在文件名中包含百分號,就必須指定兩個百分號(%%)。表1-32中包含了所有有效的百分號替代。
表1-32
模式
描述
模式
描述
%t
系統(tǒng)臨時目錄的完整路徑
%g
用于分辨循環(huán)日志的生成序號
%h
user.home系統(tǒng)屬性的值
%u
用于解決進程沖突的惟一序號
例如,如果在Windows 95中執(zhí)行它并且指定文件名模式%t/app_log.txt,那么FileHandler類將該模式擴展到C:\TEMP\app_log.txt。注意,%t和%h命令不包括后面的正斜杠。%u用來說明多個線程/進程何時將訪問相同的日志文件。只有一個進程能夠使文件打開以執(zhí)行寫入操作,因此為了防止日志信息的丟失,就可以使用%u來向一個與其他文件具有相似名稱的日志文件進行輸出。例如,可以指定文件名模式%t/logfile%u.txt,并且如果兩個進程打開此相同文件來輸出,那么第一個進程將打開C:\TEMP\logfile0.txt,第二個進程將打開C:\TEMP\logfile1. txt。MemoryHandler是存儲器中的一個循環(huán)緩沖器。它作為一種快速的方式用來實現(xiàn)存儲消息,這樣消息就必須發(fā)送到另一個處理程序,從而可以將它們寫入外部資源。因為緩沖器是循環(huán)的,所以較舊的日志記錄將最終被較新的日志記錄所覆蓋。格式化操作可以延遲到另一個Hander,這使得向一個MemoryHandler進行記錄變得非常快捷。將導(dǎo)致MemoryHandler向另一個Handler發(fā)送數(shù)據(jù)(推數(shù)據(jù))的條件如下所列: ● 被傳入的日志記錄比指定的pushLevel具有更高的級別。 ● 另一個類調(diào)用MemoryHander上的push方法。 ● 一個子類根據(jù)自定義標(biāo)準實現(xiàn)一個專業(yè)化行為來推數(shù)據(jù)。MemoryHandler的屬性列于表1-33中。
表1-33
屬性名稱
描述
默認值
MemoryHandler.level
處理程序的日志級別
Level.INFO
MemoryHandler.filter
要使用的過濾程序
未定義
MemoryHandler.size
循環(huán)緩沖的長度(以字節(jié)計算)
1 000
MemoryHandler.push
定義推級別,它是使得消息被發(fā)送到目標(biāo)處理程序的最小級別
Level.SEVERE
MemoryHandler.target
指定目標(biāo)Handler類的名稱
未定義
表1-34中的構(gòu)造函數(shù)創(chuàng)建一個帶有默認或者指定配置的MemoryHandler。MemoryHandler所提供的方法創(chuàng)建和配置存儲器處理程序的行為,表1-35列出了這些方法。
表1-34
構(gòu)造方法
描述
MemoryHandler()
基于配置屬性創(chuàng)建一個MemoryHandler
MemoryHandler(Handler target,int size, Level pushLevel)
創(chuàng)建一個帶有指定的目標(biāo)處理程序、緩沖器長度和推級別的MemoryHandler
表1-35
方法
描述
void publish(LogRecord record)
如果該記錄是可記錄的(參見isLoggable),則將它存儲在內(nèi)部緩沖器中。如果日志記錄級別大于等于pushLevel,那么所有被緩存的記錄(包括當(dāng)前的記錄)都將寫入目標(biāo)Handler
void close()
關(guān)閉處理程序并釋放相關(guān)聯(lián)的資源。同時調(diào)用目標(biāo)處理程序上的close
void flush()
導(dǎo)致一個flush,它與push不同。為了向一個目的地而不是存儲器實際寫入日志記錄,就必須執(zhí)行一個push操作
Level getPushLevel()
返回當(dāng)前的推級別
boolean isLoggable(LogRecord record)
比較日志級別,如果定義了過濾程序,則通過過濾程序運行記錄。此方法忽略記錄是否將導(dǎo)致一個push操作
void push()
將當(dāng)前緩沖器中的所有記錄發(fā)送到目標(biāo)處理程序中,同時清除該緩沖器
void setPushLevel(Level newLevel)
設(shè)置一個新的push級別
6.Formatter類
Formatter類用于對日志記錄執(zhí)行某種自定義的處理。這種格式化活動也許包括本地化、添加附加的程序信息(例如向日志記錄添加時間和日期),或者任意其他需要的處理。Formatter返回一個字符串,它是被處理的日志記錄。Formatter類也支持在所有日志記錄之前或者之后出現(xiàn)的head和tail字符串。本節(jié)稍后將會實現(xiàn)的一個示例是自定義Formatter,它將日志記錄寫入一個HTML表中。對于這個格式化程序,頭字符串將是<table>標(biāo)簽,尾字符串是</table>標(biāo)簽。Formatter類中定義的方法在表1-36中列出。
表1-36
方法
描述
abstract String format(LogRecord record)
執(zhí)行日志記錄的指定格式化并返回格式化過的字符串
String formatMessage(LogRecord record)
LogRecord中的消息字符串通過使用記錄的Resource Bundle來本地化,并且也根據(jù)java.text樣式格式化(替代諸如{0}之類的字符串)進行格式化
String getHead(Handler h)
返回指定處理程序的頭字符串,它可能為null
String getTail(Handler h)
返回指定處理程序的尾字符串,它可能為null
7.主要格式化程序
日志包提供了兩個有用的格式化程序。SimpleFormatter提供了一個格式化程序的基本實現(xiàn)。XMLFormatter按照預(yù)定義的XML格式輸出日志記錄。這兩個主要的格式化程序?qū)采w多種基本日志場景,但是如果需要這兩個格式化程序都未提供的行為,您也可以自己編寫格式化程序。(1)SimpleFormatterSimpleFormatter完成格式化日志消息所需的最小級別的工作。SimpleFormatter格式化方法返回一行或者多行被傳入的日志記錄摘要。使用SimpleFormatter記錄一個簡單的日志消息(例如test 1)將會顯示如下輸出:Apr 18, 2004 12:18:25 PM LoggingTest mainINFO: test 1SimpleFormatter格式化了消息中的日期、時間、源類名稱、源方法名稱,第二行是日志消息的級別和日志消息本身。(2)XMLFormatterXMLFormatter根據(jù)XML DTD格式化日志記錄??梢耘c任意字符編碼一起使用XMLForma- tter,但是建議只與“UTF-8”一起使用。getHead()和getTail()方法用來輸出XML文件的開始和結(jié)尾部分,這些內(nèi)容對于每個日志記錄來說并不需要重復(fù),但是對于創(chuàng)建一個有效的XML文件來說是必需的。下面是XMLFormatter的一個輸出示例:<?xml version="1.0" encoding="windows-1252" standalone="no"?><!DOCTYPE log SYSTEM "logger.dtd"><log><record> <date>2004-04-18T12:22:36</date> <millis>1082305356235</millis> <sequence>0</sequence> <logger>LoggingTest</logger> <level>INFO</level> <class>LoggingTest</class> <method>main</method> <thread>10</thread> <message>test 1</message></record><record> <date>2004-04-18T12:22:36</date> <millis>1082305356265</millis> <sequence>1</sequence> <logger>LoggingTest</logger> <level>INFO</level> <class>LoggingTest</class> <method>main</method> <thread>10</thread> <message>test 2</message></record></log>日志系統(tǒng)使用的XML DTD如下所示:<!-- DTD used by the java.util.logging. XMLFormatter --><!-- This provides an XML formatted log message. --><!-- The document type is "log" which consists of a sequenceof record elements --><!ELEMENT log (record*)><!-- Each logging call is described by a record element. --><!ELEMENT record (date, millis, sequence, logger?, level,class?, method?, thread?, message, key?, catalog?, param*, exception?)><!-- Date and time when LogRecord was created in ISO 8601 format --><!ELEMENT date (#PCDATA)><!-- Time when LogRecord was created in milliseconds sincemidnight January 1st, 1970, UTC. --><!ELEMENT millis (#PCDATA)><!-- Unique sequence number within source VM. --><!ELEMENT sequence (#PCDATA)><!-- Name of source Logger object. --><!ELEMENT logger (#PCDATA)><!-- Logging level, may be either one of the constantnames from java.util.logging. Constants (such as "SEVERE"or "WARNING") or an integer value such as "20". --><!ELEMENT level (#PCDATA)><!-- Fully qualified name of class that issuedlogging call, e.g. "javax.marsupial.Wombat". --><!ELEMENT class (#PCDATA)><!-- Name of method that issued logging call.It may be either an unqualified method name such as"fred" or it may include argument type informationin parenthesis, for example "fred(int,String)". --><!ELEMENT method (#PCDATA)><!-- Integer thread ID. --><!ELEMENT thread (#PCDATA)><!-- The message element contains the text string of a log message. --><!ELEMENT message (#PCDATA)><!-- If the message string was localized, the key element providesthe original localization message key. --><!ELEMENT key (#PCDATA)><!-- If the message string was localized, the catalog element providesthe logger's localization resource bundle name. --><!ELEMENT catalog (#PCDATA)><!-- If the message string was localized, each of the param elementsprovides the String value (obtained using Object.toString())of the corresponding LogRecord parameter. --><!ELEMENT param (#PCDATA)><!-- An exception consists of an optional message string followedby a series of StackFrames. Exception elements are usedfor Java exceptions and other java Throwables. --><!ELEMENT exception (message?, frame+)><!-- A frame describes one line in a Throwable backtrace. --><!ELEMENT frame (class, method, line?)><!-- an integer line number within a class's source file. --><!ELEMENT line (#PCDATA)>(3)創(chuàng)建自己的格式化程序開發(fā)一個自定義的Formatter并不是很困難。作為一個示例,這里給出一個前面提到過的HTML- TableFormatter的實現(xiàn)。輸出的HTML代碼內(nèi)容如下:<table border> <tr><th>Time</th><th>Log Message</th></tr> <tr><td>...</td><td>...</td></tr> <tr><td>...</td><td>...</td></tr></table>每個日志記錄以<tr>開始,并以</tr>結(jié)束,因為在每個表行中只有一個日志記錄。<table>標(biāo)簽和該表的第一行組成了頭字符串。</table>標(biāo)簽組成了日志記錄集合的尾部。自定義格式化程序只需要getHead()、getTail()和format(LogRecord record)方法的實現(xiàn):import java.util.logging.*;class HTMLTableFormatter extends java.util.logging. Formatter{ public String format(LogRecord record) { return(" <tr><td>" + record.getMillis() + "</td><td>" + record.getMessage() + "</td></tr>\n"); } public String getHead(Handler h) { return("<table border>\n" + "<tr><th>Time</th><th>Log Message</th></tr>\n"); } public String getTail(Handler h) { return("</table>\n"); }}8.Filter接口
過濾程序用于提供附加的標(biāo)準,以判斷是應(yīng)該丟棄還是應(yīng)該保留日志記錄。每個記錄器以及每個處理程序都具有一個已定義的過濾程序。Filter接口定義了單個方法:boolean isLoggable(LogRecord record)如果應(yīng)該發(fā)布日志消息,那么isLoggable方法將返回true;如果應(yīng)該丟棄日志消息,那么將返回false。9.創(chuàng)建自己的過濾程序
一個自定義過濾程序的示例是丟棄任意沒有以"client"開頭的日志消息的過濾程序。當(dāng)日志消息來自于大量源端,并且從一個特定的客戶端(或者多個客戶端)發(fā)出的每個日志消息都以"client"字符串為前綴時,這種過濾程序是非常有用的:import java.util.logging.*;public class ClientFilter implements java.util.logging. Filter{ public boolean isLoggable(LogRecord record) { if(record.getMessage().startsWith("client")) return(true); else return(false); }}10.ErrorManager
ErrorManager與處理程序相關(guān)聯(lián),用于處理發(fā)生的任意錯誤,例如拋出的異常。記錄器的客戶端很可能不關(guān)心或者無法處理錯誤,因此使用一個ErrorManager是Handler報告錯誤條件的一種靈活而且簡單的方式。錯誤管理器定義了單個方法:void error(String msg, Exception ex, int code)此方法的參數(shù)包括錯誤消息(一個字符串)、拋出的異常和一個表示發(fā)生了什么錯誤的代碼。這些代碼在ErrorManager類中被定義為靜態(tài)整數(shù),列出在表1-37中。
表1-37
錯誤代碼
描述
CLOSE_FAILURE
用于close()失敗時
FLUSH_FAILURE
用于flush()失敗時
FORMAT_FAILURE
用于任意原因引起的格式化失敗時
GENERIC_FAILURE
用于其他錯誤代碼不匹配的任意其他錯誤
OPEN_FAILURE
用于打開一個輸出源失敗時
WRITE_FAILURE
用于向一個輸出源寫入失敗時
11.記錄示例
默認情況下,日志消息通過層次結(jié)構(gòu)向上傳送到每一個父記錄器。下面這個小程序使用已命名的記錄器,使用XMLFormatter來記錄消息:import java.util.logging.*;public class LoggingExample1 { public static void main(String args[]) { try{ LogManager lm = LogManager.getLogManager(); Logger logger; FileHandler fh = new FileHandler("log_test.txt"); logger = Logger.getLogger("LoggingExample1"); lm.addLogger(logger); logger.setLevel(Level.INFO); fh.setFormatter(new XMLFormatter()); logger.addHandler(fh); // root logger defaults to SimpleFormatter. // We don't want messages logged twice. //logger.setUseParentHandlers(false); logger.log(Level. INFO, "test 1"); logger.log(Level. INFO, "test 2"); logger.log(Level.INFO, "test 3"); fh.close(); } catch(Exception e) { System.out.println("Exception thrown: "+ e); e.printStackTrace(); } }}這里發(fā)生的是XML輸出被發(fā)送到log_test.txt。此文件如下所示:<?xml version="l.0" encoding="windows-1252" standalone="no"?><!DOCTYPE log SYSTEM "logger.dtd"><log><record> <date>2004-04-20T2:09:55</date> <millis>1082472395876</millis> <sequence>0</sequence> <logger>LoggingExample1</logger> <level>INFO</level> <class>LoggingExample1</class> <method>main</method> <thread>10</thread> <message>test 1</message></record><record> <date>2004-04-20T2:09:56</date> <millis>1082472396096</millis> <sequence>1</sequence> <logger>LoggingExample1</logger> <level>INFO</level> <class>LoggingExample1</class> <method>main</method> <thread>10</thread> <message>test 2</message></record></log>因為日志消息接著被發(fā)送到父記錄器,所以消息也將使用SimpleFormatter被輸出到System. err。下面是輸出的內(nèi)容:Feb 11, 2004 2:09:55 PM LoggingExample1 mainINFO: test 1Feb 11, 2004 2:09:56 PM LoggingExample1 mainINFO: test 2這里有一個更詳細的示例,其中使用了已開發(fā)的HTMLTableFormatter。在父-子關(guān)系中定義了兩個記錄器ParentLogger和ChildLogger。父記錄器將使用XMLFormatter輸出到一個文本文件,子記錄器將使用HTMLTableFormatter輸出到一個不同的文件。默認情況下,根記錄器將會執(zhí)行,日志消息將會使用SimpleFormatter轉(zhuǎn)到控制臺。HTMLTableFormatter被擴展成一個HTMLFormatter,從而生成一個完整的HTML文件(而不僅僅是表格標(biāo)簽):import java.util.logging.*;import java.util.*;class HTMLFormatter extends java.util.logging. Formatter{ public String format(LogRecord record) { return(" <tr><td>" + (new Date(record.getMillis())).toString() + "</td>" + "<td>" + record.getMessage() + "</td></tr>\n"); } public String getHead(Handler h) { return("<html>\n <body>\n" + " <table border>\n "+ "<tr><th>Time</th><th>Log Message</th></tr>\n"); } public String getTail(Handler h) { return(" </table>\n </body>\n</html>"); }}public class LoggingExample2 { public static void main(String args[]) { try { LogManager lm = LogManager.getLogManager(); Logger parentLogger, childLogger; FileHandler xml_handler = new FileHandler("log_output.xml"); FileHandler html_handler = new FileHandler("log_output.html"); parentLogger = Logger.getLogger("ParentLogger"); childLogger = Logger.getLogger("ParentLogger.ChildLogger"); lm.addLogger(parentLogger); lm.addLogger(childLogger); // log all messages, WARNING and above parentLogger.setLevel(Level.WARNING); // log ALL messages childLogger.setLevel(Level.ALL); xml_handler.setFormatter(new XMLFormatter()); html_handler.setFormatter(new HTMLFormatter()); parentLogger.addHandler(xml_handler); childLogger.addHandler(html_handler); childLogger.log(Level.FINE, "This is a fine log message"); childLogger.log(Level.SEVERE, "This is a severe log message"); xml_handler.close(); html_handler.close(); } catch(Exception e) { System.out.println("Exception thrown: " + e); e.printStackTrace(); } }}下面是輸出到屏幕的內(nèi)容:Apr 20, 2004 12:43:09 PM LoggingExample2 mainSEVERE: This is a severe log message下面是輸出到log_output.xml文件的內(nèi)容:<?xml version="1.0" encoding="windows-1252" standalone="no"?><!DOCTYPE log SYSTEM "logger.dtd"><log><record> <date>2004-04-20T12:43:09</date> <millis>1082479389122</millis> <sequence>0</sequence> <logger>ParentLogger. ChildLogger</logger> <level>FINE</level> <class>LoggingExample2</class> <method>main</method> <thread>10</thread> <message>This is a fine log message</message></record><record> <date>2004-04-20T12:43:09</date> <millis>1082479389242</millis> <sequence>1</sequence> <logger>ParentLogger. ChildLogger</logger> <level>SEVERE</level> <class>LoggingExample2</class> <method>main</method> <thread>10</thread> <message>This is a severe log message</message></record></log>log_output.html文件的內(nèi)容如下所示:<html> <body> <table border> <tr><th>Time</th><th>Log Message</th></tr> <tr><td>Tue Apr 20 12:43:09 EDT 2004</td><td>This is a fine log
message</td></tr> <tr><td>Tue Apr 20 12:43:09 EDT 2004</td><td>This is a severe log message</td></tr> </table> </body></html>注意,默認情況下,對于根記錄器來說,日志消息的級別是INFO或者高于該級別。然而,由于ParentLogger僅僅關(guān)注WARNING以及高于該級別的日志消息,對于低于該級別的日志消息都會立即丟棄。HTML文件包含所有日志消息,因為ChildLogger被設(shè)計成可以處理所有日志消息。XML文件只包含一個SEVERE日志消息,因為所有低于WARNING級別的日志消息都丟棄了。12.正則表達式
正則表達式是一種強大的工具,可以用于解決涉及搜索、隔離和/或替代字符串中文本塊的問題。有關(guān)正則表達式的討論內(nèi)容是相當(dāng)多的,其內(nèi)容可以獨自形成一本書,實際上已經(jīng)有了有關(guān)正則表達式的書籍出版。本節(jié)對正則表達式提供一個概述,并討論Sun公司在java.util.regex包中對它的支持。正則表達式通過使用一個簡單的解析器來降低大量繁冗工作,該解析器提供了復(fù)雜的模式匹配功能。正則表達式可用于處理任意類型的文本。有關(guān)正則表達式更多更深的內(nèi)容,請參考有關(guān)正則表達式的另一本書籍。即使從來沒有在語言中見過正則表達式,但是也極有可能在Unix/DOS/Windows系統(tǒng)中見過用于文件掩碼的正則表達式的一個小子集。例如,可能見過下面位于一個目錄中的文件:Test.javaTest.classStringProcessor.javaStringProcessor.classToken.javaToken.class可以在命令行中鍵入*.*(在DOS/Windows系統(tǒng)中),則每一個文件都將進行匹配并且列出。星號用任意字符串替代,點號按照字面意義使用。如果使用文件掩碼T*.class,則只有兩個文件將被匹配,即Test.class和Token.class。星號被認為是元字符,點號及字母被認為是正常字符。元字符是正則表達式“語言”的一部分,Java具有豐富的元字符集,遠遠超過了文件掩碼中的簡單支持。正常字符根據(jù)正被測試的字符串逐字進行匹配。同時還有一個工具用來逐字解釋正則表達式語言中的元字符。在本節(jié)將考查幾個使用正則表達式的示例。作為第一個示例,假設(shè)希望生成一個在class關(guān)鍵字前沒有修飾符的Java文件中所有類的列表。如果只需要對源代碼的單一行進行檢驗,那么現(xiàn)在要做的就是忽略字符串class之前的任意空白符,然后就可以生成該列表。傳統(tǒng)上的一種方法將需要找到字符串中第一個出現(xiàn)的class,這樣就可以確保在字符串之前只有空白符而沒有其他內(nèi)容。若使用正則表達式,這個任務(wù)就變得更簡單了。簡單查看一下整個Java正則表達式語言,這種情況下需要的正則表達式是\s*class。反斜杠用來指定一個元字符,在本例中,\s匹配任意的空白符。*是另一個元字符,代表“0次或者多次出現(xiàn)的前面的術(shù)語”。單詞class是按照字面意義來處理的,因此該模式表示匹配空白符(如果存在的話),然后匹配class。使用此模式的Java代碼如下所示:Pattern pattern = Pattern.compile("\\s*class");// Need two backslashes to preserve the backslashMatcher matcher = pattern.matcher("\t\t class");if(matcher.matches()) { System.out.println("The pattern matches the string");} else { System.out.println("The pattern does not match the string");}此示例使用一個正則表達式(存儲在Pattern對象中),然后使用一個匹配程序來查看該正則表達式是否匹配一個特定字符串。這是Java中正則表達式例程的簡單使用。參看圖1-2,可以對正則表達式類如何相互作用有一個大致的了解。

圖1-2正則表達式庫的設(shè)計人員決定使用一個Pattern-Matcher(模式-匹配程序)模型,它將正則表達式與匹配程序自身分隔開。正則表達式由Pattern類編譯成一個更優(yōu)化的形式。那么此已編譯的模式就可以被多個匹配程序使用,或者可以被相同的匹配程序重用于不同的字符串。在一個正則表達式中,任意單一的字符都按字面意義進行匹配,一些特殊的情況除外。其中的一個特殊情況就是點號(.),它匹配正在被分析的字符串中的任意單個字符。提供了預(yù)定義的元字符集以匹配特定的字符。它們列在表1-38中。
表1-38
元字符
匹配內(nèi)容
\\
單個反斜杠
\ On
一個描述字符的八進制值,其中n是一個滿足0≤n≤7的數(shù)
\ Onn
\ Omnn
一個描述字符的八進制值,其中0≤m≤3并且0≤n≤7
\ Oxhh
用十六進制值hh描述的字符(其中0≤h≤F)
續(xù)表
元字符
匹配內(nèi)容
\ uhhhh
用十六進制值hhhh描述的字符(其中0≤h≤F)
\ t
一個tab制表符(字符'\u0009')
\ n
一個新行(換行符)('\u000A')
\ r
一個回車符('\u000D')
\ f
一個換頁符('\u000C')
\ a
一個鈴聲/嘟嘟聲字符('\u0007')
\ e
一個換碼符('\u001B')
\ cx
與x相對應(yīng)的控制字符,例如\cc表示control-c
.
任意單個字符
正則表達式語言同時也提供一些元字符來匹配確定的字符串邊界。這些邊界中的一部分表示一行的開始與結(jié)束,以及單詞的開始與結(jié)束。表1-39中列出了完整的邊界元字符清單。
表1-39
元字符
匹配內(nèi)容
^
行的開始
$
行的結(jié)束
\b
一個單詞的邊界
\B
一個非單詞的邊界
\A
輸入的開始
\G
以前匹配的結(jié)束
\Z
在任意行結(jié)束符(例如回車符或者換行符)之前輸入的結(jié)束
\z
輸入的結(jié)束
正則表達式語言也擁有字符類,這些類可以用來指定一個可能的字符列表,這些字符可以匹配需要匹配的字符串中的任意單個字符。如果想要顯式指定一個字符類,那么可以在方括號中指定這些字符。因此,字符類[0123456789]匹配任意單個數(shù)字。也有可能在左方括號之后使用脫字符號(^)來指定“除了這些指定字符之外的任意字符”。使用表達式[^012],除了0、1和2之外的任意單個數(shù)字將被匹配??梢允褂闷普厶栔付ㄗ址秶?。字符類[a-z]匹配任意單個的小寫字母,而[^a-z]則匹配除了小寫字母之外的任意字符??梢允褂萌我庾址秶?,例如[0-9]表示匹配一個單個的數(shù)字,或者[0-3]表示匹配0、1、2或者3??梢灾付ǘ鄠€范圍,例如[a-zA-Z]匹配任意單個的字母。正則表達式包包含了一個預(yù)定義字符類集,表1-40中列出了這些字符類。此外,還有POSIX字符類和Java字符類。這些分別列于表1-41和表1-42中。
表1-40
字符類元字符
匹配內(nèi)容
字符類元字符
匹配內(nèi)容
.
任意單個字符
\S
一個非空白符[^\s]
\d
一個數(shù)字[0-9]
\w
一個單詞字符[a-zA-Z_0-9]
\D
一個非數(shù)字[^0-9]
\W
一個非單詞字符[^\W]
\s
一個空白符[\t\n\x0B\f\r]
表1-41
字符類元字符
匹配內(nèi)容
\p{Lower}
小寫字母[a-z]
\p{Upper}
大寫字母[A-Z]
\p{ASCII}
所有ASCII字符[\ x00-\ x7F]
\p{Alpha}
任意大寫或者小寫字母
\p{Digit}
數(shù)字[0-9]
\p{Alnum}
任意字母或者數(shù)字
\p{Punct}
標(biāo)點符號[!" # $ % & ' ( ) * + , -. / : ; < => ? @ [ \ ] ^ ‘{ | } ~ ]
\p{Graph}
一個可視化字符:任意字母、數(shù)字,或者標(biāo)點符號
\p{Print}
一個可打印字符;與\ p {Graph}相同
\p{Blank}
一個空白字符或者tab制表符[ \ t]
\p{Cntrl}
一個控制字符[\ x00-x1F \ x7F]
\p{XDigit}
十六進制數(shù)字[0-9a-fA-F]
\p{Space}
一個空白字符[ \ t \ n \ x0B\ f \ r]
表1-42
字符類
匹配內(nèi)容
\p{javaLowerCase}
Character.isLowerCase()匹配的任意內(nèi)容
\p{javaUpperCase}
Character.isUpperCase()匹配的任意內(nèi)容
\p{javaWhitespace}
Character.isWhitespace()匹配的任意內(nèi)容
\p{javaMirrored}
Character.isMirrored()匹配的任意內(nèi)容
正則表達式語言的另一個特性是具有按照指定次數(shù)匹配一個特定字符的能力。在前面的示例中,*被用來匹配0個或多個空白字符。重復(fù)操作符有兩種通用的工作方式。一種操作符是貪婪式的,也就是說,它們盡可能進行匹配,一直匹配到末尾。另一種是勉強式的(或惰性的),只是遇到第一次匹配時就結(jié)束匹配。例如,正則表達式 . *;匹配任意數(shù)量的字符,一直到它找到最后的分號為止。為了只匹配第一個分號,就必須使用惰性操作版本 . * ?;。表1-43、表1-44中分別列出了所有的貪婪式操作符和勉強式操作符版本。
表1-43
貪婪式操作符
描述
貪婪式操作符
描述
X?
匹配0次或者一次X
X{n}
剛好匹配n次X,其中n可以是任意數(shù)字
X*
匹配0次或者多次X
X{n,}
匹配至少n次X
X+
匹配一次或者多次X
X{n,m}
匹配至少n次但是不超過m次X
表1-44
勉強式(或惰性)操作符
描述
X??
匹配0次或者一次X
X*?
匹配0次或者多次X
X+?
匹配一次或者多次X
X{n}?
剛好匹配n次X,其中n可以是任意數(shù)字
X{n,}?
匹配至少n次X
X{n,m}?
匹配至少n次但是不超過m次X
該語言也支持通過在正則表達式中使用圓括號來捕獲成組匹配的字符。向后引用可以用來引用這些匹配子組中的一個組。一個向后引用可以由一個反斜杠后跟與該子組序號對應(yīng)的一個數(shù)字來引用。在字符串(A(B))中,0組指的是整個表達式,然后從每個左圓括號后的子組開始編號。這樣,A(B)是第一個子組,B是第二個子組。向后引用允許匹配一個字符串。例如,如果希望匹配在一行中兩次出現(xiàn)的相同單詞,就可以使用[([a-zA-Z]) \ b\ 1]。記住\ b代表一個單詞邊界。因為用于字母的字符類是在圓括號中,所以匹配的文本則只能使用向后引用元字符\ 1來引用。13.Pattern類
Pattern類負責(zé)編譯和存儲一個指定的正則表達式。有幾個控制處理正則表達式方式的標(biāo)志。編譯regex以提供高效使用。正則表達式的文本表示形式意味著程序員易于使用和理解,見表1-45。
表1-45
方法
描述
static Pattern compile(String regex)
編譯方法接受一個字符串中的正則表達式并將其進行編譯,以供內(nèi)部使用。變體形式允許指定標(biāo)志,這些標(biāo)志可以修改正則表達式如何被處理
static Pattern compile(String regex,int flags)
static boolean matches(String regex,CharSequence input)
編譯一個指定的正則表達式并且將其同input進行匹配。如果正則表達式描述了輸入的數(shù)據(jù),則返回true;否則返回false。使用這種方式只是為了快速匹配。要對不同的輸入重復(fù)地匹配正則表達式,正則表達式就應(yīng)該只編譯一次
續(xù)表
方法
描述
static String quote(String s)
返回將會匹配被傳入的字符串的一個按照字面意義來表示的正則表達式。返回的字符串以\Q開頭,后跟被傳入的字符串,以\E結(jié)束。這些用于引用一個字符串,因此正則表達式語言中的元字符所表示的意義將會按照字面意義來處理
int flags()
返回包含了何時編譯正則表達式的標(biāo)志集合的一個整數(shù)
Matcher matcher(CharSequence input)
返回一個Matcher以用來對指定的輸入類型進行匹配
String pattern()
返回用于創(chuàng)建模式的正則表達式
String[] split(CharSequence input)
String[]split(CharSequence input,int limit)
在使用正則表達式作為分隔符將輸入分隔成塊之后返回一個字符串?dāng)?shù)組。limit可用于限制正則表達式被匹配的次數(shù)。匹配文本不能置于數(shù)組中。如果limit是正數(shù),那么模式將至少被應(yīng)用“limit減1”次。如果limit是0,那么模式將被應(yīng)用盡可能多的次數(shù),并將刪除末尾的空字符串。如果limit是負數(shù),那么模式將被應(yīng)用盡可能多的次數(shù),末尾的空串被留在數(shù)組中
14.Matcher類
Matcher類使用一個模式同一個輸入字符串進行比較,并且執(zhí)行范圍廣闊的有用任務(wù)。Matcher類提供多個方法,用于獲取大量的信息(例如模式是在字符串的什么位置進行匹配的)、使用其他的字符串來替代字符串的一個匹配子集,以及其他有用的操作。表1-46中列出了這些方法。表1-46
方法
描述
static String quoteReplacement (String s)
返回一個字符串,該字符串通過\Q和\E引用,可以用來按照字面意義同其他的輸入進行匹配
Matcher appendReplacement (StringBuffer sb,String replacement)
首先將所有要匹配的字符附加到字符串緩沖器中,然后使用replacement來代替匹配的文本,接著在匹配的文本之后的某個位置處設(shè)置索引,以準備下一次對此方法的調(diào)用。在最后一次匹配之后,使用appendTail來附加剩余的輸入
StringBuffer appendTail(String Buffer sb)
將輸入序列的剩余部分附加到已被傳入的字符串緩沖器中
MatchResult asResult()
返回一個對描述匹配程序狀態(tài)的MatchResult的引用
int end()
返回用于最后匹配的結(jié)束位置的索引
int end(int group)
返回用于一個指定捕獲組的結(jié)束位置的索引
boolean find()
如果發(fā)現(xiàn)一個匹配是始于緊接前一個匹配之后的索引時,或者在匹配程序已經(jīng)被重置的情況下始于一行的開始時返回true
boolean find(int start)
重置匹配程序并且試圖匹配從位置Start處開始的輸入文本模式。如果發(fā)現(xiàn)匹配,則返回true
boolean hitEnd()
如果最后匹配達到輸入的末尾,則返回true
boolean requireEnd()
如果更多的輸入能將正向匹配變成負向匹配,則返回true
續(xù)表
方法
描述
boolean lookingAt()
如果模式匹配,但是不需要該模式必須完全匹配輸入文本,則返回true
boolean matches()
如果模式匹配字符串,則返回true。該模式必須描述此方法的整個字符串來返回true。對于部分匹配,則使用find()或者lookingAr()
Pattern pattern()
返回當(dāng)前在匹配程序中使用的模式的一個引用
Matcher reset()
完全重置匹配程序的狀態(tài)
Matcher reset(CharSequence input)
完全重置匹配程序的狀態(tài),并且向input設(shè)置新輸入
int start()
返回前一個匹配的起始位置
int start(int group)
返回一個指定捕獲組的起始位置
Matcher usePattern(Pattern newPattern)
設(shè)置一個新的模式以用于匹配。輸入的當(dāng)前位置沒有改變
String group()
返回一個包含前一個匹配內(nèi)容的字符串
String group(int group)
返回一個包含一個特定被匹配組內(nèi)容的字符串。第0個組總是表示整個表達式
int groupCount()
返回在匹配程序模式中的捕獲組的數(shù)量
Matcher region(int start,int end)
返回一個Matcher,它被限制為字符串的一個子字符串以用于搜索。脫字符號(^)和美元符號($)元字符將在所定義區(qū)域的開始和結(jié)束處匹配
int regionEnd()
返回當(dāng)前定義區(qū)域的結(jié)束索引(實際檢查匹配的最后位置的索引)
int regionStart()
返回當(dāng)前定義區(qū)域的起始索引
String replaceAll(String replacement)
使用字符串replacement替代所有匹配了模式出現(xiàn)的字符串。如果在此方法調(diào)用之后Matcher仍然被使用,則它應(yīng)該重置
String replaceFirst(String replacement)
僅使用字符串replacement替代匹配模式的第一個字符串。如果在此方法調(diào)用之后Matcher仍然被使用,則它應(yīng)該重置
15.MatchResult接口
MatchResult接口包含了組方法、start和end方法,以便提供一個允許描述Matcher當(dāng)前狀態(tài)的完整方法集。Matcher類實現(xiàn)了此接口并且定義了所有這些方法。toMatchResult方法返回MatchResult的一個句柄,提供它用以保存和處理Matcher類的當(dāng)前狀態(tài)。16.正則表達式示例
使用Pattern/Matcher類來處理一個Java源代碼文件。不是公用的所有類將被列出(實際上指所有沒有修改符的類),并且使用反索引將雙寫的單詞(例如在一行中的兩個標(biāo)識符)列出。輸入源代碼文件(它沒有編譯)如下所示:import java.util.*;class EmptyClass {}class MyArrayList extends extends ArrayList {}public class RETestSource { public static void main(String args[]) { System.out.println("Sample RE test test source code code"); }}使用正則表達式處理此源代碼的程序如下所示:import java.util.*.import java.util.regex.*;import java.io.*;public class RegExpExample { public static void main(String args[]) { String fileName = "RETestSource.java"; String unadornedClassRE = "^\\s*class (
程序一般需要按照某種方式存儲配置信息,該方式容易修改配置信息并且可以將信息輸出到程序自身之外。Java提供了用于存儲和獲取系統(tǒng)定義和用戶定義的配置信息的實用程序類。用戶和系統(tǒng)信息有各自獨立的層次結(jié)構(gòu)。所有用戶共享系統(tǒng)樹中定義的首選項信息;每個用戶都擁有自己的樹結(jié)構(gòu)以配置與其他用戶分隔的數(shù)據(jù)。這允許自定義配置,包括重寫系統(tǒng)值。首選項類庫的核心是抽象類java.util.prefs.Preferences。此類定義了一個方法集,該集提供首選項類庫的所有特性。首選項層次結(jié)構(gòu)中的每個節(jié)點都具有一個名稱,它并不一定是惟一的。首選項樹的根節(jié)點將空字符串("")作為它的名稱。正斜杠用作首選項節(jié)點名稱的分隔符,這與Unix上使用它作為目錄名稱的分隔符非常相似。只有兩個字符串不是有效的節(jié)點名稱,一個是空字符串(因為它是給根節(jié)點預(yù)留的),一個是正斜杠自身(因為它是一個節(jié)點分隔符)。根節(jié)點的路徑是正斜杠自身。同使用目錄非常相似,絕對和相對路徑都可以使用。絕對路徑總是以正斜杠開始,因為絕對路徑總是從根節(jié)點開始,沿著樹向下直到一個指定節(jié)點。相對路徑從來不是以正斜杠開始。只要一個路徑的路徑名中沒有連續(xù)的正斜杠,那么該路徑就是有效的,除了根的路徑之外其他路徑都不會以正斜杠結(jié)束。因為首選項是通過第三方實現(xiàn)者實現(xiàn)的,因此對首選項的修改永遠不會立即寫入后備存儲器。單個節(jié)點名稱及其任意關(guān)鍵字的最大長度都是80個字符長。一個節(jié)點中字符串值的最大長度是8192個字符長。1.Preferences類
Preferences類是用于處理首選項的主要類。它表示首選項樹的一個節(jié)點,并且包含操作此樹及該樹中節(jié)點的大量方法。它基本上是使用首選項的單站場所。接下來的一節(jié)將概述Preferences方法。 (1)對首選項樹的操作Preferences類定義了大量方法,這些方法用于創(chuàng)建或者刪除節(jié)點,以及獲取樹中的確定節(jié)點,見表1-47。表1-47
返回一個指定的節(jié)點。如果節(jié)點不存在,則將創(chuàng)建(所有不存在的祖先節(jié)點都將被創(chuàng)建)該節(jié)點并且返回它
移除此首選項節(jié)點及其所有的子節(jié)點。在一個節(jié)點被移除之后能夠被調(diào)用的方法只有name()、absolutePath()、isUserNode()、flush()、nodeExists("")和從Object繼承而來的方法。所有其他的方法都將拋出IllegalStateException。在調(diào)用flush()對樹進行持久性修改之前,這種移除可能不是永久移除
對于沒有包的類,返回的節(jié)點名稱照字面意義就是<unnamed>。不應(yīng)該長期使用此節(jié)點,因為它是由使用它的所有程序員共享的,因此配置設(shè)置沒被分隔開。
(2)獲取有關(guān)節(jié)點的信息每一個節(jié)點都具有與其相關(guān)聯(lián)的信息,例如它的路徑、父和子節(jié)點,以及該節(jié)點的名稱。表1-48中列出了操作此信息的方法。
(3)獲取節(jié)點的首選項值表1-49中的方法同Hashtable類中的方法行為極為相似。關(guān)鍵不同的一點是對于絕大多數(shù)基本類型來說,存在著多種get版本。每一個類型都與一個特定的關(guān)鍵字相關(guān)聯(lián),這個關(guān)鍵字是代表配置參數(shù)名稱的一個字符串。
返回與一個指定關(guān)鍵字相關(guān)聯(lián)的字符串。如果關(guān)鍵字不存在,則使用默認值def來創(chuàng)建它,并且將此默認值返回
返回與一個指定關(guān)鍵字相關(guān)聯(lián)的boolean值。如果關(guān)鍵字不存在,則使用默認值def來創(chuàng)建它,并且將此默認值返回
返回與一個指定關(guān)鍵字相關(guān)聯(lián)的byte數(shù)組。如果關(guān)鍵字不存在,則使用默認值def來創(chuàng)建它,并且將此默認值返回
返回與一個指定關(guān)鍵字關(guān)聯(lián)的double值。如果關(guān)鍵字不存在,則使用默認值def來創(chuàng)建它,并且將此默認值返回
返回與一個指定關(guān)鍵字相關(guān)聯(lián)的float值。如果關(guān)鍵字不存在,則使用默認值def來創(chuàng)建它,并且將此默認值返回
返回與一個指定關(guān)鍵字相關(guān)聯(lián)的integer值。如果關(guān)鍵字不存在,則使用默認值def來創(chuàng)建它,并且將此默認值返回
返回與一個指定關(guān)鍵字相關(guān)聯(lián)的long值。如果關(guān)鍵字不存在,則使用默認值def來創(chuàng)建它,并且將此默認值返回
(4)設(shè)置節(jié)點上的首選項值同每個get方法一起使用的是put版本,它用于設(shè)置與一個給定配置參數(shù)的關(guān)鍵字名稱相關(guān)聯(lián)的信息,見表1-50。
這些方法向一個特定類型設(shè)置一個配置參數(shù)(它的名稱作為key傳入)。如果key或者value為null,則將會拋出一個異常。key至多可以有80個字符長(在MAX_KEY_LENGTH中定義),其值最大可以達到8 192個字符數(shù)(在MAX_VALUE_ LENGTH中定義)
(5)事件為Preferences類定義了兩個事件,一個事件是在首選項樹中的節(jié)點被修改時會觸發(fā),第二個事件是在一個首選項被修改時會觸發(fā)。用于這些事件的方法列于表1-51中。
(6)其他操作表1-52中列出了Preferences類中的其他方法,例如將任意附加的更改寫入后備存儲器,將首先項層次結(jié)構(gòu)重置成空,將首選項層次結(jié)構(gòu)保存到磁盤中,以及其他一些操作。表1-52
將該節(jié)點(僅限于當(dāng)前節(jié)點)的全部內(nèi)容作為一個XML文件(在下一小節(jié)中列出的preferences.dtd之后)寫入輸出流中
將此節(jié)點以及首選項樹中位于該節(jié)點之下的所有節(jié)點的全部內(nèi)容作為一個XML文件(在下一小節(jié)中列出的preferences.dtd之后)寫入輸出流中
Preferences系統(tǒng)定義了一個向XML文件導(dǎo)出整個關(guān)鍵字/值的完整樹的標(biāo)準操作。在http://java.sun. com/dtd /preferences.dtd上可以訪問到此XML文件的DTD。下面列出了此DTD的內(nèi)容:<?xml version="1.0" encoding="UTF-8"?> <!-- DTD for a Preferences tree. --> <!-- The preferences element is at the root of an XML document representing a Preferences tree. --> <!ELEMENT preferences (root)> <!-- The preferences element contains an optional version attribute, which specifies version of DTD. --> <!ATTLIST preferences EXTERNAL_XML_VERSION CDATA "0.0" > <!-- The root element has a map representing the root's preferences (if any), and one node for each child of the root (if any). --> <!ELEMENT root (map, node*) > <!-- Additionally, the root contains a type attribute, which specifies whether it's the system or user root. --> <!ATTLIST root type (system|user) #REQUIRED > <!-- Each node has a map representing its preferences (if any), and one node for each child (if any). --> <!ELEMENT node (map, node*) > <!-- Additionally, each node has a name attribute --> <!ATTLIST node name CDATA #REQUIRED > <!-- A map represents the preferences stored at a node (if any). --> <!ELEMENT map (entry*) > <!-- An entry represents a single preference, which is simply a key-value pair. --> <!ELEMENT entry EMPTY > <!ATTLIST entry key CDATA #REQUIRED value CDATA #REQUIRED >3.使用首選項
下列示例設(shè)置了用戶樹的一個節(jié)點中的幾個首選項,打印有關(guān)該節(jié)點的信息,然后將該信息導(dǎo)出到一個XML文件中:import java.util.*;import java.util.prefs.*;import java.io.*;public class PreferenceExample { public void printInformation(Preferences p) throws BackingStoreException { System.out.println("Node's absolute path: "+ p.absolutePath()); System.out.print("Node's children: "); for(String s : p.childrenNames()) { System.out.print(s +" "); } System.out.println(""); System.out.print("Node's keys: "); for(String s : p.keys()) { System.out.print(s +" "); } System.out.println(""); System.out.println("Node's name: "+ p.name()); System.out.println("Node's parent: "+ p.parent()); System.out.println("NODE: "+ p); System.out.println("userNodeForPackage: " + Preferences.userNodeForPackage(PreferenceExample.class)); System.out.println("All information in node"); for(String s : p.keys()) { System.out.println(" "+ s + "=" + p.get(s, "")); } } public void setSomeProperties(Preferences p) throws BackingStoreException { p.put("fruit", "apple"); p.put("cost", "1.01"); p.put("store", "safeway"); } public void exportToFile(Preferences p, String fileName) throws BackingStoreException { try { FileOutputStream fos = new FileOutputStream(fileName); p.exportSubtree(fos); fos.close(); } catch(IOException ioe) { System.out.println("IOException in exportToFile\n" + ioe); ioe.printStackTrace (); } } public static void main(String args[]) { PreferenceExample pe = new PreferenceExample(); Preferences prefsRoot = Preferences.userRoot(); Preferences myPrefs = prefsRoot.node("PreferenceExample"); try { pe.setSomeProperties(myPrefs); pe.printInformation(myPrefs); pe.exportToFile(myPrefs, "prefs.xml"); } catch(BackingStoreException bse) { System.out.println("Problem with accessing the backing store\n" + bse); bse.printStackTrace(); } }}屏幕的輸出如下所示:Node's absolute path: /PreferenceExampleNode's children:Node's keys: fruit cost storeNode's name: PreferenceExampleNode's parent: User Preference Node: /NODE: User Preference Node: /PreferenceExampleuserNodeForPackage: User Preference Node: /<unnamed>All information in node fruit = apple cost = 1.01 store = safewayXML文件中的導(dǎo)出信息顯示如下:<?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="PreferenceExample"> <map> <entry key="fruit" value="apple"/> <entry key="cost" value="1.01"/> <entry key="store" value="safeway"/> </map> </node> </root></preferences>