<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-精彩的人生-随笔分类-Java</title><link>http://www.blogjava.net/hopeshared/category/5326.html</link><description>好好工作，好好生活</description><language>zh-cn</language><lastBuildDate>Thu, 31 Jan 2008 10:32:09 GMT</lastBuildDate><pubDate>Thu, 31 Jan 2008 10:32:09 GMT</pubDate><ttl>60</ttl><item><title>当cmd里输入路径包含空格的时候</title><link>http://www.blogjava.net/hopeshared/archive/2008/01/31/178685.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 31 Jan 2008 07:41:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2008/01/31/178685.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/178685.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2008/01/31/178685.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/178685.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/178685.html</trackback:ping><description><![CDATA[公司的产品有点复杂，有些功能又只能通过命令行完成。<br />
<br />
但是有的时候产品容易被默认安装到c:\Program Files\这样包含空格的路径下，那么在命令行中输入一个带空格的file path作为参数往往会出错。所以大家都流传着这样一种说法：咱们公司的产品安装的时候选择的目录千万不要包含空格。<br />
<br />
但是，一个那么大的产品装好了，使用命令行配置的时候发现path带空格命令过不去，而卸载重装太恐怖了，怎么办？<br />
<br />
现在有两个办法来解决这个问题<br />
<br />
1）用缩写。比如c:\Program Files缩写为c:\Progra~1<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 再来刨根问底查查这个命名是否有规则，于是找到：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;&nbsp; <span style="color: #789aff;">文件夹（sub-directry)名称，以前是不允许带空白的，后来允许带空白，但由于有了空白，许多命令出现二义性，于是采用双引号括起来的办法。例如：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; cd Documents and Settings<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 按老定义 等于 CD Documents, CD 命令找不到名叫Documents 的 directry<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 于是采用双引号：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; cd &#8220;Documents and Settings&#8220;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 但用到 set PATH 时很麻烦，名字太长，双引号时常括错。于是采用8个字符缩写，即写头六个字母(略去空白），另加波浪号和1。例如：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; "Documents and Settings&#8220; -- DOCUME~1<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; "Local Settings" -- LOCALS~1 （注意略去空白，用了第二个词的字母，凑成六个，再加波浪号和1）。<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 于是，这种方法成了规定。</span><br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  再来个十万个为什么的下一个，如果多个文件前6字符一样怎么办？为什么最后是1而不是0或者其他数字呢？看看这个例子<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;  <span style="color: #789aff;">假设下面是你的C盘根目录中的文件夹：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Program Files<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Progra file<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Progra zhang<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 则三个目录分别表示为：C:\Progra~1; C:\Progra~2; C:\Progra~3;</span><br />
2）绕过去，创建一个镜像。例如在cmd中输入&nbsp; subst w: "C:\Documents and Settings\hopeshared"。然后就可以直接用w:\替代C:\Documents and Settings\hopeshared了<br />
<br />
<img src ="http://www.blogjava.net/hopeshared/aggbug/178685.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2008-01-31 15:41 <a href="http://www.blogjava.net/hopeshared/archive/2008/01/31/178685.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java类加载的表现形式（zz）</title><link>http://www.blogjava.net/hopeshared/archive/2006/10/23/76781.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 23 Oct 2006 08:20:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/10/23/76781.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/76781.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/10/23/76781.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/76781.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/76781.html</trackback:ping><description><![CDATA[
		<div class="con_all">
				<p>
				</p>
				<div>java中的类是动态加载的，我们先看一下我们常用的类加载方式，先有一个感性的认识，才能进一步<br />深入讨论,类加载无非就是下面三种方式。<br />class A{}<br />class B{}<br />class C{}<br />public class Loader{<br />    public static void main(String[] args) throws Exception{<br />       Class aa=A.class; <br />       Class bb=Class.forName("B");<br />       Class cc=ClassLoader.getSystemClassLoader().loadClass("C");<br />    }<br />}<br />我们先看.class字面量方式，很多人可能不知道这种方式，因为这种用法不是一般java语法。<br />通过javap我们可以发现，这种方式的大致等价于定义了一个静态成员变量<br />    static Class class$0;(后面的编号是增长的)<br />你可以试图再定义一个  static Class class$0,应该会收到一个编译错误(重复定义)。</div>
				<div>
				</div>
				<div>Class aa=A.class;<br />就相当于<br />    if(class$0==null){<br /> try{ <br />           Class.forName("A");<br /> }<br /> cacth(ClassNotFoundException e){<br />    throw new NoClassDefFoundError(e);<br /> }<br />    }<br />    Class aa=class$0;</div>
				<div>
				</div>
				<div>可以很清楚的看到，这种类的字面量定义其实不是加载类的方式，而是被编译器处理了，实质<br />上是使用了Class.forName方法，但是使用这种方式有一个很大的好处就是不用处理异常，因为<br />编译器处理的时候如果找不到类会抛出一个NoClassDefFoundError。也许你觉得需要处理<br />ClassNotFoundException这种异常，事实上99%的情况下我们可以把这种异常认为是一个错误。<br />所以大部分情况我们使用这种方式会更简洁。</div>
				<div>
				</div>
				<div>最常用的方式就是Class.forName方式了，这也是一个通用的上层调用。这个方法有两个重载，<br />可能很多人都忽略了第二个方法。<br />public static Class forName(String name) throws ClassNotFoundException<br />public static Class forName(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException</div>
				<div>
				</div>
				<div>第二个方法后面多了两个参数，第二个参数表示是否初始化，第三个参数为指定的类加载器。<br />在上面的例子中：<br />Class bb=Class.forName("B");等价于<br />Class bb=Class.forName("B",true,Loader.class.getClassLoader());<br />这里要详细说一下这个类的初始化这个参数,如果这个参数为false的话，<br />类中的static成员不会被初始化，static语句块也不会被执行。<br />也就是类虽然被加载了，但是没有被初始化，不过在第一次使用时仍然会初始化。<br />所以我们有时候会看到Class.forName("XXX").newInstance()这样的语句，为什么这里要创建一个<br />不用的实例呢?不过是为了保证类被初始化(兼容以前的系统)。<br />其实第二个方法是比较难用的，需要指定类加载器，如果不指定而且又没有安装安全管理器的化，<br />是无法加载类的，只要看一下具体的实现就明白了。</div>
				<div>
				</div>
				<div>最本质的方式当然是直接使用ClassLoader加载了，所有的类最终都是通过ClassLoader加载的，<br />Class cc=ClassLoader.getSystemClassLoader().loadClass("C");<br />这里通过使用系统类加载器来加载某个类，很直接的方式，但是很遗憾的是通过这种方式加载类，<br />类是没有被初始化的(也就是初始化被延迟到真正使用的时候).不过我们也可以借鉴上面的经验，加载<br />后实例化一个对象Class cc=ClassLoader.getSystemClassLoader().loadClass("C").newInstance()。<br />这里使用了系统类加载器，也是最常用的类加载器，从classpath中寻找要加载的类。<br />java中默认有三种类加载器：引导类加载器，扩展类加载器，系统类加载器。<br />java中的类加载有着规范的层次结构，如果我们要了解类加载的过程，需要明确知道哪个类被谁<br />加载，某个类加载器加载了哪些类等等，就需要深入理解ClassLoader的本质。<br />以上只是类加载的表面的东西，我们还将讨论深层次的东西。</div>
				<div>
				</div>
				<div>
				</div>
				<p>原文：<a href="http://dev.csdn.net/author/treeroot/a481eb323af84caab1149221432e46b9.html">http://dev.csdn.net/author/treeroot/a481eb323af84caab1149221432e46b9.html</a></p>
		</div>
<img src ="http://www.blogjava.net/hopeshared/aggbug/76781.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2006-10-23 16:20 <a href="http://www.blogjava.net/hopeshared/archive/2006/10/23/76781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>初探Java类加载机制的奥秘（zz）</title><link>http://www.blogjava.net/hopeshared/archive/2006/10/23/76780.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 23 Oct 2006 08:19:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/10/23/76780.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/76780.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/10/23/76780.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/76780.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/76780.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: [文章信息]																																																																																						作者:																		cqfz																												时间:														...&nbsp;&nbsp;<a href='http://www.blogjava.net/hopeshared/archive/2006/10/23/76780.html'>阅读全文</a><img src ="http://www.blogjava.net/hopeshared/aggbug/76780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2006-10-23 16:19 <a href="http://www.blogjava.net/hopeshared/archive/2006/10/23/76780.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java类加载内幕(zz)</title><link>http://www.blogjava.net/hopeshared/archive/2006/10/23/76779.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 23 Oct 2006 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/10/23/76779.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/76779.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/10/23/76779.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/76779.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/76779.html</trackback:ping><description><![CDATA[
		<a class="f1">
				<div class="guanggao">
						<span id="contentAdv">
						</span>
				</div>
				<p>　　类加载是java语言提供的最强大的机制之一。尽管类加载并不是讨论的热点话题，但所有的编程人员都应该了解其工作机制，明白如何做才能让其满足我们的需要。这能有效节省我们的编码时间，从不断调试ClassNotFoundException, ClassCastException的工作中解脱出来。</p>
				<p>　　这篇文章从基础讲起，比如代码与数据的不同之处是什么，他们是如何构成一个实例或对象的。然后深入探讨java虚拟机(</p>
		</a>JVM)是如何利用类加载器读取代码，以及java中类加载器的主要类型。接着用一个类加载的基本算法看一下类加载器如何加载一个内部类。本文的下一节演示一段代码来说明扩展和开发属于自己的类加载器的必要性。紧接着解释如何使用定制的类加载器来完成一个一般意义上的任务，使其可以加载任意远端客户的代码，在JVM中定义，实例化并执行它。本文包括了J2EE关于类加载的规范——事实上这已经成为了J2EE的标准之一。<p>　　<strong>类与数据</strong></p><p>　　一个类代表要执行的代码，而数据则表示其相关状态。状态时常改变，而代码则不会。当我们将一个特定的状态与一个类相对应起来，也就意味着将一个类事例化。尽管相同的类对应的实例其状态千差万别，但其本质都对应着同一段代码。在JAVA中，一个类通常有着一个.class文件，但也有例外。在JAVA的运行时环境中(Java runtime)，每一个类都有一个以第一类(first-class)的Java对象所表现出现的代码，其是java.lang.Class的实例。我们编译一个JAVA文件，编译器都会嵌入一个public, static, final修饰的类型为java.lang.Class，名称为class的域变量在其字节码文件中。因为使用了public修饰，我们可以采用如下的形式对其访问:</p><p>　　java.lang.Class klass = Myclass.class;</p><p>　　一旦一个类被载入JVM中，同一个类就不会被再次载入了(切记，同一个类)。这里存在一个问题就是什么是“同一个类”?正如一个对象有一个具体的状态，即标识，一个对象始终和其代码(类)相关联。同理，载入JVM的类也有一个具体的标识，我们接下来看。</p><p>　　在JAVA中，一个类用其完全匹配类名(fully qualified class name)作为标识，这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此，如果一个名为Pg的包中，有一个名为Cl的类，被类加载器KlassLoader的一个实例kl1加载，Cl的实例，即C1.class在JVM中表示为(Cl, Pg, kl1)。这意味着两个类加载器的实例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的，被它们所加载的类也因此完全不同，互不兼容的。那么在JVM中到底有多少种类加载器的实例?下一节我们揭示答案。</p><p>　　<strong>类加载器</strong></p><p>　　在JVM中，每一个类都被java.lang.ClassLoader的一些实例来加载.类ClassLoader是在包中java.lang里，开发者可以自由地继承它并添加自己的功能来加载类。</p><p>　　无论何时我们键入java MyMainClass来开始运行一个新的JVM，“引导类加载器(bootstrap class <a class="bluekey" href="http://www.yesky.com/key/3465/593465.html" target="_blank">loader</a>)”负责将一些关键的Java类，如java.lang.Object和其他一些运行时代码先加载进内存中。运行时的类在JRE\lib\rt.jar包文件中。因为这属于系统底层执行动作，我们无法在JAVA文档中找到引导类加载器的工作细节。基于同样的原因，引导类加载器的行为在各JVM之间也是大相径庭。</p><p>　　同理，如果我们按照如下方式:</p><p>　　log(java.lang.String.class.getClassLoader());</p><p>　　来获取java的核心运行时类的加载器，就会得到null。</p><p>　　接下来介绍java的扩展类加载器。扩展库提供比java运行代码更多的特性，我们可以把扩展库保存在由java.ext.dirs属性提供的路径中。</p><p>　　(编辑注:java.ext.dirs属性指的是系统属性下的一个key，所有的系统属性可以通过System.getProperties()方法获得。在编者的系统中，java.ext.dirs的value是” C:\Program Files\Java\jdk1.5.0_04\jre\lib\ext”。下面将要谈到的如java.class.path也同属系统属性的一个key。)</p><p>　　类ExtClassLoader专门用来加载所有java.ext.dirs下的.jar文件。开发者可以通过把自己的.jar文件或库文件加入到扩展目录的classpath，使其可以被扩展类加载器读取。</p><p>　　从开发者的角度，第三种同样也是最重要的一种类加载器是AppClassLoader。这种类加载器用来读取所有的对应在java.class.path系统属性的路径下的类。</p><p>　　Sun的java指南中，文章“理解扩展类加载”(Understanding Extension Class Loading)对以上三个类加载器路径有更详尽的解释，这是其他几个JDK中的类加载器</p><p><font face="Verdana">　　●java.net.URLClassLoader<br />　　●java.security.SecureClassLoader<br />　　●java.rmi.server.RMIClassLoader<br />　　●sun.applet.AppletClassLoader</font></p><p>　　java.lang.Thread，包含了public ClassLoader getContextClassLoader()方法，这一方法返回针对一具体线程的上下文环境类加载器。此类加载器由线程的创建者提供，以供此线程中运行的代码在需要加载类或资源时使用。如果此加载器未被建立，缺省是其父线程的上下文类加载器。原始的类加载器一般由读取应用程序的类加载器建立。</p><p>　　<strong>类加载器如何工作?</strong></p><p>　　除了引导类加载器，所有的类加载器都有一个父类加载器，不仅如此，所有的类加载器也都是java.lang.ClassLoader类型。以上两种类加载器是不同的，而且对于开发者自订制的类加载器的正常运行也至关重要。最重要的方面是正确设置父类加载器。任何类加载器，其父类加载器是加载该类加载器的类加载器实例。(记住，类加载器本身也是一个类!)</p><p>　　使用loadClass()方法可以从类加载器中获得该类。我们可以通过java.lang.ClassLoader的源代码来了解该方法工作的细节，如下:</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">protected synchronized Class&lt;?&gt; loadClass<br />    (String name, boolean resolve)<br />    throws ClassNotFoundException{</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    // First check if the class is already loaded<br />    Class c = findLoadedClass(name);<br />    if (c == null) {<br />        try {<br />            if (parent != null) {<br />                c = parent.loadClass(name, false);<br />            } else {<br />                c = findBootstrapClass0(name);<br />            }<br />        } catch (ClassNotFoundException e) {<br />            // If still not found, then invoke<br />            // findClass to find the class.<br />            c = findClass(name);<br />        }<br />    }<br />    if (resolve) {<br />            resolveClass(c);<br />    }<br />    return c;<br />}</font></p><p>　　我们可以使用ClassLoader的两种构造方法来设置父类加载器:</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">public class MyClassLoader extends ClassLoader{</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public MyClassLoader(){<br />        super(MyClassLoader.class.getClassLoader());<br />    }<br />}</font></p><p>　　或</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">public class MyClassLoader extends ClassLoader{</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public MyClassLoader(){<br />        super(getClass().getClassLoader());<br />    }<br />}</font></p><p>　　第一种方式较为常用，因为通常不建议在构造方法里调用getClass()方法，因为对象的初始化只是在构造方法的出口处才完全完成。因此，如果父类加载器被正确建立，当要示从一个类加载器的实例获得一个类时，如果它不能找到这个类，它应该首先去访问其父类。如果父类不能找到它(即其父类也不能找不这个类，等等)，而且如果findBootstrapClass0()方法也失败了，则调用findClass()方法。findClass()方法的缺省实现会抛出ClassNotFoundException，当它们继承java.lang.ClassLoader来订制类加载器时开发者需要实现这个方法。findClass()的缺省实现方式如下:</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    protected Class&lt;?&gt; findClass(String name)<br />        throws ClassNotFoundException {<br />        throw new ClassNotFoundException(name);<br />    }</font></p><p>　　在findClass()方法内部，类加载器需要获取任意来源的字节码。来源可以是文件系统，URL，数据库，可以产生字节码的另一个应用程序，及其他类似的可以产生java规范的字节码的来源。你甚至可以使用BCEL (Byte Code Engineering Library:字节码工程库)，它提供了运行时创建类的捷径。BCEL已经被成功地使用在以下方面:编译器，优化器，混淆器，代码产生器及其他分析工具。一旦字节码被检索，此方法就会调用defineClass()方法，此行为对不同的类加载实例是有差异的。因此，如果两个类加载实例从同一个来源定义一个类，所定义的结果是不同的。</p><p>　　JAVA语言规范(Java language specification)详细解释了JAVA执行引擎中的类或接口的加载(loading)，链接(linking)或初始化(initialization)过程。</p><p>　　图一显示了一个主类称为MyMainClass的应用程序。依照之前的阐述，MyMainClass.class会被AppClassLoader加载。 MyMainClass创建了两个类加载器的实例:CustomClassLoader1 和 CustomClassLoader2,他们可以从某数据源(比如网络)获取名为Target的字节码。这表示类Target的类定义不在应用程序类路径或扩展类路径。在这种情况下，如果MyMainClass想要用自定义的类加载器加载Target类，CustomClassLoader1和CustomClassLoader2会分别独立地加载并定义Target.class类。这在java中有重要的意义。如果Target类有一些静态的初始化代码，并且假设我们只希望这些代码在JVM中只执行一次，而这些代码在我们目前的步骤中会执行两次——分别被不同的CustomClassLoaders加载并执行。如果类Target被两个CustomClassLoaders加载并创建两个实例Target1和Target2，如图一显示，它们不是类型兼容的。换句话说，在JVM中无法执行以下代码:</p><p>　　Target target3 = (Target) target2;</p><p>　　以上代码会抛出一个ClassCastException。这是因为JVM把他们视为分别不同的类，因为他们被不同的类加载器所定义。这种情况当我们不是使用两个不同的类加载器CustomClassLoader1 和 CustomClassLoader2，而是使用同一个类加载器CustomClassLoader的不同实例时，也会出现同样的错误。这些会在本文后边用具体代码说明。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/2097hf979897.jpg" border="0" /></p><p>　　图1. 在同一个JVM中多个类加载器加载同一个目标类</p><p>　　关于类加载、定义和链接的更多解释，请参考Andreas Schaefer的"Inside Class Loaders."</p><p>　　<strong>为什么我们需要我们自己的类加载器</strong></p><p>　　原因之一为开发者写自己的类加载器来控制JVM中的类加载行为，java中的类靠其包名和类名来标识，对于实现了java.io.Serializable接口的类，serialVersionUID扮演了一个标识类版本的重要角色。这个唯一标识是一个类名、接口名、成员方法及属性等组成的一个64位的哈希字段，而且也没有其他快捷的方式来标识一个类的版本。严格说来，如果以上的都匹配，那么则属于同一个类。</p><p>　　但是让我们思考如下情况:我们需要开发一个通用的执行引擎。可以执行实现某一特定接口的任何任务。当任务被提交到这个引擎，首先需要加载这个任务的代码。假设不同的客户对此引擎提交了不同的任务，凑巧，这些所有的任务都有一个相同的类名和包名。现在面临的问题就是这个引擎是否可以针对不同的用户所提交的信息而做出不同的反应。这一情况在下文的参考一节有可供下载的代码样例，samepath 和 differentversions，这两个目录分别演示了这一概念。</p><p>　　图2 显示了文件目录结构，有三个子目录samepath, differentversions, 和 differentversionspush，里边是例子:</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/xw33i9xt766e.jpg" border="0" /></p><p>　　图2. 文件夹结构组织示例</p><p>　　在samepath 中，类version.Version保存在v1和v2两个子目录里，两个类具有同样的类名和包名，唯一不同的是下边这行:</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public void fx(){<br />        log("this = " + this + "; Version.fx(1).");<br />    }</font></p><p>　　V1中，日志记录中有Version.fx(1)，而在v2中则是Version.fx(2)。把这个两个存在细微不同的类放在一个classpath下，然后运行Test类:</p><p>　　set CLASSPATH=.;%CURRENT_ROOT%\v1;%CURRENT_ROOT%\v2</p><p>　　%JAVA_HOME%\bin\java Test</p><p>　　图3显示了控制台输出。我们可以看到对应着Version.fx(1)的代码被执行了，因为类加载器在classpath首先看到此版本的代码。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/047utg4n29dt.jpg" border="0" /></p><p>　　图3. 在类路径中samepath测试排在最前面的version 1</p><p>　　再次运行，类路径做如下微小改动。</p><p>　　set CLASSPATH=.;%CURRENT_ROOT%\v2;%CURRENT_ROOT%\v1</p><p>　　%JAVA_HOME%\bin\java Test</p><p>　　控制台的输出变为图4。对应着Version.fx(2)的代码被加载，因为类加载器在classpath中首先找到它的路径。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/wply7f5z7u13.jpg" border="0" /></p><p>　　图4. 在类路径中samepath测试排在最前面的version 2</p><p>　　根据以上例子可以很明显地看出，类加载器加载在类路径中被首先找到的元素。如果我们在v1和v2中删除了version.Version，做一个非version.Version形式的.jar文件，如myextension.jar，把它放到对应java.ext.dirs的路径下，再次执行后看到version.Version不再被AppClassLoader加载，而是被扩展类加载器加载。如图5所示。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/6ybfc43l7lt2.jpg" border="0" /></p><p>　　图5. AppClassLoader及ExtClassLoader</p><p>　　继续这个例子，文件夹differentversions包含了一个RMI执行引擎，客户端可以提供给执行引擎任何实现了common.TaskIntf接口的任务。子文件夹client1 和 client2包含了类client.TaskImpl有个细微不同的两个版本。两个类的区别在以下几行:</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    static{<br />        log("client.TaskImpl.class.getClassLoader<br />        (v1) : " + TaskImpl.class.getClassLoader());<br />    }</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public void execute(){<br />        log("this = " + this + "; execute(1)");<br />    }</font></p><p>　　在client1和client2里分别有getClassLoader(v1) 与 execute(1)和getClassLoader(v2) 与 execute(2)的的log语句。并且，在开始执行引擎RMI服务器的代码中，我们随意地将client2的任务实现放在类路径的前面。</p><p>　　CLASSPATH=%CURRENT_ROOT%\common;%CURRENT_ROOT%\server;</p><p>　　%CURRENT_ROOT%\client2;%CURRENT_ROOT%\client1</p><p>　　%JAVA_HOME%\bin\java server.Server</p><p>　　如图6，7，8的屏幕截图，在客户端VM，各自的client.TaskImpl类被加载、实例化，并发送到服务端的VM来执行。从服务端的控制台，可以明显看到client.TaskImpl代码只被服务端的VM执行一次，这个单一的代码版本在服务端多次生成了许多实例，并执行任务。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/r1c12i2ia7o3.jpg" border="0" /></p><p>　　图6. 执行引擎服务器控制台</p><p>　　图6显示了服务端的控制台，加载并执行两个不同的客户端的请求，如图7，8所示。需要注意的是，代码只被加载了一次(从静态初始化块的日志中也可以明显看出)，但对于客户端的调用这个方法被执行了两次。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/siv7a6p52k6z.jpg" border="0" /></p><p>　　图7. 执行引擎客户端 1控制台</p><p>　　图7中，客户端VM加载了含有client.TaskImpl.class.getClassLoader(v1)的日志内容的类TaskImpl的代码，并提供给服务端的执行引擎。图8的客户端VM加载了另一个TaskImpl的代码，并发送给服务端。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/4j7kax69v4x5.jpg" border="0" /></p><p>　　图8. 执行引擎客户端 2控制台</p><p>　　在客户端的VM中，类client.TaskImpl被分别加载，初始化，并发送到服务端执行。图6还揭示了client.TaskImpl的代码只在服务端的VM中加载了一次，但这“唯一的一次”却在服务端创造了许多实例并执行。或许客户端1该不高兴了因为并不是它的client.TaskImpl(v1)的方法调用被服务端执行了，而是其他的一些代码。如何解决这一问题?答案就是实现定制的类加载器。</p><p>　　<strong>定制类加载器</strong></p><p>　　要较好地控制类的加载，就要实现定制的类加载器。所有自定义的类加载器都应继承自java.lang.ClassLoader。而且在构造方法中，我们也应该设置父类加载器。然后重写findClass()方法。differentversionspush文件夹包含了一个叫做FileSystemClassLoader的自订制的类加载器。其结构如图9所示。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/22cve6owje59.jpg" border="0" /></p><p>　　图9. 定制类加载器关系</p><p>　　以下是在common.FileSystemClassLoader实现的主方法:</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">public byte[] findClassBytes(String className){</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        try{<br />            String pathName = currentRoot +<br />                File.separatorChar + className.<br />                replace('.', File.separatorChar)<br />                + ".class";<br />            FileInputStream inFile = new<br />                FileInputStream(pathName);<br />            byte[] classBytes = new<br />                byte[inFile.available()];<br />            inFile.read(classBytes);<br />            return classBytes;<br />        }<br />        catch (java.io.IOException ioEx){<br />            return null;<br />        }<br />    }</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public Class findClass(String name)throws<br />        ClassNotFoundException{</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        byte[] classBytes = findClassBytes(name);<br />        if (classBytes==null){<br />            throw new ClassNotFoundException();<br />        }<br />        else{<br />            return defineClass(name, classBytes,<br />                0, classBytes.length);<br />        }<br />    }</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public Class findClass(String name, byte[]<br />        classBytes)throws ClassNotFoundException{</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        if (classBytes==null){<br />            throw new ClassNotFoundException(<br />                "(classBytes==null)");<br />        }<br />        else{<br />            return defineClass(name, classBytes,<br />                0, classBytes.length);<br />        }<br />    }</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public void execute(String codeName,<br />        byte[] code){</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        Class klass = null;<br />        try{<br />            klass = findClass(codeName, code);<br />            TaskIntf task = (TaskIntf)<br />                klass.newInstance();<br />            task.execute();<br />        }<br />        catch(Exception exception){<br />            exception.printStackTrace();<br />        }<br />    }</font></p><p>　　这个类供客户端把client.TaskImpl(v1)转换成字节数组，之后此字节数组被发送到RMI服务端。在服务端，一个同样的类用来把字节数组的内容转换回代码。客户端代码如下:</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">public class Client{</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    public static void main (String[] args){</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        try{<br />            byte[] code = getClassDefinition<br />                ("client.TaskImpl");<br />            serverIntf.execute("client.TaskImpl",<br />                code);<br />            }<br />            catch(RemoteException remoteException){<br />                remoteException.printStackTrace();<br />            }<br />        }</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">    private static byte[] getClassDefinition<br />        (String codeName){<br />        String userDir = System.getProperties().<br />            getProperty("BytePath");<br />        FileSystemClassLoader fscl1 = null;</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        try{<br />            fscl1 = new FileSystemClassLoader<br />                (userDir);<br />        }<br />        catch(FileNotFoundException<br />            fileNotFoundException){<br />            fileNotFoundException.printStackTrace();<br />        }<br />        return fscl1.findClassBytes(codeName);<br />    }<br />}</font></p><p>　　在执行引擎中，从客户端收到的代码被送到定制的类加载器中。定制的类加载器把其从字节数组定义成类，实例化并执行。需要指出的是，对每一个客户请求，我们用类FileSystemClassLoader的不同实例来定义客户端提交的client.TaskImpl。而且，client.TaskImpl并不在服务端的类路径中。这也就意味着当我们在FileSystemClassLoader调用findClass()方法时，findClass()调用内在的defineClass()方法。类client.TaskImpl被特定的类加载器实例所定义。因此，当FileSystemClassLoader的一个新的实例被使用，类又被重新定义为字节数组。因此，对每个客户端请求类client.TaskImpl被多次定义，我们就可以在相同执行引擎JVM中执行不同的client.TaskImpl的代码。</p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">public void execute(String codeName, byte[] code)throws RemoteException{</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        FileSystemClassLoader fileSystemClassLoader = null;</font></p><p><font style="BACKGROUND-COLOR: #dddddd" face="Verdana">        try{<br />            fileSystemClassLoader = new FileSystemClassLoader();<br />            fileSystemClassLoader.execute(codeName, code);<br />        }<br />        catch(Exception exception){<br />            throw new RemoteException(exception.getMessage());<br />        }<br />    }</font></p><p>　　示例在differentversionspush文件夹下。服务端和客户端的控制台界面分别如图10，11，12所示:</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/sth3br076g80.jpg" border="0" /></p><p>　　图10. 定制类加载器执行引擎</p><p>　　图10显示的是定制的类加载器控制台。我们可以看到client.TaskImpl的代码被多次加载。实际上针对每一个客户端，类都被加载并初始化。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/5975e618h5l8.jpg" border="0" /></p><p>　　图11. 定制类加载器，客户端1</p><p>　　图11中，含有client.TaskImpl.class.getClassLoader(v1)的日志记录的类TaskImpl的代码被客户端的VM加载，然后送到服务端。图12 另一个客户端把包含有client.TaskImpl.class.getClassLoader(v1)的类代码加载并送往服务端。</p><p><img src="http://searchwebservices.techtarget.com.cn/imagelist/05/10/t5690273m6d9.jpg" border="0" /></p><p>　　图12. 定制类加载器，客户端1</p><p>　　这段代码演示了我们如何利用不同的类加载器实例来在同一个VM上执行不同版本的代码。</p><p>　　<strong>J2EE的类加载器</strong></p><p>　　J2EE的服务器倾向于以一定间隔频率，丢弃原有的类并重新载入新的类。在某些情况下会这样执行，而有些情况则不。同样，对于一个web服务器如果要丢弃一个servlet实例，可能是服务器管理员的手动操作，也可能是此实例长时间未相应。当一个JSP页面被首次请求，容器会把此JSP页面翻译成一个具有特定形式的servlet代码。一旦servlet代码被创建，容器就会把这个servlet翻译成class文件等待被使用。对于提交给容器的每次请求，容器都会首先检查这个JSP文件是否刚被修改过。是的话就重新翻译此文件，这可以确保每次的请求都是及时更新的。企业级的部署方案以.ear, .war, .rar等形式的文件，同样需要重复加载，可能是随意的也可能是依照某种配置方案定期执行。对所有的这些情况——类的加载、卸载、重新加载……全部都是建立在我们控制应用服务器的类加载机制的基础上的。实现这些需要扩展的类加载器，它可以执行由其自身所定义的类。Brett Peterson已经在他的文章 Understanding J2EE Application Server Class Loading Architectures给出了J2EE应用服务器的类加载方案的详细说明，详见网站TheServerSide.com。</p><p>　　<strong>结要</strong></p><p>　　本文探讨了类载入到虚拟机是如何进行唯一标识的，以及类如果存在同样的类名和包名时所产生的问题。因为没有一个直接可用的类版本管理机制，所以如果我们要按自己的意愿来加载类时，需要自己订制类加载器来扩展其行为。我们可以利用许多J2EE服务器所提供的“热部署”功能来重新加载一个新版本的类，而不改动服务器的VM。即使不涉及应用服务器，我们也可以利用定制类加载器来控制java应用程序载入类时的具体行为。Ted Neward的书Server-Based Java Programming中详细阐述java的类加载，J2EE的API以及使用他们的最佳途径。<br /><br /><br />原文：<a href="http://searchwebservices.techtarget.com.cn/tips/362/2158862.shtml">http://searchwebservices.techtarget.com.cn/tips/362/2158862.shtml</a></p><img src ="http://www.blogjava.net/hopeshared/aggbug/76779.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2006-10-23 16:16 <a href="http://www.blogjava.net/hopeshared/archive/2006/10/23/76779.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Jakarta Commons Pool处理对象池化（zz）</title><link>http://www.blogjava.net/hopeshared/archive/2006/10/10/74275.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Tue, 10 Oct 2006 04:57:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/10/10/74275.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/74275.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/10/10/74275.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/74275.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/74275.html</trackback:ping><description><![CDATA[内容： <br /><br />对象池化技术 <br />Jakarta Commons Pool组件 <br />下载和安装 <br />PoolableObjectFactory、ObjectPool和ObjectPoolFactory <br />创立PoolableObjectFactory <br />使用ObjectPool <br />利用ObjectPoolFactory <br />借助BasePoolableObjectFactory <br />各式各样的ObjectPool <br />StackObjectPool <br />SoftReferenceObjectPool <br />GenericObjectPool <br />GenericObjectPool.Config <br />带键值的对象池 <br />当出借少于归还 <br />线程安全问题 <br />什么时候不要池化 <br />结束语 <br />参考资料 <br />关于作者 <br />对于本文的评价 <br /><br />孙海涛 (alexhsun@hotmail.com)<br /><br />2003 年 12 月<br /><br />恰当地使用对象池化技术，可以有效地减少对象生成和初始化时的消耗，提高系统的运行效率。Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架，以及若干种各具特色的对象池实现，可以有效地减少处理对象池化时的工作量，为其它重要的工作留下更多的精力和时间。<br />创建新的对象并初始化的操作，可能会消耗很多的时间。在这种对象的初始化工作包含了一些费时的操作（例如，从一台位于20,000千米以外的主机上读出一些数据）的时候，尤其是这样。在需要大量生成这样的对象的时候，就可能会对性能造成一些不可忽略的影响。要缓解这个问题，除了选用更好的硬件和更棒的虚拟机以外，适当地采用一些能够减少对象创建次数的编码技巧，也是一种有效的对策。对象池化技术（Object Pooling）就是这方面的著名技巧，而Jakarta Commons Pool组件则是处理对象池化的得力外援。 <br /><br />对象池化技术<br />对象池化的基本思路是：将用过的对象保存起来，等下一次需要这种对象的时候，再拿出来重复使用，从而在一定程度上减少频繁创建对象所造成的开销。用于充当保存对象的“容器”的对象，被称为“对象池”（Object Pool，或简称Pool）。 <br /><br />对于没有状态的对象（例如String），在重复使用之前，无需进行任何处理；对于有状态的对象（例如StringBuffer），在重复使用之前，就需要把它们恢复到等同于刚刚生成时的状态。由于条件的限制，恢复某个对象的状态的操作不可能实现了的话，就得把这个对象抛弃，改用新创建的实例了。 <br /><br />并非所有对象都适合拿来池化??因为维护对象池也要造成一定开销。对生成时开销不大的对象进行池化，反而可能会出现“维护对象池的开销”大于“生成新对象的开销”，从而使性能降低的情况。但是对于生成时开销可观的对象，池化技术就是提高性能的有效策略了。 <br /><br />说明：英语中的Pool除了“池”之外，还有“供多方共享的资源”意思。作者十分怀疑第二种才是“Object Pool”中的Pool的实际含义，但是“对象池”的说法已经广为流传，而一时又没有足以替代的贴切译法，因此这里仍然沿用这种译名。 <br />Jakarta Commons Pool组件 <br /><br /><br />Jakarta Commons Pool是一个用于在Java程序中实现对象池化的组件。它的基本情况是： <br /><br />主要作者：Morgan Delagrange、Geir Magnusson、Craig McClanahan、Rodney Waldhoff、David Weinrich和Dirk Verbeeck <br /><br /><br />最新版本：1.1 <br /><br /><br />所含包数：2个（org.apache.commons.pool和org.apache.commons.pool.impl） <br /><br /><br />所含类数：21个（其中有4个抽象类和6个接口） <br /><br /><br />适用平台：Java 2, Standard Edition. <br /><br /><br />单纯地使用Pool组件不需要太多的Java 2的知识和经验，对语法和基本概念（对象、异常、类、接口、实例、继承和实现等）有一般了解即可。 <br /><br /><br />下载和安装 <br />为了顺利的按照本文中提到的方法使用Pool组件，除去Java 2 SDK外，还需要先准备下列一些东西： <br /><br />Jakarta Commons Pool<br /><br />所需版本：1.0.1+ <br />下载地址：<a href="http://jakarta.apache.org/commons/pool" target="_blank"> http://jakarta.apache.org/commons/pool</a><br />作用：处理对象池化 <br />Jakarta Commons Collections <br /><br />所需版本：2.1+ <br />下载地址：<a href="http://jakarta.apache.org/commons/collections" target="_blank"> http://jakarta.apache.org/commons/collections</a><br />作用：支持Jakarta Commons Pool的运行 <br />以上两种软件均有已编译包和源代码包两种形式可供选择。一般情况下，使用已编译包即可。不过建议同时也下载源代码包，作为参考资料使用。 <br /><br />如果打算使用源代码包自行编译，那么还需要准备以下一些东西： <br /><br />Ant <br /><br />所需版本：1.5.3+ <br />下载地址：<a href="http://ant.apache.org/" target="_blank"> http://ant.apache.org</a><br />作用：运行编译用脚本 <br />JUnit <br /><br />所需版本：3.8.1+ <br />下载地址：<a href="http://www.junit.org/" target="_blank"> http://www.junit.org</a><br />作用：编译和运行单元测试 <br />具体的编译方法，可以参看有关的Ant文档。 <br /><br />将解压或编译后得到的commons-pool.jar和commons-collections.jar放入CLASSPATH，就可以开始使用Pool组件了。 <br /><br />PoolableObjectFactory、ObjectPool和ObjectPoolFactory <br />在Pool组件中，对象池化的工作被划分给了三类对象： <br /><br />PoolableObjectFactory用于管理被池化的对象的产生、激活、挂起、校验和销毁；<br /><br /><br />ObjectPool用于管理要被池化的对象的借出和归还，并通知PoolableObjectFactory完成相应的工作； <br /><br /><br />ObjectPoolFactory则用于大量生成相同类型和设置的ObjectPool。 <br /><br /><br />相应地，使用Pool组件的过程，也大体可以划分成“创立PoolableObjectFactory”、“使用ObjectPool”和可选的“利用ObjectPoolFactory”三种动作。 <br /><br />创立PoolableObjectFactory <br />Pool组件利用PoolableObjectFactory来照看被池化的对象。ObjectPool的实例在需要处理被池化的对象的产生、激活、挂起、校验和销毁工作时，就会调用跟它关联在一起的PoolableObjectFactory实例的相应方法来操作。 <br /><br />PoolableObjectFactory是在org.apache.commons.pool包中定义的一个接口。实际使用的时候需要利用这个接口的一个具体实现。Pool组件本身没有包含任何一种PoolableObjectFactory实现，需要根据情况自行创立。 <br /><br />创立PoolableObjectFactory的大体步骤是： <br /><br />创建一个实现了PoolableObjectFactory接口的类。<br /><br /><br />import org.apache.commons.pool.PoolableObjectFactory;<br /><br />public class PoolableObjectFactorySample<br />implements PoolableObjectFactory {<br />private static int counter = 0;<br />}<br /><br /><br /><br /><br />为这个类添加一个Object makeObject()方法。这个方法用于在必要时产生新的对象。 <br /><br /><br />public Object makeObject() throws Exception {<br />Object obj = String.valueOf(counter++);<br />System.err.println("Making Object " + obj);<br />return obj;<br />}<br /><br /><br /><br /><br /><br />为这个类添加一个void activateObject(Object obj)方法。这个方法用于将对象“激活”??设置为适合开始使用的状态。 <br /><br />public void activateObject(Object obj) throws Exception {<br />System.err.println("Activating Object " + obj);<br />}<br /><br /><br /><br /><br /><br />为这个类添加一个void passivateObject(Object obj)方法。这个方法用于将对象“挂起”??设置为适合开始休眠的状态。 <br /><br />public void passivateObject(Object obj) throws Exception {<br />System.err.println("Passivating Object " + obj);<br />}<br /><br /><br /><br /><br /><br />为这个类添加一个boolean validateObject(Object obj)方法。这个方法用于校验一个具体的对象是否仍然有效，已失效的对象会被自动交给destroyObject方法销毁 <br /><br />public boolean validateObject(Object obj) {<br />boolean result = (Math.random() &gt; 0.5);<br />System.err.println("Validating Object "<br />+ obj + " : " + result);<br />return result;<br />}<br /><br /><br /><br /><br />为这个类添加一个void destroyObject(Object obj)方法。这个方法用于销毁被validateObject判定为已失效的对象。 <br /><br />public void destroyObject(Object obj) throws Exception {<br />System.err.println("Destroying Object " + obj);<br />}<br /><br /><br /><br /><br />最后完成的PoolableObjectFactory类似这个样子： <br /><br /><br />PoolableObjectFactorySample.java<br /><br /><br /><br />import org.apache.commons.pool.PoolableObjectFactory;<br /><br />public class PoolableObjectFactorySample<br />implements PoolableObjectFactory {<br />private static int counter = 0;<br /><br />public Object makeObject() throws Exception {<br />Object obj = String.valueOf(counter++);<br />System.err.println("Making Object " + obj);<br />return obj;<br />}<br /><br />public void activateObject(Object obj) throws Exception {<br />System.err.println("Activating Object " + obj);<br />}<br /><br />public void passivateObject(Object obj) throws Exception {<br />System.err.println("Passivating Object " + obj);<br />}<br /><br />public boolean validateObject(Object obj) {<br />/* 以1/2的概率将对象判定为失效 */<br />boolean result = (Math.random() &gt; 0.5);<br />System.err.println("Validating Object "<br />+ obj + " : " + result);<br />return result;<br />}<br /><br />public void destroyObject(Object obj) throws Exception {<br />System.err.println("Destroying Object " + obj);<br />}<br />}<br /><br /><br /><br /><br />使用ObjectPool <br />有了合适的PoolableObjectFactory之后，便可以开始请出ObjectPool来与之同台演出了。 <br /><br />ObjectPool是在org.apache.commons.pool包中定义的一个接口，实际使用的时候也需要利用这个接口的一个具体实现。Pool组件本身包含了若干种现成的ObjectPool实现，可以直接利用。如果都不合用，也可以根据情况自行创建。具体的创建方法，可以参看Pool组件的文档和源码。 <br /><br />ObjectPool的使用方法类似这样： <br /><br />生成一个要用的PoolableObjectFactory类的实例。 <br /><br />PoolableObjectFactory factory = new PoolableObjectFactorySample();<br /><br /><br /><br /><br />利用这个PoolableObjectFactory实例为参数，生成一个实现了ObjectPool接口的类（例如StackObjectPool）的实例，作为对象池。 <br /><br />ObjectPool pool = new StackObjectPool(factory);<br /><br /><br /><br /><br />需要从对象池中取出对象时，调用该对象池的Object borrowObject()方法。 <br /><br />Object obj = null;<br />obj = pool.borrowObject();<br /><br /><br /><br /><br />需要将对象放回对象池中时，调用该对象池的void returnObject(Object obj)方法。 <br /><br />pool.returnObject(obj);<br /><br /><br /><br /><br />当不再需要使用一个对象池时，调用该对象池的void close()方法，释放它所占据的资源。 <br /><br />pool.close();<br /><br /><br /><br /><br />这些操作都可能会抛出异常，需要另外处理。 <br /><br />比较完整的使用ObjectPool的全过程，可以参考这段代码： <br /><br /><br />ObjectPoolSample.java <br /><br /><br /><br />import org.apache.commons.pool.ObjectPool;<br />import org.apache.commons.pool.PoolableObjectFactory;<br />import org.apache.commons.pool.impl.StackObjectPool;<br /><br />public class ObjectPoolSample {<br /><br />public static void main(String[] args) {<br />Object obj = null;<br />PoolableObjectFactory factory <br />= new PoolableObjectFactorySample();<br />ObjectPool pool = new StackObjectPool(factory);<br />try {<br />for(long i = 0; i &lt; 100 ; i++) {<br />System.out.println("== " + i + " ==");<br />obj = pool.borrowObject();<br />System.out.println(obj);<br />pool.returnObject(obj);<br />}<br />obj = null;//明确地设为null，作为对象已归还的标志<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />finally {<br />try{<br />if (obj != null) {//避免将一个对象归还两次<br />pool.returnObject(obj);<br />}<br />pool.close();<br />}<br />catch (Exception e){<br />e.printStackTrace();<br />}<br />}<br />}<br />}<br /><br /><br /><br /><br />另外，ObjectPool接口还定义了几个可以由具体的实现决定要不要支持的操作，包括： <br /><br />void clear() <br /><br />清除所有当前在此对象池中休眠的对象。<br /><br />int getNumActive() <br /><br />返回已经从此对象池中借出的对象的总数。 <br /><br />int getNumIdle() <br /><br />返回当前在此对象池中休眠的对象的数目。 <br /><br />void setFactory(PoolableObjectFactory factory) <br /><br />将当前对象池与参数中给定的PoolableObjectFactory相关联。如果在当前状态下，无法完成这一操作，会有一个IllegalStateException异常抛出。 <br /><br />如果所用的ObjectPool实现不支持这些操作，那么调用这些方法的时候，会抛出一个UnsupportedOperationException异常。 <br />利用ObjectPoolFactory <br />有时候，要在多处生成类型和设置都相同的ObjectPool。如果在每个地方都重写一次调用相应构造方法的代码，不但比较麻烦，而且日后修改起来，也有所不便。这种时候，正是使用ObjectPoolFactory的时机。 <br /><br />ObjectPoolFactory是一个在org.apache.commons.pool中定义的接口，它定义了一个称为ObjectPool createPool()方法，可以用于大量生产类型和设置都相同的ObjectPool。 <br /><br />Pool组件中，对每一个ObjectPool实现，都有一个对应的ObjectPoolFactory实现。它们相互之间，有一一对应的参数相同的构造方法。使用的时候，只要先用想要的参数和想用的ObjectPoolFactory实例，构造出一个ObjectPoolFactory对象，然后在需要生成ObjectPool的地方，调用这个对象的createPool()方法就可以了。日后无论想要调整所用ObjectPool的参数还是类型，只需要修改这一处，就可以大功告成了。 <br /><br />将《使用ObjectPool》一节中的例子，改为使用ObjectPoolFactory来生成所用的ObjectPool对象之后，基本就是这种形式： <br /><br /><br />ObjectPoolFactorySample.java<br /><br /><br />import org.apache.commons.pool.ObjectPool;<br />import org.apache.commons.pool.ObjectPoolFactory;<br />import org.apache.commons.pool.PoolableObjectFactory;<br />import org.apache.commons.pool.impl.StackObjectPoolFactory;<br /><br />public class ObjectPoolFactorySample {<br /><br />public static void main(String[] args) {<br />Object obj = null;<br />PoolableObjectFactory factory<br />= new PoolableObjectFactorySample();<br />ObjectPoolFactory poolFactory<br />= new StackObjectPoolFactory(factory);<br />ObjectPool pool = poolFactory.createPool();<br />try {<br />for(long i = 0; i &lt; 100 ; i++) {<br />System.out.println("== " + i + " ==");<br />obj = pool.borrowObject();<br />System.out.println(obj);<br />pool.returnObject(obj);<br />}<br />obj = null;<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />finally {<br />try{<br />if (obj != null) {<br />pool.returnObject(obj);<br />}<br />pool.close();<br />}<br />catch (Exception e){<br />e.printStackTrace();<br />}<br />}<br />}<br />}<br /><br /><br /><br /><br />借助BasePoolableObjectFactory <br />PoolableObjectFactory定义了许多方法，可以适应多种不同的情况。但是，在并没有什么特殊需要的时候，直接实现PoolableObjectFactory接口，就要编写若干的不进行任何操作，或是始终返回true的方法来让编译通过，比较繁琐。这种时候就可以借助BasePoolableObjectFactory的威力，来简化编码的工作。 <br /><br />BasePoolableObjectFactory是org.apache.commons.pool包中的一个抽象类。它实现了PoolableObjectFactory接口，并且为除了makeObject之外的方法提供了一个基本的实现??activateObject、passivateObject和destroyObject不进行任何操作，而validateObject始终返回true。通过继承这个类，而不是直接实现PoolableObjectFactory接口，就可以免去编写一些只起到让编译通过的作用的代码的麻烦了。 <br /><br />这个例子展示了一个从BasePoolableObjectFactory扩展而来的PoolableObjectFactory： <br /><br /><br />BasePoolableObjectFactorySample.java <br /><br /><br /><br />import org.apache.commons.pool.BasePoolableObjectFactory;<br /><br />public class BasePoolableObjectFactorySample <br />extends BasePoolableObjectFactory {<br /><br />private int counter = 0;<br /><br />public Object makeObject() throws Exception {<br />return String.valueOf(counter++);<br />}<br />}<br /><br /><br /><br /><br />各式各样的ObjectPool <br />可口可乐公司的软饮料有可口可乐、雪碧和芬达等品种，百事可乐公司的软饮料有百事可乐、七喜和美年达等类型，而Pool组件提供的ObjectPool实现则有StackObjectPool、SoftReferenceObjectPool和GenericObjectPool等种类。 <br /><br />不同类型的软饮料各有各自的特点，分别适应不同消费者的口味；而不同类型的ObjectPool也各有各自的特色，分别适应不同的情况。 <br /><br />StackObjectPool <br />StackObjectPool利用一个java.util.Stack对象来保存对象池里的对象。这种对象池的特色是： <br /><br />可以为对象池指定一个初始的参考大小（当空间不够时会自动增长）。 <br />在对象池已空的时候，调用它的borrowObject方法，会自动返回新创建的实例。 <br />可以为对象池指定一个可保存的对象数目的上限。达到这个上限之后，再向池里送回的对象会被自动送去回收。 <br />StackObjectPool的构造方法共有六个，其中： <br /><br />最简单的一个是StackObjectPool()，一切采用默认的设置，也不指明要用的PoolableObjectFactory实例。 <br />最复杂的一个则是StackObjectPool(PoolableObjectFactory factory, int max, int init)。其中：<br /><br />参数factory指明要与之配合使用的PoolableObjectFactory实例； <br />参数max设定可保存对象数目的上限； <br />参数init则指明初始的参考大小。 <br />剩余的四个构造方法则是最复杂的构造方法在某方面的简化版本，可以根据需要选用。它们是： <br /><br />StackObjectPool(int max) <br />StackObjectPool(int max, int init) <br />StackObjectPool(PoolableObjectFactory factory) <br />StackObjectPool(PoolableObjectFactory factory, int max) <br />用不带factory参数的构造方法构造的StackObjectPool实例，必须要在用它的setFactory(PoolableObjectFactory factory)方法与某一PoolableObjectFactory实例关联起来后才能正常使用。 <br /><br />这种对象池可以在没有Jakarta Commmons Collections组件支持的情况下正常运行。 <br /><br />SoftReferenceObjectPool <br />SoftReferenceObjectPool利用一个java.util.ArrayList对象来保存对象池里的对象。不过它并不在对象池里直接保存对象本身，而是保存它们的“软引用”（Soft Reference）。这种对象池的特色是： <br /><br />可以保存任意多个对象，不会有容量已满的情况发生。 <br />在对象池已空的时候，调用它的borrowObject方法，会自动返回新创建的实例。 <br />可以在初始化同时，在池内预先创建一定量的对象。 <br />当内存不足的时候，池中的对象可以被Java虚拟机回收。 <br />SoftReferenceObjectPool的构造方法共有三个，其中： <br /><br />最简单的是SoftReferenceObjectPool()，不预先在池内创建对象，也不指明要用的PoolableObjectFactory实例。 <br />最复杂的一个则是SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize)。其中： <br /><br />参数factory指明要与之配合使用的PoolableObjectFactory实例 <br />参数initSize则指明初始化时在池中创建多少个对象。 <br />剩下的一个构造方法，则是最复杂的构造方法在某方面的简化版本，适合在大多数情况下使用。它是：<br /><br />SoftReferenceObjectPool(PoolableObjectFactory factory) <br />用不带factory参数的构造方法构造的SoftReferenceObjectPool实例，也要在用它的setFactory(PoolableObjectFactory factory)方法与某一PoolableObjectFactory实例关联起来后才能正常使用。 <br /><br />这种对象池也可以在没有Jakarta Commmons Collections组件支持的情况下正常运行。 <br /><br />GenericObjectPool <br />GenericObjectPool利用一个org.apache.commons.collections.CursorableLinkedList对象来保存对象池里的对象。这种对象池的特色是： <br /><br />可以设定最多能从池中借出多少个对象。 <br />可以设定池中最多能保存多少个对象。 <br />可以设定在池中已无对象可借的情况下，调用它的borrowObject方法时的行为，是等待、创建新的实例还是抛出异常。 <br />可以分别设定对象借出和还回时，是否进行有效性检查。 <br />可以设定是否使用一个单独的线程，对池内对象进行后台清理。 <br />GenericObjectPool的构造方法共有七个，其中： <br /><br />最简单的一个是GenericObjectPool(PoolableObjectFactory factory)。仅仅指明要用的PoolableObjectFactory实例，其它参数则采用默认值。 <br />最复杂的一个是GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle)。其中： <br /><br />参数factory指明要与之配合使用的PoolableObjectFactory实例。 <br />参数maxActive指明能从池中借出的对象的最大数目。如果这个值不是正数，表示没有限制。 <br />参数whenExhaustedAction指定在池中借出对象的数目已达极限的情况下，调用它的borrowObject方法时的行为。可以选用的值有： <br />GenericObjectPool.WHEN_EXHAUSTED_BLOCK，表示等待； <br />GenericObjectPool.WHEN_EXHAUSTED_GROW，表示创建新的实例（不过这就使maxActive参数失去了意义）； <br />GenericObjectPool.WHEN_EXHAUSTED_FAIL，表示抛出一个java.util.NoSuchElementException异常。 <br />参数maxWait指明若在对象池空时调用borrowObject方法的行为被设定成等待，最多等待多少毫秒。如果等待时间超过了这个数值，则会抛出一个java.util.NoSuchElementException异常。如果这个值不是正数，表示无限期等待。 <br />参数testOnBorrow设定在借出对象时是否进行有效性检查。 <br />参数testOnBorrow设定在还回对象时是否进行有效性检查。 <br />参数timeBetweenEvictionRunsMillis，设定间隔每过多少毫秒进行一次后台对象清理的行动。如果这个值不是正数，则实际上不会进行后台对象清理。 <br />参数numTestsPerEvictionRun，设定在进行后台对象清理时，每次检查几个对象。如果这个值不是正数，则每次检查的对象数是检查时池内对象的总数乘以这个值的负倒数再向上取整的结果??也就是说，如果这个值是-2（-3、-4、-5……）的话，那么每次大约检查当时池内对象总数的1/2（1/3、1/4、1/5……）左右。 <br />参数minEvictableIdleTimeMillis，设定在进行后台对象清理时，视休眠时间超过了多少毫秒的对象为过期。过期的对象将被回收。如果这个值不是正数，那么对休眠时间没有特别的约束。 <br />参数testWhileIdle，则设定在进行后台对象清理时，是否还对没有过期的池内对象进行有效性检查。不能通过有效性检查的对象也将被回收。 <br />另一个比较特别的构造方法是GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) 。其中： <br /><br />参数factory指明要与之配合使用的PoolableObjectFactory实例； <br />参数config则指明一个包括了各个参数的预设值的对象（详见《GenericObjectPool.Config》一节）。 <br />剩下的五个构造函数则是最复杂的构造方法在某方面的简化版本，可以根据情况选用。它们是： <br /><br />GenericObjectPool(PoolableObjectFactory factory, int maxActive) <br />GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait) <br />GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) <br />GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) <br />GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) <br />这种对象池不可以在没有Jakarta Commmons Collections组件支持的情况下运行。 <br /><br />GenericObjectPool.Config <br />调用一个有很多的参数的方法的时候，很可能将参数的位置和个数搞错，导致编译或运行时的错误；阅读包含了有很多参数的方法调用的代码的时候，也很可能因为没有搞对参数的位置和个数，产生错误的理解。因此，人们往往避免给一个方法安排太多的参数的做法（所谓的“Long Parameter List”）。不过，有些方法又确实需要许多参数才能完成工作。于是，就有人想到了一种将大批的参数封装到一个对象（称为参数对象，Parameter Object）里，然后将这个对象作为单一的参数传递的两全其美的对策。 <br /><br />因为生成GenericKeyedObjectPool时可供设置的特性非常之多，所以它的构造方法里也就难免会需要不少的参数。GenericKeyedObjectPool除了提供了几个超长的构造方法之外，同时也定义了一个使用参数对象的构造方法。所用参数对象的类型是GenericKeyedObjectPool.Config。 <br /><br />GenericKeyedObjectPool.Config定义了许多的public字段，每个对应一种可以为GenericKeyedObjectPool设置的特性，包括： <br /><br />int maxActive <br />int maxIdle <br />long maxWait <br />long minEvictableIdleTimeMillis <br />int numTestsPerEvictionRun <br />boolean testOnBorrow <br />boolean testOnReturn <br />boolean testWhileIdle <br />long timeBetweenEvictionRunsMillis <br />byte whenExhaustedAction <br />这些字段的作用，与在GenericKeyedObjectPool最复杂的构造方法中与它们同名的参数完全相同。 <br /><br />使用的时候，先生成一个GenericKeyedObjectPool.Config对象，然后将个字段设置为想要的值，最后用这个对象作为唯一的参数调用GenericKeyedObjectPool的构造方法即可。 <br /><br />注意：使用有许多public字段、却没有任何方法的类，也是一个人们往往加以避免的行为（所谓的“Data Class”）。不过这次GenericKeyedObjectPool特立独行了一回。 <br /><br />带键值的对象池 <br />有时候，单用对池内所有对象一视同仁的对象池，并不能解决的问题。例如，对于一组某些参数设置不同的同类对象??比如一堆指向不同地址的java.net.URL对象或者一批代表不同语句的java.sql.PreparedStatement对象，用这样的方法池化，就有可能取出不合用的对象的麻烦。 <br /><br />可以通过为每一组参数相同的同类对象建立一个单独的对象池来解决这个问题。但是，如果使用普通的ObjectPool来实施这个计策的话，因为普通的PoolableObjectFactory只能生产出大批设置完全一致的对象，就需要为每一组参数相同的对象编写一个单独的PoolableObjectFactory，工作量相当可观。这种时候就适合调遣Pool组件中提供的一种“带键值的对象池”来展开工作了。 <br /><br />Pool组件采用实现了KeyedObjectPool接口的类，来充当带键值的对象池。相应的，这种对象池需要配合实现了KeyedPoolableObjectFactory接口的类和实现了KeyedObjectPoolFactory接口的类来使用（这三个接口都在org.apache.commons.pool包中定义）： <br /><br />KeyedPoolableObjectFactory和PoolableObjectFactory形式如出一辙，只是每个方法都增加了一个Object key参数而已： <br /><br />makeObject的参数变为(Object key) <br />activateObject的参数变为(Object key, Object obj) <br />passivateObject的参数变为(Object key, Object obj) <br />validateObject的参数变为Object key, Object obj) <br />destroyObject的参数变为(Object key, Object obj) <br />另外Pool组件也提供了BaseKeyedPoolableObjectFactory，用于充当和BasePoolableObjectFactory差不多的角色。<br /><br />KeyedObjectPool和ObjectPool的形式大同小异，只是某些方法的参数类型发生了变化，某些方法分成了两种略有不同的版本： <br /><br />用Object borrowObject(Object key)和void returnObject(Object key, Object obj)来负责对象出借和归还的动作。 <br />用void close()来关闭不再需要的对象池。 <br />用void clear(Object key)和void clear()来清空池中的对象，前者针对与特定键值相关联的实例，后者针对整个对象池。 <br />用int getNumActive(Object key)和int getNumActive()来查询已借出的对象数，前者针对与特定键值相关联的实例，后者针对整个对象池。 <br />用int getNumIdle(Object key)和int getNumIdle()来查询正在休眠的对象数，前者针对与特定键值相关联的实例，后者针对整个对象池。 <br />用void setFactory(KeyedPoolableObjectFactory factory)来设置要用的KeyedPoolableObjectFactory实例。 <br />void clear、int getNumActive、int getNumIdle和void setFactory的各种版本都仍然是可以由具体实现自行决定是否要支持的方法。如果所用的KeyedObjectPool实现不支持这些操作，那么调用这些方法的时候，会抛出一个UnsupportedOperationException异常。 <br /><br />KeyedObjectPoolFactory和ObjectPoolFactory的形式完全相同，只是所代表的对象不同而已。 <br />这一类对象池的基本使用方法接近于这样： <br /><br /><br />KeyedObjectPoolSample.java <br /><br /><br /><br />import org.apache.commons.pool.BaseKeyedPoolableObjectFactory;<br />import org.apache.commons.pool.KeyedObjectPool;<br />import org.apache.commons.pool.KeyedObjectPoolFactory;<br />import org.apache.commons.pool.KeyedPoolableObjectFactory;<br />import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory;<br /><br />class KeyedPoolableObjectFactorySample<br />extends BaseKeyedPoolableObjectFactory {<br /><br />public Object makeObject(Object key) throws Exception {<br />return new String("[" + key.hashCode() + "]");<br />}<br /><br />}<br /><br />public class KeyedObjectPoolSample {<br />public static void main(String[] args) {<br />Object obj = null;<br />KeyedPoolableObjectFactory factory<br />= new KeyedPoolableObjectFactorySample();<br />KeyedObjectPoolFactory poolFactory <br />= new StackKeyedObjectPoolFactory(factory);<br />KeyedObjectPool pool = poolFactory.createPool();<br />String key = null;<br />try {<br />for (long i = 0; i &lt; 100 ; i++) {<br />key = "" + (int) (Math.random() * 10);<br />System.out.println("== " + i + " ==");<br />System.out.println("Key:" + key);<br />obj = pool.borrowObject(key);<br />System.out.println("Object:" + obj);<br />pool.returnObject(key, obj);<br />obj = null;<br />}<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />finally {<br />try{<br />if (obj != null) {<br />pool.returnObject(key, obj);<br />}<br />pool.close();<br />}<br />catch (Exception e){<br />e.printStackTrace();<br />}<br />}<br />}<br />}<br /><br /><br /><br /><br /><br />Pool组件自带的KeyedObjectPool的实现有StackKeyedObjectPool和GenericKeyedObjectPool两种。它们的使用方法分别与它们各自的近亲KeyedObjectPool和KeyedObjectPool基本一致，只是原来使用GenericObjectPool.Config的地方要使用GenericKeyedObjectPool.Config代替。 <br /><br />当出借少于归还 <br />Java并未提供一种机制来保证两个方法被调用的次数之间呈现一种特定的关系（相等，相差一个常数，或是其它任何关系）。因此，完全可以做到建立一个ObjectPool对象，然后调用一次borrowObject方法，借出一个对象，之后重复两次returnObject方法调用，进行两次归还。而调用一个从不曾借出对象的ObjectPool的returnObject方法也并不是一个不可完成的任务。 <br /><br />尽管这些使用方法并不合乎returnObject的字面意思，但是Pool组件中的各个ObjectPool/KeyedObjectPool实现都不在乎这一点。它们的returnObject方法都只是单纯地召唤与当前对象池关联的PoolableObjectFactory实例，看这对象能否经受得起validateObject的考验而已。考验的结果决定了这个对象是应该拿去作passivateObject处理，而后留待重用；还是应该拿去作destroyObject处理，以免占用资源。也就是说，当出借少于归还的时候，并不会额外发生什么特别的事情（当然，有可能因为该对象池处于不接受归还对象的请求的状态而抛出异常，不过这是常规现象）。 <br /><br />在实际使用中，可以利用这一特性来向对象池内加入通过其它方法生成的对象。 <br /><br />线程安全问题<br />有时候可能要在多线程环境下使用Pool组件，这时候就会遇到和Pool组件的线程安全程度有关的问题。 <br /><br />因为ObjectPool和KeyedObjectPool都是在org.apache.commons.pool中定义的接口，而在接口中无法使用“synchronized”来修饰方法，所以，一个ObjectPool/KeyedObjectPool下的各个方法是否是同步方法，完全要看具体的实现。而且，单纯地使用了同步方法，也并不能使对象就此在多线程环境里高枕无忧。 <br /><br />就Pool组件中自带的几个ObjectPool/KeyedObjectPool的实现而言，它们都在一定程度上考虑了在多线程环境中使用的情况。不过还不能说它们是完全“线程安全”的。 <br /><br />例如，这段代码有些时候就会有一些奇怪的表现，最后输出的结果比预期的要大： <br /><br /><br />UnsafeMultiThreadPoolingSample.java<br /><br /><br /><br />import org.apache.commons.pool.ObjectPool;<br />import org.apache.commons.pool.impl.StackObjectPool;<br /><br />class UnsafePicker extends Thread {<br />private ObjectPool pool;<br />public UnsafePicker(ObjectPool op) {<br />pool = op;<br />}<br />public void run() {<br />Object obj = null;<br />try {<br />/* 似乎…… */<br />if ( pool.getNumActive() &lt; 5 ) {<br />sleep((long) (Math.random() * 10));<br />obj = pool.borrowObject();<br />}<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />}<br />}<br /><br />public class UnsafeMultiThreadPoolingSample {<br /><br />public static void main(String[] args) {<br />ObjectPool pool = new StackObjectPool<br />(new BasePoolableObjectFactorySample());<br />Thread ts[] = new Thread[20];<br />for (int j = 0; j &lt; ts.length; j++) {<br />ts[j] = new UnsafePicker(pool);<br />ts[j].start();<br />}<br />try {<br />Thread.sleep(1000);<br />/* 然而…… */<br />System.out.println("NumActive:" + pool.getNumActive());<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />}<br />}<br /><br /><br /><br /><br /><br /><br />要避免这种情况，就要进一步采取一些措施才行： <br /><br /><br />SafeMultiThreadPoolingSample.java <br /><br /><br /><br />import org.apache.commons.pool.ObjectPool;<br />import org.apache.commons.pool.impl.StackObjectPool;<br /><br />class SafePicker extends Thread {<br />private ObjectPool pool;<br />public SafePicker(ObjectPool op) {<br />pool = op;<br />}<br />public void run() {<br />Object obj = null;<br />try {<br />/* 略加处理 */<br />synchronized (pool) {<br />if ( pool.getNumActive() &lt; 5 ) {<br />sleep((long) (Math.random() * 10));<br />obj = pool.borrowObject();<br />}<br />}<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />}<br />}<br /><br />public class SafeMultiThreadPoolingSample {<br /><br />public static void main(String[] args) {<br />ObjectPool pool = new StackObjectPool<br />(new BasePoolableObjectFactorySample());<br />Thread ts[] = new Thread[20];<br />for (int j = 0; j &lt; ts.length; j++) {<br />ts[j] = new SafePicker(pool);<br />ts[j].start();<br />}<br />try {<br />Thread.sleep(1000);<br />System.out.println("NumActive:" + pool.getNumActive());<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />}<br />}<br /><br /><br /><br /><br />基本上，可以说Pool组件是线程相容的。但是要在多线程环境中使用，还需要作一些特别的处理。<br /><br />什么时候不要池化 <br />采用对象池化的本意，是要通过减少对象生成的次数，减少花在对象初始化上面的开销，从而提高整体的性能。然而池化处理本身也要付出代价，因此，并非任何情况下都适合采用对象池化。 <br /><br />Dr. Cliff Click在JavaOne 2003上发表的《Performance Myths Exposed》中，给出了一组其它条件都相同时，使用与不使用对象池化技术的实际性能的比较结果。他的实测结果表明： <br /><br />对于类似Point这样的轻量级对象，进行池化处理后，性能反而下降，因此不宜池化； <br /><br />对于类似Hashtable这样的中量级对象，进行池化处理后，性能基本不变，一般不必池化（池化会使代码变复杂，增大维护的难度）； <br />对于类似JPanel这样的重量级对象，进行池化处理后，性能有所上升，可以考虑池化。 <br />根据使用方法的不同，实际的情况可能与这一测量结果略有出入。在配置较高的机器和技术较强的虚拟机上，不宜池化的对象的范围可能会更大。不过，对于像网络和数据库连接这类重量级的对象来说，目前还是有池化的必要。 <br /><br />基本上，只在重复生成某种对象的操作成为影响性能的关键因素的时候，才适合进行对象池化。如果进行池化所能带来的性能提高并不重要的话，还是不采用对象池化技术，以保持代码的简明，而使用更好的硬件和更棒的虚拟机来提高性能为佳。 <br /><br />结束语 <br />恰当地使用对象池化，可以有效地降低频繁生成某些对象所造成的开销，从而提高整体的性能。而借助Jakarta Commons Pool组件，可以有效地减少花在处理对象池化上的工作量，进而，向其它重要的工作里，投入更多的时间和精力。 <br /><br />参考资料 <br /><br />很多疑难问题的答案都可以通过查阅Pool组件的Javadoc文档和源代码的方法解决。<br /><br /><br />从 Pool组件的官方站点 上，还可以进一步得到许多有用的信息。 <br /><br /><br />DBCP 是一个基于Pool组件的Java数据库连接池管理组件，同时也可以作为Pool组件的用法示例使用。 <br /><br /><br />蔡学镛在 《Java夜未眠（Sleepless Java）》 中的 《测不准原理》一文里，介绍了Java中的包括“软引用”（Soft Reference）在内的各种不同的引用的特点和用处。 <br /><br /><br />Martin Fowler在 《Refactoring -- Improving the Design of Existing Code》（中译本名为《重构??改善既有代码的设计》，由侯捷、熊节合译）一书的第三章《代码的坏味道（Bad Smells in Code）》中讨论了被称为“Long Parameter List”和“Data Class”的“坏味道”。并指明了一些可以用于对付这些问题的重构手法。 <br /><br /><br />Brian Goetz在IBM developerWorks上发表的《Java 理论与实践：描绘线程安全性》一文中，说明了为什么单纯地使用同步方法还不能让对象就此在多线程环境里高枕无忧的原因。 <br /><br /><br />Dr. Cliff Click发表在JavaOne 2003上的《Performance Myths Exposed》（Session #1522），给出了一组包括“对象池化”在内的、对“能提高Java程序性能”的做法的实际效果的测试数据，和一些恰当使用这些做法的建议。 <br /><br /><br /><br />关于作者 <br />孙海涛从1994年6月的一个风雨交加的下午开始了他的编程生涯。目前，他的兴趣集中于Java、Web、开源软件和人机交互。但是，这并不表示他不会对其它的事物给予足够的关心和重视。可以通过 alexhsun@hotmail.com 与他取得联系。 <br />　<br /><br />原文：<a href="http://www.jspcn.net/htmlnews/11049384467341731.html">http://www.jspcn.net/htmlnews/11049384467341731.html</a><img src ="http://www.blogjava.net/hopeshared/aggbug/74275.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2006-10-10 12:57 <a href="http://www.blogjava.net/hopeshared/archive/2006/10/10/74275.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对象池(对象缓冲池)-高手进阶(zz)</title><link>http://www.blogjava.net/hopeshared/archive/2006/10/10/74272.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Tue, 10 Oct 2006 04:55:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/10/10/74272.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/74272.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/10/10/74272.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/74272.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/74272.html</trackback:ping><description><![CDATA[import java.util.*;<br /><br />/**<br />* &lt;p&gt;Title: 对象缓冲池－－－采用最近最久未使用策略管理对象,同时带有事件监听功能&lt;/p&gt;<br />*&lt;p&gt; 工作原理<br />* &lt;LI&gt;采用集合框架(java.connection包)来实现最近最久未使用对象池&lt;/li&gt;<br />* &lt;LI&gt;首先构造对象池、设置池的大小&lt;/li&gt;<br />* &lt;li&gt;放置对象到池中，保存时候，池的指针指向该对象，以表明该对象最近最短被使用过&lt;/li&gt;<br />* &lt;li&gt;当把新的对象放入到池中时候，池已经满，那把删除最久没有被使用的对象，然后放入对象&lt;/li&gt;<br />* &lt;li&gt;当从池中读取对象时候，根据条件从池中获得对象；然后把池的指针指向该取出的对象，以表明该对象最近最短被使用过&lt;/li&gt;<br />* &lt;li&gt;当池中有对象被清除时候（当成垃圾清除），会触发相关事件<br />* &lt;li&gt;当池被清空时候，会出发相关事件<br />* &lt;/p&gt;<br />* &lt;p&gt;其他说明<br />* 这个类参考了org.apache.tomcat.util.collections.LRUCache的实现细节。<br />* 当然原代码采用Hashtable来存储池的对象列表，这里采用另外的存储方式－－－HashMap来存储<br />* &lt;/p&gt;<br />* &lt;p&gt;Copyright: Copyright (c) 2004&lt;/p&gt;<br />* &lt;p&gt;Company: &lt;/p&gt;<br />*@see org.apache.tomcat.util.collections.LRUCache<br />* &lt;li&gt; 文件位置jakarta-tomcat-5.5.6\jakarta-tomcat-connectors\\util<br />* @author wdz123@hotmail.com<br />* @version 1.0<br />*/<br /><br />public class LRUCacheWithListener {<br /><br />/**<br />* 池对象的包裹类，这样便于相关处理<br />* **/<br />class CacheNode {<br />CacheNode prev;<br />CacheNode next;<br />Abandon value;<br />Object key;<br />public CacheNode() {<br />}<br />}<br /><br />/**<br />* 对象池大小<br />**/<br />private int cacheSize;<br />/**<br />* 对象列表、当然可以采用泛型编程，这样就实现自动装箱、解箱(boxing/unboxing)<br />* **/<br />private HashMap nodes;<br /><br />/**<br />* 对象池当前对象数<br />* **/<br />private int currentSize;<br />/**<br />* 第一个节点<br />* **/<br />private CacheNode first;<br />/***<br />* 最后一个节点<br />* **/<br />private CacheNode last;<br />/**<br />* 对象锁<br />* 实际上下面也就几个地方使用了该锁，可以用同步方法来取代调使用这个对象锁<br />* **/<br />private static int DEFAULT_SIZE = 10;<br />public LRUCacheWithListener() {<br />this(DEFAULT_SIZE);<br />}<br /><br />public LRUCacheWithListener(int poolSize) {<br />cacheSize = poolSize;<br />currentSize = 0;<br />first = null; //<br />last = null; //<br />nodes = new HashMap(poolSize);<br />}<br /><br />/**<br />* 读取一个对象<br />* ***/<br />public synchronized Object get(Object key) {<br />CacheNode node = (CacheNode) nodes.get(key);<br />if (node != null) {<br />moveToHead(node);<br />return node.value;<br />}<br />else {<br />return null;<br />}<br /><br />}<br /><br />/**<br />* 把指定对象移动到链表的头部<br />* */<br />private void moveToHead(CacheNode node) {<br />if (node == first) {<br />return;<br />}<br />if (node.prev != null) {<br />node.prev.next = node.next;<br />}<br />if (node.next != null) {<br />node.next.prev = node.prev;<br />}<br />if (last == node) {<br />last = node.prev;<br />}<br />if (first != null) {<br />node.next = first;<br />first.prev = node;<br />}<br />first = node;<br />node.prev = null;<br />if (last == null) {<br />last = first;<br />}<br />}<br /><br />/**<br />* 删除池中指定对象<br />* **/<br />public synchronized Object remove(Object key) {<br />CacheNode node = (CacheNode) nodes.get(key);<br />if (node != null) {<br />if (node.prev != null) {<br />node.prev.next = node.next;<br />}<br />if (node.next != null) {<br />node.next.prev = node.prev;<br />}<br />if (last == node) {<br />last = node.prev;<br />}<br />if (first == node) {<br />first = node.next;<br />}<br />}<br />return node;<br /><br />}<br /><br /><br /><br /><br />/****<br />* 放置一个对象到池中<br />* */<br />public synchronized void put(Object key, Abandon value) {<br /><br />CacheNode node = (CacheNode) nodes.get(key);<br />if (node == null) {<br />//池满，删除最久没有使用的对象<br />if (currentSize &gt;= cacheSize) {<br />if (last != null) {<br />nodes.remove(last.key);<br />}<br />removeLast();<br />}<br />//池没有满，直接把对象放入池中<br />else {<br />currentSize++;<br />}<br />node = getANewCacheNode();<br />}<br />node.value = value;<br />node.key = key;<br />// 把放入池的这个对象移动到链表的头部，表示最近最短被使用过<br />moveToHead(node);<br />nodes.put(key, node);<br /><br />}<br /><br />/**晴空池中对象<br />* **/<br />public synchronized void clear() {<br />if (first!=null){<br />Iterator i= nodes.values().iterator();<br />//触发事件，该池已经被清空<br />CacheNode n;<br />while(i.hasNext()){<br />n = (CacheNode)(i.next());<br />n.value.poolClear();<br />}<br />}<br /><br />first = null;<br />last = null;<br /><br />}<br /><br />/***<br />* 获得一个新的包裹对象<br />* **/<br />private CacheNode getANewCacheNode(){<br />CacheNode node = new CacheNode();<br />return node;<br />}<br /><br />/**<br />* 删除池中最久没有使用的对象<br />* **/<br />private void removeLast() {<br />if (last != null) {<br /><br />//对象从池中被抛弃，触发事件<br />last.value.onAbandon();<br /><br />if (last.prev != null) {<br />last.prev.next = null;<br />}<br />else {<br />first = null;<br />}<br />last = last.prev;<br />}<br /><br />}<br />}<br /><br />******代码2<br />package memorymanager.lrucache;<br /><br />/**<br />* &lt;p&gt;Title: &lt;/p&gt;<br />*<br />* &lt;p&gt;Description: 定义对象被抛弃和池被清空的事件接口&lt;/p&gt;<br />*<br />* &lt;p&gt;Copyright: Copyright (c) 2003&lt;/p&gt;<br />*<br />*@see org.apache.tomcat.util.collections.LRUCache<br />* &lt;li&gt; 文件位置jakarta-tomcat-5.5.6\jakarta-tomcat-connectors\\util<br />* @author wdz123@hotmail.com<br />* @version 1.0<br />*/<br />public interface Abandon {<br />//<br />public void onAbandon();<br />public void poolClear();<br />}<br /><br /><br /><br />代码3，测试部分<br />package memorymanager.lrucache;<br /><br />/**<br />* &lt;p&gt;Title: &lt;/p&gt;<br />*<br />* &lt;p&gt;Description: 测试使用带有事件回调机制的对象缓冲池－－－采用最近最久未使用策略管理对象&lt;/p&gt;<br />*<br />* &lt;p&gt;Copyright: Copyright (c) 2003&lt;/p&gt;<br />*<br />*@see org.apache.tomcat.util.collections.LRUCache<br />* &lt;li&gt; 文件位置jakarta-tomcat-5.5.6\jakarta-tomcat-connectors\\util<br />* @author wdz123@hotmail.com<br />* @version 1.0<br />*/<br />public class CacheNodeWithListener implements Abandon {<br />int id;<br />public CacheNodeWithListener() {<br /><br />}<br />public CacheNodeWithListener(int i) {<br />id=i;<br />}<br /><br />/**<br />* 当对象被池所抛弃时候，进行相关处理<br />* ***/<br />public void onAbandon(){<br />System.out.println(this+"---onAbandon()");<br />}<br />/**<br />* 当对象池被清空时候，进行相关处理<br />* ***/<br />public void poolClear(){<br />System.out.println(this+"---poolClear()");<br />}<br />public String toString(){<br />return "id="+id ;<br />}<br /><br />static public void main(String [] s){<br />LRUCacheWithListener pool =new LRUCacheWithListener(3);<br />int i;<br />for (i=1;i&lt;=5;i++) {<br />pool.put("obj"+i,new CacheNodeWithListener(i));<br />}<br />pool.clear();<br />}<br />} <br /><br /><br />原文：<a href="http://www.aptime.cn/forum/ShowPost.asp?menu=Previous&amp;ForumID=26&amp;ThreadID=3107">http://www.aptime.cn/forum/ShowPost.asp?menu=Previous&amp;ForumID=26&amp;ThreadID=3107</a><img src ="http://www.blogjava.net/hopeshared/aggbug/74272.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2006-10-10 12:55 <a href="http://www.blogjava.net/hopeshared/archive/2006/10/10/74272.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简单的将String类型的xml转换为对象的代码</title><link>http://www.blogjava.net/hopeshared/archive/2006/07/06/56919.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 06 Jul 2006 03:17:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/07/06/56919.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/56919.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/07/06/56919.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/56919.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/56919.html</trackback:ping><description><![CDATA[
		<p>利用w3c的dom：</p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #000000">DocumentBuilderFactory factory</span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000">DocumentBuilderFactory.newInstance(); <br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />  DocumentBuilder builder;<br /><img id="Codehighlighter1_103_226_Open_Image" onclick="this.style.display='none'; Codehighlighter1_103_226_Open_Text.style.display='none'; Codehighlighter1_103_226_Closed_Image.style.display='inline'; Codehighlighter1_103_226_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_103_226_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_103_226_Closed_Text.style.display='none'; Codehighlighter1_103_226_Open_Image.style.display='inline'; Codehighlighter1_103_226_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />  </span>
				<span style="COLOR: #0000ff">try</span>
				<span style="COLOR: #000000"> </span>
				<span id="Codehighlighter1_103_226_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.blogjava.net/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_103_226_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />   builder </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> factory.newDocumentBuilder();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />   Document doc </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> builder.parse(</span>
						<span style="COLOR: #0000ff">new</span>
						<span style="COLOR: #000000"> ByteArrayInputStream(str.getBytes())); <br /><img id="Codehighlighter1_267_333_Open_Image" onclick="this.style.display='none'; Codehighlighter1_267_333_Open_Text.style.display='none'; Codehighlighter1_267_333_Closed_Image.style.display='inline'; Codehighlighter1_267_333_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_267_333_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_267_333_Closed_Text.style.display='none'; Codehighlighter1_267_333_Open_Image.style.display='inline'; Codehighlighter1_267_333_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />  }</span>
				</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">catch</span>
				<span style="COLOR: #000000"> (ParserConfigurationException e) </span>
				<span id="Codehighlighter1_267_333_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.blogjava.net/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_267_333_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />   </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> TODO Auto-generated catch block</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">   e.printStackTrace();<br /><img id="Codehighlighter1_358_424_Open_Image" onclick="this.style.display='none'; Codehighlighter1_358_424_Open_Text.style.display='none'; Codehighlighter1_358_424_Closed_Image.style.display='inline'; Codehighlighter1_358_424_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_358_424_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_358_424_Closed_Text.style.display='none'; Codehighlighter1_358_424_Open_Image.style.display='inline'; Codehighlighter1_358_424_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />  }</span>
				</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">catch</span>
				<span style="COLOR: #000000"> (SAXException e) </span>
				<span id="Codehighlighter1_358_424_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.blogjava.net/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_358_424_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />   </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> TODO Auto-generated catch block</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">   e.printStackTrace();<br /><img id="Codehighlighter1_448_514_Open_Image" onclick="this.style.display='none'; Codehighlighter1_448_514_Open_Text.style.display='none'; Codehighlighter1_448_514_Closed_Image.style.display='inline'; Codehighlighter1_448_514_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_448_514_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_448_514_Closed_Text.style.display='none'; Codehighlighter1_448_514_Open_Image.style.display='inline'; Codehighlighter1_448_514_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />  }</span>
				</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">catch</span>
				<span style="COLOR: #000000"> (IOException e) </span>
				<span id="Codehighlighter1_448_514_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.blogjava.net/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_448_514_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />   </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> TODO Auto-generated catch block</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">   e.printStackTrace();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />  }</span>
				</span>
				<span style="COLOR: #000000"> </span>
		</div>
		<br />利用dom4j<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">SAXReader saxReader </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SAXReader();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />        Document document;<br /><img id="Codehighlighter1_66_195_Open_Image" onclick="this.style.display='none'; Codehighlighter1_66_195_Open_Text.style.display='none'; Codehighlighter1_66_195_Closed_Image.style.display='inline'; Codehighlighter1_66_195_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_66_195_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_66_195_Closed_Text.style.display='none'; Codehighlighter1_66_195_Open_Image.style.display='inline'; Codehighlighter1_66_195_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000"> </span><span id="Codehighlighter1_66_195_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_66_195_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            document </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> saxReader.read(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ByteArrayInputStream(str.getBytes()));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            Element incomingForm </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> document.getRootElement();<br /><img id="Codehighlighter1_225_291_Open_Image" onclick="this.style.display='none'; Codehighlighter1_225_291_Open_Text.style.display='none'; Codehighlighter1_225_291_Closed_Image.style.display='inline'; Codehighlighter1_225_291_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_225_291_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_225_291_Closed_Text.style.display='none'; Codehighlighter1_225_291_Open_Image.style.display='inline'; Codehighlighter1_225_291_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />        }</span></span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000"> (DocumentException e) </span><span id="Codehighlighter1_225_291_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_225_291_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> TODO Auto-generated catch block</span><span style="COLOR: #008000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #000000">            e.printStackTrace();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />        }</span></span></div><img src ="http://www.blogjava.net/hopeshared/aggbug/56919.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2006-07-06 11:17 <a href="http://www.blogjava.net/hopeshared/archive/2006/07/06/56919.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一段很好用的判断数据库中某张表是否存在的代码</title><link>http://www.blogjava.net/hopeshared/archive/2006/06/28/55568.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Wed, 28 Jun 2006 09:12:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/06/28/55568.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/55568.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/06/28/55568.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/55568.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/55568.html</trackback:ping><description><![CDATA[刚刚被一个比较麻烦的问题所困扰。这个问题就是如何判断数据中某张表是否存在，如果不存在则创建它。<br /><br />恩，我先用了最笨的方法，就是写个select从表中读数据，捕获异常的同时就知道了改表没有创建。<br /><br />此法不通，因为这个时候的异常似乎被认定为了系统错误，于是后面创建表的代码被忽略了。<br /><br />大部分人的做法类似于select system.table where tabblename='***'，反正我曾经用类似的句子查询过DB2，是成功的。<br /><br />但是，我现在面对的不是DB2，而是7个不同的数据库，基本上常用的都包括了。是不是每类数据库都有上面的查询语句呢？是否查询语句相似呢？于是我挑了hsqldb，也是当前的默认数据库，来寻找解决办法。<br /><br />很遗憾，我没有找到类似前面的句子。正当我打算放弃的时候发现了下面的代码，这段代码是我从一个国外的论坛中找到的，尽管我不知道它是不是万能钥匙，但是他这次对我而言确成了万能的：<br /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">java.sql.Connection con </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getYourConnection();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />   <br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />ResultSet rs </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> con.getMetaData().getTables(</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">, </span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">yourTable</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">);<br /><img id="Codehighlighter1_141_161_Open_Image" onclick="this.style.display='none'; Codehighlighter1_141_161_Open_Text.style.display='none'; Codehighlighter1_141_161_Closed_Image.style.display='inline'; Codehighlighter1_141_161_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_141_161_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_141_161_Closed_Text.style.display='none'; Codehighlighter1_141_161_Open_Image.style.display='inline'; Codehighlighter1_141_161_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (rs.next()) </span><span id="Codehighlighter1_141_161_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_141_161_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">yourTable exist</span><span style="COLOR: #008000"><br /><img id="Codehighlighter1_167_191_Open_Image" onclick="this.style.display='none'; Codehighlighter1_167_191_Open_Text.style.display='none'; Codehighlighter1_167_191_Closed_Image.style.display='inline'; Codehighlighter1_167_191_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_167_191_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_167_191_Closed_Text.style.display='none'; Codehighlighter1_167_191_Open_Image.style.display='inline'; Codehighlighter1_167_191_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span style="COLOR: #000000">}</span></span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"> </span><span id="Codehighlighter1_167_191_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_167_191_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">yourTable not exist</span><span style="COLOR: #008000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" /></span><span style="COLOR: #000000">}</span></span></div><p>  </p><img src ="http://www.blogjava.net/hopeshared/aggbug/55568.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hopeshared/" target="_blank">hopeshared</a> 2006-06-28 17:12 <a href="http://www.blogjava.net/hopeshared/archive/2006/06/28/55568.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：Java Annotation 高级应用</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38920.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 05:36:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38920.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38920.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38920.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38920.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38920.html</trackback:ping><description><![CDATA[
		<span style="COLOR: red">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span>
		<br />作者:cleverpig(作者的Blog:<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new"><font color="#002c99">http://blog.matrix.org.cn/page/cleverpig</font></a>)<br />原文:<a href="http://www.matrix.org.cn/resource/article/44/44062_Java+Annotation+Apt.html" target="_new"><font color="#002c99">http://www.matrix.org.cn/resource/article/44/44062_Java+Annotation+Apt.html</font></a><br />关键字:java,annotation,apt<br /><br /><span style="COLOR: blue">前言：</span><br />前不久在matrix上先后发表了<a href="http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html" target="_new"><font color="#002c99">《java annotation 入门》</font></a>、<a href="http://www.matrix.org.cn/resource/article/44/44055_Java+Annotation+Reflect.html" target="_new"><font color="#002c99">《java annotation 手册》</font></a>两篇文章，比较全面的对java annotation的语法、原理、使用三方面进行了阐述。由于《入门》中的简单例程虽然简单明了的说明了annotation用法，但给大家的感觉可能是意犹未见，所以在此行文《java annotation高级应用》，具体实例化解释annotation和annotation processing tool（APT）的使用。望能对各位的有所帮助。<br /><br /><span style="COLOR: blue">一、摘要：</span><br />《java annotation高级应用》具体实例化解释annotation和annotation processing tool（APT）的使用。望能对各位的有所帮助。本文列举了用于演示annotation的BRFW演示框架、演示APT的apt代码实例，并对其进行较为深度的分析，希望大家多多提意见。<br /><br /><span style="COLOR: blue">二、annotation实例分析</span><br /><span style="COLOR: blue">1．BRFW（Beaninfo Runtime FrameWork）定义：</span><br />本人编写的一个annotation功能演示框架。顾名思义，BRFW就是在运行时取得bean信息的框架。<br /><br /><span style="COLOR: blue">2．BRFW的功能：</span><br /><span style="COLOR: green">A．源代码级annotation</span>：在bean的源代码中使用annotation定义bean的信息；<br /><span style="COLOR: green">B．运行时获取bean数据</span>：在运行时分析bean class中的annotation，并将当前bean class中field信息取出，功能类似xdoclet；<br /><span style="COLOR: green">C．运行时bean数据的xml绑定</span>：将获得的bean数据构造为xml文件格式展现。熟悉j2ee的朋友知道，这个功能类似jaxb。<br /><br /><span style="COLOR: blue">3．BRFW框架：</span><br />BRFW主要包含以下几个类：<br /><span style="COLOR: green">A．Persistent类</span>：定义了用于修饰类的固有类型成员变量的annotation。<br /><span style="COLOR: green">B．Exportable类</span>：定义了用于修饰Class的类型的annotation。<br /><span style="COLOR: green">C．ExportToXml类</span>：核心类，用于完成BRFW的主要功能：将具有Exportable Annotation的bean对象转换为xml格式文本。<br /><span style="COLOR: green">D．AddressForTest类</span>：被A和B修饰过的用于测试目的的地址bean类。其中包含了地址定义所必需的信息：国家、省级、城市、街道、门牌等。<br /><span style="COLOR: green">E．AddressListForTest类</span>：被A和B修饰过的友人通讯录bean类。其中包含了通讯录所必备的信息：友人姓名、年龄、电话、住址（成员为AddressForTest类型的ArrayList）、备注。需要说明的是电话这个bean成员变量是由字符串类型组成的ArrayList类型。由于朋友的住址可能不唯一，故这里的住址为由AddressForTest类型组成的ArrayList。<br />从上面的列表中，可以发现A、B用于修饰bean类和其类成员；C主要用于取出bean类的数据并将其作xml绑定，代码中使用了E作为测试类；E中可能包含着多个D。<br />在了解了这个简单框架后，我们来看一下BRFW的代码吧！<br /><br /><span style="COLOR: blue">4．BRFW源代码分析：</span><br /><span style="COLOR: green">A．Persistent类：</span><br /><b>清单1：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.runtimeframework;<br /><br />import java.lang.annotation.*;<br /><br />/**<br /> * 用于修饰类的固有类型成员变量的annotation<br /> * @author cleverpig<br /> *<br /> */<br />@Retention(RetentionPolicy.RUNTIME)<br />@Target(ElementType.FIELD)<br />public @interface Persistent { <br />        String value() default "";<br />}<br /></pre><br /><br /><span style="COLOR: green">B．Exportable类：</span><br /><b>清单2：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.runtimeframework;<br /><br />import java.lang.annotation.*;<br /><br />/**<br /> * 用于修饰类的类型的annotation<br /> * @author cleverpig<br /> *<br /> */<br />@Retention(RetentionPolicy.RUNTIME)<br />@Target(ElementType.TYPE)<br />public @interface Exportable {<br />        //名称<br />        String name() default "";<br />        //描述<br />        String description() default "";<br />        //省略name和description后，用来保存name值<br />        String value() default "";<br />        <br />}<br /></pre><br /><br /><span style="COLOR: green">C．AddressForTest类：</span><br /><b>清单3：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.runtimeframework;<br /><br />/**<br /> * 用于测试的地址类<br /> * @author cleverpig<br /> *<br /> */<br />@Exportable("address")<br />public class AddressForTest {<br />        //国家<br />        @Persistent<br />        private String country=null;<br />        <br />        //省级<br />        @Persistent<br />        private String province=null;<br />        <br />        //城市<br />        @Persistent<br />        private String city=null;<br />        <br />        //街道<br />        @Persistent<br />        private String street=null;<br /><br />        //门牌<br />        @Persistent<br />        private String doorplate=null;<br />        <br />        public AddressForTest(String country,String province,<br />                        String city,String street,String doorplate){<br />                this.country=country;<br />                this.province=province;<br />                this.city=city;<br />                this.street=street;<br />                this.doorplate=doorplate;<br />        }<br />        <br />}<br /></pre><br /><br /><span style="COLOR: green">D．AddressListForTest类：</span><br /><b>清单4：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.runtimeframework;<br /><br />import java.util.*;<br /><br />/**<br /> * 友人通讯录<br /> * 包含：姓名、年龄、电话、住址（多个）、备注<br /> * @author cleverpig<br /> *<br /> */<br />@Exportable(name="addresslist",description="address list")<br />public class AddressListForTest {<br />        //友人姓名<br />        @Persistent<br />        private String friendName=null;<br />        <br />        //友人年龄<br />        @Persistent<br />        private int age=0;<br />        <br />        //友人电话<br />        @Persistent<br />        private ArrayList&lt;String&gt; telephone=null;<br />        <br />        //友人住址：家庭、单位<br />        @Persistent<br />        private ArrayList&lt;AddressForTest&gt; AddressForText=null;<br />        <br />        //备注<br />        @Persistent<br />        private String note=null;<br />        <br />        public AddressListForTest(String name,int age,<br />                        ArrayList&lt;String&gt; telephoneList, <br />                        ArrayList&lt;AddressForTest&gt; addressList,<br />                        String note){<br />                this.friendName=name;<br />                this.age=age;<br />                this.telephone=new ArrayList&lt;String&gt;(telephoneList);<br />                this.AddressForText=new ArrayList&lt;AddressForTest&gt;(addressList);<br />                this.note=note;<br />                <br />        }<br />}<br /></pre><br /><br /><span style="COLOR: green">E．ExportToXml类：</span><br /><b>清单5：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.runtimeframework;<br /><br />import java.lang.reflect.Field;<br />import java.util.Collection;<br />import java.util.Iterator;<br />import java.util.Map;<br />import java.util.ArrayList;<br /><br />/**<br /> * 将具有Exportable Annotation的对象转换为xml格式文本 <br /> * @author cleverpig<br /> *<br /> */<br />public class ExportToXml {<br />        /**<br />         * 返回对象的成员变量的值（字符串类型）<br />         * @param field 对象的成员变量<br />         * @param fieldTypeClass 对象的类型<br />         * @param obj 对象<br />         * @return 对象的成员变量的值（字符串类型）<br />         */<br />        private String getFieldValue(Field field,Class fieldTypeClass,Object obj){<br />                String value=null;<br />                <br />                try{<br />                        if (fieldTypeClass==String.class){<br />                                value=(String)field.get(obj);<br />                        }<br />                        else if (fieldTypeClass==int.class){<br />                                value=Integer.toString(field.getInt(obj));<br />                        }<br />                        else if (fieldTypeClass==long.class){<br />                                value=Long.toString(field.getLong(obj));<br />                        }<br />                        else if (fieldTypeClass==short.class){<br />                                value=Short.toString(field.getShort(obj));<br />                        }<br />                        else if (fieldTypeClass==float.class){<br />                                value=Float.toString(field.getFloat(obj));<br />                        }<br />                        else if (fieldTypeClass==double.class){<br />                                value=Double.toString(field.getDouble(obj));<br />                        }<br />                        else if (fieldTypeClass==byte.class){<br />                                value=Byte.toString(field.getByte(obj));<br />                        }<br />                        else if (fieldTypeClass==char.class){<br />                                value=Character.toString(field.getChar(obj));<br />                        }<br />                        else if (fieldTypeClass==boolean.class){<br />                                value=Boolean.toString(field.getBoolean(obj));<br />                        }<br />                }<br />                catch(Exception ex){<br />                        ex.printStackTrace();<br />                        value=null;<br />                }<br />                return value;<br />        }<br />        <br />        /**<br />         * 输出对象的字段，当对象的字段为Collection或者Map类型时，要调用exportObject方法继续处理<br />         * @param obj 被处理的对象<br />         * @throws Exception<br />         */<br />        public void exportFields(Object obj) throws Exception{<br />                Exportable exportable=obj.getClass().getAnnotation(Exportable.class);        <br />                if (exportable!=null){<br />                        if (exportable.value().length()&gt;0){<br />//                                System.out.println("Class annotation Name:"+exportable.value());<br />                        }<br />                        else{<br />//                                System.out.println("Class annotation Name:"+exportable.name());<br />                        }<br />                }<br />                else{<br />//                        System.out.println(obj.getClass()+"类不是使用Exportable标注过的");<br />                }<br />                <br />                //取出对象的成员变量<br />                Field[] fields=obj.getClass().getDeclaredFields();<br />                <br />                for(Field field:fields){<br />                        //获得成员变量的标注<br />                        Persistent fieldAnnotation=field.getAnnotation(Persistent.class);<br />                        if (fieldAnnotation==null){<br />                                continue;<br />                        }<br />                        //重要:避免java虚拟机检查对私有成员的访问权限<br />                        field.setAccessible(true);<br />                        Class typeClass=field.getType();<br />                        String name=field.getName();<br />                        String value=getFieldValue(field,typeClass,obj);<br />                        <br />                        //如果获得成员变量的值，则输出<br />                        if (value!=null){<br />                               