??xml version="1.0" encoding="utf-8" standalone="yes"?> 首先当然是得到l(f)og4j的jar档,推荐使用1.2.X版,下蝲地址Q?/p>
http://logging.apache.org/log4j/1.2/download.html 我们先看一个最单的CZQ?/p>
【示??/strong> 目l构Q?/span> 配置文glog4j.properties:
【注Q?span style="color: #ff0000">׃q里的多个项目公用一个jar档,我们可以创徏一个专门放|jar档的Java工程Q然后将jar档放到l(f)ib目录下。在要用的工程中按图所C行引?/span> ?/p>
2
3 import org.apache.log4j.Logger;
4
5 public class HelloLog4j {
6
7 private static Logger logger = Logger.getLogger(HelloLog4j.class);
8
9 /**
10 * @param args
11 */
12 public static void main(String[] args) {
13 // System.out.println("This is println message.");
14
15 // 记录debugU别的信?/span>
16 logger.debug("This is debug message.");
17 // 记录infoU别的信?/span>
18 logger.info("This is info message.");
19 // 记录errorU别的信?/span>
20 logger.error("This is error message.");
21 }
22 }
2 #debugQ显Cdebug、info、error
3 #infoQ显Cinfo、error
4 #errorQ只error
5 log4j.rootLogger=debug,appender1
6 #log4j.rootLogger=info,appender1
7 #log4j.rootLogger=error,appender1
8
9 #输出到控制台
10 log4j.appender.appender1=org.apache.log4j.ConsoleAppender
11 #样式为TTCCLayout
12 log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout
通过配置文g可知Q我们需要配|?个方面的内容Q?/p>
1、根目录Q别和目的圎ͼQ?/p>
2、目的地Q控制台、文件等{)Q?/p>
3、输出样式?/p>
下面我们来看看Log4J的类图:
Logger - 日志写出器,供程序员输出日志信息
Appender - 日志目的圎ͼ把格式化好的日志信息输出到指定的地方?
ConsoleAppender - 目的Cؓ控制台的Appender
FileAppender - 目的Cؓ文g的Appender
RollingFileAppender - 目的Cؓ大小受限的文件的Appender
Layout - 日志格式化器Q用来把E序员的logging request格式化成字符?/strong>
PatternLayout - 用指定的pattern格式化logging request的Layout
Log4j基本使用Ҏ(gu)
Log4j׃个重要的lg构成Q日志信息的优先U,日志信息的输出目的地Q日志信息的输出格式。日志信息的优先U从高到低有ERROR、WARN?INFO、DEBUGQ分别用来指定这条日志信息的重要E度Q日志信息的输出目的地指定了日志打印到控制台还是文件中Q而输出格式则控制了日志信息的昄内容?br />
一、定义配|文?/span>
其实(zhn)也可以完全不用配|文Ӟ而是在代码中配置Log4j环境。但是,使用配置文g(zhn)的应用E序更加灉|。Log4j支持两种配置文g格式Q一U是XML格式的文Ӟ一U是JavaҎ(gu)文Ӟ?|。下面我们介l用JavaҎ(gu)文件做为配|文件的Ҏ(gu)Q?br />
1.配置根LoggerQ其语法为:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
其中Qlevel 是日志记录的优先U,分ؓOFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者?zhn)定义的别。Log4j只用四个别,优先U从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的U别Q?zhn)可以控制到应用程序中相应U别的日志信息的开兟뀂比如在q里定义了INFOU别Q则应用E序中所有DEBUGU别的日志信息将不被打印出来?appenderName是指B日志信息输出到哪个地斏V?zhn)可以同时指定多个输出目的地?br />
2.配置日志信息输出目的地AppenderQ其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN
其中QLog4j提供的appender有以下几U:
org.apache.log4j.ConsoleAppenderQ控制台Q,
org.apache.log4j.FileAppenderQ文ӞQ?br /> org.apache.log4j.DailyRollingFileAppenderQ每天生一个日志文ӞQ?br /> org.apache.log4j.RollingFileAppenderQ文件大到达指定尺寸的时候生一个新的文ӞQ?br /> org.apache.log4j.WriterAppenderQ将日志信息以流格式发送到L指定的地方)
3.配置日志信息的格式(布局Q,其语法ؓQ?/span>
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN
其中QLog4j提供的layout有以e几种Q?br /> org.apache.log4j.HTMLLayoutQ以HTML表格形式布局Q,
org.apache.log4j.PatternLayoutQ可以灵zd指定布局模式Q,
org.apache.log4j.SimpleLayoutQ包含日志信息的U别和信息字W串Q,
org.apache.log4j.TTCCLayoutQ包含日志生的旉、线E、类别等{信息)
Log4J采用cMC语言中的printf函数的打印格式格式化日志信息Q打印参数如下: %m 输出代码中指定的消息
%p 输出优先U,即DEBUGQINFOQWARNQERRORQFATAL
%r 输出自应用启动到输出该log信息耗费的毫U数
%c 输出所属的cȝQ通常是所在类的全?br /> %t 输出产生该日志事件的U程?br /> %n 输出一个回车换行符QWindowsq_?#8220;rn”QUnixq_?#8220;n”
%d 输出日志旉点的日期或时_默认格式为ISO8601Q也可以在其后指定格式,比如Q?d{yyy MMM dd HH:mm:ss,SSS}Q输出类|2002q?0?8?22Q?0Q?8Q?21
%l 输出日志事g的发生位|,包括cȝ名、发生的U程Q以及在代码中的行数。D例:Testlog4.main(TestLog4.java:10)
二、在代码中用Log4j
1.得到记录?/span>
使用Log4jQ第一步就是获取日志记录器Q这个记录器负责控制日志信息。其语法为:
public static Logger getLogger( String name)
通过指定的名字获得记录器Q如果必要的话,则ؓq个名字创徏一个新的记录器。Name一般取本类的名字,比如Q?br />
static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () )
2.d配置文g
当获得了日志记录器之后,W二步将配置Log4j环境Q其语法为:
BasicConfigurator.configure ()Q?自动快速地使用~省Log4j环境?br /> PropertyConfigurator.configure ( String configFilename) Q读取用Java的特性文件编写的配置文g?br /> DOMConfigurator.configure ( String filename ) Q读取XML形式的配|文件?br />
3.插入记录信息Q格式化日志信息Q?/span>
当上两个必要步骤执行完毕Q?zhn)可以轻村֜使用不同优先U别的日志记录语句插入到(zhn)想记录日志的Q何地方,其语法如下:
Logger.debug ( Object message ) ;
Logger.info ( Object message ) ;
Logger.warn ( Object message ) ;
Logger.error ( Object message ) ;
CZ2~示?
【示??/strong> 输出为文本文件或HTML文g
?/strong> CZ3?/strong> 输出为文本文件或HTML文g
输出l果Q?/p>
【示??/strong> 【企业应用】设|?/span> 特定包的U别和目的地 先增加一个包Q新Z个类Q?/p>
Z提高效率Q我们可以在写日志前增加判断Q?/p>
输出l果Q?/p>
INFO - This is info message.
ERROR - This is error message.
log4j.rootLogger=debug,appender1
#输出到控制台
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
#讄输出样式
log4j.appender.appender1.layout=org.apache.log4j.PatternLayout
#自定义样?br /># %r 旉 0
# %t Ҏ(gu)?nbsp;main
# %p 优先U?nbsp;DEBUG/INFO/ERROR
# %c 所属类的全?包括包名)
# %l 发生的位|,在某个类的某?br /># %m 输出代码中指定的讯息Q如log(message)中的message
# %n 输出一个换?br />
log4j.appender.appender1.layout.ConversionPattern=%r [%t] [%p] - %c -%l -%m%n
2 -com.coderdream.log4j.HelloLog4j.main(HelloLog4j.java:16) -This is debug message.
3 31 [main] [INFO] - com.coderdream.log4j.HelloLog4j
4 -com.coderdream.log4j.HelloLog4j.main(HelloLog4j.java:18) -This is info message.
5 31 [main] [ERROR] - com.coderdream.log4j.HelloLog4j
6 -com.coderdream.log4j.HelloLog4j.main(HelloLog4j.java:20) -This is error message.#讄U别和目的地
log4j.rootLogger=debug,appender1,appender2
#输出到控制台
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
#讄输出样式
log4j.appender.appender1.layout=org.apache.log4j.PatternLayout
#自定义样?br />
# %r 旉 0
# %t Ҏ(gu)?nbsp;main
# %p 优先U?nbsp;DEBUG/INFO/ERROR
# %c 所属类的全?包括包名)
# %l 发生的位|,在某个类的某?br />
# %m 输出代码中指定的讯息Q如log(message)中的message
# %n 输出一个换行符?br />
log4j.appender.appender1.layout.ConversionPattern=[%d{yy/MM/dd HH:mm:ss:SSS}][%C-%M] %m%n
#输出到文?q里默认加方?
log4j.appender.appender2=org.apache.log4j.FileAppender
#讄文g输出路径
#?/span>1】文本文?br />
log4j.appender.appender2.File=c:/Log4JDemo06.log
#讄文g输出样式
log4j.appender.appender2.layout=org.apache.log4j.PatternLayout
log4j.appender.appender2.layout.ConversionPattern=[%d{HH:mm:ss:SSS}][%C-%M] -%m%n
import org.apache.log4j.Logger;
public class HelloDao {
private static Logger logger = Logger.getLogger(HelloDao.class);
/**
* @param args
*/
public static void main(String[] args) {
// 记录debugU别的信?/span>
logger.debug("This is debug message from Dao.");
// 记录infoU别的信?/span>
logger.info("This is info message from Dao.");
// 记录errorU别的信?/span>
logger.error("This is error message from Dao.");
}
}
如果q个cM为基c,如J2EE中的BaseDao、BaseAction、BaseService{等Q则我们可以各层的日志信息分类输出到各个文件?/p>
log4j.logger.com.coderdream.log4jDao=info,appender1,appender2
#输出到控制台
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
#讄输出样式
log4j.appender.appender1.layout=org.apache.log4j.PatternLayout
#自定义样?br /># %r 旉 0
# %t Ҏ(gu)?nbsp;main
# %p 优先U?nbsp;DEBUG/INFO/ERROR
# %c 所属类的全?包括包名)
# %l 发生的位|,在某个类的某?br /># %m 输出代码中指定的讯息Q如log(message)中的message
# %n 输出一个换行符?br />log4j.appender.appender1.layout.ConversionPattern=[%d{yy/MM/dd HH:mm:ss:SSS}][%C-%M] %m%n
#输出到文?q里默认加方?
log4j.appender.appender2=org.apache.log4j.FileAppender
#讄文g输出路径
#?/span>1】文本文?br />log4j.appender.appender2.File=c:/Log4JDemo07_Dao.log
#讄文g输出样式
log4j.appender.appender2.layout=org.apache.log4j.PatternLayout
log4j.appender.appender2.layout.ConversionPattern=[%d{HH:mm:ss:SSS}][%C-%M] -%m%n
2 <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
3
4 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
5
6 <appender name="appender1"
7 class="org.apache.log4j.RollingFileAppender">
8 <param name="File" value="logfile08.html" />
9 <param name="MaxFileSize" value="1MB" />
10 <param name="MaxBackupIndex" value="5" />
11 <layout class="org.apache.log4j.HTMLLayout">
12 </layout>
13 </appender>
14
15 <root>
16 <level value="debug" />
17 <appender-ref ref="appender1" />
18 </root>
19 </log4j:configuration>
if (logger.isDebugEnabled()) {
logger.debug("This is debug message from Dao.");
}
// 记录infoU别的信?/span>
if (logger.isInfoEnabled()) {
logger.info("This is info message from Dao.");
}
// 记录errorU别的信?/span>
logger.error("This is error message from Dao.");
当时我在找{换控件时Q发现网易也转蝲了一偏关于Jacob使用帮助Q但其中出现了比较严重的错误QString htmlfile = "C:\\AA";
只指定到了文件夹一U,正确写法是String htmlfile = "C:\\AA\\xxx.html";
到此WORD/EXCEL转换HTML已l差不多了,怿大家应该很清楚了:)
二、用XPDFPDF转换为HTML
1、下载xpdf最新版本,地址Q?a >http://www.foolabs.com/xpdf/download.html
我下载的?span class="Apple-style-span" style="word-spacing: 0px; text-transform: none; color: #000000; text-indent: 0px; font-family: Simsun; white-space: normal; letter-spacing: normal; border-collapse: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px">xpdf-3.02pl2-win32.zip
2、下载中文支持包
我下载的?span class="Apple-style-span" style="word-spacing: 0px; text-transform: none; color: #000000; text-indent: 0px; font-family: Simsun; white-space: normal; letter-spacing: normal; border-collapse: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px">xpdf-chinese-simplified.tar.gz
3、下载pdftohtml支持?/p>
地址Q?a >http://sourceforge.net/projects/pdftohtml/
我下载的是:pdftohtml-0.39-win32.tar.gz
4、解压调?/p>
1) 先将xpdf-3.02pl2-win32.zip解压Q解压后的内容可Ҏ(gu)需要进行删减,如果只需要{换ؓtxt格式Q其他的exe文g可以删除Q只保留pdftotext.exeQ以此类推;
2) 然后xpdf-chinese-simplified.tar.gz解压到刚才xpdf-3.02pl2-win32.zip的解压目录;
3) pdftohtml-0.39-win32.tar.gz解压Qpdftohtml.exe解压到xpdf-3.02pl2-win32.zip的解压目录;
4) 目录l构Q?/p>
+---[X:\xpdf]
|-------各种转换用到的exe文g
|
|-------xpdfrc
|
+------[X:\xpdf\xpdf-chinese-simplified]
|
|
+-------很多转换旉要用到的字符文g
xpdfrcQ此文g是用来声明{换字W集对应路径的文?/p>
5) 修改xpdfrc文g(文g原名为sample-xpdfrc)
修改文g内容为:
6) 创徏bat文gpdftohtml.batQ放|的路径不能包含I格Q?/p>
内容为:
7) 创徏c?/p>
String cmd = "....";此处代码是调用创建的bat文gq行转换
8) 试转换
以上是整理的内容,后箋q会d视频转换为FLV格式Q如果需要的?)
说的不是很详l,到问题的朋友可以自己努力解决一?)
java.lang.Class klass = Myclass.class; |
log(java.lang.String.class.getClassLoader()); |
来获取java的核心运行时cȝ加蝲器,׃得到null?br />
接下来介ljava的扩展类加蝲器。扩展库提供比javaq行代码更多的特性,我们可以把扩展库保存在由java.ext.dirs属性提供的路径中?br />
(~辑注:java.ext.dirs属性指的是pȝ属性下的一个keyQ所有的pȝ属性可以通过System.getProperties()Ҏ(gu)获得。在~者的pȝ中,java.ext.dirs的value?#8221;
C:\Program
Files\Java\jdk1.5.0_04\jre\lib\ext”。下面将要谈到的如java.class.path也同属系l属性的一个key?
cExtClassLoader专门用来加蝲所有java.ext.dirs下的.jar文g。开发者可以通过把自q.jar文g或库文g加入到扩展目录的classpathQ其可以被扩展cd载器d?br />
从开发者的角度Q第三种同样也是最重要的一U类加蝲器是AppClassLoader。这U类加蝲器用来读取所有的对应在java.class.pathpȝ属性的路径下的cR?br />
Sun的java指南中,文章“理解扩展cd?#8221;QUnderstanding
Extension Class
LoadingQ对以上三个cd载器路径有更详尽的解释,q是其他几个JDK中的cd载器
●java.net.URLClassLoader
●java.security.SecureClassLoader
●java.rmi.server.RMIClassLoader
●sun.applet.AppletClassLoader
java.lang.ThreadQ包含了public
ClassLoader
getContextClassLoader()Ҏ(gu)Q这一Ҏ(gu)q回针对一具体U程的上下文环境cd载器。此cd载器qE的创徏者提供,以供此线E中q行的代码在需要加载类或资源时使用。如果此加蝲器未被徏立,~省是其父线E的上下文类加蝲器。原始的cd载器一般由d应用E序的类加蝲器徏立?/p>
<?gt;
cd载器如何工作Q?br />
除了引导cd载器Q所有的cd载器都有一个父cd载器Q不仅如此,所有的cd载器也都是java.lang.ClassLoadercd。以上两U类加蝲器是不同的,而且对于开发者自订制的类加蝲器的正常q行也至关重要。最重要的方面是正确讄父类加蝲器。Q何类加蝲器,其父cd载器是加载该cd载器的类加蝲器实例。(CQ类加蝲器本w也是一个类Q)
使用loadClass()Ҏ(gu)可以从类加蝲器中获得该类。我们可以通过java.lang.ClassLoader的源代码来了解该Ҏ(gu)工作的细节,如下Q?br />
protected synchronized Class<?> loadClass (String name, boolean
resolve) throws ClassNotFoundException { // First check if the class is already loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // If still not found, then invoke // findClass to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } |
public class MyClassLoader extends ClassLoader { public MyClassLoader() { super(MyClassLoader.class.getClassLoader()); } } |
public class MyClassLoader extends ClassLoader { public MyClassLoader() { super(getClass().getClassLoader()); } } |
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } |
Target target3 = (Target) target2; |
?. 在同一个JVM中多个类加蝲器加载同一个目标类 |
<?gt;
Z么我们需要我们自qcd载器
原因之一为开发者写自己的类加蝲器来控制JVM中的cd载行为,java中的c靠其包名和cd来标识,对于实现了java.io.Serializable接口的类QserialVersionUID扮演了一个标识类版本的重要角艌Ӏ这个唯一标识是一个类名、接口名、成员方法及属性等l成的一?4位的哈希字段Q而且也没有其他快L方式来标识一个类的版本。严D来,如果以上的都匚wQ那么则属于同一个类?br />
但是让我们思考如下情况:我们需要开发一个通用的执行引擎。可以执行实现某一特定接口的Q何Q务。当d被提交到q个引擎Q首先需要加载这个Q务的代码。假设不同的客户Ҏ(gu)引擎提交了不同的dQ凑巧,q些所有的d都有一个相同的cd和包名。现在面临的问题是q个引擎是否可以针对不同的用h提交的信息而做Z同的反应。这一情况在下文的参考一节有可供下蝲的代码样例,samepath
?differentversionsQ这两个目录分别演示了这一概念?? 昄了文件目录结构,有三个子目录samepath,
differentversions, ?differentversionspushQ里Ҏ(gu)例子Q?br />
?. 文g夹结构组l示?/div> |
public void fx(){ log("this = " + this + "; Version.fx(1)."); } |
set CLASSPATH=.;%CURRENT_ROOT%\v1;%CURRENT_ROOT%\v2 %JAVA_HOME%\bin\java Test |
?. 在类路径中samepath试排在最前面的version 1 |
set CLASSPATH=.;%CURRENT_ROOT%\v2;%CURRENT_ROOT%\v1 %JAVA_HOME%\bin\java Test |
?. 在类路径中samepath试排在最前面的version 2 |
?. AppClassLoader及ExtClassLoader |
static { log("client.TaskImpl.class.getClassLoader (v1) : " + TaskImpl.class.getClassLoader()); } public void execute(){ log("this = " + this + "; execute(1)"); } |
CLASSPATH=%CURRENT_ROOT%\common;%CURRENT_ROOT%\server; %CURRENT_ROOT%\client2;%CURRENT_ROOT%\client1 %JAVA_HOME%\bin\java server.Server |
?. 执行引擎服务器控制台 |
?. 执行引擎客户? 1控制台 |
?. 执行引擎客户? 2控制台 |
<?gt;
定制cd载器
要较好地控制cȝ加蝲Q就要实现定制的cd载器。所有自定义的类加蝲器都应承自java.lang.ClassLoader。而且在构造方法中Q我们也应该讄父类加蝲器。然后重写findClass()Ҏ(gu)。differentversionspush文g夹包含了一个叫做FileSystemClassLoader的自订制的类加蝲器。其l构如图9所C?br />
?. 定制cd载器关系 |
public byte[] findClassBytes(String
className) { try { String pathName = currentRoot + File.separatorChar + className. replace(’.’, File.separatorChar) + ".class"; FileInputStream inFile = new FileInputStream(pathName); byte[] classBytes = new byte[inFile.available()]; inFile.read(classBytes); return classBytes; } catch (java.io.IOException ioEx) { return null; } } public Class findClass(String name)throws ClassNotFoundException { byte[] classBytes = findClassBytes(name); if (classBytes==null) { throw new ClassNotFoundException(); } else{ return defineClass(name, classBytes, 0, classBytes.length); } } public Class findClass(String name, byte[] classBytes)throws ClassNotFoundException { if (classBytes==null) { throw new ClassNotFoundException( "(classBytes==null)"); } else{ return defineClass(name, classBytes, 0, classBytes.length); } } public void execute(String codeName, byte[] code) { Class klass = null; try { klass = findClass(codeName, code); TaskIntf task = (TaskIntf) klass.newInstance(); task.execute(); } catch(Exception exception){ exception.printStackTrace(); } } |
public class Client { public static void main (String[] args) { try{ byte[] code = getClassDefinition ("client.TaskImpl"); serverIntf.execute("client.TaskImpl", code); } catch(RemoteException remoteException) { remoteException.printStackTrace(); } } private static byte[] getClassDefinition (String codeName) { String userDir = System.getProperties(). getProperty("BytePath"); FileSystemClassLoader fscl1 = null; try { fscl1 = new FileSystemClassLoader (userDir); } catch(FileNotFoundException fileNotFoundException) { fileNotFoundException.printStackTrace(); } return fscl1.findClassBytes(codeName); } } |
public void execute(String codeName, byte[] code)throws
RemoteException { FileSystemClassLoader fileSystemClassLoader = null; try { fileSystemClassLoader = new FileSystemClassLoader(); fileSystemClassLoader.execute(codeName, code); } catch(Exception exception) { throw new RemoteException(exception.getMessage()); } } |
?0. 定制cd载器执行引擎 |
?1. 定制cd载器Q客L1 |
?2. 定制cd载器Q客L1 |
什么是ThreadLocalQ?/strong>
思义它是local variableQ线E局部变量)。它的功用非常简单,是为每一个用该变量的线E都提供一个变量值的副本Q是每一个线E都可以独立地改变自q副本Q而不会和其它U程的副本冲H。从U程的角度看Q就好像每一个线E都完全拥有该变量?/p>
使用场景
ThreadLocalc?/strong>
它主要由四个Ҏ(gu)l成initialValue()Qget()Qset(T)Qremove()Q其中值得注意的是initialValue()Q该Ҏ(gu)是一个protected的方法,昄是ؓ了子c重写而特意实现的。该Ҏ(gu)q回当前U程在该U程局部变量的初始|q个Ҏ(gu)是一个gq调用方法,在一个线E第1ơ调用get()或者set(Object)时才执行Qƈ且仅执行1ơ。ThreadLocal中的实实现直接q回一个nullQ?/p>
ThreadLocal的原?/p>
ThreadLocal是如何做Cؓ每一个线E维护变量的副本的呢Q其实实现的思\很简单,在ThreadLocalcM有一个MapQ用于存储每一个线E的变量的副本。比如下面的CZ实现Q?/p>
public class ThreadLocal
{
private Map values =
Collections.synchronizedMap(new HashMap());
public Object
get()
{
Thread curThread = Thread.currentThread();
Object o =
values.get(curThread);
if (o == null &&
!values.containsKey(curThread))
{
o =
initialValue();
values.put(curThread, o);
}
return o;
}
public void set(Object
newValue)
{
values.put(Thread.currentThread(), newValue);
}
public Object initialValue()
{
return null;
}
}
ThreadLocal 的?/strong>
使用Ҏ(gu)一Q?/p>
Hibernate的文档时看到了关于ThreadLocal理多线E访问的部分。具体代码如?
1. public static
final ThreadLocal session = new ThreadLocal();
2. public static Session
currentSession() {
3. Session s = (Session)session.get();
4. //open a new session,if this session has none
5. if(s ==
null){
6. s = sessionFactory.openSession();
7. session.set(s);
8. }
return s;
9. }
我们逐行分析
1?
初始化一个ThreadLocal对象QThreadLocal有三个成员方?get()、set()、initialvalue()?
如果不初始化initialvalueQ则initialvalueq回null?
3。session的getҎ(gu)当前U程q回其对应的U程内部变量Q也是我们需要的net.sf.hibernate.SessionQ相当于对应每个数据库连接).多线E情况下׃n数据库链接是不安全的。ThreadLocal保证了每个线E都有自qsQ数据库q接Q?
5。如果是该线E初ơ访问,自然QsQ数据库q接Q会是nullQ接着创徏一个SessionQ具体就是行6?
6。创Z个数据库q接实例 s
7。保存该数据库连接s到ThreadLocal中?
8。如果当前线E已l访问过数据库了Q则从session中get()可以获取该U程上次获取q的q接实例?
使用Ҏ(gu)?/p>
当要l线E初始化一个特D值时Q需要自己实现ThreadLocal的子cdƈ重写该方法,通常使用一个内部匿名类对ThreadLocalq行子类化,EasyDBO中创建jdbcq接上下文就是这样做的:
public class JDBCContext{
private static Logger logger =
Logger.getLogger(JDBCContext.class);
private DataSource ds;
protected
Connection connection;
private boolean isValid = true;
private static
ThreadLocal jdbcContext;
private JDBCContext(DataSource
ds){
this.ds = ds;
createConnection();
}
public static
JDBCContext getJdbcContext(javax.sql.DataSource
ds)
{
if(jdbcContext==null)jdbcContext=new
JDBCContextThreadLocal(ds);
JDBCContext context = (JDBCContext)
jdbcContext.get();
if (context == null) {
context = new
JDBCContext(ds);
}
return context;
}
private static class JDBCContextThreadLocal extends ThreadLocal
{
public javax.sql.DataSource ds;
public
JDBCContextThreadLocal(javax.sql.DataSource
ds)
{
this.ds=ds;
}
protected synchronized Object
initialValue() {
return new JDBCContext(ds);
}
}
}
使用单例模式Q不同的U程调用getJdbcContext()获得自己的jdbcContextQ都是通过JDBCContextThreadLocal
内置子类来获得JDBCContext对象的线E局部变量,q个变量是该U程所独有的?/p>
ClassLoader是一个抽象类Q我们用它的实例对象来装载类Q?font face="宋体">它负责将 Java 字节码装载到
JVM 中,
q其成?/span>
JVM 一部分?/span>
JVM 的类动态装载技术能够在q行时刻动态地加蝲或者替换系l的某些功能模块Q?/span>而不影响pȝ其他功能模块的正常运行?/span>一般是通过cdd一个class文g来装载这个类Q(其它加蝲形式暂时没有研究q)?/p>
ClassLoader装蝲q程 c装载就是寻找一个类或是一个接口的字节码文件ƈ通过解析该字节码来构造代表这个类或是q个接口?/span>
class 对象的过E。在
Java 中,c装载器把一个类装入
Java 虚拟ZQ要l过三个步骤来完成:装蝲、链接和初始化,其中链接又可以分成校验、准备和解析三步Q除了解析外Q其它步骤是严格按照序完成的,各个步骤的主要工作如下:
1.
装蝲Q查扑֒导入cL接口的字节码Q?/span>
2.
链接Q执行下面的校验、准备和解析步骤Q其中解析步骤是可以选择的;
l
校验Q检查导入类或接口的二进制数据的正确性;
l
准备Q给cȝ静态变量分配ƈ初始化存储空_
l
解析Q将W号引用转成直接引用Q?/span>
3.
初始化:Ȁzȝ的静态变量的初始?/span>
Java 代码和静?/span>
Java 代码块?/span> 装蝲的实?/font> JVM 中类的装载是?/span>
ClassLoader 和它的子cL实现的?/span>
Java ClassLoader 是一个重要的
Java q行时系l组Ӟ它负责在q行时查扑֒装入
Java 字节码?/span>
?/span>
Java 中,
ClassLoader 是一个抽象类Q它在包
java.lang 中。可以这栯Q只要了解了
ClassLoader 中的一些重要的Ҏ(gu)Q再l合上面所介绍?/span>
JVM 中类装蝲的具体的q程Q对动态装载类q项技术就有了一个比较大概的掌握Q这些重要的Ҏ(gu)包括以下几个Q?/span>
1.
loadCass Ҏ(gu)Q?/span>
loadClass(String name ,boolean resolve) 其中
name 参数指定?/span>
JVM 需要的cȝ名称
, 该名UCcȝ全限定名表示Q如
Java.lang.Object Q?/span>
resolve 参数告诉Ҏ(gu)是否需要解析类Q在初始化类之前Q应考虑c解析,q不是所有的c都需要解析,如果
JVM 只需要知道该cL否存在或扑և该类的超c,那么׃需要解析。这个方法是
ClassLoader 的入口点?/span>
2.
defineClass Ҏ(gu)
q个Ҏ(gu)接受cL件的字节数组q把它{换成
Class 对象。字节数l可以是从本地文件系l或|络装入的数据。它把字节码分析成运行时数据l构、校验有效性等{?/span>
3.
findSystemClass Ҏ(gu)
findSystemClass
Ҏ(gu)从本地文件系l装?/span>
Java 字节码。它在本地文件系l中LcLӞ如果存在Q就使用
defineClass 字节数l{换成
Class 对象。当q行
Java 应用E序?/span>
, q是
JVM 正常装入cȝ~省机制?/span>
4.
resolveClass Ҏ(gu)
resolveClass(Class c) Ҏ(gu)解析装入的类Q如果该cdl被解析q那么将不做处理。当调用
loadClass Ҏ(gu)?/span>
, 通过它的
resolve 参数军_是否要进行解析?/span>
5.
findLoadedClass Ҏ(gu)
当调?/span>
loadClass Ҏ(gu)装入cL
, 调用
findLoadedClass Ҏ(gu)来查?/span>
ClassLoader 是否已装入这个类
, 如果已装?/span>
, 那么q回
Class 对象
, 否则q回
NULL 。如果强行装载已存在的类
, 会抛出链接错误?/span>
java.lang.Classc?/font> 某个cȝ所有实例内部都有一个引用,指向该类对应的Class的实例的位置Q每个javacd应的Class实例可以当作是类在内存中的代理h。所以当要获得类的信?如有哪些cd?有哪些方?Ӟ都可以让cd应的Class的实例代?java的Reflection机制大量的使用q种Ҏ(gu)来实现。但是ClasscL法手工实例化Q当载入Lcȝ时候自动创Z个该cd应的Class的实例。每个javac都是由某个classLoader(ClassLoader的实?来蝲入的Q因此Classcd的实例中都会他的ClassLoader的实例的引用。可以通过getClass.getClassLoader()得到CLassLoader的实例?/p>
java动态蝲入class的两U方?
各种javacȝ哪些classLoader加蝲? 1)javacd以通过实例.getClass.getClassLoader()得到 ClassLoader hierachy: 1)jvm初始化生bootstrap loader。ƈ讑֮它的父ClassLoader为null 2)bootstrap loader建立AppClassLoader,载入 q行java.exe?
?cp?classpath中的c(每个q行中的U程都有一个成员contextClassLoaderQ用来在q行时动态地载入其它cȝl默认的contextClassLoader是systemClassLoaderQ?/p>
Java c装载的代理l构
?/span>
(Bootstrap) 装蝲器:该装载器没有父装载器Q它?/span>
JVM 实现的一部分Q从
sun.boot.class.path 装蝲q行时库的核心代码?/span>
扩展
(Extension) 装蝲器:l承的父装蝲器ؓ根装载器Q不像根装蝲器可能与q行时的操作pȝ有关Q这个类装蝲器是用纯
Java 代码实现的,它从
java.ext.dirs ( 扩展目录
) 中装载代码?/span>
pȝ
(System or Application) 装蝲器:装蝲器ؓ扩展装蝲器,我们都知道在安装
JDK 的时候要讄环境变量
(CLASSPATH ) Q这个类装蝲器就是从
java.class.path(CLASSPATH 环境变量
) 中装载代码的Q它也是用纯
Java 代码实现的,同时q是用户自定义类装蝲器的~省父装载器?/span>
应用程?/span>
(Applet) 装蝲器:父装载器为系l装载器Q它从用h定的|络上的特定目录装蝲应用程序代码?/span>
java中的代理l构是自上而下查找cȝQ这与很多web装蝲器不同?/p>
tomcat中的实现的子ClassLoader的结?/font> Tomcat Server在启动的时候将构造一个ClassLoader树,以保证模块的cd是私有的 ClassLoader被组l成树ŞQ一般的工作原理是:
1)implicit隐式,卛_用实例化才蝲入的Ҏ(gu)来动态蝲入class
2)explicit昑ּ方式,又分两种方式:
a)java.lang.Class的forName()Ҏ(gu)
b)java.lang.ClassLoader的loadClass()Ҏ(gu)
2)接口由AppClassLoader(SystemClassLoader)载入
QSystemClassLoaderQ可以由ClassLoader.getSystemClassLoader()获得实例
3)ClassLoadercȝbootstrap
loader载入
Tomcat
Server的ClassLoaderl构如下Q?
---------------------------+
| Bootstrap |
| | |
| System
|
| | |
|
Common |
| / \ |
| Catalina Shared |
| / \
|
| WebApp1 WebApp2 |
---------------------------+
其中Q?
- Bootstrap -
载入JVM自带的类?JAVA_HOME/jre/lib/ext/*.jar
- System - 载入$CLASSPATH/*.class
-
Common - 载入$CATALINA_HOME/common/...Q它们对TOMCAT和所有的WEB APP都可?
- Catalina -
载入$CATALINA_HOME/server/...Q它们仅对TOMCAT可见Q对所有的WEB APP都不可见
- Shared -
载入$CATALINA_HOME/shared/...Q它们仅Ҏ(gu)有WEB APP可见Q对TOMCAT不可见(也不必见Q?
- WebApp? -
载入ContextBase?/WEB-INF/...Q它们仅对该WEB APP可见
1) U程需要用到某个类Q于是contextClassLoader被请求来载入该类
2) contextClassLoaderh它的父ClassLoader来完成该载入h
3)
如果父ClassLoader无法载入c,则contextClassLoader试图自己来蝲?
注意QWebApp?ClassLoader的工作原理和上述有少怸同:
它先试图自己载入c(在ContextBase?/WEB-INF/...中蝲入类Q,如果无法载入Q再h父ClassLoader完成
]]>
GC调优是个很实验很伽利略的zdQGC日志是先决的数据参考和最l验证:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps(GC发生的时? -XX:+PrintGCApplicationStoppedTime(GC消耗了多少旉) -XX:+PrintGCApplicationConcurrentTime(GC之间q行了多时?
配置参数Q?XX:+UseConcMarkSweepGC
已默认无需配置的参敎ͼ-XX:+UseParNewGC(Parallel攉新生? -XX:+CMSPermGenSweepingEnabled(CMS攉持久? -XX:UseCMSCompactAtFullCollection(FullGC时压~年老代)
初始效果Q?G堆内存的新生代约60mQminor gcU?-20毫秒Qfull gcU?30毫秒?/p>
Parallel攉高达1U的暂停旉基本不可忍受Q所以选择CMS攉器?/p>
不知Z在被压的Mule 2.0应用里,每秒都有大约400M的v量短命对象生:
对这两个参数q行调优Q既要改善上面两U情况,又要避免新生代过大,复制ơ数q多造成minor gc的暂停时间过ѝ?/p>
优化后,大约1.1U才发生一ơminor GCQ同时年老代的增镉K度大大减缓Q预计几个小时才会发生一ơGCQ而minor gc速度依然保持?5-20ms之间?/p>
参数定稿Q?/p>
-Xms1024m -Xmx1024m -Xmn500m -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=5 -XX:+ExplicitGCInvokesConcurrent
最后服务处理速度?180 tps 上升?380 tpsQ调整两个参数提?7%的性能q是W很划算的买卖?/p>
(4.1)HashMapc:一个最常用的MapQ它Ҏ(gu)键的hashCode值存储数据,Ҏ(gu)键可以直接获取它的|h很快的访问速度Q但不支持线E同步。HashMap最多允怸条记录的键ؓnullQ但是允许多条记录的gؓnull?br />
(4.2)Hashtablec:与HashMapcMQ但是它不允许记录的键或者gؓnullQ支持线E同步,因而Hashtable在写入数据时会很慢?br />
(4.3)LinkedHashMapc:保存了记录的插入序Q在用Iterator遍历它时Q先得到的记录肯定时先插入的Q在遍历的时候比HashMap慢?br />
(4.4)TreeMapc:能够把它保存的记录根据键排序Q默认ؓ升序排列。当用Iterator遍历它时Q得到的记录是排q序的记录?/p>
许多语言Q包括Perl、PHP、Python、JavaScript和JScriptQ都支持用正则表辑ּ处理文本Q一些文本编辑器用正则表辑ּ实现高“搜烦-替换”功能。那么Java又怎样呢?本文写作Ӟ一个包含了用正则表辑ּq行文本处理的Java规范需求(Specification RequestQ已l得到认可,你可以期待在JDK的下一版本中看到它?/td> |
然而,如果现在需要用正则表辑ּQ又该怎么办呢Q你可以从Apache.org下蝲源代码开攄Jakarta-ORO库。本文接下来的内容先要地介绍正则表达式的入门知识Q然后以Jakarta-ORO APIZ介绍如何使用正则表达式?/td> |
一、正则表辑ּ基础知识 |
我们先从单的开始。假设你要搜索一个包含字W?#8220;cat”的字W串Q搜索用的正则表辑ּ是“cat”。如果搜索对大小写不敏感Q单?#8220;catalog”?#8220;Catherine”?#8220;sophisticated”都可以匹配。也是_ |
![]() |
1.1 句点W号 |
假设你在玩英文拼字游戏,惌扑և三个字母的单词,而且q些单词必须?#8220;t”字母开_?#8220;n”字母l束。另外,假设有一本英文字典,你可以用正则表达式搜索它的全部内宏V要构造出q个正则表达式,你可以用一个通配W——句点符?#8220;.”。这P完整的表辑ּ是“t.n”Q它匚w“tan”?#8220;ten”?#8220;tin”?#8220;ton”Q还匚w“t#n”?#8220;tpn”甚至“t n”Q还有其他许多无意义的组合。这是因为句点符号匹配所有字W,包括I格、Tab字符甚至换行W: |
![]() |
1.2 Ҏ(gu)L?/strong> |
Z解决句点W号匚w范围q于q泛q一问题Q你可以在方括号Q?#8220;[]”Q里面指定看来有意义的字W。此Ӟ只有Ҏ(gu)号里面指定的字符才参与匹配。也是_正则表达?#8220;t[aeio]n”只匹?#8220;tan”?#8220;Ten”?#8220;tin”?#8220;ton”。但“Toon”不匹配,因ؓ在方括号之内你只能匹配单个字W: |
![]() |
1.3 “?#8221;W号 |
如果除了上面匚w的所有单词之外,你还惌匚w“toon”Q那么,你可以?#8220;|”操作W?#8220;|”操作W的基本意义是“?#8221;q算。要匚w“toon”Q?#8220;t(a|e|i|o|oo)n”正则表达式。这里不能用方扩号Q因为方括号只允许匹配单个字W;q里必须使用圆括?#8220;()”。圆括号q可以用来分l,具体请参见后面介l?/td> |
![]() |
1.4 表示匚wơ数的符?/strong> |
表一昄了表C匹配次数的W号Q这些符L来确定紧靠该W号左边的符号出现的ơ数Q?/td> |
|
假设我们要在文本文g中搜索美国的C会安全L。这个号码的格式?99-99-9999。用来匹配它的正则表辑ּ如图一所C。在正则表达式中Q连字符Q?#8220;-”Q有着Ҏ(gu)的意义,它表CZ个范_比如??。因此,匚wC会安全L中的q字W号Ӟ它的前面要加上一个{义字W?#8220;\”?/td> |
|
图一Q匹配所?23-12-1234形式的社会安全号?/p> |
假设q行搜烦的时候,你希望连字符号可以出玎ͼ也可以不出现——即Q?99-99-9999?99999999都属于正的格式。这Ӟ你可以在q字W号后面加上“Q?#8221;数量限定W号Q如图二所C: |
|
图二Q匹配所?23-12-1234?23121234形式的社会安全号?/p> |
下面我们再来看另外一个例子。美国汽车牌照的一U格式是四个数字加上二个字母。它的正则表辑ּ前面是数字部?#8220;[0-9]{4}”Q再加上字母部分“[A-Z]{2}”。图三显CZ完整的正则表辑ּ?/td> |
|
图三Q匹配典型的国汽R牌照LQ如8836KV |
1.5 “?#8221;W号 |
“^”W号UCؓ“?#8221;W号。如果用在方括号内,“^”表示不想要匹配的字符。例如,囑֛的正则表辑ּ匚w所有单词,但以“X”字母开头的单词除外?/td> |
|
囑֛Q匹配所有单词,?#8220;X”开头的除外 |
1.6 圆括号和I白W号 |
假设要从格式?#8220;June 26, 1951”的生日日期中提取出月份部分,用来匚w该日期的正则表达式可以如图五所C: |
|
图五Q匹配所有Moth DD,YYYY格式的日?/p> |
新出现的“\s”W号是空白符P匚w所有的I白字符Q包括Tab字符。如果字W串正确匚wQ接下来如何提取出月份部分呢Q只需在月份周围加上一个圆括号创徏一个组Q然后用ORO APIQ本文后面详l讨论)提取出它的倹{修改后的正则表辑ּ如图六所C: |
|
囑օQ匹配所有Month DD,YYYY格式的日期,定义月䆾gؓW一个组 |
1.7 其它W号 |
为简便v见,你可以用一些ؓ常见正则表达式创建的快捷W号。如表二所C: |
表二Q常用符?/td> |
|
例如Q在前面C会安全L的例子中Q所有出?#8220;[0-9]”的地Ҏ(gu)们都可以使用“\d”。修改后的正则表辑ּ如图七所C: |
|
图七Q匹配所?23-12-1234格式的社会安全号?/p> |
二、Jakarta-ORO?/font> |
有许多源代码开攄正则表达式库可供JavaE序员用,而且它们中的许多支持Perl 5兼容的正则表辑ּ语法。我在这里选用的是Jakarta-ORO正则表达式库Q它是最全面的正则表辑ּAPI之一Q而且它与Perl 5正则表达式完全兼宏V另外,它也是优化得最好的API之一?/td> |
Jakarta-ORO库以前叫做OROMatcherQDaniel Savarese大方地把它赠送给了Jakarta Project。你可以按照本文最后参考资源的说明下蝲它?/td> |
我首先将要介l用Jakarta-ORO库时你必d建和讉K的对象,然后介绍如何使用Jakarta-ORO API?/td> |
?PatternCompiler对象 |
首先Q创Z个Perl5Compilercȝ实例Qƈ把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实玎ͼ允许你把正则表达式编译成用来匚w的Pattern对象?/td> |
![]() |
?Pattern对象 |
要把正则表达式编译成Pattern对象Q调用compiler对象的compile()Ҏ(gu)Qƈ在调用参C指定正则表达式。例如,你可以按照下面这U方式编译正则表辑ּ“t[aeio]n”Q?/td> |
![]() |
默认情况下,~译器创Z个大写敏感的模式(patternQ。因此,上面代码~译得到的模式只匚w“tin”?#8220;tan”?“ten”?#8220;ton”Q但不匹?#8220;Tin”?#8220;taN”。要创徏一个大写不敏感的模式Q你应该在调用编译器的时候指定一个额外的参数Q?/td> |
![]() |
创徏好Pattern对象之后Q你可以通过PatternMatchercȝ该Pattern对象q行模式匚w?/td> |
?PatternMatcher对象 |
PatternMatcher对象Ҏ(gu)Pattern对象和字W串q行匚w查。你要实例化一个Perl5Matchercdƈ把结果赋值给PatternMatcher接口。Perl5MatchercLPatternMatcher接口的一个实玎ͼ它根据Perl 5正则表达式语法进行模式匹配: |
![]() |
使用PatternMatcher对象Q你可以用多个方法进行匹配操作,q些Ҏ(gu)的第一个参数都是需要根据正则表辑ּq行匚w的字W串Q?/td> |
· boolean matches(String input, Pattern pattern)Q当输入字符串和正则表达式要_匚w时用。换句话_正则表达式必d整地描述输入字符丌Ӏ?/td> |
· boolean matchesPrefix(String input, Pattern pattern)Q当正则表达式匹配输入字W串起始部分时用?/td> |
· boolean contains(String input, Pattern pattern)Q当正则表达式要匚w输入字符串的一部分时用(卻I它必L一个子Ԍ?/td> |
另外Q在上面三个Ҏ(gu)调用中,你还可以用PatternMatcherInput对象作ؓ参数替代String对象Q这Ӟ你可以从字符串中最后一ơ匹配的位置开始l进行匹配。当字符串可能有多个子串匚wl定的正则表辑ּӞ用PatternMatcherInput对象作ؓ参数很有用了。用PatternMatcherInput对象作ؓ参数替代StringӞ上述三个Ҏ(gu)的语法如下: |
· boolean matches(PatternMatcherInput input, Pattern pattern) |
· boolean matchesPrefix(PatternMatcherInput input, Pattern pattern) |
· boolean contains(PatternMatcherInput input, Pattern pattern) |
三、应用实?/font> |
下面我们来看看Jakarta-ORO库的一些应用实例?/td> |
3.1 日志文g处理 |
dQ分析一个Web服务器日志文Ӟ定每一个用戯在网站上的时间。在典型的BEA WebLogic日志文g中,日志记录的格式如下: |
![]() |
分析q个日志记录Q可以发玎ͼ要从q个日志文g提取的内Ҏ(gu)两项QIP地址和页面访问时间。你可以用分l符P圆括P从日志记录提取出IP地址和时间标记?/td> |
首先我们来看看IP地址。IP地址?个字节构成,每一个字节的值在0?55之间Q各个字节通过一个句点分隔。因此,IP地址中的每一个字节有臛_一个、最多三个数字。图八显CZ为IP地址~写的正则表辑ּQ?/td> |
|
囑օQ匹配IP地址 |
IP地址中的句点字符必须q行转义处理Q前面加?#8220;\”Q,因ؓIP地址中的句点h它本来的含义Q而不是采用正则表辑ּ语法中的Ҏ(gu)含义。句点在正则表达式中的特D含义本文前面已l介l?/td> |
日志记录的时间部分由一Ҏ(gu)括号包围。你可以按照如下思\提取出方括号里面的所有内容:首先搜烦起始Ҏ(gu)号字W(“[”Q,提取出所有不过l束Ҏ(gu)号字W(“]”Q的内容Q向前寻扄x到结束方括号字符。图?ji)显CZq部分的正则表达式?/td> |
|
图九(ji)Q匹配至一个字W,直至扑ֈ“]” |
现在Q把上述两个正则表达式加上分l符P圆括P后合q成单个表达式,q样可以从日志记录提取出IP地址和时间。注意,Z匚w“- -”Q但不提取它Q,正则表达式中间加入了“\s-\s-\s”。完整的正则表达式如囑֍所C?/td> |
|
囑֍Q匹配IP地址和时间标?/p> |
现在正则表达式已l编写完毕,接下来可以编写用正则表辑ּ库的Java代码了?/td> |
Z用Jakarta-ORO库,首先创徏正则表达式字W串和待分析的日志记录字W串Q?/td> |
![]() |
q里使用的正则表辑ּ与图十的正则表达式差不多完全相同Q但有一点例外:在Java中,你必d每一个向前的斜杠Q?#8220;\”Q进行{义处理。图十不是Java的表CŞ式,所以我们要在每?#8220;\”前面加上一?#8220;\”以免出现~译错误。遗憄是,转义处理q程很容易出现错误,所以应该小心}慎。你可以首先输入未经转义处理的正则表辑ּQ然后从左到右依ơ把每一?#8220;\”替换?#8220;\\”。如果要复检Q你可以试着把它输出到屏q上?/td> |
初始化字W串之后Q实例化PatternCompiler对象Q用PatternCompiler~译正则表达式创Z个Pattern对象Q?/td> |
![]() |
现在Q创建PatternMatcher对象Q调用PatternMatcher接口的contain()Ҏ(gu)查匹配情况: |
![]() |
接下来,利用PatternMatcher接口q回的MatchResult对象Q输出匹配的l。由于logEntry字符串包含匹配的内容Q你可以看到cd下面的输出: |
![]() |
3.2 HTML处理实例一 |
下面一个Q务是分析HTML面内FONT标记的所有属性。HTML面内典型的FONT标记如下所C: |
![]() |
E序按照如下Ş式,输出每一个FONT标记的属性: |
![]() |
在这U情况下Q我你用两个正则表辑ּ。第一个如囑֍一所C,它从字体标记提取?#8220;"face="Arial, Serif" size="+2" color="red"”?/td> |
|
囑֍一Q匹配FONT标记的所有属?/p> |
W二个正则表辑ּ如图十二所C,它把各个属性分割成名字-值对?/td> |
|
囑֍二:匚w单个属性,q把它分割成名字-值对 |
分割l果为: |
![]() |
现在我们来看看完成这个Q务的Java代码。首先创Z个正则表辑ּ字符Ԍ用Perl5Compiler把它们编译成Pattern对象。编译正则表辑ּ的时候,指定Perl5Compiler.CASE_INSENSITIVE_MASK选项Q得匹配操作不区分大小写?/td> |
接下来,创徏一个执行匹配操作的Perl5Matcher对象?/td> |
![]() |
假设有一个Stringcd的变量htmlQ它代表了HTML文g中的一行内宏V如果html字符串包含FONT标记Q匹配器返回true。此Ӟ你可以用匚w器对象返回的MatchResult对象获得W一个组Q它包含了FONT的所有属性: |
![]() |
接下来创Z个PatternMatcherInput对象。这个对象允怽从最后一ơ匹配的位置开始l进行匹配操作,因此Q它很适合于提取FONT标记内属性的名字-值对。创建PatternMatcherInput对象Q以参数形式传入待匹配的字符丌Ӏ然后,用匹配器实例提取出每一个FONT的属性。这通过指定PatternMatcherInput对象Q而不是字W串对象Qؓ参数Q反复地调用PatternMatcher对象的contains()Ҏ(gu)完成。PatternMatcherInput对象之中的每一ơP代将把它内部的指针向前移动,下一ơ检将从前一ơ匹配位|的后面开始?/td> |
本例的输出结果如下: |
![]() |
3.3 HTML处理实例?/strong> |
下面我们来看看另一个处理HTML的例子。这一ơ,我们假定Web服务器从widgets.acme.comUd了newserver.acme.com。现在你要修改一些页面中的链接: |
![]() |
执行q个搜烦的正则表辑ּ如图十三所C: |
|
囑֍三:匚w修改前的链接 |
如果能够匚wq个正则表达式,你可以用下面的内Ҏ(gu)换图十三的链接: |
![]() |
注意#字符的后面加上了$1。Perl正则表达式语法用$1?2{表C已l匹配且提取出来的组。图十三的表辑ּ把所有作Z个组匚w和提取出来的内容附加到链接的后面?/td> |
现在Q返回Java。就象前面我们所做的那样Q你必须创徏试字符Ԍ创徏把正则表辑ּ~译到Pattern对象所必需的对象,以及创徏一个PatternMatcher对象Q?img style="width: 500px; height: 181px" alt="" src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_v.jpg" border="0" _extended="true" /> |
接下来,用com.oroinc.text.regex包Utilcȝsubstitute()静态方法进行替换,输出l果字符Ԍ |
![]() |
Util.substitute()Ҏ(gu)的语法如下: |
![]() |
q个调用的前两个参数是以前创建的PatternMatcher和Pattern对象。第三个参数是一个Substiution对象Q它军_了替换操作如何进行。本例用的是Perl5Substitution对象Q它能够q行Perl5风格的替换。第四个参数是想要进行替换操作的字符Ԍ最后一个参数允许指定是否替换模式的所有匹配子ԌUtil.SUBSTITUTE_ALLQ,或只替换指定的次数?/td> |
【结束语?/strong>在这文章中Q我Z介绍了正则表辑ּ的强大功能。只要正运用,正则表达式能够在字符串提取和文本修改中v到很大的作用。另外,我还介绍了如何在JavaE序中通过Jakarta-ORO库利用正则表辑ּ。至于最l采用老式的字W串处理方式Q用StringTokenizerQcharAtQ和substringQ,q是采用正则表达式,q就有待你自己决定了?/td> |