??xml version="1.0" encoding="utf-8" standalone="yes"?>
* 加码解码工具
* @author lwm
*
*/
public class Encode {
/*
* 对应javascript的escape()函数, 加码后的串可直接使用javascript的unescape()q行解码
*/
public static String escape(String src) {
int i;
char j;
StringBuffer tmp = new StringBuffer();
tmp.ensureCapacity(src.length() * 6);
for (i = 0; i < src.length(); i++) {
j = src.charAt(i);
if (Character.isDigit(j) || Character.isLowerCase(j)
|| Character.isUpperCase(j))
tmp.append(j);
else if (j < 256) {
tmp.append("%");
if (j < 16)
tmp.append("0");
tmp.append(Integer.toString(j, 16));
} else {
tmp.append("%u");
tmp.append(Integer.toString(j, 16));
}
}
return tmp.toString();
}
/*
* 对应javascript的unescape()函数, 可对javascript的escape()q行解码
*/
public static String unescape(String src) {
StringBuffer tmp = new StringBuffer();
tmp.ensureCapacity(src.length());
int lastPos = 0, pos = 0;
char ch;
while (lastPos < src.length()) {
pos = src.indexOf("%", lastPos);
if (pos == lastPos) {
if (src.charAt(pos + 1) == 'u') {
ch = (char) Integer.parseInt(src
.substring(pos + 2, pos + 6), 16);
tmp.append(ch);
lastPos = pos + 6;
} else {
ch = (char) Integer.parseInt(src
.substring(pos + 1, pos + 3), 16);
tmp.append(ch);
lastPos = pos + 3;
}
} else {
if (pos == -1) {
tmp.append(src.substring(lastPos));
lastPos = src.length();
} else {
tmp.append(src.substring(lastPos, pos));
lastPos = pos;
}
}
}
return tmp.toString();
}
}
抽象cBuffer是java.nio包支持缓冲区的基?Buffer 的工作方式就象内存中用于d基本数据cd?RandomAccessFile 。象 RandomAccessFile 一P使用 Buffer Q所执行的下一个操作(读/写)在当前某个位|发生。执行读Q写操作中的M个都会改变那个位|,所以在写操作之后进行读操作不会d刚才所写的内容Q而会d刚才所写内容之后的数据?Buffer 提供了四个指C方法,用于讉KU性结构(从最高值到最低|Q?
capacity() Q表明缓冲区的容量大? 一旦确定了大小, 不能再改变;
limit() Q告诉您到目前ؓ止已l往~冲区填了多字节,或者让您用 :limit(int newLimit) 来改变这个限?
position() Q告诉您当前的位|,以执行下一个读Q写操作
mark() Qؓ了稍后用 reset() q行重新讄而记住某个位|?
flip() Q交换限制指针和位置指针Q然后将位置|ؓ 0Qƈ废弃已经做的mark标记
~冲区的基本操作是读 get() 和写 put() Q然而,q些Ҏ在子cM都是针对每种数据cd的特定方法。ؓ了说明这一情况Q让我们研究一个简单示例,该示例演CZ从同一个缓冲区d写一个字W。在清单 1 中, flip() Ҏ交换限制和位|,然后位|置?0Qƈ废弃标记Q让您读刚才所写的数据Q?
清单 1. 读/写示?br>import java.nio.*;
...
CharBuffer buff = ...;
buff.put('A');
buff.flip();
char c = buff.get();
System.out.println("An A: " + c);
现在让我们研I一些具体的 Buffer 子类?
~冲区类?/p>
Merlin h 7 U特定的 Buffer cdQ每U类型对应着一个基本数据类型(不包?booleanQ:
ByteBuffer //存放M除booleancd外的其他基本cd
CharBuffer //存放char
DoubleBuffer //存放double
FloatBuffer //存放float
IntBuffer //存放int
LongBuffer //存放long
ShortBuffer //存放short
在本文后面,我将讨论W?8 U类?MappedByteBuffer Q它用于内存映射文g。如果您必须使用的类型不是这些基本类型,则可以先?ByteBuffer 获得字节cdQ然后将其{换成 Object 或其它Q何类型?
创徏~冲?br>一共有两种cd的缓冲区Q直接缓冲区和非直接~冲区?br>
在创建缓冲区Ӟ可以要求创徏直接~冲区,创徏直接~冲区的成本要比创徏间接~冲区高Q但q可以ɘq行时环境直接在该缓冲区上进行较快的本机 I/O 操作。因为创建直接缓冲区所增加的成本,所以直接缓冲区只用于长生存期的~冲区,而不用于短生存期、一ơ性且用完׃弃的~冲区。而且Q只能在 ByteBuffer q个U别上创建直接缓冲区Q如果希望用其它类型,则必d Buffer 转换成更具体的类型?br>
判断一个缓冲区是否是直接缓冲区Q可以调用isDirect()Ҏ?br>
有三U方式来获取一个缓冲区的对象:
a. 调用allocate()或者allocateDirect()Ҏ直接分配Q其中allocateDirect()q回的是直接~冲区?br>b. 包装一个数l,如:
byte[] b = new byte[1024];
ByteBuffer bb = ByteBuffer.wrap(b);
c. 内存映射Q即调用FileChannel的map()Ҏ?br>
~冲区基本属?br>q几个属性是每个~冲区都有的q且是常用的操作?br>a. 定w(capacity),~冲区大?br>b. 限制(limit),W一个不应被d或写入的字节的烦引,L于定w?br>c. 位置(position)Q下一个被d或写入的字节的烦引,L于限制?br>d. clear()ҎQ设|limit为capacityQposition??br>e. filp()ҎQ设|limit为当前positionQ然后设|position??br>f. rewind()ҎQ保持limit不变Q设|position??br>
~冲区数据操?br>操作包括了读取和写入数据两种?br>d数据使用get()及其pdҎQ除boolean外,每一U类型包括了对应的get()ҎQ如getInt(),getChar(){,get()Ҏ用来d字节Q支持相对和l对索引两种方式?br>写入数据使用put()及其pdҎQ和get()Ҏ是对应的?br>package nio;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class BufferDemo ...{
public static void main(String[] args) throws Exception...{
//分配一个非直接~冲?br>
ByteBuffer bb = ByteBuffer.allocate(100);
//向缓冲区写入0?00的字节制
for(int i = 0; i <100; i++)...{
byte b = (byte) (Math.random() * 100);
bb.put(b);
}
System.out.println("写入文g前的~冲区数?);
bb.flip();
while(bb.hasRemaining())
System.out.print(bb.get() + " ");
System.out.println();
//获取一个关联到文gbuffer.txt的信?br>
FileChannel fc = new FileOutputStream("buffer.txt").getChannel();
//缓冲区数据写到文g?br>
bb.flip();
fc.write(bb);
//防止~存
fc.force(true);
//关闭信道
fc.close();
bb = null;
fc = null;
//下面从文件中d数据
fc = new FileInputStream("buffer.txt").getChannel();
ByteBuffer bb2 = ByteBuffer.allocate((int) fc.size());
fc.read(bb2);
System.out.println("从文件读取的~冲区数?);
bb2.flip();
while(bb2.hasRemaining())
System.out.print(bb2.get() + " ");
System.out.println();
fc.close();
bb2 = null;
fc = null;
}
}
内存映射文g
W?8 U?Buffer cd MappedByteBuffer 只是一U特D的 ByteBuffer ?MappedByteBuffer 文件所在区域直接映到内存。通常Q该区域包含整个文gQ但也可以只映射部分文g。所以,必须指定要映文件的哪部分。而且Q与其它 Buffer 对象一Pq里没有构造函敎ͼ必须?java.nio.channels.FileChannel ?map() Ҏ来获?MappedByteBuffer 。此外,无需q多涉及通道可以用 getChannel() Ҏ?FileInputStream ?FileOutputStream 获取 FileChannel 。通过从命令行传入文g名来d文本文g的内容,清单 4 昄?MappedByteBuffer Q?
清单 4. d内存映射文本文g
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class ReadFileBuff {
public static void main(String args[]) throws IOException {
if (args.length != 0) {
String filename = args[0];
FileInputStream fis = new FileInputStream(filename);
FileChannel channel = fis.getChannel();
int length = (int)channel.size();
MappedByteBuffer byteBuffer =
channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
Charset charset = Charset.forName("ISO-8859-1");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(byteBuffer);
for (int i=0, n=charBuffer.length(); i<n; i++) {
System.out.print(charBuffer.get());
}
}
}
}
|
在本例中Q每Q务在开始之前必ȝ待前一Q务完成,即所涉及的Q务毫不相关也是这栗但是,在现实生zMQ我们经怋用多U程模型。我们在处理某些d的同时也可以让孩子、配偶和父母完成别的d。例如,我在写信的同时可能打发我的儿子去邮局买邮。用软g术语来说Q这UCؓ多个控制Q或执行Q线E?/p>
可以用两U不同的Ҏ来获得多个控制线E:
多个q程
在大多数操作pȝ中都可以创徏多个q程。当一个程序启动时Q它可以为即开始的每项d创徏一个进E,q允许它们同时运行。当一个程序因{待|络讉K或用戯入而被dӞ另一个程序还可以q行Q这样就增加了资源利用率。但是,按照q种方式创徏每个q程要付Z定的代hQ设|一个进E要占用相当一部分处理器时间和内存资源。而且Q大多数操作pȝ不允许进E访问其他进E的内存I间。因此,q程间的通信很不方便Qƈ且也不会它自己提供l容易的~程模型?
U程
U程也称型进E?(LWP)。因为线E只能在单个q程的作用域内活动,所以创建线E比创徏q程要廉价得多。这P因ؓU程允许协作和数据交换,q且在计资源方面非常廉P所以线E比q程更可取。线E需要操作系l的支持Q因此不是所有的机器都提供线E。Java ~程语言Q作为相当新的一U语aQ已线E支持与语言本n合ؓ一体,q样对U程提供了强健的支持?
使用 Java ~程语言实现U程
Java ~程语言使多U程如此单有效,以致于某些程序员说它实际上是自然的。尽在 Java 中用线E比在其他语a中要Ҏ得多Q仍然有一些概念需要掌握。要C的一仉要的事情?main() 函数也是一个线E,q可用来做有用的工作。程序员只有在需要多个线E时才需要创建新的线E?/p>
Thread c?
Thread cL一个具体的c,即不是抽象类Q该cd装了U程的行为。要创徏一个线E,E序员必dZ个从 Thread cd出的新类。程序员必须覆盖 Thread ?run() 函数来完成有用的工作。用户ƈ不直接调用此函数Q而是必须调用 Thread ?start() 函数Q该函数再调?run()。下面的代码说明了它的用法:
创徏两个新线E?/p>
|
在本例中Q我们可以看C个简单的E序Q它按两个不同的旉间隔Q? U和 3 U)在屏q上昄当前旉。这是通过创徏两个新线E来完成的,包括 main() ׃个线E。但是,因ؓ有时要作为线E运行的cd能已l是某个cdơ的一部分Q所以就不能再按q种机制创徏U程。虽然在同一个类中可以实CQ意数量的接口Q但 Java ~程语言只允怸个类有一个父cR同Ӟ某些E序员避免从 Thread cd出,因ؓ它强加了cdơ。对于这U情况,p runnable 接口?/p>
Runnable 接口
此接口只有一个函敎ͼrun()Q此函数必须由实C此接口的cd现。但是,p行这个类而论Q其语义与前一个示例稍有不同。我们可以用 runnable 接口改写前一个示例。(不同的部分用黑体表示。)
创徏两个新线E而不强加cd?/p>
|
h意,当?runnable 接口Ӟ您不能直接创建所需cȝ对象q运行它Q必M Thread cȝ一个实例内部运行它。许多程序员更喜?runnable 接口Q因Z Thread cȝ承会强加cdơ?/p>
synchronized 关键?
到目前ؓ止,我们看到的示例都只是以非常简单的方式来利用线E。只有最的数据,而且不会出现两个U程讉K同一个对象的情况。但是,在大多数有用的程序中Q线E之间通常有信息流。试考虑一个金融应用程序,它有一?Account 对象Q如下例中所C:
一个银行中的多Ҏ?/p>
|
在此代码样例中潜伏着一个错误。如果此cȝ于单U程应用E序Q不会有M问题。但是,在多U程应用E序的情况中Q不同的U程有可能同时讉K同一?Account 对象Q比如说一个联合帐L所有者在不同?ATM 上同时进行访问。在q种情况下,存入和支出就可能以这L方式发生Q一个事务被另一个事务覆盖。这U情况将是灾难性的。但是,Java ~程语言提供了一U简单的机制来防止发生这U覆盖。每个对象在q行旉有一个关联的锁。这个锁可通过为方法添加关键字 synchronized 来获得。这P修订q的 Account 对象Q如下所C)不会遭受像数据损坏q样的错误:
对一个银行中的多Ҏ动进行同步处?/p>
|
deposit() ?withdraw() 函数都需要这个锁来进行操作,所以当一个函数运行时Q另一个函数就被阻塞。请注意QcheckBalance() 未作更改Q它严格是一个读函数。因?checkBalance() 未作同步处理Q所以Q何其他方法都不会d它,它也不会dM其他ҎQ不那些方法是否进行了同步处理?/p>
Java ~程语言中的高多线E支?/p>
U程l?
U程是被个别创徏的,但可以将它们归类到线E组中,以便于调试和监视。只能在创徏U程的同时将它与一个线E组相关联。在使用大量U程的程序中Q用线E组l织U程可能很有帮助。可以将它们看作是计机上的目录和文件结构?/p>
U程间发?
当线E在l箋执行前需要等待一个条件时Q仅?synchronized 关键字是不够的。虽?synchronized 关键字阻止ƈ发更C个对象,但它没有实现U程间发信。Object cMؓ此提供了三个函数Qwait()、notify() ?notifyAll()。以全球气候预程序ؓ例。这些程序通过地球分多单元,在每个@环中Q每个单元的计算都是隔离q行的,直到q些D于稳定,然后盔R单元之间׃交换一些数据。所以,从本质上Ԍ在每个@环中各个U程都必ȝ待所有线E完成各自的d以后才能q入下一个@环。这个模型称为屏蔽同步,下例说明了这个模型:
屏蔽同步
|
当对一个线E调?wait() Ӟ该线E就被有效阻塞,只到另一个线E对同一个对象调?notify() ?notifyAll() 为止。因此,在前一个示例中Q不同的U程在完成它们的工作以后调?waitForAll() 函数Q最后一个线E将触发 notifyAll() 函数Q该函数释放所有的U程。第三个函数 notify() 只通知一个正在等待的U程Q当Ҏơ只能由一个线E用的资源q行讉K限制Ӟq个函数很有用。但是,不可能预知哪个线E会获得q个通知Q因取决?Java 虚拟?(JVM) 调度法?/p>
?CPU 让给另一个线E?
当线E放弃某个稀有的资源Q如数据库连接或|络端口Q时Q它可能调用 yield() 函数临时降低自己的优先Q以便某个其他线E能够运行?/p>
守护U程
有两cȝE:用户U程和守护线E。用LE是那些完成有用工作的线E。守护线E是那些仅提供辅助功能的U程。Thread cL供了 setDaemon() 函数。Java E序运行到所有用LE终止,然后它将破坏所有的守护U程。在 Java 虚拟?(JVM) 中,即?main l束以后Q如果另一个用LE仍在运行,则程序仍然可以l运行?/p>
避免不提倡用的Ҏ
不提倡用的Ҏ是ؓ支持向后兼容性而保留的那些ҎQ它们在以后的版本中可能出现Q也可能不出现。Java 多线E支持在版本 1.1 和版?1.2 中做了重大修订,stop()、suspend() ?resume() 函数已不提倡用。这些函数在 JVM 中可能引入微妙的错误。虽然函数名可能听v来很׃hQ但h制诱惑不要用它们?
调试U程化的E序
在线E化的程序中Q可能发生的某些常见而讨厌的情况是死锁、活锁、内存损坏和资源耗尽?/p>
死锁
死锁可能是多U程E序最常见的问题。当一个线E需要一个资源而另一个线E持有该资源的锁Ӟ׃发生死锁。这U情况通常很难。但是,解决Ҏ却相当好Q在所有的U程中按相同的次序获取所有资源锁。例如,如果有四个资?—A、B、C ?D ?q且一个线E可能要获取四个资源中Q何一个资源的锁,则请保在获取对 B 的锁之前首先获取?A 的锁Q依此类推。如?#8220;U程 1”希望获取?B ?C 的锁Q?#8220;U程 2”获取?A、C ?D 的锁Q则q一技术可能导致阻塞,但它永远不会在这四个锁上造成死锁?/p>
z锁
当一个线E忙于接受新d以致它永q没有机会完成Q何Q务时Q就会发生活锁。这个线E最l将出~冲区ƈDE序崩溃。试想一个秘书需要录入一信Q但她一直在忙于接电话,所以这信永远不会被录入?/p>
内存损坏
如果明智C?synchronized 关键字,则完全可以避免内存错误这U气Mh的问题?/p>
资源耗尽
某些pȝ资源是有限的Q如文g描述W。多U程E序可能耗尽资源Q因为每个线E都可能希望有一个这L资源。如果线E数相当大,或者某个资源的侯选线E数q远过了可用的资源敎ͼ则最好用资源池。一个最好的CZ是数据库q接池。只要线E需要用一个数据库q接Q它׃池中取出一个,使用以后再将它返回池中。资源池也称源库?/p>
调试大量的线E?
有时一个程序因为有大量的线E在q行而极难调试。在q种情况下,下面的这个类可能会派上用场:
|
限制U程优先U和调度
Java U程模型涉及可以动态更改的U程优先U。本质上Q线E的优先U是?1 ?10 之间的一个数字,数字大表明d紧急。JVM 标准首先调用优先U较高的U程Q然后才调用优先U较低的U程。但是,该标准对h相同优先U的U程的处理是随机的。如何处理这些线E取决于基层的操作系l策略。在某些情况下,优先U相同的U程分时q行Q在另一些情况下Q线E将一直运行到l束。请CQJava 支持 10 个优先Q基层操作系l支持的优先U可能要得多,q样会造成一些乱。因此,只能优先作ؓ一U很_略的工具用。最后的控制可以通过明智C?yield() 函数来完成。通常情况下,请不要依靠线E优先来控制线E的状态?/p>
结
本文说明了在 Java E序中如何用线E。像是否应该使用U程q样的更重要的问题在很大E序上取决于手头的应用程序。决定是否在应用E序中用多U程的一U方法是Q估计可以ƈ行运行的代码量。ƈC以下几点Q?/p>
使用多线E不会增?CPU 的能力。但是如果?JVM 的本地线E实玎ͼ则不同的U程可以在不同的处理器上同时q行Q在?CPU 的机器中Q,从而?CPU 机器得到充分利用?
如果应用E序是计密集型的,q受 CPU 功能的制U,则只有多 CPU 机器能够从更多的U程中受益?
当应用程序必ȝ待缓慢的资源Q如|络q接或数据库q接Q时Q或者当应用E序是非交互式的Ӟ多线E通常是有利的?
Z Internet 的Y件有必要是多U程的;否则Q用户将感觉应用E序反映q钝。例如,当开发要支持大量客户机的服务器时Q多U程可以使编E较为容易。在q种情况下,每个U程可以Z同的客户或客L服务Q从而羃短了响应旉?/p>
某些E序员可能在 C 和其他语a中用过U程Q在那些语言中对U程没有语言支持。这些程序员可能通常都被搞得对线E失M信心?/p>
//再运行getXmlXPP3()
bt = System.currentTimeMillis();
String s1 =getXmlXPP3("xml/test.xml");
if(log.isDebugEnabled()){
log.debug("\ngetXmlXPP3() use time: " + (System.currentTimeMillis() - bt) + " millis\n");
}
}
public static String getXmlSAX(String xmlFile){
String result = "";
try {
SAXReader reader = new SAXReader();
Document document = reader.read(new File(xmlFile));
result = document.asXML();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String getXmlXPP3(String xmlFile){
String result = "";
try {
XPP3Reader reader = new XPP3Reader();
Document document = reader.read(new File(xmlFile));
result = document.asXML();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
有没有这一?Document doc = DocumentHelper.createDocument()",Ҏ能的媄响很?特别是对大xml文g(管q没有用doc)
另外Q?getXmlXSAX()和getXmlXPP3()q行的先后次序对性能的媄响也很大Q?br>
试Q?br> 在我的机器上Q对一?00k左右的XML文gq行多次试后的均值结果ؓQ?br>
getXmlXPP3() use time: 265 millis
...
getXmlXSAX() use time: 359 millis
...
l论Q?br> 通过比较Q在dXML文g上,XPP3略ؓ优于SAXQ?br>
注意Q?br>
要运行例子,classpath需包含Q?br>dom4j-1.6.1.jar
jaxen-1.1-beta-10.jar
log4j-1.2.9.jar
pull-parser-2.1.10.jar
xpp3-1.1.4c.jar
参考:
dom4j : http://www.dom4j.org/
XPP : http://www.extreme.indiana.edu/xgws/xsoap/xpp/
正则表达式是一U文本模式,包括普通字W(例如Qa ?z 之间的字母)和特D字W(UCؓ“元字W?#8221;Q。模式描q在搜烦文本时要匚w的一个或多个字符丌Ӏ?/p>
正则表达式示?br>
表达?nbsp; 匚w
/^\s*$/
匚wI?br>
/\d{2}-\d{5}/
验证׃位数字、一个连字符再加 5 位数字组成的 ID 受?br>
/<\s*(\S+)(\s[^>]*)?>[\s\S]*<\s*\/\1\s*>/
匚w HTML 标记?br>
下表包含了元字符的完整列表以及它们在正则表达式上下文中的行ؓQ?/p>
字符 说明
\
下一字符标记为特D字W、文本、反向引用或八进制{义符。例如,“n”匚w字符“n”?#8220;\n”匚w换行W。序?#8220;\\”匚w“\”Q?#8220;\(”匚w“(”?br>
^
匚w输入字符串开始的位置。如果设|了 RegExp 对象?Multiline 属性,^ q会?#8220;\n”?#8220;\r”之后的位|匹配?br>
$
匚w输入字符串结位置。如果设|了 RegExp 对象?Multiline 属性,$ q会?#8220;\n”?#8220;\r”之前的位|匹配?br>
*
零次或多ơ匹配前面的字符或子表达式。例如,zo* 匚w“z”?#8220;zoo”? {效?{0,}?br>
+
一ơ或多次匚w前面的字W或子表辑ּ。例如,“zo+”?#8220;zo”?#8220;zoo”匚wQ但?#8220;z”不匹配? {效?{1,}?br>
?
零次或一ơ匹配前面的字符或子表达式。例如,“do(es)?”匚w“do”?#8220;does”中的“do”? {效?{0,1}?br>
{n}
n 是非负整数。正好匹?n ơ。例如,“o{2}”?#8220;Bob”中的“o”不匹配,但与“food”中的两个“o”匚w?br>
{n,}
n 是非负整数。至匹?n ơ。例如,“o{2,}”不匹?#8220;Bob”中的“o”Q而匹?#8220;foooood”中的所?o?#8220;o{1,}”{效?#8220;o+”?#8220;o{0,}”{效?#8220;o*”?br>
{n,m}
M ?n 是非负整敎ͼ其中 n <= m。匹配至?n ơ,臛_ m ơ。例如,“o{1,3}”匚w“fooooood”中的头三?o?o{0,1}' {效?'o?'。注意:您不能将I格插入逗号和数字之间?br>
?
当此字符紧随M其他限定W(*??、{n}、{n,}、{n,m}Q之后时Q匹配模式是“非贪心的”?#8220;非贪心的”模式匚w搜烦到的、尽可能短的字符Ԍ而默认的“贪心?#8221;模式匚w搜烦到的、尽可能长的字符丌Ӏ例如,在字W串“oooo”中,“o+?”只匹配单?#8220;o”Q?#8220;o+”匚w所?#8220;o”?br>
.
匚w?#8220;\n”之外的Q何单个字W。若要匹配包?#8220;\n”在内的Q意字W,请用诸?#8220;[\s\S]”之类的模式?br>
(pattern)
匚w pattern q捕莯匚w的子表达式。可以?$0…$9 属性从l果“匚w”集合中检索捕L匚w。若要匹配括号字W?( )Q请使用“\(”或?#8220;\)”?br>
(?:pattern)
匚w pattern 但不捕获该匹配的子表辑ּQ即它是一个非捕获匚wQ不存储供以后用的匚w。这对于?#8220;or”字符 (|) l合模式部g的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经的表达式?br>
(?=pattern)
执行正向预测先行搜烦的子表达式,该表辑ּ匚w处于匚w pattern 的字W串的v始点的字W串。它是一个非捕获匚wQ即不能捕获供以后用的匚w。例如,'Windows (?=95|98|NT|2000)' 匚w“Windows 2000”中的“Windows”Q但不匹?#8220;Windows 3.1”中的“Windows”。预先行不占用字符Q即发生匚w后,下一匚w的搜索紧随上一匚w之后Q而不是在l成预测先行的字W后?br>
(?!pattern)
执行反向预测先行搜烦的子表达式,该表辑ּ匚w不处于匹?pattern 的字W串的v始点的搜索字W串。它是一个非捕获匚wQ即不能捕获供以后用的匚w。例如,'Windows (?!95|98|NT|2000)' 匚w“Windows 3.1”中的 “Windows”Q但不匹?#8220;Windows 2000”中的“Windows”。预先行不占用字符Q即发生匚w后,下一匚w的搜索紧随上一匚w之后Q而不是在l成预测先行的字W后?br>
x|y
匚w x ?y。例如,'z|food' 匚w“z”?#8220;food”?(z|f)ood' 匚w“zood”?#8220;food”?
[xyz]
字符集。匹配包含的M字符。例如,“[abc]”匚w“plain”中的“a”?
[^xyz]
反向字符集。匹配未包含的Q何字W。例如,“[^abc]”匚w“plain”中的“p”?
[a-z]
字符范围。匹配指定范围内的Q何字W。例如,“[a-z]”匚w“a”?#8220;z”范围内的M写字母?
[^a-z]
反向范围字符。匹配不在指定的范围内的M字符。例如,“[^a-z]”匚wM不在“a”?#8220;z”范围内的M字符?
\b
匚w一个字边界Q即字与I格间的位置。例如,“er\b”匚w“never”中的“er”Q但不匹?#8220;verb”中的“er”?
\B
非字边界匚w?#8220;er\B”匚w“verb”中的“er”Q但不匹?#8220;never”中的“er”?
\cx
匚w x 指示的控制字W。例如,\cM 匚w Control-M 或回车符。x 的值必d A-Z ?a-z 之间。如果不是这P则假?c 是“c”字符本n?
\d
数字字符匚w。等效于 [0-9]?
\D
非数字字W匹配。等效于 [^0-9]?
\f
换页W匹配。等效于 \x0c ?\cL?br>
\n
换行W匹配。等效于 \x0a ?\cJ?br>
\r
匚w一个回车符。等效于 \x0d ?\cM?br>
\s
匚wMI白字符Q包括空根{制表符、换늬{。与 [ \f\n\r\t\v] {效?br>
\S
匚wM非空白字W。与 [^ \f\n\r\t\v] {效?
\t
制表W匹配。与 \x09 ?\cI {效?br>
\v
垂直制表W匹配。与 \x0b ?\cK {效?br>
\w
匚wM字类字符Q包括下划线。与“[A-Za-z0-9_]”{效?
\W
与Q何非单词字符匚w。与“[^A-Za-z0-9_]”{效?
\xn
匚w nQ此处的 n 是一个十六进制{义码。十六进制{义码必须正好是两位数ѝ例如,“\x41”匚w“A”?#8220;\x041”?#8220;\x04”&“1”{效。允许在正则表达式中使用 ASCII 代码?br>
\num
匚w numQ此处的 num 是一个正整数。到捕获匚w的反向引用。例如,“(.)\1”匚w两个q箋的相同字W?
\n
标识一个八q制转义码或反向引用。如?\n 前面臛_?n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八q制?(0-7)Q那?n 是八q制转义码?br>
\nm
标识一个八q制转义码或反向引用。如?\nm 前面臛_?nm 个捕获子表达式,那么 nm 是反向引用。如?\nm 前面臛_?n 个捕P?n 是反向引用,后面跟有字符 m。如果两U前面的情况都不存在Q则 \nm 匚w八进制?nmQ其?n ?m 是八q制数字 (0-7)?br>
\nml
?n 是八q制?(0-3)Qm ?l 是八q制?(0-7) Ӟ匚w八进制{义码 nml?br>
\un
匚w nQ其?n 是以四位十六q制数表C的 Unicode 字符。例如,\u00A9 匚w版权W号 (?)?br>
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
public class TestDom {
//xml串{换ؓdocument
public static Document xml2Document(String xml) {
Document doc = null;
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
doc = builder.parse(new InputSource(new StringReader(xml)));
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
//xml文g串{换ؓdocument
public static Document xmlFile2Document(String xmlFile) {
Document doc = null;
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
doc = builder.parse(new File(xmlFile));
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
//删除命名I间: xmlns="..."
public static String delNamespace(String xml) {
String result = xml;
try {
Document doc = xml2Document(xml);
Element root = doc.getDocumentElement();
root.removeAttribute("xmlns");
result = asXml(doc);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
//doc转换为xml?br> public static String asXml(Document doc) {
String strxml = "";
try {
OutputFormat format = new OutputFormat(doc);
// format.setEncoding("UTF-8");
StringWriter stringOut = new StringWriter();
XMLSerializer serial = new XMLSerializer(stringOut, format);
serial.asDOMSerializer();
serial.serialize(doc.getDocumentElement());
strxml = stringOut.toString();
} catch (Exception e) {
e.printStackTrace();
}
return strxml;
}
//node转换为xml?br> public static String asXml(Node node, Document doc) {
String strxml = "";
try {
OutputFormat format = new OutputFormat(doc);
// format.setEncoding("UTF-8");
StringWriter stringOut = new StringWriter();
XMLSerializer serial = new XMLSerializer(stringOut, format);
serial.asDOMSerializer();
serial.serialize((Element)node);
strxml = stringOut.toString();
} catch (Exception e) {
e.printStackTrace();
}
return strxml;
}
}
1、用JdbcTemplate的execute()Ҏ执行SQL语句
JdbcTemplate我们用的JDBC的流E封装v来,包括了异常的捕捉、SQL的执行、查询结果的转换{等。spring大量使用Template Method模式来封装固定流E的动作QXXXTemplate{类别都是基于这U方式的实现?
除了大量使用Template Method来封装一些底层的操作l节Qspring也大量用callback方式cd调相关类别的Ҏ以提供JDBC相关cd的功能,使传l的JDBC的用者也能清楚了解spring所提供的相兛_装类别方法的使用?/p>
JDBC的PreparedStatement
在getUser(id)里面使用UserRowMapper
|上攉
org.springframework.jdbc.core.PreparedStatementCreator q回预编译SQL 不能于Object[]一L
问题描述
看下q?XML:
<catalog>
<cd>
<artist>Sufjan Stevens</artist>
<title>Illinois</title>
<src>http://www.sufjan.com/</src>
</cd>
<cd>
<artist>Stoat</artist>
<title>Future come and get me</title>
<src>http://www.stoatmusic.com/</src>
</cd>
<cd>
<artist>The White Stripes</artist>
<title>Get behind me satan</title>
<src>http://www.whitestripes.com/</src>
</cd>
</catalog>
你可以用?/cd”来得到没有在Q何命名空间中定义的“cd”节炏V?
现在让我们来攚w这?XMLQ让它的所有元素都属于 'http://www.edankert.com/examples/' 命名I间中?br />
Z避免在每个不同的元素前都要加个前~Q我们在根元素上定义通常所说的~省命名I间。改造后?XML 如下Q?
<catalog xmlns="
http://www.edankert.com/examples/
">
<cd>
<artist>Sufjan Stevens</artist>
<title>Illinois</title>
<src>http://www.sufjan.com/</src>
</cd>
<cd>
<artist>Stoat</artist>
<title>Future come and get me</title>
<src>http://www.stoatmusic.com/</src>
</cd>
<cd>
<artist>The White Stripes</artist>
<title>Get behind me satan</title>
<src>http://www.whitestripes.com/</src>
</cd>
</catalog>
当我们用与上文相同?XPath ?/cd”,得不到M元素。这是因为指定的 XPath q回的是所有不属于M命名I间的“cd”节点,而本例中Q所有的“cd”元素都属于~省的命名空间“http://www.edankert.com/examples/”?
“前~Q命名空间”映?br /> Z取出命名I间“http://www.edankert.com/examples/”中的所有“cd”元素,我们需要对 XPath 表达式做一些额外的工作?
Z解决q个问题QXPath 规范允许我们使用 QName 来指定元素或者属性。QName 可以是元素的直接名称QŞ如“element”)Q或者包含一个前~QŞ如“pre:element”)。这个前~需要映到一个命名空间的 URI 上。例如,如果把“pre”前~映射到“http://www.edankert.com/test”上Q则通过“pre:element”可以查扑և属于命名I间“http://www.edankert.com/test”的所?“element”元素?br />
在本例中Q我们把“edx”映到?http://www.edankert.com/examples/”命名空间上。通过 XPath?/edx:cd”就可以查找出属于?http://www.edankert.com/examples/”命名空间的所有“cd”元素。?
XPath 处理器允许设|“前~Q命名空间”的映射Q但是,如何L,却要依赖于具体的实现。下文D例说?Jaxen (JDOM/dom4j/XOM)、JAXP 以及 XSLT 中是如何q行“前~Q命名空间”的映射的?/font>
Jaxen ?Dom4J
下述代码从文件系l读入一?XML 文g?org.dom4j.Document 对象中,q且?Document 中查扑ֱ于“http://www.edankert.com/examples/”命名空间的所有“cd”元素?br />
try {
SAXReader reader = new SAXReader();
Document document = reader.read( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "
http://www.edankert.com/examples/
");
XPath xpath = new Dom4jXPath( "http://edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( DocumentException e) {
// the document is not well-formed.
...
}
W一步,创徏一?SAXReaderQ用来从文gpȝ中读取“catalog.xml”ƈ创徏一个特定于 Dom4j ?Document 对象?
W二步,对于所?Jaxen 实现都一P是创徏一?HashMap 对象Q用于保存“前~Q命名空间的 URI”的映射?br />
Z能通过 Dom4j 使用 Jaxen ?XPath 功能Q需要创Z个与 Dom4j 相关?XPath 对象QDom4jXPath。创建方法是?XPath 的表辑ּQ即?/edx:cd”)传给 Dom4jXPath 的构造方法?br />
现在Q我们已l创Z XPath 对象Q接下来可以把“前~Q命名空间”的映射表传递给 XPath 引擎Q把q个 HashMap 映射表用 SimpleNamespaceContext 包装h。SimpleNamespaceContext ?Jaxen ?NamespaceContext 接口的默认实现类?
最后一步就是调?XPath 对象?selectNodes() Ҏq行查找。ƈ把完整的 Dom4j Document 对象作ؓ参数传递进厅R实际上QDocument 中的Ml点都可以作为参数?
Jaxen ?XOM
XOM 是基于简单的 Java DOM APIs 之上的最新工P它的设计初衷是提供简单和易学易用的接口?
try {
Builder builder = new Builder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "
http://www.edankert.com/examples/
");
XPath xpath = new XOMXPath( "http://edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( ParsingException e) {
// An error occurred parsing the document
...
}
我们需要创Z?Builder 对象Q从文gpȝ中读取“catalog.xml”文Ӟq创建出?XOM 相关?Document 对象?
下一步创建出包含了“前~Q命名空间”映关pȝ HashMap 对象?
我们需要创Z个特定于 XOM ?XPath 对象QXOMXPath。创建方法是?XPath 表达式传递给构造方法,然后可以通过 XOM 使用 Jaxen ?XPath 功能了?
创徏?XPath 对象后,同样Q我们把“前~Q命名空间”的映射表用 SimpleNamespaceContext 对象装后,传递给 XPath 引擎?
最后调?XPath 对象的“selectNodes()”方法进行查找,?XOM Document 对象作ؓ本方法的参数?
Jaxen ?JDOM
JDOM 是第一个提供简单的 XML 讉K API 的工兗?
try {
SAXBuilder builder = new SAXBuilder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "
http://www.edankert.com/examples/
");
XPath xpath = new JDOMXPath( "http://edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( JDOMException e) {
// An error occurred parsing the document
...
}
首先Q通过 SAXBuilder 创徏了一个特定于 JDom ?Document 对象?
接着创徏一个特定于 JDOM ?XPath 对象QJDOMXPath?br />
然后Q把“前~Q命名空间”的映射表(HashMapQ用 SimpleNamespaceContext 对象装hQ传递给 XPath 引擎?
最后调?XPath 对象的“selectNodes()”方法来q行查找Qƈ?JDOM ?Document 对象作ؓ本方法的输入参数?
JAXP XPathFactory
?1.3 版vQ?JAXP q提供了一U在 XML Object Models 上进行查询的通用机制?
try {
DocumentBuilderFactory domFactory =DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware( true);
DocumentBuilder builder = domFactory.newDocumentBuilder();Document document = builder.parse( new InputSource( "file:catalog.xml"));
XPathFactory factory =XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext( new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if ( prefix.equals( "edx")) {
return "
http://www.edankert.com/examples/
";
} else if ...
...
}
return XPathConstants.NULL_NS_URI;
}
public String getPrefix(String namespaceURI) {
if ( namespaceURI.equals( "
http://www.edankert.com/examples/
")) {
return "edx";
} else if ...
...
}
return null;
}
public Iterator getPrefixes(String namespaceURI) {
ArrayList list = new ArrayList();
if ( namespaceURI.equals( "
http://www.edankert.com/examples/
")) {
list.add( "edx");
} else if ...
...
}
return list.iterator();
}
});
Object nodes = xpath.evaluate( "http://edx:cd", document.getDocumentElement(),
XPathConstants.NODESET);
...
} catch (ParserConfigurationException e) {
...
} catch (XPathExpressionException e) {
...
} catch (SAXException e) {
...
} catch (IOException e) {
...
}
首先?JAXP ?DocumentBuilderFactory 创徏一个org.w3c.dom.Document 对象Q确保启用了 namespace 处理功能?
现在可以通过 XPathFactory 来创?XPath 对象Qƈ通过 XPath 对象Ҏ进行查询?br />
Z创徏“前~Q命名空间”映ƈ传递给 XPath 引擎Q我们需要实?NamespaceContext 接口Q该接口目前q没有默认实现类。这意味着要实?getNamespaceURI、getPrefix 和getPrefixes ҎQƈ保q些Ҏ能返回正的|包括“xmlns”和“xml”前~所对应的命名空间的 URI 倹{?
把我们自己实现的 NamespaceContext 对象传递给 XPath 引擎后,可以通过 evaluate Ҏ来查?XPath 表达式所对应的元素:使用上文中提到的 XPath 表达式,q?Document 的根节点作ؓ输入入参敎ͼq接收一?NodeList 对象作ؓq回l果?
XSLT
XPath 设计的初h用于 XSLT。这也许能解释“ؓ什么在 XSLT 中定义命名空间的前缀是一件很q_的事”(也许因ؓ XSLT 也是一?XML 名词的缘故吧Q?
<xsl:stylesheet version="1.1" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform
">
<xsl:template match="http://edx:cd" xmlns:edx="
http://www.edankert.com/examples/
">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
只需要?XML 本n的机Ӟ单地?edx 前缀赋予一个命名空间的 URI 倹{?
通过与我们的 XPath 表达式?/edx:cd”相匚w?xsl:templateQ能得到与上文其他例子相同的输出l果?
l束?br /> Z在(~省Q命名空间上使用 XPath 表达式,我们需要指定一个“前~Q命名空间”映。正如我们所看到的,具体使用什么样的前~名称Q是无关紧要的?
同样的方法,也可以用于查询那些用其他前缀修饰的元素。这意味着上面的例子对下述 XML 也有效。下q?XML 没有使用~省命名I间Q而是使用?examples 作命名空间的前缀Q?
<examples:catalog xmlns:examples="
http://www.edankert.com/examples/
">
<examples:cd>
<examples:artist>Sufjan Stevens</examples:artist>
<examples:title>Illinois</examples:title>
<examples:src>http://www.sufjan.com/</examples:src>
</examples:cd>
<examples:cd>
<examples:artist>Stoat</examples:artist>
<examples:title>Future come and get me</examples:title>
<examples:src>http://www.stoatmusic.com/</examples:src>
</examples:cd>
<examples:cd>
<examples:artist>The White Stripes</examples:artist>
<examples:title>Get behind me satan</examples:title>
<examples:src>http://www.whitestripes.com/</examples:src>
</examples:cd>
</examples:catalog>
使用?/edx:cd”作?XPath 表达式,使用与前文例子相同的“前~Q命名空间”映,在这?XML 上同栯查询出属于“http://www.edankert.com/examples/”命名空间的所有“cd”元素?
资源
Extensible Markup Language (XML) 1.0 (Third Edition)
http://www.w3.org/TR/REC-xml/
Namespaces in XML
http://www.w3.org/TR/REC-xml-names/
XML Path Language (XPath) Version 1.0
http://www.w3.org/TR/xpath
XSL Transformations (XSLT) Version 1.0
http://www.w3.org/TR/xslt
dom4j
http://www.dom4j.org/
XOM
http://www.xom.nu/
JDOM
http://www.jdom.org/
Jaxen
http://www.jaxen.org/
Java 5.0
http://java.sun.com/j2se/1.5.0/
<!-- data base -->
<property file="${dir.web}/WEB-INF/config/jdbc.properties"/>
<property name="database.driver" value="${jdbc.driverClassName}"/>
<property name="database.url" value="${jdbc.url}"/>
<property name="database.username" value="${jdbc.username}"/>
<property name="database.password" value="${jdbc.password}"/>
<!-- the classpth of ant -->
<path id="classpath">
<fileset dir="${dir.lib}" includes="**/*.jar" />
<pathelement path="${dir.classes}"/>
</path>
<!-- compile the src and test source code -->
<target name="compile" description="Compile main source and test tree java files">
<delete dir="${dir.classes}"/>
<mkdir dir="${dir.classes}"/>
<javac srcdir="${dir.src}" destdir="${dir.classes}" debug="on">
<classpath refid="classpath"/>
</javac>
<copy todir="${dir.classes}">
<fileset dir="${dir.src}" includes="**/*.xml,**/*.properties">
<!--exclude name="**/*.hbm.xml" /-->
</fileset>
<fileset dir="${dir.etc}" includes="**/*.properties"/>
</copy>
<native2ascii dest="${dir.classes}" src="${dir.etc}" encoding="UTF-8" includes="**/*.src" ext=".properties" />
</target>
<!-- setup the database and initialize the data -->
<target name="setup-db">
<echo message="=========================================================="/>
<echo message="database driver:${database.driver}"/>
<echo message="database
url:${database.url}"/
>
<echo message="database username:${database.username}"/>
<echo message="database password:${database.password}"/>
<echo message="=========================================================="/>
<antcall target="runSql" inheritAll="false">
<param name="script.sql" value="${dir.meta}/database-setup.sql"/>
<param name="database.driver" value="${database.driver}"/>
<param name="database.url" value="${database.url}"/>
<param name="database.username" value="${database.username}"/>
<param name="database.password" value="${database.password}"/>
</antcall>
<echo message="initialize Sample data..."/>
<antcall target="runSql" inheritAll="false">
<param name="script.sql" value="${dir.meta}/database-init.sql"/>
<param name="database.driver" value="${database.driver}"/>
<param name="database.url" value="${database.url}"/>
<param name="database.username" value="${database.username}"/>
<param name="database.password" value="${database.password}"/>
</antcall>
</target>
<!-- run the sql to setup database -->
<target name="runSql">
<sql src="${script.sql}"
driver="${database.driver}"
url="${database.url}"
userid="${database.username}"
password="${database.password}"
classpathref="classpath"
onerror="continue"/>
</target>
<target name="clean" description="Clean output directories">
<delete dir="${dir.classes}"/>
</target>
<target name="deploy" description="deploy......" depends="clean,compile" >
</target>
</project>