打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Java中几个关键的实用程序库
描述了Java中几个关键的实用程序库。这些库包括:  ●    Java日志(Java logging):一个强大的日志系统,它对于工作于该领域的终端用户、开发人员和其他相关人员能否获得有意义的错误消息来说至关重要。  ●    正则表达式(Regular expression):一个强大的“缩微语言”,可以用来按照各种方式处理字符串,例如搜索匹配一个特定模式的子字符串。  ●    Java首选项(Java preference):一种存储和获取系统和用户定义的配置选项的方法。每一个库都设计为可以灵活使用。熟悉这些库对于在Java中开发解决方案是非常重要的。作为一个开发人员,掌握的工具越多,就意味着装备越好。1.4.1  Java日志
Java拥有一个设计良好的类集,用于通过日志系统来控制、格式化以及发布消息。对于一个程序来说,记录错误和状态消息显得非常重要。有许多人可以从日志消息中获益,包括开发人员、测试人员、终端用户,以及工作在该领域却没有源代码来对程序进行疑难处理的人员。在一个程序中提供大量高质量的日志消息至关重要,从状态更新到错误条件(例如在捕获某种异常时)。通过使用日志系统,就有可能在不查看源代码的情况下,观察到程序正在干什么,而且最重要的是,可以向下追踪错误条件一直到程序的一个特定部分。日志系统的价值是显然的,特别是在大系统中,一个具备很少或者没有日志消息的偶见错误需要花费数天或者更长时间来向下追踪。java.util.logging中的日志系统功能非常强大,包括了满足以下条件的日志消息的优先级处理方法:只有特定记录器感兴趣的消息才被记录,并且消息可以输出到Handler对象能够处理的任意源。日志目标文件的例子包括文件、数据库和输出流。仔细观看图1-1可以看到整个日志系统的概览。
图1-1特定的Logger对象确实是层次结构的,并且尽管没有强制,但它还是能够反映类层次结构。当一个Logger接收一个日志消息时,该消息也将自动被传送到Logger的父对象。根记录器被命名为" "(空字符串),并且没有父对象。每一个其他的Logger通常都被命名为诸如java.util或者java.util.ArrayList之类的内容,以反映包/类层次结构。从树结构向下的Logger对象的名称都用点号来分隔。因此,java.util是java.util.ArrayList的父Logger。可以将记录器命名为任意字符串,但是需要用点号分隔约定来帮助清晰命名。日志系统的最简单用法是创建一个Logger,并使用日志系统的所有系统默认设置(在一个属性文件中定义)。下面的示例使用一个被称为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");     }}下面的内容是BasicLoggingExample的输出:Feb 22,  2004 4:07:06 PM BasicLoggingExample mainINFO: Test of logging system1.日志管理器
一个特定应用程序的整个日志系统都由LogManager类的一个实例来控制。此实例在Log- Manager初始化期间创建。LogManager包括了拥有所有指定Logger对象的层次式名字空间。LogManager也包括了日志系统配置中的Handlers以及其他对象所使用的日志控制属性。这些配置属性保存在lib/logging.properties文件中,该文件位于JRE安装路径中。有两个系统属性可以用来初始化带有不同属性的日志系统。第一种方法是覆盖属性java.util. logging.config.file,并且将完整路径规定为自身的logging.properties版本。另一个属性java.util.logging.config.class用来指出自身的LogManager。这种自定义LogManager负责读入其配置信息。如果这两个属性都没有被配置,那么Java将默认使用JRE目录下的logging. properties文件。查看下表1-11中的有关属性,这些属性可以在此文件中的LogManager上设置。同时也可以在此文件中为Loggers和Handles指定属性。本节后面将介绍这些属性。
表1-11
属性关键字
属性值
Handlers
逗号分隔的Handler类的列表。每个处理程序必须位于系统类路径中的某个位置
.level
设置特定Logger的最小级别。
level前面必须带有一个特定Logger的完整路径。一个独立的点号设置了根记录器的级别
(1)LogManager类LogManager类可以通过大量配置方法来配置日志系统的当前实例,追踪记录器并提供对这些记录器的访问,同时处理某些日志事件。这些方法在表1-12到表1-14中列出。(2)配置表1-12中列出的方法涉及存储和获取LogManager中的配置信息。表1-12
方法
描述
String getProperty(String name)
返回与一个指定日志属性相对应的值
void readConfiguration()
使用与启动时相同的过程重新加载该配置。如果控制初始化的系统属性尚未改变,那么在启动时读过的相同文件将在这里再次被读出
void readConfiguration(InputStream ins)
从InputStream读取格式为java.util.Properties的配置信息
void reset()
重新设置日志系统。所有Handlers都被关闭和移除,并且除了根级别之外的所有记录器级别都被设置为null。根记录器的级别被设置为Level.INFO
(3)记录器控制表1-13中列出的方法涉及存储、获取和管理单个Logger引用。这些是LogManager类上最常用的方法。表1-13
方法
描述
static LogManager getLogManager()
返回LogManager对象的惟一实例
boolean addLogger(Logger logger)
如果传入的Logger没有注册过(它的名称尚不在列表中),则返回true。注册记录器。
如果Logger对象的名称已经存在于已注册记录器列表中,则返回false
Logger getLogger(String name)
返回一个被命名为“name”的Logger对象的引用;如果没有发现记录器,则返回null
Enumeration getLoggerNames()
返回一个包含了所有当前已注册记录器名称列表的Enumeration
(4)事件表1-14中列出的方法用于,在LogManager上的属性被修改时,添加和移除应该被通知的侦听器的引用。表1-14
方法
描述
void addPropertyChangeListener
(PropertyChangeListener 1)
向一个侦听器列表添加一个属性更改侦听器,侦听器希望在属性发生变化时会得到通知。可以多次添加相同的侦听器
void removePropertyChangeListener
(PropertyChangeListener 1)
在侦听器列表中移除一个属性更改侦听器
2.Logger类
客户端代码使用Logger类的一个实例来记录一个消息。所有的日志消息以及每个记录器都具有相关联的级别。如果日志消息的级别等同于或者高于记录器的级别,那么消息将会被处理。否则,记录器将会丢弃日志消息。测试是否丢弃日志消息的代价并不大,此操作在日志系统的入口(Logger类)处就可以完成。这些级别是在Level类中定义的。可以查看表1-15中的一个完整级别列表。表1-15
记录器级别
描述
SEVERE
最高日志级别。它具有最高的优先级
WARNING
比SEVERE低一个级别。表示是一个需要注意但不很严重的警告消息
INFO
比SEVERE低两个级别。表示一种信息消息
CONFIG
比SEVERE低三个级别。表示与配置相关的输出
FINE
比SEVERE低四个级别。表示程序追踪信息
续表
记录器级别
描述
FINER
比SEVERE低五个级别。表示程序追踪信息
FINEST
最低日志级别。它具有最低优先级
ALL
使得系统将记录所有消息的特殊级别
OFF
使得系统将不记录消息的特殊级别(完全关闭日志功能)
(1)记录器方法Logger是利用日志系统的代码中所使用的主要类。有方法可以用于获得一个命名的或者匿名的记录器,配置该记录器并获取有关该记录器的信息,以及记录消息。(2)获得一个记录器表1-16中给出的方法用于获取一个Logger的句柄。这些是静态方法,提供了无需通过LogManager即可获得Logger的一种便捷途径。表1-16
方法
描述
static Logger getAnonymousLogger() static Logger getAnonymousLogger (String resourceBundleName)
创建一个匿名记录器,该记录器可以免除标准安全检查,用于applet中。匿名记录器不在LogManager名称空间中注册,但是将根记录器("")作为一个父记录器,继承了根记录器的级别和处理程序。同时也可以指定资源绑定用于日志消息的本地化
static Logger getLogger(String name)
static Logger getLogger(String name, String resourceBundleName)
返回一个来自LogManager名称空间的命名记录器;如果没有发现记录器,则创建并且返回一个新的命名记录器。同时也可以指定资源绑定用于日志消息的本地化
(3)配置一个Logger对象表1-17中的方法用于配置一个Logger对象。可以添加和移除处理程序,设置此Logger对象上的日志级别,设置它的父对象,以及选择日志消息是否应该向记录器层传递。表1-17
方法
描述
void addHandler(Handler handler)
向记录器添加一个Handler。可以添加多个处理程序。同时也要注意,根记录器配置为具有一个默认的Handlers集
void removeHandler(Handler
handler)
移除此记录器处理程序列表上的一个指定处理程序。如果没有发现该处理程序,则此方法正常地返回
void setLevel(Level newLevel)
设置此记录器将会使用的日志级别。低于记录器值的消息级别将会被自动丢弃。如果传入null,则该级别将从此记录器的父记录器继承
void setParent(Logger parent)
设置此记录器的父记录器。它不应该由应用程序代码所调用,因为它只能由日志系统使用
续表
方法
描述
void setUseParentHandlers (boolean useParentHandlers)
如果日志消息应该被传递给它们的父记录器,则指定true;如果阻止日志消息传递给它们的父记录器,则指定false
Filter getFilter()
返回用于此记录器的过滤程序,如果没有过滤程序关联将可能返回null
Handler[] getHandlers()
返回一个与此记录器关联的所有处理程序的数组
Level getLevel()
返回分配给此记录器的日志级别,如果返回null,则表示将会使用父记录器的日志级别
String getName()
返回此记录器的名称;如果这是一个匿名记录器,则返回null
Logger getParent()
返回当前记录器的最近父记录器;如果当前记录器是根记录器,则返回null
ResourceBundle getResourceBundle()
返回与此记录器关联的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)
首次调用一个方法时记录一个消息。变体形式对方法使用一个参数,或者一个参数数组,来提供方法调用的更详细的追踪信息。除其他有关方法调用的信息之外,记录的消息还有ENTRY。日志级别是Level.FINER
void entering(String sourceClass,
String sourceMethod, Object param1)
void entering(String sourceClass,
String sourceMethod, Object params [])
续表
方法
描述
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)
一般标准的记录便捷方法。各种变体可以指定日志的一个参数或者参数数组,或者Throwable信息。这个信息被放置在LogRecord对象中并且被送到日志系统中,最后一个变体使用了一个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对象中并且被发送到系统中
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。日志记录的消息被设置为THROW,并且thrown的内容被放入日志记录的thrown属性中,而不是放入日志记录的消息中
3.LogRecord类
LogRecord类封装了一个日志消息,将消息在整个日志系统中传送。Handler和Formatter使用LogRecord来获取有关处理消息的更多信息(例如消息发送的时间以及日志级别等)。如果一个日志系统的客户端拥有一个对LogRecord对象的引用,则该对象在它被传送到日志系统中之后应该不再被使用。(1)LogRecord方法LogRecord包含了大量方法用于检查和操作日志记录上的属性,例如消息来源、日志记录的级别,它是什么时候被发送到系统中的,以及任意相关联的资源绑定。表1-19中列出了这些方法。
表1-19
方法
描述
Level getLevel()
返回日志记录的级别
String getMessage()
返回日志消息在格式化/本地化之前的未格式化版本
long getMillis()
返回创建日志记录所需的时间(以毫秒为单位)
Object[] getParameters()
返回日志记录的参数数组;如果没有设置参数,则返回null
long getSequenceNumber()
返回日志记录的序列号。在日志记录的构造方法中分配序列号来为每一个日志记录创建一个惟一的序号
Throwable getThrown()
返回与此日志记录关联的Throwable,例如Exception(在一个异常被记录的情况下)。如果没有设置Throwable,则返回null
String getLoggerName()
返回记录器的名称,如果它是匿名记录器,那么该名称就可能为null
String getSourceClassName()
获取可能已经记录消息的类的名称。此信息可以被显式指定,或者可以从栈追踪中推断得到,因此可能不准确
String getSourceMethodName()
获取可能已经记录消息的方法的名称。此信息可以被显式指定,或者可以从栈追踪推断得到,因此可能不准确
int getThreadID
返回发起日志消息的线程的标识符。这是在Java VM中的ID
(2)设置有关消息来源的信息表1-20中的方法用于设置日志消息的来源信息,例如关联的异常、记录消息的类和方法,以及发起线程的ID等。
表1-20
方法
描述
void setSourceClassName(String source ClassName)
设置日志消息所来自的类的名称
void setSourceMethodName(String source MethodName)
设置日志消息所来自的方法的名称
void setThreadID(int threadID)
设置日志消息所来自的线程的标识符
void setThrown(Throwable thrown)
设置与日志消息相关联的Throwable,可能为null
(3)资源绑定方法表1-21中的方法用于获取和配置与日志消息一起使用的资源绑定。资源绑定用于本地化日志消息。
表1-21
方法
描述
ResourceBundle getResourceBundle()
返回与记录器关联的ResourceBundle,它用于本地化日志消息。如果没有关联的ResourceBundle,则有可能返回null
String getResourceBundleName()
返回用于本地化日志消息的资源绑定的名称。如果日志消息不可本地化,则返回null(没有定义资源绑定)
void setResourceBundle(Resource Bundle bundle)
设置资源绑定以用于本地化日志消息
void setResourceBundleName (String name)
设置资源绑定的名称以用于本地化日志消息
(4)设置有关消息的信息表1-22中的方法配置日志消息自身。可以配置的与日志消息相关联的一些信息包括它的级别、消息的内容以及消息被发送的时间。
表1-22
方法
描述
void setLevel(Level level)
设置日志消息的级别
void setLoggerName(String name)
设置发送此消息的记录器的名称,该名称可能为null
void setMessage(String message)
设置格式化/本地化之前消息的内容
void setMillis(long millis)
设置日志消息的时间(以毫秒为单位),从1970年开始记录
void setParameters(Object [] parameters)
设置日志消息的参数
void setSequenceNumber(long seq)
设置日志消息的序列号。此方法不常被调用,因为构造函数为每一个日志消息分配了一个惟一的序号
4.Level类
Level类定义日志级别的完整集合,并且该类的对象还代表一个特定的日志级别,该级别可被记录器、处理程序等使用。如果需要,可以对该类进行子类化,然后定义自己的自定义级别,只要它们与现有的日志级别不冲突即可。(1)日志级别表1-23中的日志级别在Level类中有定义。表1-23
日志级别
描述
OFF
初始化为Integer.MAX_VALUE的特殊值。它将关闭日志功能
SEVERE
意味着严重失败。初始化为1 000
续表
日志级别
描述
WARNING
意味着存在潜在问题。初始化为900
INFO
一般信息。初始化为800
CONFIG
意味着用于调试的有用消息。初始化为700
FINE
意味着最少量的追踪信息。初始化为500
FINER
更详细的追踪信息。初始化为400
FINEST
最详细级别的追踪信息。初始化为300
ALL
特殊值。记录所有消息。初始化为Integer.MIN_VALUE
(2)Level方法Level类定义了设置和获取一个特定日志级别的方法。可以使用级别的数字和文本版本。见表1-24。表1-24
方法
描述
static Level parse(string name)
返回一个代表被传入的级别名称的Level对象。name字符串可以是日志级别之一,例如SEVERE或者CONFIG。Integer.MIN_VALUE和Integer.MAX_VALUE之间的任意数字都能够被传入(作为一个字符串)。如果该数字代表一个存在的级别值,则返回该级别。否则,返回一个与传入数字值对应的新Level。任意无效名称或者数字都将导致抛出一个IllegalArgumentException。如果名称为null,则抛出一个Null PointerException
boolean equals(Object ox)
如果被传入的对象与当前类级别相同,则返回true
String getLocalized Name()
返回当前级别名称的本地化版本,如果没有本地化版本,则返回非本地化版本
String getName()
返回当前级别名称的非本地化版本
String getResourceBundle Name()
返回级别的本地化资源绑定名称,如果没有定义本地化资源绑定,则返回null
int hashCode()
返回一个基于级别值的哈希码
int intValue()
返回当前级别的整数值
String toString()
返回当前级别的非本地化名称
5.Handler类
Handler类用于接收日志消息,然后向一个外部目的地发布这些消息。目的地可能是存储器、文件、数据库、TCP/IP流,或者可以存储日志消息的任意个位置。如同记录器一样,处理程序具有关联的级别。低于处理程序上的级别的日志消息将被丢弃。每个特定Handler实例都具有自己的属性,并且通常都配置在logging.properties文件中。下一节将讨论java.util.logging包中的各种处理程序。创建一个自定义的处理程序是很简单的,因为只需要实现close()、flush()和publish(LogRecord record)就可以了。(1)Handler方法Handler类定义了三种抽象的方法,它们需要继承类中的指定行为。Handler类的其他方法用于处理消息编码、过滤程序、格式化程序和错误处理程序。(2)关键抽象方法在开发一个自定义处理程序的时候,必须要覆盖三个抽象方法。如表1-25所示。表1-25
方法
描述
abstract void close()
该方法应该执行一个flush(),然后释放处理程序所使用的任意资源。在调用close()之后,不应该再使用Handler
abstract void flush()
刷新任意缓存的输出以确保它被保存到相关联的资源中
abstract void publish(LogRecord record)
通过记录器转发一个日志消息,然后将它写入相关联的资源。消息应该被格式化(使用Formatter)和本地化
(3)设置和获取有关处理程序的信息表1-26中所列出的方法用于获取有关处理程序的信息,例如它的编码、相关联的错误管理器、过滤程序、格式化程序和级别,以及设置配置信息。表1-26
方法
描述
String getEncoding()
返回字符编码的名称。如果名称为null,则应该使用默认编码
ErrorManager getErrorManager()
返回与此处理程序相关联的ErrorManager
Filter getFilter()
返回与此处理程序相关联的Filter,它可能为null
Formatter getFormatter()
返回与此处理程序相关联的Formatter,它可能为null
Leve lgetLevel()
返回此处理程序的级别。低于此级别的日志消息被丢弃
boolean isLoggable(LogRecord record)
如果传入的LogRecord将由此处理程序记录,则返回true。这项检查包括将记录级别同处理程序级别进行比较、测试过滤程序(如果它已经被定义了)以及任意其他在处理程序中已定义的检查
void setEncoding(String encoding)
将编码设置为一种指定的字符编码。如果传入null,则使用默认平台编码
void setErrorManager
(Error Manager em)
设置处理程序的一个ErrorManager。如果在处理期间发生任意错误,则调用Error Manager的error方法
void setFilter(Filter new Filter)
设置一个自定义的过滤程序,它在调用publish方法时决定是丢弃或是保持一个日志消息
void setFormatter(Formatter newFormatter)
设置一个Formatter,用于在传向处理程序的日志消息被写入目的地之前对日志消息执行自定义的格式化
void setLevel(Level newLevel)
设置处事程序的级别阈值。低于此级别的日志消息将自动被丢弃
(4)主要处理程序java.util.logging包包括了大量预定义处理程序来向通用目的地写入日志消息。这些类包括ConsoleHandler、FileHandler、MemoryHandler、SocketHandler和StreamHandler。这些类提供Handler类中抽象方法的一个特定实现。表1-27中的所有属性关键字名称在实际的属性文件中都被加上java.util.logging前缀。StreamHandler主要作为所有处理程序的一个基类,这些处理程序将日志消息写入某个Output Stream。StreamHandler的子类包括ConsoleHandler、FileHandler和SocketHandler。大量流处理代码被内置于此类中。有关StreamHandler的属性列表,请参见表1-27。表1-27
属性名称
描述
默认值
StreamHandler.level
处理程序的日志级别
Level.INFO
StreamHandler.filter
要使用的过滤程序
未定义
StreamHandler.formatter
要使用的格式化程序
java.util.logging.SimpleFormatter
StreamHandler.encoding
要使用的字符集编码
默认平台编码
表1-28中的方法在StreamHandler类中被定义/实现。表1-28
方法
描述
void close()
Formatter的head字符串将被写入(若尚未写入的话),tail字符串在流关闭之前写入
void flush()
向流中写入任意被缓冲的输出(刷新流)
boolean isLoggable(LogRecord record)
执行针对level和filter的标准检查,但是,如果没有输出流打开或者被传入的记录为null,则还是返回false
void publish(LogRecord record)
如果传入的记录是可记录的,那么将调用Formatter来格式化该日志消息,然后将该消息写入输出流
void setEncoding(String encoding)
设置日志消息要使用的字符编码。传入null以使用当前平台的默认字符编码
protected void setOutputStream(Output Stream out)
设置一个要使用的OutputStream。如果已经打开了一个OutputStream,则它将被刷新并关闭。接着新的OutputStream将打开
ConsoleHandler向System.err写入日志消息。它子类化了StreamHandler,但是覆盖了close(),以便只执行一个刷新,因此System.err流不会关闭。所使用的默认格式化程序是SimpleFormatter。表1-29描述了可以在ConsoleHandler的logging.properties文件中定义的属性。表1-29
属性名称
描述
默认值
ConsoleHandler.level
处理程序的日志级别
Level.INFO
ConsoleHandler.filter
要使用的过滤程序
未定义
ConsoleHandler.formatter
要使用的格式化程序
java.util.logging.SimpleFormatter
ConsoleHandler.encoding
要使用的字符集编码
默认平台编码
SocketHandler通过一个指定的TCP端口向网络中写入日志消息。表1-30中列出了Socket Handler所使用的属性。默认的构造方法使用已定义的属性,第二个构造方法允许通过Socket Handler(String host , int port)对主机和端口进行规格说明。Close()方法刷新并且关闭输出流,publish()方法在每个记录被写入之后刷新该流。
表1-30
属性名称
描述
默认值
SocketHandler.level
处理程序的日志级别
Level.INFO
SocketHandler. filter
要使用的过滤程序
未定义
SocketHandler. formatter
要使用的格式化程序
java.util.logging.XMLFormatter
SocketHandler. encoding
要使用的字符集编码
默认平台编码
SocketHandler. host
连接到的目标主机名称
未定义
SocketHandler.port
要使用的目标TCP端口
未定义
FileHandler可以写入到单个文件,或者在每个文件达到指定的最大长度时向文件的一个循环集写入。序列中的下一个序号被添加到每个正在循环使用的文件名称尾端,除非在别处指定了一个generation(序列)模式。FileHandler的属性列在表1-31中。表1-31
属性名称
描述
默认值
FileHandler.level
处理程序的日志级别
Level.INFO
FileHandler.filter
要使用的过滤程序
未定义
FileHandler.formatter
要使用的格式化程序
java.util.logging.XMLFormatter
FileHandler.encoding
要使用的字符集编码
默认平台编码
FileHandler.limit
指定写入一个文件的最大字节数的一个近似值。0意味着没有限制
0
FileHandler.count
指定有多少输出文件要循环通过
1
FileHandler.pattern
用来生成输出文件名的模式
%h/java%u.log
FileHandler.append
布尔值,指定是否附加到一个已经存在的文件中,或者重写该文件
false
FileHandler类支持文件名模式,允许路径(例如用户的主目录或系统的临时目录)的替代。正斜杠(/)用作目录分隔符,并且这对Unix和Windows机器都有效。同时受支持的是能够在循环日志文件时指定,生成序号位于文件名中的何处。这些模式每个前面都加上百分号(%)。要在文件名中包含百分号,就必须指定两个百分号(%%)。表1-32中包含了所有有效的百分号替代。
表1-32
模式
描述
模式
描述
%t
系统临时目录的完整路径
%g
用于分辨循环日志的生成序号
%h
user.home系统属性的值
%u
用于解决进程冲突的惟一序号
例如,如果在Windows 95中执行它并且指定文件名模式%t/app_log.txt,那么FileHandler类将该模式扩展到C:\TEMP\app_log.txt。注意,%t和%h命令不包括后面的正斜杠。%u用来说明多个线程/进程何时将访问相同的日志文件。只有一个进程能够使文件打开以执行写入操作,因此为了防止日志信息的丢失,就可以使用%u来向一个与其他文件具有相似名称的日志文件进行输出。例如,可以指定文件名模式%t/logfile%u.txt,并且如果两个进程打开此相同文件来输出,那么第一个进程将打开C:\TEMP\logfile0.txt,第二个进程将打开C:\TEMP\logfile1. txt。MemoryHandler是存储器中的一个循环缓冲器。它作为一种快速的方式用来实现存储消息,这样消息就必须发送到另一个处理程序,从而可以将它们写入外部资源。因为缓冲器是循环的,所以较旧的日志记录将最终被较新的日志记录所覆盖。格式化操作可以延迟到另一个Hander,这使得向一个MemoryHandler进行记录变得非常快捷。将导致MemoryHandler向另一个Handler发送数据(推数据)的条件如下所列:  ●    被传入的日志记录比指定的pushLevel具有更高的级别。  ●    另一个类调用MemoryHander上的push方法。  ●    一个子类根据自定义标准实现一个专业化行为来推数据。MemoryHandler的属性列于表1-33中。
表1-33
属性名称
描述
默认值
MemoryHandler.level
处理程序的日志级别
Level.INFO
MemoryHandler.filter
要使用的过滤程序
未定义
MemoryHandler.size
循环缓冲的长度(以字节计算)
1 000
MemoryHandler.push
定义推级别,它是使得消息被发送到目标处理程序的最小级别
Level.SEVERE
MemoryHandler.target
指定目标Handler类的名称
未定义
表1-34中的构造函数创建一个带有默认或者指定配置的MemoryHandler。MemoryHandler所提供的方法创建和配置存储器处理程序的行为,表1-35列出了这些方法。
表1-34
构造方法
描述
MemoryHandler()
基于配置属性创建一个MemoryHandler
MemoryHandler(Handler target,int size, Level pushLevel)
创建一个带有指定的目标处理程序、缓冲器长度和推级别的MemoryHandler
表1-35
方法
描述
void publish(LogRecord record)
如果该记录是可记录的(参见isLoggable),则将它存储在内部缓冲器中。如果日志记录级别大于等于pushLevel,那么所有被缓存的记录(包括当前的记录)都将写入目标Handler
void close()
关闭处理程序并释放相关联的资源。同时调用目标处理程序上的close
void flush()
导致一个flush,它与push不同。为了向一个目的地而不是存储器实际写入日志记录,就必须执行一个push操作
Level getPushLevel()
返回当前的推级别
boolean isLoggable(LogRecord record)
比较日志级别,如果定义了过滤程序,则通过过滤程序运行记录。此方法忽略记录是否将导致一个push操作
void push()
将当前缓冲器中的所有记录发送到目标处理程序中,同时清除该缓冲器
void setPushLevel(Level newLevel)
设置一个新的push级别
6.Formatter类
Formatter类用于对日志记录执行某种自定义的处理。这种格式化活动也许包括本地化、添加附加的程序信息(例如向日志记录添加时间和日期),或者任意其他需要的处理。Formatter返回一个字符串,它是被处理的日志记录。Formatter类也支持在所有日志记录之前或者之后出现的head和tail字符串。本节稍后将会实现的一个示例是自定义Formatter,它将日志记录写入一个HTML表中。对于这个格式化程序,头字符串将是<table>标签,尾字符串是</table>标签。Formatter类中定义的方法在表1-36中列出。
表1-36
方法
描述
abstract String format(LogRecord record)
执行日志记录的指定格式化并返回格式化过的字符串
String formatMessage(LogRecord record)
LogRecord中的消息字符串通过使用记录的Resource Bundle来本地化,并且也根据java.text样式格式化(替代诸如{0}之类的字符串)进行格式化
String getHead(Handler h)
返回指定处理程序的头字符串,它可能为null
String getTail(Handler h)
返回指定处理程序的尾字符串,它可能为null
7.主要格式化程序
日志包提供了两个有用的格式化程序。SimpleFormatter提供了一个格式化程序的基本实现。XMLFormatter按照预定义的XML格式输出日志记录。这两个主要的格式化程序将会覆盖多种基本日志场景,但是如果需要这两个格式化程序都未提供的行为,您也可以自己编写格式化程序。(1)SimpleFormatterSimpleFormatter完成格式化日志消息所需的最小级别的工作。SimpleFormatter格式化方法返回一行或者多行被传入的日志记录摘要。使用SimpleFormatter记录一个简单的日志消息(例如test 1)将会显示如下输出:Apr 18, 2004 12:18:25 PM LoggingTest mainINFO:  test 1SimpleFormatter格式化了消息中的日期、时间、源类名称、源方法名称,第二行是日志消息的级别和日志消息本身。(2)XMLFormatterXMLFormatter根据XML DTD格式化日志记录。可以与任意字符编码一起使用XMLForma- tter,但是建议只与“UTF-8”一起使用。getHead()和getTail()方法用来输出XML文件的开始和结尾部分,这些内容对于每个日志记录来说并不需要重复,但是对于创建一个有效的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>日志系统使用的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)创建自己的格式化程序开发一个自定义的Formatter并不是很困难。作为一个示例,这里给出一个前面提到过的HTML- TableFormatter的实现。输出的HTML代码内容如下:<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>结束,因为在每个表行中只有一个日志记录。<table>标签和该表的第一行组成了头字符串。</table>标签组成了日志记录集合的尾部。自定义格式化程序只需要getHead()、getTail()和format(LogRecord record)方法的实现: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接口
过滤程序用于提供附加的标准,以判断是应该丢弃还是应该保留日志记录。每个记录器以及每个处理程序都具有一个已定义的过滤程序。Filter接口定义了单个方法:boolean isLoggable(LogRecord record)如果应该发布日志消息,那么isLoggable方法将返回true;如果应该丢弃日志消息,那么将返回false。9.创建自己的过滤程序
一个自定义过滤程序的示例是丢弃任意没有以"client"开头的日志消息的过滤程序。当日志消息来自于大量源端,并且从一个特定的客户端(或者多个客户端)发出的每个日志消息都以"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与处理程序相关联,用于处理发生的任意错误,例如抛出的异常。记录器的客户端很可能不关心或者无法处理错误,因此使用一个ErrorManager是Handler报告错误条件的一种灵活而且简单的方式。错误管理器定义了单个方法:void error(String msg, Exception ex, int code)此方法的参数包括错误消息(一个字符串)、抛出的异常和一个表示发生了什么错误的代码。这些代码在ErrorManager类中被定义为静态整数,列出在表1-37中。
表1-37
错误代码
描述
CLOSE_FAILURE
用于close()失败时
FLUSH_FAILURE
用于flush()失败时
FORMAT_FAILURE
用于任意原因引起的格式化失败时
GENERIC_FAILURE
用于其他错误代码不匹配的任意其他错误
OPEN_FAILURE
用于打开一个输出源失败时
WRITE_FAILURE
用于向一个输出源写入失败时
11.记录示例
默认情况下,日志消息通过层次结构向上传送到每一个父记录器。下面这个小程序使用已命名的记录器,使用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();          }     }}这里发生的是XML输出被发送到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>因为日志消息接着被发送到父记录器,所以消息也将使用SimpleFormatter被输出到System. err。下面是输出的内容:Feb 11, 2004 2:09:55 PM LoggingExample1 mainINFO:  test 1Feb 11, 2004 2:09:56 PM LoggingExample1 mainINFO: test 2这里有一个更详细的示例,其中使用了已开发的HTMLTableFormatter。在父-子关系中定义了两个记录器ParentLogger和ChildLogger。父记录器将使用XMLFormatter输出到一个文本文件,子记录器将使用HTMLTableFormatter输出到一个不同的文件。默认情况下,根记录器将会执行,日志消息将会使用SimpleFormatter转到控制台。HTMLTableFormatter被扩展成一个HTMLFormatter,从而生成一个完整的HTML文件(而不仅仅是表格标签):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();          }     }}下面是输出到屏幕的内容:Apr 20, 2004 12:43:09 PM LoggingExample2 mainSEVERE: This is a severe log message下面是输出到log_output.xml文件的内容:<?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文件的内容如下所示:<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仅仅关注WARNING以及高于该级别的日志消息,对于低于该级别的日志消息都会立即丢弃。HTML文件包含所有日志消息,因为ChildLogger被设计成可以处理所有日志消息。XML文件只包含一个SEVERE日志消息,因为所有低于WARNING级别的日志消息都丢弃了。12.正则表达式
正则表达式是一种强大的工具,可以用于解决涉及搜索、隔离和/或替代字符串中文本块的问题。有关正则表达式的讨论内容是相当多的,其内容可以独自形成一本书,实际上已经有了有关正则表达式的书籍出版。本节对正则表达式提供一个概述,并讨论Sun公司在java.util.regex包中对它的支持。正则表达式通过使用一个简单的解析器来降低大量繁冗工作,该解析器提供了复杂的模式匹配功能。正则表达式可用于处理任意类型的文本。有关正则表达式更多更深的内容,请参考有关正则表达式的另一本书籍。即使从来没有在语言中见过正则表达式,但是也极有可能在Unix/DOS/Windows系统中见过用于文件掩码的正则表达式的一个小子集。例如,可能见过下面位于一个目录中的文件:Test.javaTest.classStringProcessor.javaStringProcessor.classToken.javaToken.class可以在命令行中键入*.*(在DOS/Windows系统中),则每一个文件都将进行匹配并且列出。星号用任意字符串替代,点号按照字面意义使用。如果使用文件掩码T*.class,则只有两个文件将被匹配,即Test.class和Token.class。星号被认为是元字符,点号及字母被认为是正常字符。元字符是正则表达式“语言”的一部分,Java具有丰富的元字符集,远远超过了文件掩码中的简单支持。正常字符根据正被测试的字符串逐字进行匹配。同时还有一个工具用来逐字解释正则表达式语言中的元字符。在本节将考查几个使用正则表达式的示例。作为第一个示例,假设希望生成一个在class关键字前没有修饰符的Java文件中所有类的列表。如果只需要对源代码的单一行进行检验,那么现在要做的就是忽略字符串class之前的任意空白符,然后就可以生成该列表。传统上的一种方法将需要找到字符串中第一个出现的class,这样就可以确保在字符串之前只有空白符而没有其他内容。若使用正则表达式,这个任务就变得更简单了。简单查看一下整个Java正则表达式语言,这种情况下需要的正则表达式是\s*class。反斜杠用来指定一个元字符,在本例中,\s匹配任意的空白符。*是另一个元字符,代表“0次或者多次出现的前面的术语”。单词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正则表达式库的设计人员决定使用一个Pattern-Matcher(模式-匹配程序)模型,它将正则表达式与匹配程序自身分隔开。正则表达式由Pattern类编译成一个更优化的形式。那么此已编译的模式就可以被多个匹配程序使用,或者可以被相同的匹配程序重用于不同的字符串。在一个正则表达式中,任意单一的字符都按字面意义进行匹配,一些特殊的情况除外。其中的一个特殊情况就是点号(.),它匹配正在被分析的字符串中的任意单个字符。提供了预定义的元字符集以匹配特定的字符。它们列在表1-38中。
表1-38
元字符
匹配内容
\\
单个反斜杠
\ On
一个描述字符的八进制值,其中n是一个满足0≤n≤7的数
\ Onn
\ Omnn
一个描述字符的八进制值,其中0≤m≤3并且0≤n≤7
\ Oxhh
用十六进制值hh描述的字符(其中0≤h≤F)
续表
元字符
匹配内容
\ uhhhh
用十六进制值hhhh描述的字符(其中0≤h≤F)
\ t
一个tab制表符(字符'\u0009')
\ n
一个新行(换行符)('\u000A')
\ r
一个回车符('\u000D')
\ f
一个换页符('\u000C')
\ a
一个铃声/嘟嘟声字符('\u0007')
\ e
一个换码符('\u001B')
\ cx
与x相对应的控制字符,例如\cc表示control-c
.
任意单个字符
正则表达式语言同时也提供一些元字符来匹配确定的字符串边界。这些边界中的一部分表示一行的开始与结束,以及单词的开始与结束。表1-39中列出了完整的边界元字符清单。
表1-39
元字符
匹配内容
^
行的开始
$
行的结束
\b
一个单词的边界
\B
一个非单词的边界
\A
输入的开始
\G
以前匹配的结束
\Z
在任意行结束符(例如回车符或者换行符)之前输入的结束
\z
输入的结束
正则表达式语言也拥有字符类,这些类可以用来指定一个可能的字符列表,这些字符可以匹配需要匹配的字符串中的任意单个字符。如果想要显式指定一个字符类,那么可以在方括号中指定这些字符。因此,字符类[0123456789]匹配任意单个数字。也有可能在左方括号之后使用脱字符号(^)来指定“除了这些指定字符之外的任意字符”。使用表达式[^012],除了0、1和2之外的任意单个数字将被匹配。可以使用破折号指定字符范围。字符类[a-z]匹配任意单个的小写字母,而[^a-z]则匹配除了小写字母之外的任意字符。可以使用任意字符范围,例如[0-9]表示匹配一个单个的数字,或者[0-3]表示匹配0、1、2或者3。可以指定多个范围,例如[a-zA-Z]匹配任意单个的字母。正则表达式包包含了一个预定义字符类集,表1-40中列出了这些字符类。此外,还有POSIX字符类和Java字符类。这些分别列于表1-41和表1-42中。
表1-40
字符类元字符
匹配内容
字符类元字符
匹配内容
.
任意单个字符
\S
一个非空白符[^\s]
\d
一个数字[0-9]
\w
一个单词字符[a-zA-Z_0-9]
\D
一个非数字[^0-9]
\W
一个非单词字符[^\W]
\s
一个空白符[\t\n\x0B\f\r]
表1-41
字符类元字符
匹配内容
\p{Lower}
小写字母[a-z]
\p{Upper}
大写字母[A-Z]
\p{ASCII}
所有ASCII字符[\ x00-\ x7F]
\p{Alpha}
任意大写或者小写字母
\p{Digit}
数字[0-9]
\p{Alnum}
任意字母或者数字
\p{Punct}
标点符号[!" # $ % & ' ( ) * + , -. / : ; < => ? @ [ \ ] ^ ‘{ | } ~ ]
\p{Graph}
一个可视化字符:任意字母、数字,或者标点符号
\p{Print}
一个可打印字符;与\ p {Graph}相同
\p{Blank}
一个空白字符或者tab制表符[ \ t]
\p{Cntrl}
一个控制字符[\ x00-x1F \ x7F]
\p{XDigit}
十六进制数字[0-9a-fA-F]
\p{Space}
一个空白字符[ \ t \ n \ x0B\ f \ r]
表1-42
字符类
匹配内容
\p{javaLowerCase}
Character.isLowerCase()匹配的任意内容
\p{javaUpperCase}
Character.isUpperCase()匹配的任意内容
\p{javaWhitespace}
Character.isWhitespace()匹配的任意内容
\p{javaMirrored}
Character.isMirrored()匹配的任意内容
正则表达式语言的另一个特性是具有按照指定次数匹配一个特定字符的能力。在前面的示例中,*被用来匹配0个或多个空白字符。重复操作符有两种通用的工作方式。一种操作符是贪婪式的,也就是说,它们尽可能进行匹配,一直匹配到末尾。另一种是勉强式的(或惰性的),只是遇到第一次匹配时就结束匹配。例如,正则表达式 . *;匹配任意数量的字符,一直到它找到最后的分号为止。为了只匹配第一个分号,就必须使用惰性操作版本 . * ?;。表1-43、表1-44中分别列出了所有的贪婪式操作符和勉强式操作符版本。
表1-43
贪婪式操作符
描述
贪婪式操作符
描述
X?
匹配0次或者一次X
X{n}
刚好匹配n次X,其中n可以是任意数字
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可以是任意数字
X{n,}?
匹配至少n次X
X{n,m}?
匹配至少n次但是不超过m次X
该语言也支持通过在正则表达式中使用圆括号来捕获成组匹配的字符。向后引用可以用来引用这些匹配子组中的一个组。一个向后引用可以由一个反斜杠后跟与该子组序号对应的一个数字来引用。在字符串(A(B))中,0组指的是整个表达式,然后从每个左圆括号后的子组开始编号。这样,A(B)是第一个子组,B是第二个子组。向后引用允许匹配一个字符串。例如,如果希望匹配在一行中两次出现的相同单词,就可以使用[([a-zA-Z]) \ b\ 1]。记住\ b代表一个单词边界。因为用于字母的字符类是在圆括号中,所以匹配的文本则只能使用向后引用元字符\ 1来引用。13.Pattern类
Pattern类负责编译和存储一个指定的正则表达式。有几个控制处理正则表达式方式的标志。编译regex以提供高效使用。正则表达式的文本表示形式意味着程序员易于使用和理解,见表1-45。
表1-45
方法
描述
static Pattern compile(String regex)
编译方法接受一个字符串中的正则表达式并将其进行编译,以供内部使用。变体形式允许指定标志,这些标志可以修改正则表达式如何被处理
static Pattern compile(String regex,int flags)
static boolean matches(String regex,CharSequence input)
编译一个指定的正则表达式并且将其同input进行匹配。如果正则表达式描述了输入的数据,则返回true;否则返回false。使用这种方式只是为了快速匹配。要对不同的输入重复地匹配正则表达式,正则表达式就应该只编译一次
续表
方法
描述
static String quote(String s)
返回将会匹配被传入的字符串的一个按照字面意义来表示的正则表达式。返回的字符串以\Q开头,后跟被传入的字符串,以\E结束。这些用于引用一个字符串,因此正则表达式语言中的元字符所表示的意义将会按照字面意义来处理
int flags()
返回包含了何时编译正则表达式的标志集合的一个整数
Matcher matcher(CharSequence input)
返回一个Matcher以用来对指定的输入类型进行匹配
String pattern()
返回用于创建模式的正则表达式
String[] split(CharSequence input)
String[]split(CharSequence input,int limit)
在使用正则表达式作为分隔符将输入分隔成块之后返回一个字符串数组。limit可用于限制正则表达式被匹配的次数。匹配文本不能置于数组中。如果limit是正数,那么模式将至少被应用“limit减1”次。如果limit是0,那么模式将被应用尽可能多的次数,并将删除末尾的空字符串。如果limit是负数,那么模式将被应用尽可能多的次数,末尾的空串被留在数组中
14.Matcher类
Matcher类使用一个模式同一个输入字符串进行比较,并且执行范围广阔的有用任务。Matcher类提供多个方法,用于获取大量的信息(例如模式是在字符串的什么位置进行匹配的)、使用其他的字符串来替代字符串的一个匹配子集,以及其他有用的操作。表1-46中列出了这些方法。表1-46
方法
描述
static String quoteReplacement (String s)
返回一个字符串,该字符串通过\Q和\E引用,可以用来按照字面意义同其他的输入进行匹配
Matcher appendReplacement (StringBuffer sb,String replacement)
首先将所有要匹配的字符附加到字符串缓冲器中,然后使用replacement来代替匹配的文本,接着在匹配的文本之后的某个位置处设置索引,以准备下一次对此方法的调用。在最后一次匹配之后,使用appendTail来附加剩余的输入
StringBuffer appendTail(String Buffer sb)
将输入序列的剩余部分附加到已被传入的字符串缓冲器中
MatchResult asResult()
返回一个对描述匹配程序状态的MatchResult的引用
int end()
返回用于最后匹配的结束位置的索引
int end(int group)
返回用于一个指定捕获组的结束位置的索引
boolean find()
如果发现一个匹配是始于紧接前一个匹配之后的索引时,或者在匹配程序已经被重置的情况下始于一行的开始时返回true
boolean find(int start)
重置匹配程序并且试图匹配从位置Start处开始的输入文本模式。如果发现匹配,则返回true
boolean hitEnd()
如果最后匹配达到输入的末尾,则返回true
boolean requireEnd()
如果更多的输入能将正向匹配变成负向匹配,则返回true
续表
方法
描述
boolean lookingAt()
如果模式匹配,但是不需要该模式必须完全匹配输入文本,则返回true
boolean matches()
如果模式匹配字符串,则返回true。该模式必须描述此方法的整个字符串来返回true。对于部分匹配,则使用find()或者lookingAr()
Pattern pattern()
返回当前在匹配程序中使用的模式的一个引用
Matcher reset()
完全重置匹配程序的状态
Matcher reset(CharSequence input)
完全重置匹配程序的状态,并且向input设置新输入
int start()
返回前一个匹配的起始位置
int start(int group)
返回一个指定捕获组的起始位置
Matcher usePattern(Pattern newPattern)
设置一个新的模式以用于匹配。输入的当前位置没有改变
String group()
返回一个包含前一个匹配内容的字符串
String group(int group)
返回一个包含一个特定被匹配组内容的字符串。第0个组总是表示整个表达式
int groupCount()
返回在匹配程序模式中的捕获组的数量
Matcher region(int start,int end)
返回一个Matcher,它被限制为字符串的一个子字符串以用于搜索。脱字符号(^)和美元符号($)元字符将在所定义区域的开始和结束处匹配
int regionEnd()
返回当前定义区域的结束索引(实际检查匹配的最后位置的索引)
int regionStart()
返回当前定义区域的起始索引
String replaceAll(String replacement)
使用字符串replacement替代所有匹配了模式出现的字符串。如果在此方法调用之后Matcher仍然被使用,则它应该重置
String replaceFirst(String replacement)
仅使用字符串replacement替代匹配模式的第一个字符串。如果在此方法调用之后Matcher仍然被使用,则它应该重置
15.MatchResult接口
MatchResult接口包含了组方法、start和end方法,以便提供一个允许描述Matcher当前状态的完整方法集。Matcher类实现了此接口并且定义了所有这些方法。toMatchResult方法返回MatchResult的一个句柄,提供它用以保存和处理Matcher类的当前状态。16.正则表达式示例
使用Pattern/Matcher类来处理一个Java源代码文件。不是公用的所有类将被列出(实际上指所有没有修改符的类),并且使用反索引将双写的单词(例如在一行中的两个标识符)列出。输入源代码文件(它没有编译)如下所示: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 (\\w+)";          String doubleIdentifierRE = "\\b(\\w+)\\s+\\1\\b";          Pattern classPattern = Pattern.compile(unadornedClassRE);          Pattern doublePattern = Pattern.compile(doubleIdentifierRE);          Matcher classMatcher, doubleMatcher;          int lineNumber=0;          try {               BufferedReader br = new BufferedReader(new FileReader(fileName));               String line;               while((line=br.readLine())  != null)  {                    lineNumber++;                    classMatcher = classPattern.matcher(line);                    doubleMatcher = doublePattern.matcher(line);                    if(classMatcher.find())  {                         System.out.println("The class [" +                                                     classMatcher.group(1) +                                                 "] is not public");                    }                    while(doubleMatcher.find())  {                         System.out.println("The word \" "+ doubleMatcher.group(1) +                                                "\" occurs twice at position " +                                                doubleMatcher.start() + "on line" +                                                lineNumber);                     }               }          } catch(IOException ioe) {               System.out.println("IOException: " + ioe);               ioe.printStackTrace();          }     }}第一个正则表达式^ \ \ s * class ( \ \ w +)在该行的起始处开始搜索未修饰的class关键字,该表达式后跟着的是0个或者多个空白字符,然后是class单词。组操作符同一个或者多个单词字符(A-Z,a-z,0-9,以及下划线)一起使用,因此类名称被匹配。第二个正则表达式\ \ b( \ \ w+) \ \ s+ \ \ 1 \ \ b使用单词边界元字符(\ b)确保单词被分隔。若没有这个正则表达式,那么字符串public class将在字母c位置处匹配。一个向后索引用来匹配已经被匹配的字符串,在本例中,将匹配一个或多个单词字符。一个或多个空白字符必须在单词之间出现。对前面给出的测试Java源文件执行上面的程序将会得到如下输出:The class [EmptyClass] is not publicThe class [MyArrayList] is not publicThe word "extends" occurs twice at position 18 on line 6The word "test" occurs twice at position 32 on line 11The word "code" occurs twice at position 49 on line 111.4.2  Java首选项
程序一般需要按照某种方式存储配置信息,该方式容易修改配置信息并且可以将信息输出到程序自身之外。Java提供了用于存储和获取系统定义和用户定义的配置信息的实用程序类。用户和系统信息有各自独立的层次结构。所有用户共享系统树中定义的首选项信息;每个用户都拥有自己的树结构以配置与其他用户分隔的数据。这允许自定义配置,包括重写系统值。首选项类库的核心是抽象类java.util.prefs.Preferences。此类定义了一个方法集,该集提供首选项类库的所有特性。首选项层次结构中的每个节点都具有一个名称,它并不一定是惟一的。首选项树的根节点将空字符串("")作为它的名称。正斜杠用作首选项节点名称的分隔符,这与Unix上使用它作为目录名称的分隔符非常相似。只有两个字符串不是有效的节点名称,一个是空字符串(因为它是给根节点预留的),一个是正斜杠自身(因为它是一个节点分隔符)。根节点的路径是正斜杠自身。同使用目录非常相似,绝对和相对路径都可以使用。绝对路径总是以正斜杠开始,因为绝对路径总是从根节点开始,沿着树向下直到一个指定节点。相对路径从来不是以正斜杠开始。只要一个路径的路径名中没有连续的正斜杠,那么该路径就是有效的,除了根的路径之外其他路径都不会以正斜杠结束。因为首选项是通过第三方实现者实现的,因此对首选项的修改永远不会立即写入后备存储器。单个节点名称及其任意关键字的最大长度都是80个字符长。一个节点中字符串值的最大长度是8192个字符长。1.Preferences类
Preferences类是用于处理首选项的主要类。它表示首选项树的一个节点,并且包含操作此树及该树中节点的大量方法。它基本上是使用首选项的单站场所。接下来的一节将概述Preferences方法。  (1)对首选项树的操作Preferences类定义了大量方法,这些方法用于创建或者删除节点,以及获取树中的确定节点,见表1-47。表1-47
方法
描述
Preferences node(String pathName)
返回一个指定的节点。如果节点不存在,则将创建(所有不存在的祖先节点都将被创建)该节点并且返回它
boolean nodeExists(String pathName)
如果当前树中存在到一个节点的路径,则返回true。该路径可以是绝对或者相对路径
void removeNode()
移除此首选项节点及其所有的子节点。在一个节点被移除之后能够被调用的方法只有name()、absolutePath()、isUserNode()、flush()、nodeExists("")和从Object继承而来的方法。所有其他的方法都将抛出IllegalStateException。在调用flush()对树进行持久性修改之前,这种移除可能不是永久移除
static Preferences system NodeForPackage
(Class c)
此方法返回其中含有指定类的包的首先项节点。包名称中的所有点号都使用正斜杠替代。
对于没有包的类,返回的节点的名称照字面意义就是<unnamed>。不应该长期使用此节点,因为它是由使用它的所有程序员共享的。
如果该节点尚不存在,那么该节点以及所有不存在的祖先节点都将自动被创建
static Preferences system Root()
此方法返回系统首选项树的根节点
续表
方法
描述
static Preferences user Node ForPackage
(Class c)
此方法返回其中含有指定类的包的首先项节点。包名称中的所有点号都用正斜杠替代。
对于没有包的类,返回的节点名称照字面意义就是<unnamed>。不应该长期使用此节点,因为它是由使用它的所有程序员共享的,因此配置设置没被分隔开。
如果该节点尚不存在,那么该节点以及所有不存在的祖先节点都将自动被创建
static Preferences userRoot()
此方法返回用户首选项树的根节点
(2)获取有关节点的信息每一个节点都具有与其相关联的信息,例如它的路径、父和子节点,以及该节点的名称。表1-48中列出了操作此信息的方法。
表1-48
方法
描述
String absolute Path()
此方法返回当前节点的绝对路径。绝对路径从根节点/开始,并且一直继续到当前节点
String[] children Names()
返回当前节点的所有子节点的名称数组
boolean isUserNode()
如果此节点是用户配置树的一部分,则返回true;或者此节点是系统配置树的一部分,则返回false
String name()
返回当前节点的名称
Preferences parent()
返回当前节点的父节点的Preferences引用;如果试图获取根节点的父节点,则返回null
(3)获取节点的首选项值表1-49中的方法同Hashtable类中的方法行为极为相似。关键不同的一点是对于绝大多数基本类型来说,存在着多种get版本。每一个类型都与一个特定的关键字相关联,这个关键字是代表配置参数名称的一个字符串。
表1-49
方法
描述
String[] keys()
返回包含当前首选项节点中所有关键字名称的字符串数组
String get(String key, String def)
返回与一个指定关键字相关联的字符串。如果关键字不存在,则使用默认值def来创建它,并且将此默认值返回
boolean getBoolean(String key, boolean def)
返回与一个指定关键字相关联的boolean值。如果关键字不存在,则使用默认值def来创建它,并且将此默认值返回
byte[] getByteArray(String key, byte [] def)
返回与一个指定关键字相关联的byte数组。如果关键字不存在,则使用默认值def来创建它,并且将此默认值返回
续表
方法
描述
double getDouble(String key, double def)
返回与一个指定关键字关联的double值。如果关键字不存在,则使用默认值def来创建它,并且将此默认值返回
float getFloat(String key, float def)
返回与一个指定关键字相关联的float值。如果关键字不存在,则使用默认值def来创建它,并且将此默认值返回
int getInt(String key, int def)
返回与一个指定关键字相关联的integer值。如果关键字不存在,则使用默认值def来创建它,并且将此默认值返回
long getLong(String key, long def)
返回与一个指定关键字相关联的long值。如果关键字不存在,则使用默认值def来创建它,并且将此默认值返回
(4)设置节点上的首选项值同每个get方法一起使用的是put版本,它用于设置与一个给定配置参数的关键字名称相关联的信息,见表1-50。
表1-50
方法
描述
void put(String key, String value)
这些方法向一个特定类型设置一个配置参数(它的名称作为key传入)。如果key或者value为null,则将会抛出一个异常。key至多可以有80个字符长(在MAX_KEY_LENGTH中定义),其值最大可以达到8 192个字符数(在MAX_VALUE_ LENGTH中定义)
void putBoolean(String key, boolean value)
void putByteArray(String key, byte[] value)
void putDouble(String key, double value)
void putInt(String key, int value)
void putFloat(String key, float value)
void putLong(String key, long value )
(5)事件为Preferences类定义了两个事件,一个事件是在首选项树中的节点被修改时会触发,第二个事件是在一个首选项被修改时会触发。用于这些事件的方法列于表1-51中。
表1-51
方法
描述
void addNodeChangeListener
(NodeChangeListener ncl)
添加一个侦听器,以在向当前首选项节点添加或者从当前首选项节点移除一个子节点时获得通知
void addPreferenceChangeListener
(PreferenceChangeListener pcl)
为首选项更改事件添加一个侦听器,这样任何时候只要一个首选项被添加,或者被删除,或者值被修改时,侦听器都将获得通知
void removeNodeChangeListener
(NodeChangeListener ncl)
移除一个指定的节点更改侦听器
void removePreferenceChangeListener
(PreferenceChangeListener pcl)
移除一个指定的首选项更改侦听器
(6)其他操作表1-52中列出了Preferences类中的其他方法,例如将任意附加的更改写入后备存储器,将首先项层次结构重置成空,将首选项层次结构保存到磁盘中,以及其他一些操作。表1-52
方法
描述
void clear()
移除此节点上的所有首选项
void exportNode(Output Stream os)
将该节点(仅限于当前节点)的全部内容作为一个XML文件(在下一小节中列出的preferences.dtd之后)写入输出流中
void exportSubtree(Output Stream os)
将此节点以及首选项树中位于该节点之下的所有节点的全部内容作为一个XML文件(在下一小节中列出的preferences.dtd之后)写入输出流中
void flush()
将首选项节点的任意更改都写入后备存储器,包括所有子节点上的数据
void remove(String key)
移除与指定关键字相关联的值
void sync()
确保存储器中首选项的当前版本匹配已存储版本。如果首选项节点中的数据需要写入后备存储器,则将它写入
String toString()
根据节点所处的层次结构以及当前节点的绝对路径来判断返回一个包含User还是System的字符串
2.导出到XML
Preferences系统定义了一个向XML文件导出整个关键字/值的完整树的标准操作。在http://java.sun. com/dtd /preferences.dtd上可以访问到此XML文件的DTD。下面列出了此DTD的内容:<?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.使用首选项
下列示例设置了用户树的一个节点中的几个首选项,打印有关该节点的信息,然后将该信息导出到一个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文件中的导出信息显示如下:<?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>
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
一篇文章教你如何用 Python 记录日志
java中Logger.getLogger(Test.class)
为什么pycharm输出的日志全部是红色!
【Python】自动化编程之基础库 logging 库
第10章:python自动化——logging日志库
用tornado搭建日志服务器
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服