﻿<?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 />                                System.out.println(getIndent()+"&lt;"+name+"&gt;\n"<br />                                                +getIndent()+"\t"+value+"\n"+getIndent()+"&lt;/"+name+"&gt;");<br />                        }<br />                        //处理成员变量中类型为Collection或Map<br />                        else if ((field.get(obj) instanceof Collection)||<br />                                        (field.get(obj) instanceof Map)){<br />                                exportObject(field.get(obj));<br />                        }<br />                        else{<br />                                exportObject(field.get(obj));<br />                        }<br />                        <br />                }<br />        }<br />        <br />        //缩进深度<br />        int levelDepth=0;<br />        //防止循环引用的检查者，循环引用现象如：a包含b，而b又包含a<br />        Collection&lt;Object&gt; cyclicChecker=new ArrayList&lt;Object&gt;();<br />        <br />        /**<br />         * 返回缩进字符串<br />         * @return<br />         */<br />        private String getIndent(){<br />                String s="";<br />                for(int i=0;i&lt;levelDepth;i++){<br />                        s+="\t";<br />                }<br />                return s;<br />        }<br />        /**<br />         * 输出对象，如果对象类型为Collection和Map类型，则需要递归调用exportObject进行处理<br />         * @param obj<br />         * @throws Exception<br />         */<br />        public void exportObject(Object obj) throws Exception{<br />                Exportable exportable=null;<br />                String elementName=null;<br />                <br />                //循环引用现象处理<br />                if (cyclicChecker.contains(obj)){<br />                        return;<br />                }<br />                <br />                cyclicChecker.add(obj);<br />                <br />                //首先处理Collection和Map类型<br />                if (obj instanceof Collection){<br />                        for(Iterator i=((Collection)obj).iterator();i.hasNext();){<br />                                exportObject(i.next());<br />                        }<br />                }<br />                else if (obj instanceof Map){<br />                        for(Iterator i=((Map)obj).keySet().iterator();i.hasNext();){<br />                                exportObject(i.next());<br />                        }<br />                }<br />                else{<br /><br />                        exportable=obj.getClass().getAnnotation(Exportable.class);<br />                        //如果obj已经被Exportable Annotation修饰过了（注意annotation是具有继承性的），<br />                        //则使用其name作为输出xml的元素name<br />                        if (exportable!=null){<br />                                if (exportable.value().length()&gt;0){<br />                                        elementName=exportable.value();<br />                                }<br />                                else{<br />                                        elementName=exportable.name();<br />                                }<br />                        }<br />                        //未被修饰或者Exportable Annotation的值为空字符串，<br />                        //则使用类名作为输出xml的元素name<br />                        if (exportable==null||elementName.length()==0){<br />                                elementName=obj.getClass().getSimpleName();<br />                        }<br />                        //输出xml元素头<br />                        System.out.println(getIndent()+"&lt;"+elementName+"&gt;");<br />                        levelDepth++;<br />                        //如果没有被修饰，则直接输出其toString()作为元素值<br />                        if (exportable==null){<br />                                System.out.println(getIndent()+obj.toString());<br />                        }<br />                        //否则将对象的成员变量导出为xml<br />                        else{<br />                                exportFields(obj);<br />                        }<br />                        levelDepth--;<br />                        //输出xml元素结尾<br />                        System.out.println(getIndent()+"&lt;/"+elementName+"&gt;");<br />                        <br />                }<br />                cyclicChecker.remove(obj);<br />        }<br />        <br />        public static void main(String[] argv){<br />                try{<br />                        AddressForTest ad=new AddressForTest("China","Beijing",<br />                                        "Beijing","winnerStreet","10");<br />                        <br />                        ExportToXml test=new ExportToXml();<br />                        <br />                        ArrayList&lt;String&gt; telephoneList=new ArrayList&lt;String&gt;();<br />                        telephoneList.add("66608888");<br />                        telephoneList.add("66608889");<br />                        <br />                        ArrayList&lt;AddressForTest&gt; adList=new ArrayList&lt;AddressForTest&gt;();<br />                        adList.add(ad);<br />                        <br />                        AddressListForTest adl=new AddressListForTest("coolBoy",<br />                                        18,telephoneList,adList,"some words");<br />                        <br />                        test.exportObject(adl);<br />                }<br />                catch(Exception ex){<br />                        ex.printStackTrace();<br />                }<br />        }<br />}<br /></pre><br /><br />在ExportToXml类之前的类比较简单，这里必须说明一下ExportToXml类：此类的核心函数是exportObject和exportFields方法，前者输出对象的xml信息，后者输出对象成员变量的信息。由于对象类型和成员类型的多样性，所以采取了以下的逻辑：<br /><br />在exportObject方法中，当对象类型为Collection和Map类型时，则需要递归调用exportObject进行处理；<br />而如果对象类型不是Collection和Map类型的话，将判断对象类是否被Exportable annotation修饰过：<br />如果没有被修饰，则直接输出&lt;对象类名&gt;对象.toString()&lt;/对象类名&gt;作为xml绑定结果的一部分；<br />如果被修饰过，则需要调用exportFields方法对对象的成员变量进行xml绑定。<br /><br />在exportFields方法中，首先取出对象的所有成员，然后获得被Persisitent annotation修饰的成员。在其后的一句：field.setAccessible(true)是很重要的，因为bean类定义中的成员访问修饰都是private，所以为了避免java虚拟机检查对私有成员的访问权限，加上这一句是必需的。接着后面的语句便是输出&lt;成员名&gt;成员值&lt;/成员名&gt;这样的xml结构。像在exportObject方法中一般，仍然需要判断成员类型是否为Collection和Map类型，如果为上述两种类型之一，则要在exportFields中再次调用exportObject来处理这个成员。<br /><br />在main方法中，本人编写了一段演示代码：建立了一个由单个友人地址类（AddressForTest）组成的ArrayList作为通讯录类（AddressForTest）的成员的通讯录对象，并且输出这个对象的xml绑定，运行结果如下：<br /><br /><b>清单6：</b><br /><pre class="overflow"><br />&lt;addresslist&gt;<br />        &lt;friendName&gt;<br />                coolBoy<br />        &lt;/friendName&gt;<br />        &lt;age&gt;<br />                18<br />        &lt;/age&gt;<br />        &lt;String&gt;<br />                66608888<br />        &lt;/String&gt;<br />        &lt;String&gt;<br />                66608889<br />        &lt;/String&gt;<br />        &lt;address&gt;<br />                &lt;country&gt;<br />                        China<br />                &lt;/country&gt;<br />                &lt;province&gt;<br />                        Beijing<br />                &lt;/province&gt;<br />                &lt;city&gt;<br />                        Beijing<br />                &lt;/city&gt;<br />                &lt;street&gt;<br />                        winnerStreet<br />                &lt;/street&gt;<br />                &lt;doorplate&gt;<br />                        10<br />                &lt;/doorplate&gt;<br />        &lt;/address&gt;<br />        &lt;note&gt;<br />                some words<br />        &lt;/note&gt;<br />&lt;/addresslist&gt;<br /></pre><br /><br /><span style="COLOR: blue">三、APT实例分析：</span><br /><span style="COLOR: blue">1．何谓APT？</span><br />根据sun官方的解释，APT（annotation processing tool）是一个命令行工具，它对源代码文件进行检测找出其中的annotation后，使用annotation processors来处理annotation。而annotation processors使用了一套反射API并具备对JSR175规范的支持。<br />annotation processors处理annotation的基本过程如下：首先，APT运行annotation processors根据提供的源文件中的annotation生成源代码文件和其它的文件（文件具体内容由annotation processors的编写者决定），接着APT将生成的源代码文件和提供的源文件进行编译生成类文件。<br />简单的和前面所讲的annotation实例BRFW相比，APT就像一个在编译时处理annotation的javac。而且从sun开发者的blog中看到，java1.6 beta版中已将APT的功能写入到了javac中，这样只要执行带有特定参数的javac就能达到APT的功能。<br /><br /><span style="COLOR: blue">2．为何使用APT？</span><br />使用APT主要目的是简化开发者的工作量，因为APT可以在编译程序源代码的同时，生成一些附属文件（比如源文件、类文件、程序发布描述文字等），这些附属文件的内容也都是与源代码相关的。换句话说，使用APT就是代替了传统的对代码信息和附属文件的维护工作。使用过hibernate或者beehive等软件的朋友可能深有体会。APT可以在编译生成代码类的同时将相关的文件写好，比如在使用beehive时，在代码中使用annotation声明了许多struct要用到的配置信息，而在编译后，这些信息会被APT以struct配置文件的方式存放。<br /><br /><span style="COLOR: blue">3．如何定义processor？</span><br /><span style="COLOR: green">A．APT工作过程：</span><br />从整个过程来讲，首先APT检测在源代码文件中哪些annotation存在。然后APT将查找我们编写的annotation processor factories类，并且要求factories类提供处理源文件中所涉及的annotation的annotation processor。接下来，一个合适的annotation processors将被执行，如果在processors生成源代码文件时，该文件中含有annotation，则APT将重复上面的过程直到没有新文件生成。<br /><br /><span style="COLOR: green">B．编写annotation processors：</span><br />编写一个annotation processors需要使用java1.5 lib目录中的tools.jar提供的以下4个包：<br />com.sun.mirror.apt: 和APT交互的接口；<br />com.sun.mirror.declaration: 用于模式化类成员、类方法、类声明的接口；<br />com.sun.mirror.type: 用于模式化源代码中类型的接口； <br />com.sun.mirror.util: 提供了用于处理类型和声明的一些工具。 <br /><br />每个processor实现了在com.sun.mirror.apt包中的AnnotationProcessor接口，这个接口有一个名为“process”的方法，该方法是在APT调用processor时将被用到的。一个processor可以处理一种或者多种annotation类型。<br />一个processor实例被其相应的工厂返回，此工厂为AnnotationProcessorFactory接口的实现。APT将调用工厂类的getProcessorFor方法来获得processor。在调用过程中，APT将提供给工厂类一个AnnotationProcessorEnvironment 类型的processor环境类对象，在这个环境对象中，processor将找到其执行所需要的每件东西，包括对所操作的程序结构的参考，与APT通讯并合作一同完成新文件的建立和警告/错误信息的传输。<br /><br />提供工厂类有两个方式：通过APT的“-factory”命令行参数提供，或者让工厂类在APT的发现过程中被自动定位（关于发现过程详细介绍请看<a href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html" target="_new"><font color="#002c99">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</font></a>）。前者对于一个已知的factory来讲是一种主动而又简单的方式；而后者则是需要在jar文件的META-INF/services目录中提供一个特定的发现路径：<br />在包含factory类的jar文件中作以下的操作：在META-INF/services目录中建立一个名为com.sun.mirror.apt.AnnotationProcessorFactory 的UTF-8编码文件，在文件中写入所有要使用到的factory类全名，每个类为一个单独行。<br /><br /><span style="COLOR: blue">4．一个简单的APT实例分析：</span><br /><span style="COLOR: green">A．实例构成：</span><br />Review类：定义Review Annotation；<br />ReviewProcessorFactory类：生成ReviewProcessor的工厂类；<br />ReviewProcessor类：定义处理Review annotation的Processor；<br />ReviewDeclarationVisitor类：定义Review annotation声明访问者，ReviewProcessor将要使用之对Class进行访问。<br />runapt.bat：定义了使用自定义的ReviewProcessor对Review类源代码文件进行处理的APT命令行。<br /><br /><span style="COLOR: green">B．Review类：</span><br /><b>清单7：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.apt;<br /><br />/**<br /> * 定义Review Annotation<br /> * @author cleverpig<br /> *<br /> */<br />public @interface Review {<br />        public static enum TypeEnum{EXCELLENT,NICE,NORMAL,BAD};<br />        TypeEnum type();<br />        String name() default "Review";<br />}<br /></pre><br /><br /><span style="COLOR: green">C．ReviewProcessorFactory类：</span><br /><b>清单8：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.apt;<br /><br />import java.util.Collection;<br />import java.util.Set;<br />import java.util.Arrays;<br />import com.sun.mirror.apt.*;<br />import com.sun.mirror.declaration.AnnotationTypeDeclaration;<br />import com.sun.mirror.apt.AnnotationProcessorEnvironment;<br />//请注意为了方便，使用了静态import<br />import static java.util.Collections.unmodifiableCollection;<br />import static java.util.Collections.emptySet;<br /><br />/**<br /> * 生成ReviewProcessor的工厂类<br /> * @author cleverpig<br /> *<br /> */<br />public class ReviewProcessorFactory implements AnnotationProcessorFactory{<br />        /**<br />         * 获得针对某个（些）类型声明定义的Processor<br />         * @param atds 类型声明集合<br />         * @param env processor环境<br />         */<br />        public AnnotationProcessor getProcessorFor(<br />                        Set&lt;AnnotationTypeDeclaration&gt; atds, <br />                        AnnotationProcessorEnvironment env){<br />                return new ReviewProcessor(env);<br />        }<br />        /**<br />         * 定义processor所支持的annotation类型<br />         * @return processor所支持的annotation类型的集合<br />         */<br />        public Collection&lt;String&gt;         supportedAnnotationTypes(){<br />                //“*”表示支持所有的annotation类型<br />                //当然也可以修改为“foo.bar.*”、“foo.bar.Baz”，来对所支持的类型进行修饰<br />            return unmodifiableCollection(Arrays.asList("*"));<br />    }<br />        <br />        /**<br />         * 定义processor支持的选项<br />         * @return processor支持选项的集合<br />         */<br />        public Collection&lt;String&gt;         supportedOptions(){<br />                //返回空集合<br />            return emptySet();<br />    }<br />        <br />        public static void main(String[] argv){<br />                System.out.println("ok");<br />        }<br />}<br /></pre><br /><br /><span style="COLOR: green">D．ReviewProcessor类：</span><br /><b>清单9：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.apt;<br /><br />import com.sun.mirror.apt.AnnotationProcessor;<br />import com.sun.mirror.apt.AnnotationProcessorEnvironment;<br />import com.sun.mirror.declaration.TypeDeclaration;<br />import com.sun.mirror.util.DeclarationVisitors;<br />import com.sun.mirror.util.DeclarationVisitor;<br /><br />/**<br /> * 定义Review annotation的Processor<br /> * @author cleverpig<br /> *<br /> */<br />public class ReviewProcessor implements AnnotationProcessor{<br />        //Processor所工作的环境<br />        AnnotationProcessorEnvironment env=null;<br />        <br />        /**<br />         * 构造方法<br />         * @param env 传入processor环境<br />         */<br />        public ReviewProcessor(AnnotationProcessorEnvironment env){<br />                this.env=env;<br />        }<br />        <br />        /**<br />         * 处理方法：查询processor环境中的类型声明，<br />         */<br />        public void process(){<br />                //查询processor环境中的类型声明<br />                for(TypeDeclaration type:env.getSpecifiedTypeDeclarations()){<br />                        //返回对类进行扫描、访问其声明时使用的DeclarationVisitor，<br />                        //传入参数：new ReviewDeclarationVisitor()，为扫描开始前进行的对类声明的处理<br />                        //        DeclarationVisitors.NO_OP，表示在扫描完成时进行的对类声明不做任何处理<br />                        DeclarationVisitor visitor=DeclarationVisitors.getDeclarationScanner(<br />                                        new ReviewDeclarationVisitor(),DeclarationVisitors.NO_OP);<br />                        //应用DeclarationVisitor到类型<br />                        type.accept(visitor);<br />                }<br />        }<br />}<br /></pre><br /><br /><span style="COLOR: green">E．ReviewDeclarationVisitor类：</span><br /><b>清单10：</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation.apt;<br /><br />import com.sun.mirror.util.*;<br />import com.sun.mirror.declaration.*;<br /><br />/**<br /> * 定义Review annotation声明访问者<br /> * @author cleverpig<br /> *<br /> */<br />public class ReviewDeclarationVisitor extends SimpleDeclarationVisitor{<br />        /**<br />         * 定义访问类声明的方法：打印类声明的全名<br />         * @param cd 类声明对象<br />         */<br />        public void visitClassDeclaration(ClassDeclaration cd){<br />                System.out.println("获取Class声明:"+cd.getQualifiedName());<br />        }<br />        <br />        public void visitAnnotationTypeDeclaration(AnnotationTypeDeclaration atd){<br />                System.out.println("获取Annotation类型声明:"+atd.getSimpleName());<br />        }<br />        <br />        public void visitAnnotationTypeElementDeclaration(AnnotationTypeElementDeclaration aed){<br />                System.out.println("获取Annotation类型元素声明:"+aed.getSimpleName());<br />        }<br />}<br /></pre><br /><br /><span style="COLOR: green">F．runapt.bat文件内容如下：</span><br /><b>清单11：</b><br /><pre class="overflow"><br />E:<br />rem 项目根目录<br />set PROJECT_ROOT=E:\eclipse3.1RC3\workspace\tigerFeaturePractice<br />rem 包目录路径<br />set PACKAGEPATH=com\bjinfotech\practice\annotation\apt<br />rem 运行根路径<br />set RUN_ROOT=%PROJECT_ROOT%\build<br />rem 源文件所在目录路径<br />set SRC_ROOT=%PROJECT_ROOT%\test<br />rem 设置Classpath<br />set CLASSPATH=.;%JAVA_HOME%;%JAVA_HOME%/lib/tools.jar;%RUN_ROOT%<br /><br />cd %SRC_ROOT%\%PACKAGEPATH%<br />apt -nocompile -factory com.bjinfotech.practice.annotation.apt.ReviewProcessorFactory  ./*.java<br /></pre><br /><br /><span style="COLOR: blue">四、参考资源：</span><br /><a href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html" target="_new"><font color="#002c99">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</font></a><br />作者的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 /><br /><br /><span style="COLOR: blue">五、源代码下载：</span><br />[<a href="http://www.matrix.org.cn/resource/article/44/resource/upload/forum/2005_12_21_001420_nPNcNUHcHB.rar"><font color="#002c99">下载文件</font></a>] <img src ="http://www.blogjava.net/hopeshared/aggbug/38920.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-04-03 13:36 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38920.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/38919.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 05:33:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38919.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38919.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38919.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38919.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38919.html</trackback:ping><description><![CDATA[
		<center>作者：<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">cleverpig</a></center>
		<br />
		<br />
		<br />
		<br />
		<br />
		<span style="COLOR: red">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span>
		<br />作者:cleverpig(作者的Blog:<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">http://blog.matrix.org.cn/page/cleverpig</a>)<br />原文:<a href="http://www.matrix.org.cn/resource/article/44/44055_Java+Annotation+Reflect.html" target="_new">http://www.matrix.org.cn/resource/article/44/44055_Java+Annotation+Reflect.html</a><br />关键字:java,annotation,reflect<br /><br /><span style="COLOR: blue">前言：</span><br />在上篇文章<a href="http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html" target="_new">《Java Annotation入门》</a>中概要性的介绍了Annotation的定义、使用，范围涵盖较广，但是深度不够。所以作者在《Java Annotation入门》后，继续整理了Annotation的概念和知识点，与喜欢research的朋友们共享。<br /><br /><span style="COLOR: red">阅读提示：文中提到的程序成员或者程序元素是一个概念，指组成程序代码的单元：如类、方法、成员变量。</span><br /><br /><span style="COLOR: blue">一、Annotation究竟是什么？</span><br /><br />Annotation提供了一条与程序元素关联任何信息或者任何元数据（metadata）的途径。从某些方面看，annotation就像修饰符一样被使用，并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在annotation的“name=value”结构对中。annotation类型是一种接口，能够通过java反射API的方式提供对其信息的访问。<br /><br />annotation能被用来为某个程序元素（类、方法、成员变量等）关联任何的信息。需要注意的是，这里存在着一个基本的潜规则：annotaion不能影响程序代码的执行，无论增加、删除annotation，代码都始终如一的执行。另外，尽管一些annotation通过java的反射api方法在运行时被访问，而java语言解释器在工作时忽略了这些annotation。正是由于java虚拟机忽略了annotation，导致了annotation类型在代码中是“不起作用”的；只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理。本文中将涵盖标准的annotation和meta-annotation类型，陪伴这些annotation类型的工具是java编译器（当然要以某种特殊的方式处理它们）。<br /><br />由于上述原因，annotation在使用时十分简便。一个本地变量可以被一个以NonNull命名的annotation类型所标注，来作为对这个本地变量不能被赋予null值的断言。而我们可以编写与之配套的一个annotation代码分析工具，使用它来对具有前面变量的代码进行解析，并且尝试验证这个断言。当然这些代码并不必自己编写。在JDK安装后，在JDK/bin目录中可以找到名为“apt”的工具，它提供了处理annotation的框架：它启动后扫描源代码中的annotation，并调用我们定义好的annotation处理器完成我们所要完成的工作（比如验证前面例子中的断言）。说到这里，annotation的强大功能似乎可以替代XDoclet这类的工具了，随着我们的深入，大家会更加坚信这一点。<br />注：详细描述请参看jsr250规范：<br /><a href="http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/" target="_new">http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/</a><br /><br /><span style="COLOR: blue">二、Annotation的定义：</span><br /><br />这段文字开始介绍annotation相关技术。在此大家将看到java5.0的标准annotation类型，这种标准类型就是前文中所说的“内建”类型，它们可以直接被javac支持。可喜的是，在java6.0beta版中的javac已经加入了对自定义annotation的支持。<br /><br /><span style="COLOR: blue">1。Annotation的概念和语法：</span><br /><br />首先，关键的概念是理解annotation是与一个程序元素相关联信息或者元数据的标注。它从不影响java程序的执行，但是对例如编译器警告或者像文档生成器等辅助工具产生影响。<br /><br />下面是常用的annotation列表，我们应该注意在annotation和annotation类型之间的不同：<br /><br /><span style="COLOR: green">A.annotation：</span><br />annotation使用了在java5.0所带来的新语法，它的行为十分类似public、final这样的修饰符。每个annotation具有一个名字和成员个数&gt;=0。每个annotation的成员具有被称为name=value对的名字和值（就像javabean一样），name=value装载了annotation的信息。<br /><br /><span style="COLOR: green">B.annotation类型：</span><br />annotation类型定义了annotation的名字、类型、成员默认值。一个annotation类型可以说是一个特殊的java接口，它的成员变量是受限制的，而声明annotation类型时需要使用新语法。当我们通过java反射api访问annotation时，返回值将是一个实现了该annotation类型接口的对象，通过访问这个对象我们能方便的访问到其annotation成员。后面的章节将提到在java5.0的java.lang包里包含的3个标准annotation类型。<br /><br /><span style="COLOR: green">C.annotation成员：</span><br />annotation的成员在annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认语法：允许声明任何annotation成员的默认值：一个annotation可以将name=value对作为没有定义默认值的annotation成员的值，当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性，父类的构造函数可以作为子类的默认构造函数，但是也可以被子类覆盖。<br /><br /><span style="COLOR: green">D.marker annotation类型：</span><br />一个没有成员定义的annotation类型被称为marker annotation。这种annotation类型仅使用自身的存在与否来为我们提供信息。如后面要说的Override。<br /><br /><span style="COLOR: green">E.meta-annotation：</span><br />meta-annotation也称为元annotation，它是被用来声明annotation类型的annotation。Java5.0提供了一些标准的元-annotation类型。下面介绍的target、retention就是meta-annotation。<br /><br /><span style="COLOR: green">F.target：</span><br />annotation的target是一个被标注的程序元素。target说明了annotation所修饰的对象范围：annotation可被用于packages、types（类、接口、枚举、annotation类型）、类型成员（方法、构造方法、成员变量、枚举值）、方法参数和本地变量（如循环变量、catch参数）。在annotation类型的声明中使用了target可更加明晰其修饰的目标。<br /><br /><span style="COLOR: green">G.retention：</span><br />annotation的retention定义了该annotation被保留的时间长短：某些annotation仅出现在源代码中，而被编译器丢弃；而另一些却被编译在class文件中；编译在class文件中的annotation可能会被虚拟机忽略，而另一些在class被装载时将被读取（请注意并不影响class的执行，因为annotation与class在使用上是被分离的）。使用这个meta-annotation可以对annotation的“生命周期”限制。<br /><br /><span style="COLOR: green">H.metadata：</span><br />由于metadata被广泛使用于各种计算机开发过程中，所以当我们在这里谈论的metadata即元数据通常指被annotation装载的信息或者annotation本身。<br /><br /><span style="COLOR: blue">2。使用标准Annotation：</span><br />java5.0在java.lang包中定义了3种标准的annotation类型：<br /><br /><span style="COLOR: green">A.Override：</span><br />java.lang.Override是一个marker annotation类型，它被用作标注方法。它说明了被标注的方法重载了父类的方法，起到了断言的作用。如果我们使用了这种annotation在一个没有覆盖父类方法的方法时，java编译器将以一个编译错误来警示。<br />这个annotaton常常在我们试图覆盖父类方法而确又写错了方法名时发挥威力。<br /><br />使用方法极其简单：在使用此annotation时只要在被修饰的方法前面加上@Override。<br />下面的代码是一个使用@Override修饰一个企图重载父类的toString方法，而又存在拼写错误的sample：<br /><b>清单1：</b><br /><pre class="overflow"><br />@Override<br />public String toSting() {   // 注意方法名拼写错了<br />    return "[" + super.toString() + "]";<br />}<br /></pre><br /><br /><span style="COLOR: green">B.Deprecated：</span><br />同样Deprecated也是一个marker annotation。当一个类型或者类型成员使用@Deprecated修饰的话，编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的“延续性”：如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员，虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated，但编译器仍然要报警。<br />值得注意，@Deprecated这个annotation类型和javadoc中的@deprecated这个tag是有区别的：前者是java编译器识别的，而后者是被javadoc工具所识别用来生成文档（包含程序成员为什么已经过时、它应当如何被禁止或者替代的描述）。<br />在java5.0，java编译器仍然象其从前版本那样寻找@deprecated这个javadoc tag，并使用它们产生警告信息。但是这种状况将在后续版本中改变，我们应在现在就开始使用@Deprecated来修饰过时的方法而不是@deprecated javadoc tag。<br /><b>清单2：</b><br /><pre class="overflow"><br />下面是一段使用@Deprecated的代码：<br />/**<br /> * 这里是javadoc的@deprecated声明.<br /> * @deprecated No one has players for this format any more.  Use VHS instead.<br /> */<br />@Deprecated public class Betamax { ... }<br /></pre><br /><br /><span style="COLOR: green">C.SuppressWarnings：</span><br />@SuppressWarnings被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0，sun提供的javac编译器为我们提供了-Xlint选项来使编译器对合法的程序代码提出警告，此种警告从某种程度上代表了程序错误。例如当我们使用一个generic collection类而又没有提供它的类型时，编译器将提示出"unchecked warning"的警告。<br /><br />通常当这种情况发生时，我们就需要查找引起警告的代码。如果它真的表示错误，我们就需要纠正它。例如如果警告信息表明我们代码中的switch语句没有覆盖所有可能的case，那么我们就应增加一个默认的case来避免这种警告。<br />相仿，有时我们无法避免这种警告，例如，我们使用必须和非generic的旧代码交互的generic collection类时，我们不能避免这个unchecked warning。此时@SuppressWarning就要派上用场了，在调用的方法前增加@SuppressWarnings修饰，告诉编译器停止对此方法的警告。<br />SuppressWarning不是一个marker annotation。它有一个类型为String[]的成员，这个成员的值为被禁止的警告名。对于javac编译器来讲，被-Xlint选项有效的警告名也同样对@SuppressWarings有效，同时编译器忽略掉无法识别的警告名。<br /><br />annotation语法允许在annotation名后跟括号，括号中是使用逗号分割的name=value对用于为annotation的成员赋值：<br /><b>清单3：</b><br /><pre class="overflow"><br />@SuppressWarnings(value={"unchecked","fallthrough"})<br />public void lintTrap() { /* sloppy method body omitted */ }<br /></pre><br /><br />在这个例子中SuppressWarnings annotation类型只定义了一个单一的成员，所以只有一个简单的value={...}作为name=value对。又由于成员值是一个数组，故使用大括号来声明数组值。<br /><br />注意：我们可以在下面的情况中缩写annotation：当annotation只有单一成员，并成员命名为"value="。这时可以省去"value="。比如将上面的SuppressWarnings annotation进行缩写：<br /><b>清单4：</b><br /><pre class="overflow"><br />@SuppressWarnings({"unchecked","fallthrough"})<br /></pre><br />如果SuppressWarnings所声明的被禁止警告个数为一个时，可以省去大括号：<br /><pre class="overflow"><br />@SuppressWarnings("unchecked")<br /></pre><br /><br /><span style="COLOR: blue">3。Annotation语法：</span><br /><br />在上一个章节中，我们看到书写marker annotation和单一成员annotation的语法。下面本人来介绍一下完整的语法：<br /><br />annotation由“@+annotation类型名称+(..逗号分割的name-value对...)”组成。其中成员可以按照任何的顺序。如果annotation类型定义了某个成员的默认值，则这个成员可以被省略。成员值必须为编译时常量、内嵌的annotation或者数组。<br /><br />下面我们将定义一个annotation类型名为Reviews，它有一个由@Review annotation数组构成的成员。这个@Review annotation类型有三个成员："reviewer"是一个字符串，"comment" 是一个具有默认值的可选的字符串，"grade"是一个Review.Grade枚举类型值。<br /><b>清单5：</b><br /><pre class="overflow"><br />@Reviews({  // Single-value annotation, so "value=" is omitted here<br />    @Review(grade=Review.Grade.EXCELLENT,<br />            reviewer="df"),<br />    @Review(grade=Review.Grade.UNSATISFACTORY,<br />            reviewer="eg",<br />            comment="This method needs an @Override annotation")<br />})<br /></pre><br />annotation语法的另一个重要规则是没有程序成员可以有多于一个的同一annotation实例。例如在一个类中简单的放置多个@Review annotation。这也是在上面代码中定义@Reviews annotation类型数组的原因。<br /><br /><span style="COLOR: blue">4。Annotation成员类型和值：</span><br /><br />annotation成员必须是非空的编译时常量表达式。可用的成员类型为：primitive类型、, String, Class, enumerated类型, annotation类型, 和前面类型的数组。<br /><br />下面我们定义了一个名为UncheckedExceptions 的annotation类型，它的成员是一个扩展了RuntimeException类的类数组。<br /><b>清单6：</b><br /><pre class="overflow"><br />@UncheckedExceptions({<br />    IllegalArgumentException.class, StringIndexOutOfBoundsException.class<br />})<br /></pre><br /><br /><span style="COLOR: blue">5。Annotation的目标：</span><br /><br />annotation通常被放在类型定义和成员定义的前面。然而它也出现在package、方法参数、本地变量的前面。下面，我们来讨论一下这些不大常用的写法：<br /><br />package annotation出现在package声明的前面。<br />下面的例子package-info.java中不包含任何的公共类型定义，却包含一个可选的javadoc注释。<br /><b>清单7：</b><br /><pre class="overflow"><br />/**<br /> * This package holds my custom annotation types.<br /> */<br />@com.davidflanagan.annotations.Author("David Flanagan")<br />package com.davidflanagan.annotations;<br /></pre><br />当package-info.java文件被编译时，它将产生名为包含annotation（特殊的接口）声明的package-info.class的类。这个接口没有成员，它的名字package-info不是一个合法的java标识，所以它不能用在java源代码中。这个接口的存在只是简单的被看作一个为package annotation准备的占位符。<br /><br />用于修饰方法参数、catch参数、本地变量的annotation只是简单的出现在这些程序成员的修饰符位置。java类文件格式没有为本地变量或者catch参数存储annotation作准备，所以这些annotation总是保留在源代码级别（source retention）；方法参数annotation能够保存在类文件中，也可以在保留到运行时。<br /><br />最后，请注意，枚举类型定义中不允许任何的修饰符修饰其枚举值。<br /><br /><span style="COLOR: blue">6。Annotation和默认值：</span><br />在Annotation中，没有默认值的成员必须有一个成员值。而如何理解默认值是如何被处理就是一个很重要的细节：annotation类型所定义的成员默认值被存储在class文件中，不被编译到annotation里面。如果我们修改一个annotation类型使其成员的默认值发生了改变，这个改变对于所有此类型的annotation中没有明确提供成员值的成员产生影响（即修改了该成员的成员值）。即使在annotation类型使其成员的默认值被改变后annotation从没被重新编译过，该类型的annotation(改变前已经被编译的)也受到影响。<br /><br /><span style="COLOR: blue">三、Annotation工作原理：</span><br /><br /><span style="COLOR: blue">Annotation与反射</span><br />在java5.0中Java.lang.reflect提供的反射API被扩充了读取运行时annotation的能力。让我们回顾一下前面所讲的：一个annotation类型被定义为runtime retention后，它才是在运行时可见，当class文件被装载时被保存在class文件中的annotation才会被虚拟机读取。那么reflect是如何帮助我们访问class中的annotation呢？<br /><br />下文将在java.lang.reflect用于annotation的新特性，其中java.lang.reflect.AnnotatedElement是重要的接口，它代表了提供查询annotation能力的程序成员。这个接口被java.lang.Package、java.lang.Class实现，并间接地被Method类、Constructor类、java.lang.reflect的Field类实现。而annotation中的方法参数可以通过Method类、Constructor类的getParameterAnnotations()方法获得。<br /><br />下面的代码使用了AnnotatedElement类的isAnnotationPresent()方法判断某个方法是否具有@Unstable annotation，从而断言此方法是否稳定：<br /><b>清单8：</b><br /><pre class="overflow"><br />import java.lang.reflect.*;<br /><br />Class c = WhizzBangClass.class;                           <br />Method m = c.getMethod("whizzy", int.class, int.class);  <br />boolean unstable = m.isAnnotationPresent(Unstable.class);<br /></pre><br />isAnnotationPresent()方法对于检查marker annotation是十分有用的，因为marker annotation没有成员变量，所以我们只要知道class的方法是否使用了annotation修饰就可以了。而当处理具有成员的annotation时，我们通过使用getAnnotation()方法来获得annotation的成员信息（成员名称、成员值）。这里我们看到了一套优美的java annotation系统：如果annotation存在，那么实现了相应的annotation类型接口的对象将被getAnnotation()方法返回，接着调用定义在annotation类型中的成员方法可以方便地获得任何成员值。<br /><br />回想一下，前面介绍的@Reviews annotation，如果这个annotation类型被声明为runtime retention的话，我们通过下面的代码来访问@Reviews annotation的成员值：<br /><b>清单9：</b><br /><pre class="overflow"><br />AnnotatedElement target = WhizzBangClass.class; //获得被查询的AnnotatedElement<br />// 查询AnnotatedElement的@Reviews annotation信息<br />Reviews annotation = target.getAnnotation(Reviews.class);<br />// 因为@Reviews annotation类型的成员为@Review annotation类型的数组，<br />// 所以下面声明了Review[] reviews保存@Reviews annotation类型的value成员值。<br />Review[] reviews = annotation.value();<br />// 查询每个@Review annotation的成员信息<br />for(Review r : reviews) {<br />    Review.Grade grade = r.grade();<br />    String reviewer = r.reviewer();<br />    String comment = r.comment();<br />    System.out.printf("%s assigned a grade of %s and comment '%s'%n",<br />                      reviewer, grade, comment);<br />}<br /></pre><br /><br /><span style="COLOR: blue">四、如何自定义Annotation？</span><br /><br /><span style="COLOR: blue">1．详解annotation与接口的异同：</span><br />因为annotation类型是一个非凡的接口，所以两者之间存在着某些差异：<br /><br /><span style="COLOR: green">A.Annotation类型使用关键字@interface而不是interface。</span><br />这个关键字声明隐含了一个信息：它是继承了java.lang.annotation.Annotation接口，并非声明了一个interface。<br /><br /><span style="COLOR: green">B.Annotation类型、方法定义是独特的、受限制的。</span><br />Annotation类型的方法必须声明为无参数、无异常抛出的。这些方法定义了annotation的成员：方法名成为了成员名，而方法返回值成为了成员的类型。而方法返回值类型必须为primitive类型、Class类型、枚举类型、annotation类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用default和一个默认数值来声明成员的默认值，null不能作为成员默认值，这与我们在非annotation类型中定义方法有很大不同。<br />Annotation类型和它的方法不能使用annotation类型的参数、成员不能是generic。只有返回值类型是Class的方法可以在annotation类型中使用generic，因为此方法能够用类转换将各种类型转换为Class。<br /><br /><span style="COLOR: green">C.Annotation类型又与接口有着近似之处。</span><br />它们可以定义常量、静态成员类型（比如枚举类型定义）。Annotation类型也可以如接口一般被实现或者继承。<br /><br /><span style="COLOR: blue">2．实例：</span><br />下面，我们将看到如何定义annotation类型的example。它展示了annotation类型声明以及@interface与interface之间的不同：<br /><b>清单10：</b><br /><pre class="overflow"><br />package com.davidflanagan.annotations;<br />import java.lang.annotation.*;<br /><br />/**<br /> * 使用annotation来描述那些被标注的成员是不稳定的，需要更改<br />*/<br />@Retention(RetentionPolicy.RUNTIME)<br />public @interface Unstable {}<br /></pre><br /><br />下面的另一个example只定义了一个成员。并通过将这个成员命名为value，使我们可以方便的使用这种annotation的快捷声明方式：<br /><b>清单11：</b><br /><pre class="overflow"><br />/**<br /> * 使用Author这个annotation定义在程序中指出代码的作者<br /> */<br />public @interface Author {<br />    /** 返回作者名 */<br />    String value();<br />}<br /></pre><br /><br />以下的example更加复杂。Reviews annotation类型只有一个成员，但是这个成员的类型是复杂的：由Review annotation组成的数组。Review annotation类型有3个成员：枚举类型成员grade、表示Review名称的字符串类型成员Reviewer、具有默认值的字符串类型成员Comment。<br /><b>清单12：</b><br /><pre class="overflow"><br />import java.lang.annotation.*;<br />        <br />/**<br /> * Reviews annotation类型只有一个成员，<br /> * 但是这个成员的类型是复杂的：由Review annotation组成的数组<br /> */<br />@Retention(RetentionPolicy.RUNTIME)<br />public @interface Reviews {<br />    Review[] value();<br />}<br /><br />/**<br />* Review annotation类型有3个成员： <br />* 枚举类型成员grade、<br />  * 表示Review名称的字符串类型成员Reviewer、<br />  * 具有默认值的字符串类型成员Comment。<br /> */<br />public @interface Review {<br />    // 内嵌的枚举类型<br />    public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY };<br /><br />    // 下面的方法定义了annotation的成员<br />    Grade grade();                <br />    String reviewer();          <br />    String comment() default "";  <br />}<br /></pre><br /><br />最后，我们来定义一个annotation方法用于罗列出类运行中所有的unchecked异常（上文已经提到这种情况不一定是错误）。这个annotation类型将一个数组作为了唯一的成员。数组中的每个元素都是异常类。为了加强对未检查的异常（此类异常都是在运行时抛出）进行报告，我们可以在代码中对异常的类型进行限制：<br /><b>清单13：</b><br /><pre class="overflow"><br />public @interface UncheckedExceptions {<br />    Class&lt;? extends RuntimeException&gt;[] value();<br />}<br /></pre><br /><br /><span style="COLOR: blue">五、Meta-Annotation</span><br /><br />Annotation类型可以被它们自己所标注。Java5.0定义了4个标准的meta-annotation类型，它们被用来提供对其它annotation类型作说明。这些类型和它们所支持的类在java.lang.annotation包中可以找到。如果需要更详细的信息可以参考jdk5.0手册。<br /><br /><span style="COLOR: blue">1．再谈Target</span><br />作为meta-annotation类型的Target,它描述了annotation所修饰的程序成员的类型。当一个annotation类型没有Target时，它将被作为普通的annotation看待。当将它修饰一个特定的程序成员时，它将发挥其应用的作用，例如：Override用于修饰方法时，增加了@Target这个meta-annotation就使编译器对annotation作检查，从而去掉修饰错误类型的Override。<br /><br />Target meta-annotation类型有唯一的value作为成员。这个成员的类型是java.lang.annotation.ElementType[]类型的，ElementType类型是可以被标注的程序成员的枚举类型。<br /><br /><span style="COLOR: blue">2．Retention的用法</span><br />我们在文章的开头曾经提到过Retention，但是没有详细讲解。Retention描述了annotation是否被编译器丢弃或者保留在class文件；如果保留在class文件中，是否在class文件被装载时被虚拟机读取。默认情况下，annotation被保存在class文件中，但在运行时并不能被反射访问。Retention具有三个取值：source、class、runtime，这些取值来自java.lang.annotation.RetentionPolicy的枚举类型值。<br /><br />Retention meta-annotation类型有唯一的value作为成员，它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。<br /><br /><span style="COLOR: blue">3．Documented</span><br />Documented是一个meta-annotation类型，用于描述其它类型的annotation应该被作为被标注的程序成员的公共API，因此可以被例如javadoc此类的工具文档化。<br /><br />Documented是一个marker annotation，没有成员。<br /><br /><span style="COLOR: blue">4．Inherited</span><br />@Inherited meta-annotation也是一个marker annotation，它阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class，则这个annotation将被用于该class的子类。<br /><br />注意：@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation，方法并不从它所重载的方法继承annotation。<br /><br />值得思考的是，当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME，则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时，反射代码检查将展开工作：检查class和其父类，直到发现指定的annotation类型被发现，或者到达类继承结构的顶层。<br /><br /><span style="COLOR: blue">六、总结：</span><br /><br />本文几乎覆盖了所有的Annotation的概念和知识点，从annotation的定义、语法到工作原理、如何自定义annotation，直至meta-annotation。其中也具有一些配套的代码片断可参考，虽然不是很多，但是可谓言简意赅、着其重点，本人认为用好annotation的关键还在于使用。希望本手册能够帮助大家用好annotation，这也是本人的最大快乐。<br /><br /><img src ="http://www.blogjava.net/hopeshared/aggbug/38919.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-04-03 13:33 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38919.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/38918.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 05:30:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38918.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38918.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38918.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38918.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38918.html</trackback:ping><description><![CDATA[
		<span style="FONT-SIZE: 12px">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明<br />作者:cleverpig(作者的Blog:<br /><a href="http://blog.matrix.org.cn/page/cleverpig" target="_blank"><font color="#304765">http://blog.matrix.org.cn/page/cleverpig</font></a><br />)<br />原文:[<a href="http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html" target="_blank"><font color="#304765">http://www.matrix.org.cn/resourc ... ava+Annotation.html</font></a>]http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html[/url]<br />关键字:Java,annotation,标注<br />摘要：<br />本文针对java初学者或者annotation初次使用者全面地说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作简单的annotation程序，但是对于一些高级的annotation应用（例如使用自定义annotation生成javabean映射xml文件）还需要进一步的研究和探讨。涉及到深入annotation的内容，作者将在后文《Java Annotation高级应用》中谈到。<br />同时，annotation运行存在两种方式：运行时、编译时。上文中讨论的都是在运行时的annotation应用，但在编译时的annotation应用还没有涉及，<br />一、为什么使用Annotation：<br />在JAVA应用中，我们常遇到一些需要使用模版代码。例如，为了编写一个JAX-RPC web service，我们必须提供一对接口和实现作为模版代码。如果使用annotation对远程访问的方法代码进行修饰的话，这个模版就能够使用工具自动生成。<br />另外，一些API需要使用与程序代码同时维护的附属文件。例如，JavaBeans需要一个BeanInfo Class与一个Bean同时使用/维护，而EJB则同样需要一个部署描述符。此时在程序中使用annotation来维护这些附属文件的信息将十分便利而且减少了错误。<br />二、Annotation工作方式：<br />在5.0版之前的Java平台已经具有了一些ad hoc annotation机制。比如，使用transient修饰符来标识一个成员变量在序列化子系统中应被忽略。而@deprecated这个javadoc tag也是一个ad hoc annotation用来说明一个方法已过时。从Java5.0版发布以来，5.0平台提供了一个正式的annotation功能：允许开发者定义、使用自己的annoatation类型。此功能由一个定义annotation类型的语法和一个描述annotation声明的语法，读取annotaion的API，一个使用annotation修饰的class文件，一个annotation处理工具（apt）组成。<br />annotation并不直接影响代码语义，但是它能够工作的方式被看作类似程序的工具或者类库，它会反过来对正在运行的程序语义有所影响。annotation可以从源文件、class文件或者以在运行时反射的多种方式被读取。<br />当然annotation在某种程度上使javadoc tag更加完整。一般情况下，如果这个标记对java文档产生影响或者用于生成java文档的话，它应该作为一个javadoc tag；否则将作为一个annotation。<br />三、Annotation使用方法：<br /><b>1。类型声明方式：</b><br />通常，应用程序并不是必须定义annotation类型，但是定义annotation类型并非难事。Annotation类型声明于一般的接口声明极为类似，区别只在于它在interface关键字前面使用“@”符号。<br />annotation类型的每个方法声明定义了一个annotation类型成员，但方法声明不必有参数或者异常声明；方法返回值的类型被限制在以下的范围：primitives、String、Class、enums、annotation和前面类型的数组；方法可以有默认值。<br />下面是一个简单的annotation类型声明：<br /><b>清单1:</b><br />    /**<br />     * Describes the Request-For-Enhancement(RFE) that led<br />     * to the presence of the annotated API element.<br />     */<br />    public @interface RequestForEnhancement {<br />        int    id();<br />        String synopsis();<br />        String engineer() default "[unassigned]"; <br />        String date();    default "[unimplemented]"; <br />    }<br />代码中只定义了一个annotation类型RequestForEnhancement。<br /><b>2。修饰方法的annotation声明方式：</b><br />annotation是一种修饰符，能够如其它修饰符（如public、static、final）一般使用。习惯用法是annotaions用在其它的修饰符前面。annotations由“@+annotation类型+带有括号的成员-值列表”组成。这些成员的值必须是编译时常量（即在运行时不变）。<br />A：下面是一个使用了RequestForEnhancement annotation的方法声明：<br /><b>清单2:</b><br />    @RequestForEnhancement(<br />        id       = 2868724,<br />        synopsis = "Enable time-travel",<br />        engineer = "Mr. Peabody",<br />        date     = "4/1/3007"<br />    )<br />    public static void travelThroughTime(Date destination) { ... }<br />B：当声明一个没有成员的annotation类型声明时，可使用以下方式：<br /><b>清单3:</b><br />    /**<br />     * Indicates that the specification of the annotated API element<br />     * is preliminary and subject to change.<br />     */<br />    public @interface Preliminary { }<br />作为上面没有成员的annotation类型声明的简写方式：<br /><b>清单4:</b><br />    @Preliminary public class TimeTravel { ... }<br />C：如果在annotations中只有唯一一个成员，则该成员应命名为value：<br /><b>清单5:</b><br />    /**<br />     * Associates a copyright notice with the annotated API element.<br />     */<br />    public @interface Copyright {<br />        String value();<br />    }<br />更为方便的是对于具有唯一成员且成员名为value的annotation（如上文），在其使用时可以忽略掉成员名和赋值号（=）：<br /><b>清单6:</b><br />    @Copyright("2002 Yoyodyne Propulsion Systems")<br />    public class OscillationOverthruster { ... }<br /><b>3。一个使用实例：</b><br />结合上面所讲的，我们在这里建立一个简单的基于annotation测试框架。首先我们需要一个annotation类型来表示某个方法是一个应该被测试工具运行的测试方法。<br /><b>清单7:</b><br />    import java.lang.annotation.*;<br />    /**<br />     * Indicates that the annotated method is a test method.<br />     * This annotation should be used only on parameterless static methods.<br />     */<br />    @Retention(RetentionPolicy.RUNTIME)<br />    @Target(ElementType.METHOD)<br />    public @interface Test { }<br />值得注意的是annotaion类型声明是可以标注自己的，这样的annotation被称为“meta-annotations”。<br />在上面的代码中，@Retention(RetentionPolicy.RUNTIME)这个meta-annotation表示了此类型的annotation将被虚拟机保留使其能够在运行时通过反射被读取。而@Target(ElementType.METHOD)表示此类型的annotation只能用于修饰方法声明。<br />下面是一个简单的程序，其中部分方法被上面的annotation所标注：<br /><b>清单8:</b><br />    public class Foo {<br />        @Test public static void m1() { }<br />        public static void m2() { }<br />        @Test public static void m3() {<br />            throw new RuntimeException("Boom");<br />        }<br />        public static void m4() { }<br />        @Test public static void m5() { }<br />        public static void m6() { }<br />        @Test public static void m7() {<br />            throw new RuntimeException("Crash");<br />        }<br />        public static void m8() { }<br />    }<br />Here is the testing tool:<br />    import java.lang.reflect.*;<br />    public class RunTests {<br />       public static void main(String[] args) throws Exception {<br />          int passed = 0, failed = 0;<br />          for (Method m : Class.forName(args[0]).getMethods()) {<br />             if (m.isAnnotationPresent(Test.class)) {<br />                try {<br />                   m.invoke(null);<br />                   passed++;<br />                } catch (Throwable ex) {<br />                   System.out.printf("Test %s failed: %s %n", m, ex.getCause());<br />                   failed++;<br />                }<br />             }<br />          }<br />          System.out.printf("Passed: %d, Failed %d%n", passed, failed);<br />       }<br />    }<br />这个程序从命令行参数中取出类名，并且遍历此类的所有方法，尝试调用其中被上面的测试annotation类型标注过的方法。在此过程中为了找出哪些方法被annotation类型标注过，需要使用反射的方式执行此查询。如果在调用方法时抛出异常，此方法被认为已经失败，并打印一个失败报告。最后，打印运行通过/失败的方法数量。<br />下面文字表示了如何运行这个基于annotation的测试工具：<br /><b>清单9:</b><br />    $ java RunTests Foo<br />    Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom <br />    Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash <br />    Passed: 2, Failed 2<br />四、Annotation分类：<br />根据annotation的使用方法和用途主要分为以下几类：<br /><b>1。内建Annotation——Java5.0版在java语法中经常用到的内建Annotation：</b><br />@Deprecated用于修饰已经过时的方法；<br />@Override用于修饰此方法覆盖了父类的方法（而非重载）；<br />@SuppressWarnings用于通知java编译器禁止特定的编译警告。<br />下面代码展示了内建Annotation类型的用法：<br /><b>清单10:</b><br />package com.bjinfotech.practice.annotation;<br />/**<br />* 演示如何使用java5内建的annotation<br />* 参考资料：<br />* <a href="http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html" target="_blank"><font color="#304765">http://java.sun.com/docs/books/t ... OO/annotations.html</font></a><br />* <a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html" target="_blank"><font color="#304765">http://java.sun.com/j2se/1.5.0/d ... ge/annotations.html</font></a><br />* <a href="http://mindprod.com/jgloss/annotations.html" target="_blank"><font color="#304765">http://mindprod.com/jgloss/annotations.html</font></a><br />* @author cleverpig<br />*<br />*/<br />import java.util.List;<br />public class UsingBuiltInAnnotation {<br />        //食物类<br />        class Food{}<br />        //干草类<br />        class Hay extends Food{}<br />        //动物类<br />        class Animal{<br />                Food getFood(){<br />                        return null;<br />                }<br />                //使用Annotation声明Deprecated方法<br />                @Deprecated<br />                void deprecatedMethod(){<br />                }<br />        }<br />        //马类-继承动物类<br />        class Horse extends Animal{<br />                //使用Annotation声明覆盖方法<br />                @Override<br />                Hay getFood(){<br />                        return new Hay();<br />                }<br />                //使用Annotation声明禁止警告<br />                @SuppressWarnings({"deprecation","unchecked"})<br />                void callDeprecatedMethod(List horseGroup){<br />                        Animal an=new Animal();<br />                        an.deprecatedMethod();<br />                        horseGroup.add(an);<br />                }<br />        }<br />}<br /><b>2。开发者自定义Annotation：由开发者自定义Annotation类型。</b><br />下面是一个使用annotation进行方法测试的sample：<br />AnnotationDefineForTestFunction类型定义如下：<br /><b>清单11:</b><br />package com.bjinfotech.practice.annotation;<br />import java.lang.annotation.*;<br />/**<br />* 定义annotation<br />* @author cleverpig<br />*<br />*/<br />//加载在VM中，在运行时进行映射<br />@Retention(RetentionPolicy.RUNTIME)<br />//限定此annotation只能标示方法<br />@Target(ElementType.METHOD)<br />public @interface AnnotationDefineForTestFunction{}<br />测试annotation的代码如下：<br /><b>清单12:</b><br />package com.bjinfotech.practice.annotation;<br />import java.lang.reflect.*;<br />/**<br />* 一个实例程序应用前面定义的Annotation：AnnotationDefineForTestFunction<br />* @author cleverpig<br />*<br />*/<br />public class UsingAnnotation {<br />        @AnnotationDefineForTestFunction public static void method01(){}<br />        <br />        public static void method02(){}<br />        <br />        @AnnotationDefineForTestFunction public static void method03(){<br />                throw new RuntimeException("method03");<br />        }<br />        <br />        public static void method04(){<br />                throw new RuntimeException("method04");<br />        }<br />        <br />        public static void main(String[] argv) throws Exception{<br />                int passed = 0, failed = 0;<br />                //被检测的类名<br />                String className="com.bjinfotech.practice.annotation.UsingAnnotation";<br />                //逐个检查此类的方法，当其方法使用annotation声明时调用此方法<br />            for (Method m : Class.forName(className).getMethods()) {<br />               if (m.isAnnotationPresent(AnnotationDefineForTestFunction.class)) {<br />                  try {<br />                     m.invoke(null);<br />                     passed++;<br />                  } catch (Throwable ex) {<br />                     System.out.printf("测试 %s 失败: %s %n", m, ex.getCause());<br />                     failed++;<br />                  }<br />               }<br />            }<br />            System.out.printf("测试结果： 通过: %d, 失败： %d%n", passed, failed);<br />        }<br />}<br /><b>3。使用第三方开发的Annotation类型</b><br />这也是开发人员所常常用到的一种方式。比如我们在使用Hibernate3.0时就可以利用Annotation生成数据表映射配置文件，而不必使用Xdoclet。<br />五、总结：<br />1。前面的文字说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作简单的annotation程序，但是对于一些高级的annotation应用（例如使用自定义annotation生成javabean映射xml文件）还需要进一步的研究和探讨。<br />2。同时，annotation运行存在两种方式：运行时、编译时。上文中讨论的都是在运行时的annotation应用，但在编译时的annotation应用还没有涉及，因为编译时的annotation要使用annotation processing tool。<br />涉及以上2方面的深入内容，作者将在后文《Java Annotation高级应用》中谈到。<br />六、参考资源：<br />·Matrix-Java开发者社区:<br /><a href="http://www.matrix.org.cn/" target="_blank"><font color="#304765">http://www.matrix.org.cn</font></a><br />·<br /><a href="http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html" target="_blank"><font color="#304765">http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html</font></a><br />·<br /><a href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html" target="_blank"><font color="#304765">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</font></a><br />·<br /><a href="http://www.javaworld.com/javaworld/jw-03-2005/jw-0321-toolbox.html" target="_blank"><font color="#304765">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</font></a><br />·<br /><a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html" target="_blank"><font color="#304765">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</font></a><br />·作者的Blog:<br /><a href="http://blog.matrix.org.cn/page/cleverpig" target="_blank"><font color="#304765">http://blog.matrix.org.cn/page/cleverpig</font></a></span>
<img src ="http://www.blogjava.net/hopeshared/aggbug/38918.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-04-03 13:30 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38918.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：Java 理论与实践: 用弱引用堵住内存泄漏</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38863.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 03:31:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38863.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38863.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38863.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38863.html</trackback:ping><description><![CDATA[
		<p>级别: 中级</p>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/index.html#author">
						<font color="#996699">Brian Goetz</font>
				</a>, 首席顾问, Quiotix<br /></p>
		<p>2005 年 12 月 19 日</p>
		<blockquote>虽然用 Java™ 语言编写的程序在理论上是不会出现“内存泄漏”的，但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集。本月，负责保障应用程序健康的工程师 Brian Goetz 探讨了无意识的对象保留的常见原因，并展示了如何用弱引用堵住泄漏。 </blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>要让垃圾收集（GC）回收程序不再使用的对象，对象的<i>逻辑</i> 生命周期（应用程序使用它的时间）和对该对象拥有的引用的<i>实际</i> 生命周期必须是相同的。在大多数时候，好的软件工程技术保证这是自动实现的，不用我们对对象生命周期问题花费过多心思。但是偶尔我们会创建一个引用，它在内存中包含对象的时间比我们预期的要长得多，这种情况称为<i>无意识的对象保留（unintentional object retention）</i>。 </p>
		<p>
				<a name="1.0">
						<span class="atitle">
								<font face="Arial" size="4">全局 Map 造成的内存泄漏</font>
						</span>
				</a>
		</p>
		<p>无意识对象保留最常见的原因是使用 <code>Map</code> 将元数据与临时对象（transient object）相关联。假定一个对象具有中等生命周期，比分配它的那个方法调用的生命周期长，但是比应用程序的生命周期短，如客户机的套接字连接。需要将一些元数据与这个套接字关联，如生成连接的用户的标识。在创建 <code>Socket</code> 时是不知道这些信息的，并且不能将数据添加到 <code>Socket</code> 对象上，因为不能控制 <code>Socket</code> 类或者它的子类。这时，典型的方法就是在一个全局 <code>Map</code> 中存储这些信息，如清单 1 中的 <code>SocketManager</code> 类所示： </p>
		<br />
		<a name="listing1">
				<b>清单 1. 使用一个全局 Map 将元数据关联到一个对象</b>
		</a>
		<br />
		<br />
		<font face="Lucida Console">public class SocketManager {<br />    private Map&lt;Socket,User&gt; m = new HashMap&lt;Socket,User&gt;();<br />    <br />    public void setUser(Socket s, User u) {<br />        m.put(s, u);<br />    }<br />    public User getUser(Socket s) {<br />        return m.get(s);<br />    }<br />    public void removeUser(Socket s) {<br />        m.remove(s);<br />    }<br />}<br /><br />SocketManager socketManager;<br />...<br />socketManager.setUser(socket, user);<br /></font>
		<br />
		<br />
		<p>这种方法的问题是元数据的生命周期需要与套接字的生命周期挂钩，但是除非准确地知道什么时候程序不再需要这个套接字，并记住从 <code>Map</code> 中删除相应的映射，否则，<code>Socket</code> 和 <code>User</code> 对象将会永远留在 <code>Map</code> 中，远远超过响应了请求和关闭套接字的时间。这会阻止 <code>Socket</code> 和 <code>User</code> 对象被垃圾收集，即使应用程序不会再使用它们。这些对象留下来不受控制，很容易造成程序在长时间运行后内存爆满。除了最简单的情况，在几乎所有情况下找出什么时候 <code>Socket</code> 不再被程序使用是一件很烦人和容易出错的任务，需要人工对内存进行管理。 <br /><br />-----------------------------------------------------------------------------------<br /><br /></p>
		<p>
				<a name="2.0">
						<span class="atitle">
								<font face="Arial" size="4">找出内存泄漏</font>
						</span>
				</a>
		</p>
		<p>程序有内存泄漏的第一个迹象通常是它抛出一个 <code>OutOfMemoryError</code>，或者因为频繁的垃圾收集而表现出糟糕的性能。幸运的是，垃圾收集可以提供能够用来诊断内存泄漏的大量信息。如果以 <code>-verbose:gc</code> 或者 <code>-Xloggc</code> 选项调用 JVM，那么每次 GC 运行时在控制台上或者日志文件中会打印出一个诊断信息，包括它所花费的时间、当前堆使用情况以及恢复了多少内存。记录 GC 使用情况并不具有干扰性，因此如果需要分析内存问题或者调优垃圾收集器，在生产环境中默认启用 GC 日志是值得的。 </p>
		<p>有工具可以利用 GC 日志输出并以图形方式将它显示出来，JTune 就是这样的一种工具（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/index.html#resources"><font color="#996699">参考资料</font></a>）。观察 GC 之后堆大小的图，可以看到程序内存使用的趋势。对于大多数程序来说，可以将内存使用分为两部分：<i>baseline</i> 使用和 <i>current load</i> 使用。对于服务器应用程序，baseline 使用就是应用程序在没有任何负荷、但是已经准备好接受请求时的内存使用，current load 使用是在处理请求过程中使用的、但是在请求处理完成后会释放的内存。只要负荷大体上是恒定的，应用程序通常会很快达到一个稳定的内存使用水平。如果在应用程序已经完成了其初始化并且负荷没有增加的情况下，内存使用持续增加，那么程序就可能在处理前面的请求时保留了生成的对象。 </p>
		<p>清单 2 展示了一个有内存泄漏的程序。<code>MapLeaker</code> 在线程池中处理任务，并在一个 <code>Map</code> 中记录每一项任务的状态。不幸的是，在任务完成后它不会删除那一项，因此状态项和任务对象（以及它们的内部状态）会不断地积累。 </p>
		<p>
				<br />
				<a name="listing2">
						<b>清单 2. 具有基于 Map 的内存泄漏的程序</b>
				</a>
				<br />
		</p>
		<br />
		<font face="Lucida Console">public class MapLeaker {<br />    public ExecutorService exec = Executors.newFixedThreadPool(5);<br />    public Map&lt;Task, TaskStatus&gt; taskStatus <br />        = Collections.synchronizedMap(new HashMap&lt;Task, TaskStatus&gt;());<br />    private Random random = new Random();<br /><br />    private enum TaskStatus { NOT_STARTED, STARTED, FINISHED };<br /><br />    private class Task implements Runnable {<br />        private int[] numbers = new int[random.nextInt(200)];<br /><br />        public void run() {<br />            int[] temp = new int[random.nextInt(10000)];<br />            taskStatus.put(this, TaskStatus.STARTED);<br />            doSomeWork();<br />            taskStatus.put(this, TaskStatus.FINISHED);<br />        }<br />    }<br /><br />    public Task newTask() {<br />        Task t = new Task();<br />        taskStatus.put(t, TaskStatus.NOT_STARTED);<br />        exec.execute(t);<br />        return t;<br />    }<br />}<br /></font> <br /><br /><p>图 1 显示 <code>MapLeaker</code> GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。（在真实的应用程序中，坡度不会这么大，但是在收集了足够长时间的 GC 数据后，上升趋势通常会表现得很明显。） </p><p><br /><a name="figure1"><b>图 1. 持续上升的内存使用趋势</b></a><br /><img alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/heapsize.gif" /><br /></p><p>确信有了内存泄漏后，下一步就是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具，但是找出内存泄漏不一定要花钱买这些工具 —— 内置的 <code>hprof</code> 工具也可完成这项工作。要使用 <code>hprof</code> 并让它跟踪内存使用，需要以 <code>-Xrunhprof:heap=sites</code> 选项调用 JVM。 </p><p>清单 3 显示分解了应用程序内存使用的 <code>hprof</code> 输出的相关部分。（<code>hprof</code> 工具在应用程序退出时，或者用 <code>kill -3</code> 或在 Windows 中按 Ctrl+Break 时生成使用分解。）注意两次快照相比，<code>Map.Entry</code>、<code>Task</code> 和 <code>int[]</code> 对象有了显著增加。 </p><p>请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/sidefile1.html"><font color="#5c81a7">清单 3</font></a>。</p><p>清单 4 展示了 <code>hprof</code> 输出的另一部分，给出了 <code>Map.Entry</code> 对象的分配点的调用堆栈信息。这个输出告诉我们哪些调用链生成了 <code>Map.Entry</code> 对象，并带有一些程序分析，找出内存泄漏来源一般来说是相当容易的。 </p><p><br /><a name="listing4"><b>清单 4. HPROF 输出，显示 Map.Entry 对象的分配点</b></a><br /></p><br /><font face="Lucida Console">TRACE 300446:<br />java.util.HashMap$Entry.&lt;init&gt;(&lt;Unknown Source&gt;:Unknown line)<br />java.util.HashMap.addEntry(&lt;Unknown Source&gt;:Unknown line)<br />java.util.HashMap.put(&lt;Unknown Source&gt;:Unknown line)<br />java.util.Collections$SynchronizedMap.put(&lt;Unknown Source&gt;:Unknown line)<br />com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48)<br />com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64)</font><br /><br />-------------------------------------------------------------------------------------------<br /><br /><p><a name="3.0"><span class="atitle"><font face="Arial" size="4">弱引用来救援了</font></span></a></p><p><code>SocketManager</code> 的问题是 <code>Socket</code>-<code>User</code> 映射的生命周期应当与 <code>Socket</code> 的生命周期相匹配，但是语言没有提供任何容易的方法实施这项规则。这使得程序不得不使用人工内存管理的老技术。幸运的是，从 JDK 1.2 开始，垃圾收集器提供了一种声明这种对象生命周期依赖性的方法，这样垃圾收集器就可以帮助我们防止这种内存泄漏 —— 利用<i>弱引用</i>。 </p><p>弱引用是对一个对象（称为 <i>referent</i>）的引用的持有者。使用弱引用后，可以维持对 referent 的引用，而不会阻止它被垃圾收集。当垃圾收集器跟踪堆的时候，如果对一个对象的引用只有弱引用，那么这个 referent 就会成为垃圾收集的候选对象，就像没有任何剩余的引用一样，而且所有剩余的弱引用都被<i>清除</i>。（只有弱引用的对象称为<i>弱可及（weakly reachable）</i>。） </p><p><code>WeakReference</code> 的 referent 是在构造时设置的，在没有被清除之前，可以用 <code>get()</code> 获取它的值。如果弱引用被清除了（不管是 referent 已经被垃圾收集了，还是有人调用了 <code>WeakReference.clear()</code>），<code>get()</code> 会返回 <code>null</code>。相应地，在使用其结果之前，应当总是检查 <code>get()</code> 是否返回一个非 null 值，因为 referent 最终总是会被垃圾收集的。 </p><p>用一个普通的（强）引用拷贝一个对象引用时，限制 referent 的生命周期至少与被拷贝的引用的生命周期一样长。如果不小心，那么它可能就与程序的生命周期一样 —— 如果将一个对象放入一个全局集合中的话。另一方面，在创建对一个对象的弱引用时，完全没有扩展 referent 的生命周期，只是在<i>对象仍然存活的时候</i>，保持另一种到达它的方法。 </p><p>弱引用对于构造弱集合最有用，如那些在应用程序的其余部分使用对象期间存储关于这些对象的元数据的集合 —— 这就是 <code>SocketManager</code> 类所要做的工作。因为这是弱引用最常见的用法，<code>WeakHashMap</code> 也被添加到 JDK 1.2 的类库中，它对键（而不是对值）使用弱引用。如果在一个普通 <code>HashMap</code> 中用一个对象作为键，那么这个对象在映射从 <code>Map</code> 中删除之前不能被回收，<code>WeakHashMap</code> 使您可以用一个对象作为 <code>Map</code> 键，同时不会阻止这个对象被垃圾收集。清单 5 给出了 <code>WeakHashMap</code> 的 <code>get()</code> 方法的一种可能实现，它展示了弱引用的使用： </p><p><br /><a name="listing5"><b>清单 5. WeakReference.get() 的一种可能实现</b></a><br /></p><br /><pre><code class="section"><font face="Lucida Console">public class WeakHashMap&lt;K,V&gt; implements Map&lt;K,V&gt; {

    private static class Entry&lt;K,V&gt; extends WeakReference&lt;K&gt; 
      implements Map.Entry&lt;K,V&gt; {
        private V value;
        private final int hash;
        private Entry&lt;K,V&gt; next;
        ...
    }

    public V get(Object key) {
        int hash = getHash(key);
        Entry&lt;K,V&gt; e = getChain(hash);
        while (e != null) {
            K eKey= e.get();
            if (e.hash == hash &amp;&amp; (key == eKey || key.equals(eKey)))
                return e.value;
            e = e.next;
        }
        return null;
    }
</font></code></pre><br /><br /><p>调用 <code>WeakReference.get()</code> 时，它返回一个对 referent 的强引用（如果它仍然存活的话），因此不需要担心映射在 <code>while</code> 循环体中消失，因为强引用会防止它被垃圾收集。<code>WeakHashMap</code> 的实现展示了弱引用的一种常见用法 —— 一些内部对象扩展 <code>WeakReference</code>。其原因在下面一节讨论引用队列时会得到解释。</p><p>在向 <code>WeakHashMap</code> 中添加映射时，请记住映射可能会在以后“脱离”，因为键被垃圾收集了。在这种情况下，<code>get()</code> 返回 <code>null</code>，这使得测试 <code>get()</code> 的返回值是否为 <code>null</code> 变得比平时更重要了。 </p><p><a name="3.1"><span class="smalltitle"><strong><font face="Arial">用 WeakHashMap 堵住泄漏</font></strong></span></a></p><p>在 <code>SocketManager</code> 中防止泄漏很容易，只要用 <code>WeakHashMap</code> 代替 <code>HashMap</code> 就行了，如清单 6 所示。（如果 <code>SocketManager</code> 需要线程安全，那么可以用 <code>Collections.synchronizedMap()</code> 包装 <code>WeakHashMap</code>）。当映射的生命周期必须与键的生命周期联系在一起时，可以使用这种方法。不过，应当小心不滥用这种技术，大多数时候还是应当使用普通的 <code>HashMap</code> 作为 <code>Map</code> 的实现。 </p><p><br /><a name="listing6"><b>清单 6. 用 WeakHashMap 修复 SocketManager</b></a><br /></p><br /><pre><code class="section"><font face="Lucida Console">public class SocketManager {
    private Map&lt;Socket,User&gt; m = new WeakHashMap&lt;Socket,User&gt;();
    
    public void setUser(Socket s, User u) {
        m.put(s, u);
    }
    public User getUser(Socket s) {
        return m.get(s);
    }
}
</font></code></pre><br /><br /><p><a name="3.2"><span class="smalltitle"><strong><font face="Arial">引用队列</font></strong></span></a></p><p><code>WeakHashMap</code> 用弱引用承载映射键，这使得应用程序不再使用键对象时它们可以被垃圾收集，<code>get()</code> 实现可以根据 <code>WeakReference.get()</code> 是否返回 <code>null</code> 来区分死的映射和活的映射。但是这只是防止 <code>Map</code> 的内存消耗在应用程序的生命周期中不断增加所需要做的工作的一半，还需要做一些工作以便在键对象被收集后从 <code>Map</code> 中删除死项。否则，<code>Map</code> 会充满对应于死键的项。虽然这对于应用程序是不可见的，但是它仍然会造成应用程序耗尽内存，因为即使键被收集了，<code>Map.Entry</code> 和值对象也不会被收集。 </p><p>可以通过周期性地扫描 <code>Map</code>，对每一个弱引用调用 <code>get()</code>，并在 get() 返回 <code>null</code> 时删除那个映射而消除死映射。但是如果 <code>Map</code> 有许多活的项，那么这种方法的效率很低。如果有一种方法可以在弱引用的 referent 被垃圾收集时发出通知就好了，这就是<i>引用队列</i> 的作用。 </p><p>引用队列是垃圾收集器向应用程序返回关于对象生命周期的信息的主要方法。弱引用有两个构造函数：一个只取 referent 作为参数，另一个还取引用队列作为参数。如果用关联的引用队列创建弱引用，在 referent 成为 GC 候选对象时，这个引用对象（不是 referent）就在引用清除后<i>加入</i> 到引用队列中。之后，应用程序从引用队列提取引用并了解到它的 referent 已被收集，因此可以进行相应的清理活动，如去掉已不在弱集合中的对象的项。（引用队列提供了与 <code>BlockingQueue</code> 同样的出列模式 —— polled、timed blocking 和 untimed blocking。） </p><p><code>WeakHashMap</code> 有一个名为 <code>expungeStaleEntries()</code> 的私有方法，大多数 <code>Map</code> 操作中会调用它，它去掉引用队列中所有失效的引用，并删除关联的映射。清单 7 展示了 <code>expungeStaleEntries()</code> 的一种可能实现。用于存储键-值映射的 <code>Entry</code> 类型扩展了 <code>WeakReference</code>，因此当 <code>expungeStaleEntries()</code> 要求下一个失效的弱引用时，它得到一个 <code>Entry</code>。用引用队列代替定期扫描内容的方法来清理 <code>Map</code> 更有效，因为清理过程不会触及活的项，只有在有实际加入队列的引用时它才工作。 </p><p><br /><a name="listing7"><b>清单 7. WeakHashMap.expungeStaleEntries() 的可能实现</b></a><br /></p><br /><font face="Lucida Console">private void expungeStaleEntries() {<br />Entry&lt;K,V&gt; e;<br />        while ( (e = (Entry&lt;K,V&gt;) queue.poll()) != null) {<br />            int hash = e.hash;<br /><br />            Entry&lt;K,V&gt; prev = getChain(hash);<br />            Entry&lt;K,V&gt; cur = prev;<br />            while (cur != null) {<br />                Entry&lt;K,V&gt; next = cur.next;<br />                if (cur == e) {<br />                    if (prev == e)<br />                        setChain(hash, next);<br />                    else<br />                        prev.next = next;<br />                    break;<br />                }<br />                prev = cur;<br />                cur = next;<br />            }<br />        }<br />    }<br /></font><br /><br />------------------------------------------------------------------------------------------------<br /><br /><p><a name="4.0"><span class="atitle"><font face="Arial" size="4">结束语</font></span></a></p><p>弱引用和弱集合是对堆进行管理的强大工具，使得应用程序可以使用更复杂的可及性方案，而不只是由普通（强）引用所提供的“要么全部要么没有”可及性。下个月，我们将分析与弱引用有关的<i>软引用</i>，将分析在使用弱引用和软引用时，垃圾收集器的行为。 <br /><br />-----------------------------------------------------------------------------------------------------<br /><br /></p><p><br />原文地址：<a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/index.html">http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/index.html</a></p><img src ="http://www.blogjava.net/hopeshared/aggbug/38863.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-04-03 11:31 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：[Java细节]"hi there".equals("cheers !") == true</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38859.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38859.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38859.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38859.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38859.html</trackback:ping><description><![CDATA[
		<p>不知道这个标题是否让读者产生一种想打我的冲动。至少今天我的主管被我用这个小把戏诧异了一把，当他看到<span class="ts">"hi there".equals("cheers !") 的结果居然是true时，脸上的表情实在是可爱。</span></p>
		<p>
				<span class="ts">OK，言归正传。System.out.println(<span class="ts">"hi there".equals("cheers !")); 这个看来再显然不过的句子，输出的结果居然是true。聪明的读者，你知道是为什么吗？如果一时还猜不出来，给你一点提示：</span></span>
		</p>
		<p>
				<span class="ts">
						<span class="ts">1、Java语言规范规定，同一个程序中任何相同的字符串常量（literal string）都只是同一个String对象的不同引用，不论它们是否在同一个类、同一个包中。</span>
				</span>
		</p>
		<p>
				<span class="ts">
						<span class="ts">2、Java语言规范规定，由常量表达式计算得到的String对象将在编译期被求值，并在运行时被作为字符串常量对待；在运行时计算得到的String对象将是一个完全独立的新对象。</span>
				</span>
		</p>
		<p>
				<span class="ts">
						<span class="ts">如果你仍然不明就里，或者想知道这个把戏实现的细节，请看下面这篇来自artima的webLog<br /><br /></span>
				</span>
		</p>
		<p>
				<span class="ts">
						<span class="ts">——————————————————</span>
				</span>
		</p>
		<span class="ts">
				<span class="ts">
						<div class="vegies">
								<div class="tc">
										<span class="sts">Artima Weblogs</span>
										<br />
										<font size="6">
												<span class="ts">"hi there".equals("cheers !") == true</span>
												<br />
										</font>
										<span class="as">by Heinz Kabutz</span>
										<br />
										<span class="pd">May 21, 2003</span>
										<br />
								</div>
								<blockquote>
										<b>Summary</b>
										<br />Java Strings are strange animals. They are all kept in one pen, especially the constant strings. This can lead to bizarre behaviour when we intentionally modify the innards of the constant strings through reflection. Join us, as we take apart one of Java's most prolific beasts. </blockquote>
								<hr align="left" width="90%" />
								<p>
								</p>
								<p>Whenever we used to ask our dad a question that he could not possibly have known the answer to (such as: what's the point of school, dad?) he would ask back: "How long is a piece of string?" </p>
								<p>Were he to ask me that now, I would explain to him that String is immutable (supposedly) and that it contains its length, all you have to do is ask the String how long it is. This you can do by calling length(). </p>
								<p>OK, so the first thing we learn about Java is that String is immutable. It is like when we first learn about the stork that brings the babies? There are some things you are not supposed to know until you are older! Secrets so dangerous that merely knowing them would endanger the fibres of electrons pulsating through your Java Virtual Machine. </p>
								<p>So, are Strings immutable? </p>
								<h3>Playing with your sanity - Strings</h3>
								<p>Have a look at the following code: </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 id="Codehighlighter1_22_294_Open_Image" onclick="this.style.display='none'; Codehighlighter1_22_294_Open_Text.style.display='none'; Codehighlighter1_22_294_Closed_Image.style.display='inline'; Codehighlighter1_22_294_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" />
										<img id="Codehighlighter1_22_294_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_22_294_Closed_Text.style.display='none'; Codehighlighter1_22_294_Open_Image.style.display='inline'; Codehighlighter1_22_294_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />
										<span style="COLOR: #0000ff">public</span>
										<span style="COLOR: #000000"> </span>
										<span style="COLOR: #0000ff">class</span>
										<span style="COLOR: #000000"> MindWarp </span>
										<span id="Codehighlighter1_22_294_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_22_294_Open_Text">
												<span style="COLOR: #000000">{<br /><img id="Codehighlighter1_65_147_Open_Image" onclick="this.style.display='none'; Codehighlighter1_65_147_Open_Text.style.display='none'; Codehighlighter1_65_147_Closed_Image.style.display='inline'; Codehighlighter1_65_147_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_65_147_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_65_147_Closed_Text.style.display='none'; Codehighlighter1_65_147_Open_Image.style.display='inline'; Codehighlighter1_65_147_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />  </span>
												<span style="COLOR: #0000ff">public</span>
												<span style="COLOR: #000000"> </span>
												<span style="COLOR: #0000ff">static</span>
												<span style="COLOR: #000000"> </span>
												<span style="COLOR: #0000ff">void</span>
												<span style="COLOR: #000000"> main(String[] args) </span>
												<span id="Codehighlighter1_65_147_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_65_147_Open_Text">
														<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    System.out.println(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />      </span>
														<span style="COLOR: #000000">"</span>
														<span style="COLOR: #000000">Romeo, Romeo, wherefore art thou oh Romero?</span>
														<span style="COLOR: #000000">"</span>
														<span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />  }</span>
												</span>
												<span style="COLOR: #000000">
														<br />
														<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />  </span>
												<span style="COLOR: #0000ff">private</span>
												<span style="COLOR: #000000"> </span>
												<span style="COLOR: #0000ff">static</span>
												<span style="COLOR: #000000"> </span>
												<span style="COLOR: #0000ff">final</span>
												<span style="COLOR: #000000"> String OH_ROMEO </span>
												<span style="COLOR: #000000">=</span>
												<span style="COLOR: #000000">
														<br />
														<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
												<span style="COLOR: #000000">"</span>
												<span style="COLOR: #000000">Romeo, Romeo, wherefore art thou oh Romero?</span>
												<span style="COLOR: #000000">"</span>
												<span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />  </span>
												<span style="COLOR: #0000ff">private</span>
												<span style="COLOR: #000000"> </span>
												<span style="COLOR: #0000ff">static</span>
												<span style="COLOR: #000000"> </span>
												<span style="COLOR: #0000ff">final</span>
												<span style="COLOR: #000000"> Warper warper </span>
												<span style="COLOR: #000000">=</span>
												<span style="COLOR: #000000"> </span>
												<span style="COLOR: #0000ff">new</span>
												<span style="COLOR: #000000"> Warper();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
										</span>
										<span style="COLOR: #000000">
												<br />
												<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
										</span>
								</div>
								<p>
										<br />If we are told that the class Warper does not produce any visible output when you construct it, what is the output of this program? The most correct answer is, "you don't know, depends on what Warper does". Now THERE's a nice question for the Sun Certified Java Programmer Examination. </p>
								<p>In my case, running "java MindWarp" produces the following output </p>
								<font size="3">
										<pre>C:&gt; java MindWarp &lt;ENTER&gt;
Stop this romance nonsense, or I'll be sick
</pre>
								</font>
								<p>And here is the code for Warper: </p>
								<font size="3">
										<pre>
												<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: #0000ff">import</span>
														<span style="COLOR: #000000"> java.lang.reflect.</span>
														<span style="COLOR: #000000">*</span>
														<span style="COLOR: #000000">;<br /><img id="Codehighlighter1_48_743_Open_Image" onclick="this.style.display='none'; Codehighlighter1_48_743_Open_Text.style.display='none'; Codehighlighter1_48_743_Closed_Image.style.display='inline'; Codehighlighter1_48_743_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_48_743_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_48_743_Closed_Text.style.display='none'; Codehighlighter1_48_743_Open_Image.style.display='inline'; Codehighlighter1_48_743_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
														<span style="COLOR: #0000ff">public</span>
														<span style="COLOR: #000000"> </span>
														<span style="COLOR: #0000ff">class</span>
														<span style="COLOR: #000000"> Warper </span>
														<span id="Codehighlighter1_48_743_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_48_743_Open_Text">
																<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />  </span>
																<span style="COLOR: #0000ff">private</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">static</span>
																<span style="COLOR: #000000"> Field stringValue;<br /><img id="Codehighlighter1_95_280_Open_Image" onclick="this.style.display='none'; Codehighlighter1_95_280_Open_Text.style.display='none'; Codehighlighter1_95_280_Closed_Image.style.display='inline'; Codehighlighter1_95_280_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_95_280_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_95_280_Closed_Text.style.display='none'; Codehighlighter1_95_280_Open_Image.style.display='inline'; Codehighlighter1_95_280_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />  </span>
																<span style="COLOR: #0000ff">static</span>
																<span style="COLOR: #000000"> </span>
																<span id="Codehighlighter1_95_280_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_95_280_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"> String has a private char [] called "value"<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
																		<span style="COLOR: #008000">//</span>
																		<span style="COLOR: #008000"> if it does not, find the char [] and assign it to valuetry {</span>
																		<span style="COLOR: #008000">
																				<br />
																				<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
																		</span>
																		<span style="COLOR: #000000">      stringValue </span>
																		<span style="COLOR: #000000">=</span>
																		<span style="COLOR: #000000"> String.</span>
																		<span style="COLOR: #0000ff">class</span>
																		<span style="COLOR: #000000">.getDeclaredField(</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">value</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">);<br /><img id="Codehighlighter1_313_642_Open_Image" onclick="this.style.display='none'; Codehighlighter1_313_642_Open_Text.style.display='none'; Codehighlighter1_313_642_Closed_Image.style.display='inline'; Codehighlighter1_313_642_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_313_642_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_313_642_Closed_Text.style.display='none'; Codehighlighter1_313_642_Open_Image.style.display='inline'; Codehighlighter1_313_642_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    }</span>
																</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">catch</span>
																<span style="COLOR: #000000">(NoSuchFieldException ex) </span>
																<span id="Codehighlighter1_313_642_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_313_642_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"> safety net in case we are running on a VM with a<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />      </span>
																		<span style="COLOR: #008000">//</span>
																		<span style="COLOR: #008000"> different name for the char array.</span>
																		<span style="COLOR: #008000">
																				<br />
																				<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
																		</span>
																		<span style="COLOR: #000000">      Field[] all </span>
																		<span style="COLOR: #000000">=</span>
																		<span style="COLOR: #000000"> String.</span>
																		<span style="COLOR: #0000ff">class</span>
																		<span style="COLOR: #000000">.getDeclaredFields();<br /><img id="Codehighlighter1_533_636_Open_Image" onclick="this.style.display='none'; Codehighlighter1_533_636_Open_Text.style.display='none'; Codehighlighter1_533_636_Closed_Image.style.display='inline'; Codehighlighter1_533_636_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_533_636_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_533_636_Closed_Text.style.display='none'; Codehighlighter1_533_636_Open_Image.style.display='inline'; Codehighlighter1_533_636_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />      </span>
																		<span style="COLOR: #0000ff">for</span>
																		<span style="COLOR: #000000"> (</span>
																		<span style="COLOR: #0000ff">int</span>
																		<span style="COLOR: #000000"> i</span>
																		<span style="COLOR: #000000">=</span>
																		<span style="COLOR: #000000">0</span>
																		<span style="COLOR: #000000">; stringValue </span>
																		<span style="COLOR: #000000">==</span>
																		<span style="COLOR: #000000"> </span>
																		<span style="COLOR: #0000ff">null</span>
																		<span style="COLOR: #000000"> </span>
																		<span style="COLOR: #000000">&amp;&amp;</span>
																		<span style="COLOR: #000000"> i</span>
																		<span style="COLOR: #000000">&lt;</span>
																		<span style="COLOR: #000000">all.length; i</span>
																		<span style="COLOR: #000000">++</span>
																		<span style="COLOR: #000000">) </span>
																		<span id="Codehighlighter1_533_636_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_533_636_Open_Text">
																				<span style="COLOR: #000000">{<br /><img id="Codehighlighter1_586_628_Open_Image" onclick="this.style.display='none'; Codehighlighter1_586_628_Open_Text.style.display='none'; Codehighlighter1_586_628_Closed_Image.style.display='inline'; Codehighlighter1_586_628_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_586_628_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_586_628_Closed_Text.style.display='none'; Codehighlighter1_586_628_Open_Image.style.display='inline'; Codehighlighter1_586_628_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />        </span>
																				<span style="COLOR: #0000ff">if</span>
																				<span style="COLOR: #000000"> (all[i].getType().equals(</span>
																				<span style="COLOR: #0000ff">char</span>
																				<span style="COLOR: #000000">[].</span>
																				<span style="COLOR: #0000ff">class</span>
																				<span style="COLOR: #000000">)) </span>
																				<span id="Codehighlighter1_586_628_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_586_628_Open_Text">
																						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />          stringValue </span>
																						<span style="COLOR: #000000">=</span>
																						<span style="COLOR: #000000"> all[i];<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />        }</span>
																				</span>
																				<span style="COLOR: #000000">
																						<br />
																						<img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />      }</span>
																		</span>
																		<span style="COLOR: #000000">
																				<br />
																				<img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span>
																</span>
																<span style="COLOR: #000000">
																		<br />
																		<img id="Codehighlighter1_673_739_Open_Image" onclick="this.style.display='none'; Codehighlighter1_673_739_Open_Text.style.display='none'; Codehighlighter1_673_739_Closed_Image.style.display='inline'; Codehighlighter1_673_739_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" />
																		<img id="Codehighlighter1_673_739_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_673_739_Closed_Text.style.display='none'; Codehighlighter1_673_739_Open_Image.style.display='inline'; Codehighlighter1_673_739_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span>
																<span style="COLOR: #0000ff">if</span>
																<span style="COLOR: #000000"> (stringValue </span>
																<span style="COLOR: #000000">!=</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">null</span>
																<span style="COLOR: #000000">) </span>
																<span id="Codehighlighter1_673_739_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_673_739_Open_Text">
																		<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />      stringValue.setAccessible(</span>
																		<span style="COLOR: #0000ff">true</span>
																		<span style="COLOR: #000000">); </span>
																		<span style="COLOR: #008000">//</span>
																		<span style="COLOR: #008000"> make field public</span>
																		<span style="COLOR: #008000">
																				<br />
																				<img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />
																		</span>
																		<span style="COLOR: #000000">    }</span>
																</span>
																<span style="COLOR: #000000">
																		<br />
																		<img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />  }</span>
														</span>
														<span style="COLOR: #000000">
																<br />
																<img id="Codehighlighter1_763_1047_Open_Image" onclick="this.style.display='none'; Codehighlighter1_763_1047_Open_Text.style.display='none'; Codehighlighter1_763_1047_Closed_Image.style.display='inline'; Codehighlighter1_763_1047_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" />
																<img id="Codehighlighter1_763_1047_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_763_1047_Closed_Text.style.display='none'; Codehighlighter1_763_1047_Open_Image.style.display='inline'; Codehighlighter1_763_1047_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />  </span>
														<span style="COLOR: #0000ff">public</span>
														<span style="COLOR: #000000"> Warper() </span>
														<span id="Codehighlighter1_763_1047_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_763_1047_Open_Text">
																<span style="COLOR: #000000">{<br /><img id="Codehighlighter1_773_999_Open_Image" onclick="this.style.display='none'; Codehighlighter1_773_999_Open_Text.style.display='none'; Codehighlighter1_773_999_Closed_Image.style.display='inline'; Codehighlighter1_773_999_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_773_999_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_773_999_Closed_Text.style.display='none'; Codehighlighter1_773_999_Open_Image.style.display='inline'; Codehighlighter1_773_999_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span>
																<span style="COLOR: #0000ff">try</span>
																<span style="COLOR: #000000"> </span>
																<span id="Codehighlighter1_773_999_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_773_999_Open_Text">
																		<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />      stringValue.set(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">Romeo, Romeo, wherefore art thou oh Romero?</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">,<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">Stop this romance nonsense, or I'll be sick</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">.<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />          toCharArray());<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />      stringValue.set(</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">hi there</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">, </span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">cheers !</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">.toCharArray());<br /><img id="Codehighlighter1_1034_1035_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1034_1035_Open_Text.style.display='none'; Codehighlighter1_1034_1035_Closed_Image.style.display='inline'; Codehighlighter1_1034_1035_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_1034_1035_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1034_1035_Closed_Text.style.display='none'; Codehighlighter1_1034_1035_Open_Image.style.display='inline'; Codehighlighter1_1034_1035_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    }</span>
																</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">catch</span>
																<span style="COLOR: #000000">(IllegalAccessException ex) </span>
																<span id="Codehighlighter1_1034_1035_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_1034_1035_Open_Text">
																		<span style="COLOR: #000000">{}</span>
																</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #008000">//</span>
																<span style="COLOR: #008000"> shhh</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>
														<span style="COLOR: #000000">
																<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" /></span>
												</div>
										</pre>
								</font>
								<p>How is this possible? How can String manipulation in a completely different part of the program affect our class MindWarp? </p>
								<p>To understand that, we have to look under the hood of Java. In the language specification it says in ?3.10.5: </p>
								<p>"Each string literal is a reference (?4.3) to an instance (?4.3.1, ?12.5) of class String (?4.3.3). String objects have a constant value. String literals-or, more generally, strings that are the values of constant expressions (?15.28)-are "interned" so as to share unique instances, using the method String.intern." </p>
								<p>The usefulness of this is quite obvious, we will use less memory if we have two Strings which are equivalent pointing at the same object. We can also manually intern Strings by calling the intern() method. </p>
								<p>The language spec goes a bit further: </p>
								<ol>
										<li>Literal strings within the same class (?8) in the same package (?7) represent references to the same String object (?4.3.1). 
</li>
										<li>Literal strings within different classes in the same package represent references to the same String object. 
</li>
										<li>Literal strings within different classes in different packages likewise represent references to the same String object. 
</li>
										<li>Strings computed by constant expressions (?15.28) are computed at compile time and then treated as if they were literals. 
</li>
										<li>Strings computed at run time are newly created and therefore distinct. 
</li>
										<li>The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents. </li>
								</ol>
								<p>This means that if a class in another package "fiddles" with an interned String, it can cause havoc in your program. Is this a good thing? (You don't need to answer ;-) </p>
								<p>Consider this example </p>
								<font size="3">
										<pre>
												<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 id="Codehighlighter1_26_226_Open_Image" onclick="this.style.display='none'; Codehighlighter1_26_226_Open_Text.style.display='none'; Codehighlighter1_26_226_Closed_Image.style.display='inline'; Codehighlighter1_26_226_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" />
														<img id="Codehighlighter1_26_226_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_26_226_Closed_Text.style.display='none'; Codehighlighter1_26_226_Open_Image.style.display='inline'; Codehighlighter1_26_226_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />
														<span style="COLOR: #0000ff">public</span>
														<span style="COLOR: #000000"> </span>
														<span style="COLOR: #0000ff">class</span>
														<span style="COLOR: #000000"> StringEquals </span>
														<span id="Codehighlighter1_26_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_26_226_Open_Text">
																<span style="COLOR: #000000">{<br /><img id="Codehighlighter1_67_122_Open_Image" onclick="this.style.display='none'; Codehighlighter1_67_122_Open_Text.style.display='none'; Codehighlighter1_67_122_Closed_Image.style.display='inline'; Codehighlighter1_67_122_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_67_122_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_67_122_Closed_Text.style.display='none'; Codehighlighter1_67_122_Open_Image.style.display='inline'; Codehighlighter1_67_122_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" /></span>
																<span style="COLOR: #0000ff">public</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">static</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">void</span>
																<span style="COLOR: #000000"> main(String[] args) </span>
																<span id="Codehighlighter1_67_122_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_67_122_Open_Text">
																		<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />  System.out.println(</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">hi there</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">.equals(</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">cheers !</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />}</span>
																</span>
																<span style="COLOR: #000000">
																		<br />
																		<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
																</span>
																<span style="COLOR: #0000ff">private</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">static</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">final</span>
																<span style="COLOR: #000000"> String greeting </span>
																<span style="COLOR: #000000">=</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #000000">"</span>
																<span style="COLOR: #000000">hi there</span>
																<span style="COLOR: #000000">"</span>
																<span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /></span>
																<span style="COLOR: #0000ff">private</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">static</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">final</span>
																<span style="COLOR: #000000"> Warper warper </span>
																<span style="COLOR: #000000">=</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">new</span>
																<span style="COLOR: #000000"> Warper();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
														</span>
														<span style="COLOR: #000000">
																<br />
																<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
														</span>
												</div>
										</pre>
								</font>
								<p>Running this against the Warper produces a result of true, which is really weird, and in my opinion, quite mind-bending. Hey, you can SEE the values there right in front of you and they are clearly NOT equal! </p>
								<p>BTW, for simplicity, the Strings in my examples are exactly the same length, but you can change the length quite easily as well. </p>
								<p>Last example concerns the HashCode of String, which is now cached for performance reasons mentioned in "Java Idiom and Performance Guide", ISBN 0130142603. (Just for the record, I was never and am still not convinced that caching the String hash code in a wrapper object is a good idea, but caching it in String itself is almost acceptable, considering String literals.) </p>
								<font size="3">
										<pre>
												<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 id="Codehighlighter1_29_320_Open_Image" onclick="this.style.display='none'; Codehighlighter1_29_320_Open_Text.style.display='none'; Codehighlighter1_29_320_Closed_Image.style.display='inline'; Codehighlighter1_29_320_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" />
														<img id="Codehighlighter1_29_320_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_29_320_Closed_Text.style.display='none'; Codehighlighter1_29_320_Open_Image.style.display='inline'; Codehighlighter1_29_320_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />
														<span style="COLOR: #0000ff">public</span>
														<span style="COLOR: #000000"> </span>
														<span style="COLOR: #0000ff">class</span>
														<span style="COLOR: #000000"> CachingHashcode </span>
														<span id="Codehighlighter1_29_320_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_29_320_Open_Text">
																<span style="COLOR: #000000">{<br /><img id="Codehighlighter1_72_265_Open_Image" onclick="this.style.display='none'; Codehighlighter1_72_265_Open_Text.style.display='none'; Codehighlighter1_72_265_Closed_Image.style.display='inline'; Codehighlighter1_72_265_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_72_265_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_72_265_Closed_Text.style.display='none'; Codehighlighter1_72_265_Open_Image.style.display='inline'; Codehighlighter1_72_265_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />  </span>
																<span style="COLOR: #0000ff">public</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">static</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">void</span>
																<span style="COLOR: #000000"> main(String[] args) </span>
																<span id="Codehighlighter1_72_265_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_72_265_Open_Text">
																		<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    java.util.Map map </span>
																		<span style="COLOR: #000000">=</span>
																		<span style="COLOR: #000000"> </span>
																		<span style="COLOR: #0000ff">new</span>
																		<span style="COLOR: #000000"> java.util.HashMap();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    map.put(</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">hi there</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">, </span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">You found the value</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
																		<span style="COLOR: #0000ff">new</span>
																		<span style="COLOR: #000000"> Warper();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    System.out.println(map.get(</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">hi there</span>
																		<span style="COLOR: #000000">"</span>
																		<span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    System.out.println(map);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />  }</span>
																</span>
																<span style="COLOR: #000000">
																		<br />
																		<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />  </span>
																<span style="COLOR: #0000ff">private</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">static</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #0000ff">final</span>
																<span style="COLOR: #000000"> String greeting </span>
																<span style="COLOR: #000000">=</span>
																<span style="COLOR: #000000"> </span>
																<span style="COLOR: #000000">"</span>
																<span style="COLOR: #000000">hi there</span>
																<span style="COLOR: #000000">"</span>
																<span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
														</span>
														<span style="COLOR: #000000">
																<br />
																<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
														</span>
												</div>
										</pre>
								</font>
								<p>The output under JDK 1.3 is: </p>
								<font size="3">
										<pre>You found the value
{cheers !=You found the value}
</pre>
								</font>
								<p>Under JDK 1.2 it is </p>
								<font size="3">
										<pre>null
{cheers !=You found the value}
</pre>
								</font>
								<p>This is because in the JDK 1.3 SUN is caching the hash code so if it once is calculated, it doesn't get recalculated, so if the value field changes, the hashcode stays the same. </p>
								<p>Imagine trying to debug this program where SOMEWHERE, one of your hackers has done a "workaround" by modifying a String literal. The thought scares me. </p>
								<p>The practical application of this blog? Let's face it, none. </p>
								<p>This is my first blog ever, I would be keen to hear what you thought of it? <br /><br /><br /><br />摘自：<a href="http://www.daima.com.cn/Info/55/Info14695/">http://www.daima.com.cn/Info/55/Info14695/</a></p>
						</div>
				</span>
		</span>
<img src ="http://www.blogjava.net/hopeshared/aggbug/38859.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-04-03 11:23 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：Java极度性能调整</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38856.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 03:17:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38856.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38856.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38856.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38856.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38856.html</trackback:ping><description><![CDATA[有很多介绍基本的Java应用性能调整的文章。他们都讨论些简单的技术，诸如使用StringBuffer而不用String，使用synchronized关键字的开销等等。<br />　　<br />　　这篇文章不再介绍这些东西。相反，我们关注能帮助你的基于Web的应用更快、可升级型更好的技巧。一些技巧很详细，其他的相对简短，但所有的都很有用。最后以一些你可提供给你的管理者的建议结束。<br />　　<br />　　我写这篇文章的灵感来自于当我的同事和我一起回忆我们的.com（dot-com）时代的时候――我们如何设计能支持成千上万的用户和拥有紧密代码的系统，我们如何对有侵略性的致命打击。有时在为复用设计和为性能设计之间有一个权衡。基于我的情况，性能每次都获胜。即使你的商务顾客无需理解代码复用，但是他们知道快速（fast-performing）的系统是怎么回事。让我们开始看看我们的技巧。<br />　　<br />　　<strong>如何使用Exception</strong><br />　　Exception降低性能。一个异常抛出首先需要创建一个新的对象。Throwable接口中的构造器调用名为fillInStackTrace()的本地方法。这个方法负责巡检栈的整个框架来收集跟踪信息。这样无论何时有异常抛出，它要求虚拟机装载调用栈，因为一个新的对象在中部被创建。<br />　　<br />　　异常应当仅用于有错误发生时，而不要控制流。<br />　　<br />　　我有机会在一个专门用于无线内容市场的网站（名字故意隐去了）看到一段代码，其中开发者完全可以使用一个简单的对照来查看对象是否为空。相反，他或她跳过了这个检查而实际上抛出Null-PointerException。<br />　　<br />　　<strong>不要两次初始化变量</strong><br />　　Java通过调用独特的类构造器默认地初始化变量为一个已知的值。所有的对象被设置成null，integers (byte, short, int, long)被设置成0，float和double设置成0.0，Boolean变量设置成false。这对那些扩展自其它类的类尤其重要，这跟使用一个新的关键词创建一个对象时所有一连串的构造器被自动调用一样。<br />　　<br />　　<strong>对新的关键词使用优选法则</strong><br />　　正如前面提到的，通过使用一个新的关键词创建一个类的实例，在这个链中的所有构造器将被调用。如果你需要创建一个类的新实例，你可以使用一个实现了cloneable接口的对象的clone（）方法。该clone方法不调用任何类的构造器。<br />　　<br />　　如果你已经使用了设计模式作为你的体系结构的一部分，并且使用了工厂模式创建对象，变化会很简单。下面所列是工厂模式的典型实现。<br />　　<br />　　public static Account getNewAccount() {<br />　　return new Account();<br />　　} <br />　　<br />　　使用了clone方法的refactored代码看起来可能像下面这样：<br />　　<br />　　private static Account BaseAccount = new Account();<br />　　public static Account getNewAccount() {<br />　　　　return (Account) BaseAccount.clone();<br />　　}<br />　　<br />　　以上的思路对实现数组同样有用。<br />　　<br />　　如果你在应用中没有使用设计模式，我建议你停止读这篇文章，赶快跑到（不要走）书店挑一本四人著的《设计模式》。<br />　　<br />　　<strong>在任何可能的地方让类为Final</strong><br />　　标记为final的类不能被扩展。在《核心Java API》中有大量这个技术的例子，诸如java.lang.String。将String类标记为final阻止了开发者创建他们自己实现的长度方法。<br />　　<br />　　更深入点说，如果类是final的，所有类的方法也是final的。Java编译器可能会内联所有的方法（这依赖于编译器的实现）。在我的测试里，我已经看到性能平均增加了50%。<br />　　<br />　　<strong>在任何可能的地方使用局部变量</strong><br />　　属于方法调用部分的自变量和声明为此调用一部分的临时变量存储在栈中，这比较快。诸如static，实例（instance）变量和新的对象创建在堆中，这比较慢。局部变量的更深入优化依赖于你正在使用的编译器或虚拟机。<br />　　<br />　　<strong>使用Nonblocking　I/O</strong><br />　　当前的JDK版本不支持nonblocking I/O API，很多应用试图通过创建大量的线程（目光长远得用在池中）来避免阻塞。正如前述，在Java中创建线程有严重的开销。<br />　　<br />　　典型的你可能看到应用中实现的线程需要支持并发I/O流，像Web 服务器，并quote and auction components.<br />　　<br />　　JDK1.4介绍了一个nonblocking I/O包（java.nio）。如果你必须保留在较早版本的JDK，有添加了支持nonblocking I/O的第三方包。<br />　　<br />　　：www.cs.berkeley.edu/~mdw/proj/java-nbio/download.html.<br />　　<br />　　<strong>停止小聪明</strong><br />　　很多开发人员在脑子中编写可复用和灵活的代码，而有时候在他们的程序中就产生额外的开销。曾经或者另外的时候他们编写了类似这样的代码：<br />　　<br />　　public void doSomething(File file) {<br />　　FileInputStream fileIn = new FileInputStream(file);<br />　　// do something<br />　　<br />　　他够灵活，但是同时他们也产生了更多的开销。这个主意背后做的事情是操纵一个InputStream，而不是一个文件，因此它应该重写如下：<br />　　<br />　　public void doSomething(InputStream inputStream){<br />　　// do something<br />　　<br />　　<strong>乘法和除法</strong><br />　　我有太多的东东适用于摩尔法则――它声明CPU功率每年成倍增长。"摩尔法则"表明每年由开发者所写的差劲的代码数量三倍增加，划去了摩尔法则的任何好处。<br />　　<br />　　考虑下面的代码：<br />　　<br />　　for (val = 0; val &lt; 100000; val +=5) { shiftX = val * 8; myRaise = val * 2; }<br />　　<br />　　如果我们狡猾的利用位移（bit），性能将会六倍增加。这是重写的代码： <br />　　<br />　　for (val = 0; val &lt; 100000; val += 5) { shiftX = val &lt;&lt; 3; myRaise = val &lt;&lt; 1; }<br />　　<br />　　代替了乘以8，我们使用同等效果的左移3位。每一个移动相当于乘以2，变量myRaise对此做了证明。同样向右移位相当于除以2，当然这会使执行速度加快，但可能会使你的东东以后难于理解；所以这只是个建议。<br />　　<br />　　<strong>选择一个基于垃圾收集实现的虚拟机</strong><br />　　许多人可能会对Java规范不需要实现垃圾收集感到惊讶。设想时代已经是我们都拥有无限内存计算机。总之，垃圾收集器日常事务就是负责发现和抛出(hence garbage)不再需要的对象。垃圾收集必须发现那些对象不再被程序指向，并且使被对象占用的栈内存被释放掉。它还负责运行任何被释放对象的finalizer。<br />　　<br />　　垃圾收集故意不允许你释放并非由你分配的内存，从而帮助你确保程序完整，当JVM确定CPU时间的时间表并且当垃圾收集器运行时，这个进程也产生开销。<br />　　<br />　　垃圾收集器有两个不同的步骤执行他们的工作。<br />　　<br />　　实现了定位计算的垃圾收集器在栈中为每一个对象保留一个计数。当一个对象被创建并且对它的一个定位被分配给一个变量，计数增加。当对象越出范围，定位计数被设置成0并且对象可以被垃圾收集。这个步骤允许参考计数器运行在与程序执行有关的短时间增量内。定位计数在父子彼此拥有定位的应用里运行不正常。每次一个对象刷新时也会有定位计数增加和减少的开销。<br />　　<br />　　实现了跟踪的垃圾收集器从根节点开始跟踪一列定位。对象发现跟踪是否被标记。在这个过程完成后，知道不可达的任何没标记的对象可以被垃圾收集。这可能以位图（bitmap）形式实现或者在对象中被设置标志。此技术参考"Mark and Sweep."（reference：定位，翻译成“指向”好像更容易理解，是Java语言对在用对象的一个跟踪指针。译者著）<br />　　<br />　　<strong>给你的管理人员提建议</strong><br />　　其他方法可被用来使你的基于Web的应用更快并且更可升级。可实现的最简单的技术通常是支持cluster的策略。使用cluster，一组服务器能够一起透明的提供服务。多数应用服务器允许你获得cluster支持而不需要改变你的应用――一个大的胜利。<br />　　<br />　　当然在执行此步骤之前你可能需要考虑来自你使用的应用服务器提供商附加的许可权利。<br />　　<br />　　当看到cluster策略会有许多额外的事情考虑。经常在体系结构中产生的一个缺点是拥有有状态会话。如果cluster中的一个服务器或者进程当掉，cluster会舍弃整个应用。为防止此类事情发生，cluster必须给cluster中的所有成员不断复制会话Bean的状态。确保你也限制了存储在会话中的对象的大小和数量，因为这些也需要被复制。<br />　　<br />　　Cluster也允许你分期度量你的Web站点的部分。如果你需要度量静态部分，你可以添加Web服务器。如果你需要度量动态生成的部分，你可以添加应用服务器。<br />　　<br />　　在你已经把你的系统放入cluster后，下一个让你的应用跑得更快的建议步骤是选择一个更好的虚拟机。看看Hotspot虚拟机或者其他的飞速发展中的执行优化的虚拟机。随同虚拟机，看看更好的编译器是一个更好的主意。<br />　　<br />　　如果你使用了几个这儿提到的行业技术插件，并且仍然不能获得你要的可升级性和高可用性，那么我建议一个可靠的调试策略。策略的第一步是为可能的瓶颈检查整个体系结构。通常，这在你的作为单线程组件或者有很多辅助连接线组件的UML流图中很容易识别出来。<br />　　<br />　　最后的步骤是产生一个整个代码的详细性能估价。<br />　　<br />　　确保你的管理人员至少为此安排了整个项目时间的20%；否则不足的时间可能不止危及你整个成功的安全，还会导致你向系统引入新的缺点。<br />　　<br />　　许多组织者在适当的位置没有严格意义的测试基础而归咎于成本考虑也是错误的。确保你的QA环境真实反映你的生产环境，并且你的QA测试考虑以不同的负载测试应用，包括在最大的预期并发用户时一个基于低负载和一个完全负载的测试。<br />　　<br />　　性能测试，有时测试一个系统的稳定性，可能需要在每天，甚至每周的整个时期的不同关节都运行。<br /><br /><br />转自：<a href="http://www.1piao.net/articles/view.asp?p=2006/2/1142260628218">http://www.1piao.net/articles/view.asp?p=2006/2/1142260628218</a><img src ="http://www.blogjava.net/hopeshared/aggbug/38856.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-04-03 11:17 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38856.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：Java Map 集合类简介</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38852.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 03:09:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38852.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38852.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38852.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38852.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38852.html</trackback:ping><description><![CDATA[java.util 中的集合类包含 Java 中某些最常用的类。 最常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector，它们是可变大小的列表，比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。 <br /><br />Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对（称作“键”和“值”），其中每个键映射到一个值。 从概念上而言，您可以将 List 看作是具有数值键的 Map。 而实际上，除了 List 和 Map 都在定义 java.util 中外，两者并没有直接的联系。本文将着重介绍核心 Java 发行套件中附带的 Map，同时还将介绍如何采用或实现更适用于您应用程序特定数据的专用 Map。 <br /><br />了解 Map 接口和方法 <br /><br />Java 核心类中有很多预定义的 Map 类。 在介绍具体实现之前，我们先介绍一下 Map 接口本身，以便了解所有实现的共同点。 Map 接口定义了四种类型的方法，每个 Map 都包含这些方法。 下面，我们从两个普通的方法（表 1）开始对这些方法加以介绍。 <br /><br />表 1： 覆盖的方法。 我们将这 Object 的这两个方法覆盖，以正确比较 Map 对象的等价性。 equals(Object o) 比较指定对象与此 Map 的等价性 <br />hashCode() 返回此 Map 的哈希码 <br /><br /><br /><br />Map 构建 <br /><br />Map 定义了几个用于插入和删除元素的变换方法（表 2）。 <br /><br />表 2： Map 更新方法： 可以更改 Map 内容。 clear() 从 Map 中删除所有映射 <br />remove(Object key) 从 Map 中删除键和关联的值 <br />put(Object key, Object value) 将指定值与指定键相关联 <br />clear() 从 Map 中删除所有映射 <br />putAll(Map t) 将指定 Map 中的所有映射复制到此 map <br /><br /><br /><br />尽管您可能注意到，纵然假设忽略构建一个需要传递给 putAll() 的 Map 的开销，使用 putAll() 通常也并不比使用大量的 put() 调用更有效率，但 putAll() 的存在一点也不稀奇。 这是因为，putAll() 除了迭代 put() 所执行的将每个键值对添加到 Map 的算法以外，还需要迭代所传递的 Map 的元素。 但应注意，putAll() 在添加所有元素之前可以正确调整 Map 的大小，因此如果您未亲自调整 Map 的大小（我们将对此进行简单介绍），则 putAll() 可能比预期的更有效。 <br /><br />查看 Map <br /><br />迭代 Map 中的元素不存在直接了当的方法。 如果要查询某个 Map 以了解其哪些元素满足特定查询，或如果要迭代其所有元素（无论原因如何），则您首先需要获取该 Map 的“视图”。 有三种可能的视图（参见表 3） <br /><br />所有键值对 — 参见 entrySet() <br />所有键 — 参见 keySet() <br />所有值 — 参见 values() <br /><br />前两个视图均返回 Set 对象，第三个视图返回 Collection 对象。 就这两种情况而言，问题到这里并没有结束，这是因为您无法直接迭代 Collection 对象或 Set 对象。要进行迭代，您必须获得一个 Iterator 对象。 因此，要迭代 Map 的元素，必须进行比较烦琐的编码 <br /><br /><br />Iterator keyValuePairs = aMap.entrySet().iterator(); <br />Iterator keys = aMap.keySet().iterator(); <br />Iterator values = aMap.values().iterator(); <br /><br /><br />值得注意的是，这些对象（Set、Collection 和 Iterator）实际上是基础 Map 的视图，而不是包含所有元素的副本。 这使它们的使用效率很高。 另一方面，Collection 或 Set 对象的 toArray() 方法却创建包含 Map 所有元素的数组对象，因此除了确实需要使用数组中元素的情形外，其效率并不高。 <br /><br />我运行了一个小测试（随附文件中的 Test1），该测试使用了 HashMap，并使用以下两种方法对迭代 Map 元素的开销进行了比较： <br /><br /><br />int mapsize = aMap.size(); <br /><br />Iterator keyValuePairs1 = aMap.entrySet().iterator(); <br />for (int i = 0; i &lt; mapsize; i++) <br />{ <br />Map.Entry entry = (Map.Entry) keyValuePairs1.next(); <br />Object key = entry.getKey(); <br />Object value = entry.getValue(); <br />... <br />} <br /><br />Object[] keyValuePairs2 = aMap.entrySet().toArray(); <br />for (int i = 0; i &lt; rem; i++) { <br />{ <br />Map.Entry entry = (Map.Entry) keyValuePairs2[i]; <br />Object key = entry.getKey(); <br /><br /><br />Object value = entry.getValue(); <br />... <br />} <br /><br /><br />此测试使用了两种测量方法： 一种是测量迭代元素的时间，另一种测量使用 toArray 调用创建数组的其他开销。 第一种方法（忽略创建数组所需的时间）表明，使用已从 toArray 调用中创建的数组迭代元素的速度要比使用 Iterator 的速度大约快 30%-60%。 但如果将使用 toArray 方法创建数组的开销包含在内，则使用 Iterator 实际上要快 10%-20%。 因此，如果由于某种原因要创建一个集合元素的数组而非迭代这些元素，则应使用该数组迭代元素。 但如果您不需要此中间数组，则不要创建它，而是使用 Iterator 迭代元素。 <br /><br />表 3： 返回视图的 Map 方法： 使用这些方法返回的对象，您可以遍历 Map 的元素，还可以删除 Map 中的元素。 entrySet() 返回 Map 中所包含映射的 Set 视图。 Set 中的每个元素都是一个 Map.Entry 对象，可以使用 getKey() 和 getValue() 方法（还有一个 setValue() 方法）访问后者的键元素和值元素 <br />keySet() 返回 Map 中所包含键的 Set 视图。 删除 Set 中的元素还将删除 Map 中相应的映射（键和值） <br />values() 返回 map 中所包含值的 Collection 视图。 删除 Collection 中的元素还将删除 Map 中相应的映射（键和值） <br /><br /><br /><br />访问元素 <br /><br />表 4 中列出了 Map 访问方法。Map 通常适合按键（而非按值）进行访问。 Map 定义中没有规定这肯定是真的，但通常您可以期望这是真的。 例如，您可以期望 containsKey() 方法与 get() 方法一样快。 另一方面，containsValue() 方法很可能需要扫描 Map 中的值，因此它的速度可能比较慢。 <br /><br />表 4： Map 访问和测试方法： 这些方法检索有关 Map 内容的信息但不更改 Map 内容。 get(Object key) 返回与指定键关联的值 <br />containsKey(Object key) 如果 Map 包含指定键的映射，则返回 true <br />containsValue(Object value) 如果此 Map 将一个或多个键映射到指定值，则返回 true <br />isEmpty() 如果 Map 不包含键-值映射，则返回 true <br />size() 返回 Map 中的键-值映射的数目 <br /><br /><br /><br />对使用 containsKey() 和 containsValue() 遍历 HashMap 中所有元素所需时间的测试表明，containsValue() 所需的时间要长很多。 实际上要长几个数量级！ （参见图 1 和图 2，以及随附文件中的 Test2）。 因此，如果 containsValue() 是应用程序中的性能问题，它将很快显现出来，并可以通过监测您的应用程序轻松地将其识别。 这种情况下，我相信您能够想出一个有效的替换方法来实现 containsValue() 提供的等效功能。 但如果想不出办法，则一个可行的解决方案是再创建一个 Map，并将第一个 Map 的所有值作为键。 这样，第一个 Map 上的 containsValue() 将成为第二个 Map 上更有效的 containsKey()。 <br /><br /><br />图 1： 使用 JDeveloper 创建并运行 Map 测试类 <br /><br /><br /><br /><br />图 2： 在 JDeveloper 中使用执行监测器进行的性能监测查出应用程序中的瓶颈 <br /><br /><br /><br />核心 Map <br /><br />Java 自带了各种 Map 类。 这些 Map 类可归为三种类型： <br /><br /><br />通用 Map，用于在应用程序中管理映射，通常在 java.util 程序包中实现 <br />HashMap <br />Hashtable <br />Properties <br />LinkedHashMap <br />IdentityHashMap <br />TreeMap <br />WeakHashMap <br />ConcurrentHashMap <br />专用 Map，您通常不必亲自创建此类 Map，而是通过某些其他类对其进行访问 <br />java.util.jar.Attributes <br />javax.print.attribute.standard.PrinterStateReasons <br />java.security.Provider <br />java.awt.RenderingHints <br />javax.swing.UIDefaults <br />一个用于帮助实现您自己的 Map 类的抽象类 <br />AbstractMap <br /><br />内部哈希： 哈希映射技术 <br /><br />几乎所有通用 Map 都使用哈希映射。 这是一种将元素映射到数组的非常简单的机制，您应了解哈希映射的工作原理，以便充分利用 Map。 <br /><br />哈希映射结构由一个存储元素的内部数组组成。 由于内部采用数组存储，因此必然存在一个用于确定任意键访问数组的索引机制。 实际上，该机制需要提供一个小于数组大小的整数索引值。 该机制称作哈希函数。 在 Java 基于哈希的 Map 中，哈希函数将对象转换为一个适合内部数组的整数。 您不必为寻找一个易于使用的哈希函数而大伤脑筋： 每个对象都包含一个返回整数值的 hashCode() 方法。 要将该值映射到数组，只需将其转换为一个正值，然后在将该值除以数组大小后取余数即可。 以下是一个简单的、适用于任何对象的 Java 哈希函数 <br /><br /><br />int hashvalue = Maths.abs(key.hashCode()) % table.length; <br /><br /><br />（% 二进制运算符（称作模）将左侧的值除以右侧的值，然后返回整数形式的余数。） <br /><br />实际上，在 1.4 版发布之前，这就是各种基于哈希的 Map 类所使用的哈希函数。 但如果您查看一下代码，您将看到 <br /><br /><br />int hashvalue = (key.hashCode() &amp; 0x7FFFFFFF) % table.length; <br /><br /><br />它实际上是使用更快机制获取正值的同一函数。 在 1.4 版中，HashMap 类实现使用一个不同且更复杂的哈希函数，该函数基于 Doug Lea 的 util.concurrent 程序包（稍后我将更详细地再次介绍 Doug Lea 的类）。 <br /><br /><br />图 3： 哈希工作原理 <br /><br /><br /><br />该图介绍了哈希映射的基本原理，但我们还没有对其进行详细介绍。 我们的哈希函数将任意对象映射到一个数组位置，但如果两个不同的键映射到相同的位置，情况将会如何？ 这是一种必然发生的情况。 在哈希映射的术语中，这称作冲突。 Map 处理这些冲突的方法是在索引位置处插入一个链接列表，并简单地将元素添加到此链接列表。 因此，一个基于哈希的 Map 的基本 put() 方法可能如下所示 <br /><br /><br />public Object put(Object key, Object value) { <br />//我们的内部数组是一个 Entry 对象数组 <br />//Entry[] table; <br /><br />//获取哈希码，并映射到一个索引 <br />int hash = key.hashCode(); <br />int index = (hash &amp; 0x7FFFFFFF) % table.length; <br /><br />//循环遍历位于 table[index] 处的链接列表，以查明 <br />//我们是否拥有此键项 — 如果拥有，则覆盖它 <br />for (Entry e = table[index] ; e != null ; e = e.next) { <br />//必须检查键是否相等，原因是不同的键对象 <br />//可能拥有相同的哈希 <br />if ((e.hash == hash) &amp;&amp; e.key.equals(key)) { <br />//这是相同键，覆盖该值 <br />//并从该方法返回 old 值 <br />Object old = e.value; <br />e.value = value; <br />return old; <br />} <br />} <br /><br />//仍然在此处，因此它是一个新键，只需添加一个新 Entry <br />//Entry 对象包含 key 对象、 value 对象、一个整型的 hash、 <br />//和一个指向列表中的下一个 Entry 的 next Entry <br /><br />//创建一个指向上一个列表开头的新 Entry， <br />//并将此新 Entry 插入表中 <br />Entry e = new Entry(hash, key, value, table[index]); <br />table[index] = e; <br /><br />return null; <br />} <br /><br /><br /><br />如果看一下各种基于哈希的 Map 的源代码，您将发现这基本上就是它们的工作原理。 此外，还有一些需要进一步考虑的事项，如处理空键和值以及调整内部数组。 此处定义的 put() 方法还包含相应 get() 的算法，这是因为插入包括搜索映射索引处的项以查明该键是否已经存在。 （即 get() 方法与 put() 方法具有相同的算法，但 get() 不包含插入和覆盖代码。） 使用链接列表并不是解决冲突的唯一方法，某些哈希映射使用另一种“开放式寻址”方案，本文对其不予介绍。 <br /><br />优化 Hasmap <br /><br />如果哈希映射的内部数组只包含一个元素，则所有项将映射到此数组位置，从而构成一个较长的链接列表。 由于我们的更新和访问使用了对链接列表的线性搜索，而这要比 Map 中的每个数组索引只包含一个对象的情形要慢得多，因此这样做的效率很低。 访问或更新链接列表的时间与列表的大小线性相关，而使用哈希函数访问或更新数组中的单个元素则与数组大小无关 — 就渐进性质（Big-O 表示法）而言，前者为 O(n)，而后者为 O(1)。 因此，使用一个较大的数组而不是让太多的项聚集在太少的数组位置中是有意义的。 <br /><br />调整 Map 实现的大小 <br /><br />在哈希术语中，内部数组中的每个位置称作“存储桶”(bucket)，而可用的存储桶数（即内部数组的大小）称作容量 (capacity)。 为使 Map 对象有效地处理任意数目的项，Map 实现可以调整自身的大小。 但调整大小的开销很大。 调整大小需要将所有元素重新插入到新数组中，这是因为不同的数组大小意味着对象现在映射到不同的索引值。 先前冲突的键可能不再冲突，而先前不冲突的其他键现在可能冲突。 这显然表明，如果将 Map 调整得足够大，则可以减少甚至不再需要重新调整大小，这很有可能显著提高速度。 <br /><br />使用 1.4.2 JVM 运行一个简单的测试，即用大量的项（数目超过一百万）填充 HashMap。 表 5 显示了结果，并将所有时间标准化为已预先设置大小的服务器模式（关联文件中的 Test3）。 对于已预先设置大小的 JVM，客户端和服务器模式 JVM 运行时间几乎相同（在放弃 JIT 编译阶段后）。 但使用 Map 的默认大小将引发多次调整大小操作，开销很大，在服务器模式下要多用 50% 的时间，而在客户端模式下几乎要多用两倍的时间！ <br /><br />表 5： 填充已预先设置大小的 HashMap 与填充默认大小的 HashMap 所需时间的比较 客户端模式 服务器模式 <br />预先设置的大小 100% 100% <br />默认大小 294% 157% <br /><br /><br /><br />使用负载因子 <br /><br />为确定何时调整大小，而不是对每个存储桶中的链接列表的深度进行记数，基于哈希的 Map 使用一个额外参数并粗略计算存储桶的密度。 Map 在调整大小之前，使用名为“负载因子”的参数指示 Map 将承担的“负载”量，即它的负载程度。 负载因子、项数（Map 大小）与容量之间的关系简单明了： <br /><br /><br />如果（负载因子）x（容量）&gt;（Map 大小），则调整 Map 大小 <br /><br />例如，如果默认负载因子为 0.75，默认容量为 11，则 11 x 0.75 = 8.25，该值向下取整为 8 个元素。 因此，如果将第 8 个项添加到此 Map，则该 Map 将自身的大小调整为一个更大的值。 相反，要计算避免调整大小所需的初始容量，用将要添加的项数除以负载因子，并向上取整，例如， <br /><br /><br />对于负载因子为 0.75 的 100 个项，应将容量设置为 100/0.75 = 133.33，并将结果向上取整为 134（或取整为 135 以使用奇数） <br /><br />奇数个存储桶使 map 能够通过减少冲突数来提高执行效率。 虽然我所做的测试（关联文件中的Test4）并未表明质数可以始终获得更好的效率，但理想情形是容量取质数。 1.4 版后的某些 Map（如 HashMap 和 LinkedHashMap，而非 Hashtable 或 IdentityHashMap）使用需要 2 的幂容量的哈希函数，但下一个最高 2 的幂容量由这些 Map 计算，因此您不必亲自计算。 <br /><br />负载因子本身是空间和时间之间的调整折衷。 较小的负载因子将占用更多的空间，但将降低冲突的可能性，从而将加快访问和更新的速度。 使用大于 0.75 的负载因子可能是不明智的，而使用大于 1.0 的负载因子肯定是不明知的，这是因为这必定会引发一次冲突。 使用小于 0.50 的负载因子好处并不大，但只要您有效地调整 Map 的大小，应不会对小负载因子造成性能开销，而只会造成内存开销。 但较小的负载因子将意味着如果您未预先调整 Map 的大小，则导致更频繁的调整大小，从而降低性能，因此在调整负载因子时一定要注意这个问题。 <br /><br />选择适当的 Map <br /><br />应使用哪种 Map？ 它是否需要同步？ 要获得应用程序的最佳性能，这可能是所面临的两个最重要的问题。 当使用通用 Map 时，调整 Map 大小和选择负载因子涵盖了 Map 调整选项。 <br /><br />以下是一个用于获得最佳 Map 性能的简单方法 <br /><br />将您的所有 Map 变量声明为 Map，而不是任何具体实现，即不要声明为 HashMap 或 Hashtable，或任何其他 Map 类实现。 <br /><br /><br />Map criticalMap = new HashMap(); //好 <br /><br />HashMap criticalMap = new HashMap(); //差 <br /><br /><br />这使您能够只更改一行代码即可非常轻松地替换任何特定的 Map 实例。 <br /><br />下载 Doug Lea 的 util.concurrent 程序包 (http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html)。 将 ConcurrentHashMap 用作默认 Map。 当移植到 1.5 版时，将 java.util.concurrent.ConcurrentHashMap 用作您的默认 Map。 不要将 ConcurrentHashMap 包装在同步的包装器中，即使它将用于多个线程。 使用默认大小和负载因子。 <br />监测您的应用程序。 如果发现某个 Map 造成瓶颈，则分析造成瓶颈的原因，并部分或全部更改该 Map 的以下内容： Map 类；Map 大小；负载因子；关键对象 equals() 方法实现。 专用的 Map 的基本上都需要特殊用途的定制 Map 实现，否则通用 Map 将实现您所需的性能目标。 <br /><br />Map 选择 <br /><br />也许您曾期望更复杂的考量，而这实际上是否显得太容易？ 好的，让我们慢慢来。 首先，您应使用哪种 Map？ 答案很简单： 不要为您的设计选择任何特定的 Map，除非实际的设计需要指定一个特殊类型的 Map。 设计时通常不需要选择具体的 Map 实现。 您可能知道自己需要一个 Map，但不知道使用哪种。 而这恰恰就是使用 Map 接口的意义所在。 直到需要时再选择 Map 实现 — 如果随处使用“Map”声明的变量，则更改应用程序中任何特殊 Map 的 Map 实现只需要更改一行，这是一种开销很少的调整选择。 是否要使用默认的 Map 实现？ 我很快将谈到这个问题。 <br /><br />同步 Map <br /><br />同步与否有何差别？ （对于同步，您既可以使用同步的 Map，也可以使用 Collections.synchronizedMap() 将未同步的 Map 转换为同步的 Map。 后者使用“同步的包装器”）这是一个异常复杂的选择，完全取决于您如何根据多线程并发访问和更新使用 Map，同时还需要进行维护方面的考虑。 例如，如果您开始时未并发更新特定 Map，但它后来更改为并发更新，情况将如何？ 在这种情况下，很容易在开始时使用一个未同步的 Map，并在后来向应用程序中添加并发更新线程时忘记将此未同步的 Map 更改为同步的 Map。 这将使您的应用程序容易崩溃（一种要确定和跟踪的最糟糕的错误）。 但如果默认为同步，则将因随之而来的可怕性能而序列化执行多线程应用程序。 看起来，我们需要某种决策树来帮助我们正确选择。 <br /><br />Doug Lea 是纽约州立大学奥斯威戈分校计算机科学系的教授。 他创建了一组公共领域的程序包（统称 util.concurrent），该程序包包含许多可以简化高性能并行编程的实用程序类。 这些类中包含两个 Map，即 ConcurrentReaderHashMap 和 ConcurrentHashMap。 这些 Map 实现是线程安全的，并且不需要对并发访问或更新进行同步，同时还适用于大多数需要 Map 的情况。 它们还远比同步的 Map（如 Hashtable）或使用同步的包装器更具伸缩性，并且与 HashMap 相比，它们对性能的破坏很小。 util.concurrent 程序包构成了 JSR166 的基础；JSR166 已经开发了一个包含在 Java 1.5 版中的并发实用程序，而 Java 1.5 版将把这些 Map 包含在一个新的 java.util.concurrent 程序包中。 <br /><br />所有这一切意味着您不需要一个决策树来决定是使用同步的 Map 还是使用非同步的 Map， 而只需使用 ConcurrentHashMap。 当然，在某些情况下，使用 ConcurrentHashMap 并不合适。 但这些情况很少见，并且应具体情况具体处理。 这就是监测的用途。 <br /><br /><br />结束语 <br /><br />通过 Oracle JDeveloper 可以非常轻松地创建一个用于比较各种 Map 性能的测试类。 更重要的是，集成良好的监测器可以在开发过程中快速、轻松地识别性能瓶颈 - 集成到 IDE 中的监测器通常被较频繁地使用，以便帮助构建一个成功的工程。 现在，您已经拥有了一个监测器并了解了有关通用 Map 及其性能的基础知识，可以开始运行您自己的测试，以查明您的应用程序是否因 Map 而存在瓶颈以及在何处需要更改所使用的 Map。 <br /><br />以上内容介绍了通用 Map 及其性能的基础知识。 当然，有关特定 Map 实现以及如何根据不同的需求使用它们还存在更多复杂和值得关注的事项，这些将在本文第 2 部分中介绍。 <br /><br /><br />-------------------------------------------------------------------------------- <br />Jack Shirazi 是 O''Reilly 的“Java 性能调整”的作者，以及受欢迎的 JavaPerformanceTuning.com 网站（提供 Java 性能信息的全球知名站点）的总监。 Jack 在 Java 性能领域提供咨询并著书立说。 他还监督 JavaPerformanceTuning.com 提供的信息，其中包括每年大约发布 1000 条性能技巧以及许多有关性能工具、讨论组等内容的文章。 Jack 早年还曾发布有关蛋白质结构预测以及黑洞热力学方面的文章，而且在其空闲时还对某些 Perl5 核心模块作出了贡献。 <br /><br /><br />摘自<a href="http://www.5ivb.net/Info/121/Info36849/">http://www.5ivb.net/Info/121/Info36849/</a><img src ="http://www.blogjava.net/hopeshared/aggbug/38852.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-04-03 11:09 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：如何在JAVA中使用日期</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38846.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 02:56:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38846.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38846.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38846.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38846.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38846.html</trackback:ping><description><![CDATA[
		<p align="left">如果想要取得系统的时间，可以使用<b>System.currentTimeMillis()</b>方法，例如：<?XML:NAMESPACE PREFIX = O /?><o:p></o:p></p>
		<br />
		<ul type="disc">
				<li class="MsoNormal" style="TEXT-ALIGN: left">DateDemo.java</li>
		</ul>
		<p class="MsoNormal" style="TEXT-ALIGN: left">public class DateDemo {<br />    public static void main(String[]<br />args) {<br />        System.out.println(System.currentTimeMillis());<br />    }<br />} <o:p></o:p></p>
		<br />
		<p align="left">
				<br />执行结果会显示从<?XML:NAMESPACE PREFIX = ST1 /?><st1:chsdate year="1970" month="1" day="1" islunardate="False" isrocdate="False" w:st="on">1970年1月1日</st1:chsdate>开始到取得系统时间为止所经过的毫秒数，例如1115346430703这个数字，但这样的数字没有人确切了解它的意 义是什么，您可以使用Date类别来让这个数字变的更有意义一些，例如：<o:p></o:p></p>
		<br />
		<ul type="disc">
				<li class="MsoNormal" style="TEXT-ALIGN: left">DateDemo.java<br /></li>
		</ul>
		<p class="MsoNormal" style="TEXT-ALIGN: left">import java.util.Date;<br /><br />public class DateDemo {<br />    public static void main(String[]<br />args) {<br />        Date date = new Date();<br />        <br />        System.out.println(date.toString());<br />        System.out.println(date.getTime());<br />    }<br />} <o:p></o:p></p>
		<br />
		<p align="left">
				<br />执行的结果如下：<br /></p>
		<table cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>   
<td><p align="left">Fri May 06 10:31:13 GMT+08:00 2005<br />  1115346673531<o:p></o:p></p>  </td></tr>
				</tbody>
		</table>
		<br />当您生成Date对象时，实际上它会使用System.currentTimeMillis()来取得系统时间，而您使用<br />toString()方法时，会将取得的<st1:chsdate year="1970" month="1" day="1" islunardate="False" isrocdate="False" w:st="on">1970年1月1日</st1:chsdate>至今的毫秒数转为<b>dow mon dd hh:mm:ss zzz yyyy</b>的格式，分别是：「星期 月 日 时：分：秒 公元」；使用Date的<b>getTime()</b>方法则可以取得毫秒数。<br /><br />如果您想要对日期时间作格式设定，则可以使用DateFormat来作格式化，先来看看它的子类SimpleDateFormat如何使用：<o:p></o:p><br /><ul type="disc"><li class="MsoNormal" style="TEXT-ALIGN: left">DateDemo.java</li></ul><p class="MsoNormal" style="TEXT-ALIGN: left">import java.text.DateFormat;<br />import java.text.SimpleDateFormat;<br />import java.util.Date;<br /><br />public class DateDemo {<br />    public static void main(String[]<br />args) {<br />        Date date = new Date();<br /><br />        DateFormat dateFormat = <br />            new<br />SimpleDateFormat("EE-MM-dd-yyyy");<br />        <br />        System.out.println(dateFormat.format(date));<br />    }<br />} <o:p></o:p></p><br /><p align="left"><br />执行结果：<br /></p><table cellpadding="0" width="100%" border="0"><tbody><tr>   
<td><p align="left">星期<st1:chsdate year="2005" month="5" day="6" islunardate="False" isrocdate="False" w:st="on">五-05-06</st1:chsdate>-2005<o:p></o:p></p>  </td></tr></tbody></table><br />DateFormat会依计算机上的区域设定显示时间格式，EE表示星期，MM表示月份、dd表示日期，而yyyy是公元，每个字符的设定都各有其意义，您<br />可以参考 <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/text/SimpleDateFormat.html" target="_blank"><i>SimpleDateFormat</i></a> 的API说明了解每个字符设定的意义。<br /><br />您也可以直接从DateFormat指定格式生成DateFormat的实例，例如：<o:p></o:p><br /><ul type="disc"><li class="MsoNormal" style="TEXT-ALIGN: left">DateDemo.java<br /></li></ul><p class="MsoNormal" style="TEXT-ALIGN: left">import java.text.DateFormat;<br />import java.util.Date;<br /><br />public class DateDemo {<br />    public static void main(String[]<br />args) {<br />        Date date = new Date(); <br /><br />        DateFormat shortFormat = <br />            DateFormat.getDateTimeInstance( <br />                DateFormat.SHORT,<br />DateFormat.SHORT); <br /><br />        DateFormat mediumFormat = <br />            DateFormat.getDateTimeInstance( <br />                DateFormat.MEDIUM,<br />DateFormat.MEDIUM); <br /><br />        DateFormat longFormat = <br />            DateFormat.getDateTimeInstance( <br />                DateFormat.LONG, DateFormat.LONG); <br /><br />        DateFormat fullFormat = <br />            DateFormat.getDateTimeInstance( <br />                DateFormat.FULL,<br />DateFormat.FULL); <br /><br />        System.out.println(shortFormat.format(date)); <br />        System.out.println(mediumFormat.format(date)); <br />        System.out.println(longFormat.format(date)); <br />        System.out.println(fullFormat.format(date)); <br />    }<br />} <o:p></o:p></p><br /><p align="left"><br />在使用<b>getDateTimeInstance()</b>取得DateFormat实例时，可以指定的参数是日期格式与时间格式，以上所指定的格式依讯息详细度 区分，执行结果如下：<br /></p><table cellpadding="0" width="100%" border="0"><tbody><tr><td><st1:chsdate year="2005" month="5" day="6" islunardate="False" isrocdate="False" w:st="on">2005/5/6</st1:chsdate> 上午 10:45<br /><st1:chsdate year="2005" month="5" day="6" islunardate="False" isrocdate="False" w:st="on">2005/5/6</st1:chsdate> 上午 10:45:25<br /><st1:chsdate year="2005" month="5" day="6" islunardate="False" isrocdate="False" w:st="on">2005年5月6日</st1:chsdate> 上午10时45分25秒<br /><st1:chsdate year="2005" month="5" day="6" islunardate="False" isrocdate="False" w:st="on">2005年5月6日</st1:chsdate> 星期五 上午10时45分25秒 GMT+08:00<o:p></o:p>  </td></tr></tbody></table><br /><p align="left"><br />您也可以使用getDateInstance()取得DateFormat实 例，并同时指定日期的区域显示方式，例如：<o:p></o:p></p><br /><ul type="disc"><li class="MsoNormal" style="TEXT-ALIGN: left">DateDemo.java     <o:p></o:p></li></ul><p align="left"><br />import java.text.DateFormat;<br />import java.util.Date;<br />import java.util.Locale;<br /><br />public class DateDemo {<br />    public static void main(String[]<br />args) {<br />        Date date = new Date(); <br /><br />        Locale locale = new<br />Locale("en", "US");<br />        DateFormat shortFormat = <br />            DateFormat.getDateInstance( <br />                DateFormat.SHORT,<br />locale); <br /><br />        DateFormat mediumFormat = <br />            DateFormat.getDateInstance( <br />                DateFormat.MEDIUM,<br />locale); <br /><br />        DateFormat longFormat = <br />            DateFormat.getDateInstance( <br />                DateFormat.LONG, locale); <br /><br />        DateFormat fullFormat = <br />            DateFormat.getDateInstance( <br />                DateFormat.FULL, locale); <br /><br />        System.out.println(shortFormat.format(date)); <br />        System.out.println(mediumFormat.format(date)); <br />        System.out.println(longFormat.format(date)); <br />        System.out.println(fullFormat.format(date)); <br />    }<br />} <o:p></o:p></p><br /><p align="left"><br />这边指定了美国的时间显示方式，执行结果如下：<br /></p><table cellpadding="0" width="100%" border="0"><tbody><tr>   
<td><p align="left"><st1:chsdate year="2005" month="6" day="5" islunardate="False" isrocdate="False" w:st="on">5/6/05</st1:chsdate><br />  May 6, 2005<br />  May 6, 2005<br />  Friday, May 6, 2005<o:p></o:p></p></td></tr></tbody></table><br />-------------------------------------------------------------------------------------------------<br /><br />1. 怎样计算两个时间之间的间隔？ <p>间隔=Date1.getTime()-Date2.getTime();得出来的是毫秒数. <br />除1000是秒,再除60是分,再除60是小时.............................. </p><p>记住java标准库中所有时间类都以此为基础转化的,只是他写好了一些 <br />转化的方法给你用而已.但都离不开这个毫秒数为基础. </p><p>2. t=Calendar.getInstance();m=t.get(t.MONTH)+1;这里为什么要加一？ </p><p>在java语言里，date的month的取值范围是：0～11，与人们的自然表达上相差1。 <br /></p><p>3. 系统时间与当前日期的区别？ </p><p>系统时间确切的说应该是 <br />System.currentTimeMillis(); <br />new Date()是当前日期,虽然它getTime();和System.currentTimeMillis(); <br />一样,但System.currentTimeMillis(); <br /></p><p>4. 如何计算两个日期的天数差值？ </p><p>long beginTime = beginDate.getTime(); <br />long endTime2 = endDate.getTime(); <br />long betweenDays = (long)((endTime - beginTime) / (1000 * 60 * 60 *24) + 0.5); </p><p>5. 如何比较日期时间大小？ </p><p>第一种方法： <br />use Calendar object to compare <br />java.util.Calendar class can be used to compare date. In order to do this, <br />you guy should parse that string into int year, month, day and construct a <br />Calendar object, and then do comparison. </p><p>Below is a sample </p><p>StringTokenizer token = new StringTokenizer(your string,"-"); <br />int year = Integer.parseInt(token.nextToken()); <br />int month = Integer.parseInt(token.nextToken()); <br />int day = Integer.parseInt(token.nextToken()); <br />Calendar date = Calendar.getInstance(); <br />date.set(year,month,day); <br />Calendar today = Calendar.getInstacne(); <br />if(date.after(today)){ <br />//...... <br />} <br />第二种方法 <br />Date nowDate=new Date();//当前时间\r <br />long nowTime=nowDate.getTime; <br />long lastTime=userTime.longValue();//以前的时间\r <br />long time=nowTime-lastTime;//时间相减比较。 <br />if(time&gt;(long)60000)//1分钟{} <br /></p><p>另外可用以下参考 <br />用时间戳，Date.getTime()可以把当前时间改成时间戳， <br />用CompareTo(); <br />用before(),after(),equals(); </p><p>6. 格式化日期的问题\r </p><p>目的： <br />第一次求日期 <br />java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd"); <br />String riqi=formatter.format(currentTime_1); <br />第二次求时间\r <br />java.text.DateFormat format1 = new java.text.SimpleDateFormat("hhmmss"); <br />java.util.Date currentTime_2 = new java.util.Date(); <br />String shijian=format1.format(currentTime_2); <br />得到的结果是 <br />2002-02-19和115324(11点53分24秒） </p><p>实现： <br />java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd-H-mm-ss"); <br />java.util.Date currentTime_1 = new java.util.Date(); <br />String str_date = formatter.format(currentTime_1); <br />StringTokenizer token = new StringTokenizer(str_date,"-"); <br />String year = token.nextToken(); <br />String month= token.nextToken(); <br />String day = token.nextToken(); <br />String hh = token.nextToken(); <br />String mm = token.nextToken(); <br />String ss = token.nextToken(); <br />String riqi=year+"年\\"+month+"月"+day+"日"+" "+hh+"点\\"+mm+"分"+ss+"秒\\"; <br />String newdir=year+month+day; <br />String wenjian = hh+mm+ss; </p><p>7. 怎么得到一个月的天数？ </p><p>java.util.Calendar date = java.util.Calendar.getInstance(); <br />System.out.println(date.getActualMaximum(date.DAY_OF_MONTH)); <br /><br /><br />------------------------------------------------------------------------------------------------<br /><br /> <br />取得指定月份的第一天与取得指定月份的最后一天  <br />       /**  <br />         *  取得指定月份的第一天  <br />         *  <br />         *  @param  strdate  String  <br />         *  @return  String  <br />         */  <br />       public  String  getMonthBegin(String  strdate)  <br />       {  <br />               java.util.Date  date  =  parseFormatDate(strdate);  <br />               return  formatDateByFormat(date,"yyyy-MM")  +  "-01";  <br />       }  <br /><br />       /**  <br />         *  取得指定月份的最后一天  <br />         *  <br />         *  @param  strdate  String  <br />         *  @return  String  <br />         */  <br />       public  String  getMonthEnd(String  strdate)  <br />       {  <br />               java.util.Date  date  =  parseFormatDate(getMonthBegin(strdate));  <br />               Calendar  calendar  =  Calendar.getInstance();  <br />               calendar.setTime(date);  <br />               calendar.add(Calendar.MONTH,1);  <br />               calendar.add(Calendar.DAY_OF_YEAR,  -1);  <br />               return  formatDate(calendar.getTime());  <br />       }  <br /><br />       /**  <br />         *  常用的格式化日期  <br />         *  <br />         *  @param  date  Date  <br />         *  @return  String  <br />         */  <br />       public  String  formatDate(java.util.Date  date)  <br />       {  <br />               return  formatDateByFormat(date,"yyyy-MM-dd");  <br />       }  <br /><br />       /**  <br />         *  以指定的格式来格式化日期  <br />         *  <br />         *  @param  date  Date  <br />         *  @param  format  String  <br />         *  @return  String  <br />         */  <br />       public  String  formatDateByFormat(java.util.Date  date,String  format)  <br />       {  <br />               String  result  =  "";  <br />               if(date  !=  null)  <br />               {  <br />                       try  <br />                       {  <br />                               SimpleDateFormat  sdf  =  new  SimpleDateFormat(format);  <br />                               result  =  sdf.format(date);  <br />                       }  <br />                       catch(Exception  ex)  <br />                       {  <br />                               LOGGER.info("date:"  +  date);  <br />                               ex.printStackTrace();  <br />                       }  <br />               }  <br />               return  result;  <br />       }  <br />---------------------------------------------------------------  <br /><br />package  com.app.util;  <br /><br />/**  <br />*  日期操作  <br />*    <br />*  @author  xxx  <br />*  @version  2.0  jdk1.4.0  tomcat5.1.0  *  Updated  Date:2005/03/10  <br />*/  <br />public  class  DateUtil  {  <br />           /**  <br />             *  格式化日期  <br />             *    <br />             *  @param  dateStr  <br />             *                        字符型日期  <br />             *  @param  format  <br />             *                        格式  <br />             *  @return  返回日期  <br />             */  <br />           public  static  java.util.Date  parseDate(String  dateStr,  String  format)  {  <br />                       java.util.Date  date  =  null;  <br />                       try  {  <br />                                   java.text.DateFormat  df  =  new  java.text.SimpleDateFormat(format);  <br />                                   String  dt=Normal.parse(dateStr).replaceAll(  <br />                                                           "-",  "/");  <br />                                   if((!dt.equals(""))&amp;&amp;(dt.length()&lt;format.length())){  <br />                                               dt+=format.substring(dt.length()).replaceAll("[YyMmDdHhSs]","0");  <br />                                   }  <br />                                   date  =  (java.util.Date)  df.parse(dt);  <br />                       }  catch  (Exception  e)  {  <br />                       }  <br />                       return  date;  <br />           }  <br /><br />           public  static  java.util.Date  parseDate(String  dateStr)  {  <br />                       return  parseDate(dateStr,  "yyyy/MM/dd");  <br />           }  <br /><br />           public  static  java.util.Date  parseDate(java.sql.Date  date)  {  <br />                       return  date;  <br />           }  <br />             <br />           public  static  java.sql.Date  parseSqlDate(java.util.Date  date)  {  <br />                       if  (date  !=  null)  <br />                                   return  new  java.sql.Date(date.getTime());  <br />                       else  <br />                                   return  null;  <br />           }  <br /><br />           public  static  java.sql.Date  parseSqlDate(String  dateStr,  String  format)  {  <br />                       java.util.Date  date  =  parseDate(dateStr,  format);  <br />                       return  parseSqlDate(date);  <br />           }  <br /><br />           public  static  java.sql.Date  parseSqlDate(String  dateStr)  {  <br />                       return  parseSqlDate(dateStr,  "yyyy/MM/dd");  <br />           }  <br /><br />             <br />           public  static  java.sql.Timestamp  parseTimestamp(String  dateStr,  <br />                                   String  format)  {  <br />                       java.util.Date  date  =  parseDate(dateStr,  format);  <br />                       if  (date  !=  null)  {  <br />                                   long  t  =  date.getTime();  <br />                                   return  new  java.sql.Timestamp(t);  <br />                       }  else  <br />                                   return  null;  <br />           }  <br /><br />           public  static  java.sql.Timestamp  parseTimestamp(String  dateStr)  {  <br />                       return  parseTimestamp(dateStr,  "yyyy/MM/dd  HH:mm:ss");  <br />           }  <br /><br />           /**  <br />             *  格式化输出日期  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @param  format  <br />             *                        格式  <br />             *  @return  返回字符型日期  <br />             */  <br />           public  static  String  format(java.util.Date  date,  String  format)  {  <br />                       String  result  =  "";  <br />                       try  {  <br />                                   if  (date  !=  null)  {  <br />                                               java.text.DateFormat  df  =  new  java.text.SimpleDateFormat(format);  <br />                                               result  =  df.format(date);  <br />                                   }  <br />                       }  catch  (Exception  e)  {  <br />                       }  <br />                       return  result;  <br />           }  <br /><br />           public  static  String  format(java.util.Date  date)  {  <br />                       return  format(date,  "yyyy/MM/dd");  <br />           }  <br /><br />           /**  <br />             *  返回年份  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回年份  <br />             */  <br />           public  static  int  getYear(java.util.Date  date)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTime(date);  <br />                       return  c.get(java.util.Calendar.YEAR);  <br />           }  <br /><br />           /**  <br />             *  返回月份  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回月份  <br />             */  <br />           public  static  int  getMonth(java.util.Date  date)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTime(date);  <br />                       return  c.get(java.util.Calendar.MONTH)  +  1;  <br />           }  <br /><br />           /**  <br />             *  返回日份  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回日份  <br />             */  <br />           public  static  int  getDay(java.util.Date  date)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTime(date);  <br />                       return  c.get(java.util.Calendar.DAY_OF_MONTH);  <br />           }  <br /><br />           /**  <br />             *  返回小时  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回小时  <br />             */  <br />           public  static  int  getHour(java.util.Date  date)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTime(date);  <br />                       return  c.get(java.util.Calendar.HOUR_OF_DAY);  <br />           }  <br /><br />           /**  <br />             *  返回分钟  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回分钟  <br />             */  <br />           public  static  int  getMinute(java.util.Date  date)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTime(date);  <br />                       return  c.get(java.util.Calendar.MINUTE);  <br />           }  <br /><br />           /**  <br />             *  返回秒钟  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回秒钟  <br />             */  <br />           public  static  int  getSecond(java.util.Date  date)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTime(date);  <br />                       return  c.get(java.util.Calendar.SECOND);  <br />           }  <br /><br />           /**  <br />             *  返回毫秒  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回毫秒  <br />             */  <br />           public  static  long  getMillis(java.util.Date  date)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTime(date);  <br />                       return  c.getTimeInMillis();  <br />           }  <br /><br />           /**  <br />             *  返回字符型日期  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回字符型日期  <br />             */  <br />           public  static  String  getDate(java.util.Date  date)  {  <br />                       return  format(date,  "yyyy/MM/dd");  <br />           }  <br /><br />           /**  <br />             *  返回字符型时间  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回字符型时间  <br />             */  <br />           public  static  String  getTime(java.util.Date  date)  {  <br />                       return  format(date,  "HH:mm:ss");  <br />           }  <br /><br />           /**  <br />             *  返回字符型日期时间  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @return  返回字符型日期时间  <br />             */  <br />           public  static  String  getDateTime(java.util.Date  date)  {  <br />                       return  format(date,  "yyyy/MM/dd  HH:mm:ss");  <br />           }  <br /><br />           /**  <br />             *  日期相加  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @param  day  <br />             *                        天数  <br />             *  @return  返回相加后的日期  <br />             */  <br />           public  static  java.util.Date  addDate(java.util.Date  date,  int  day)  {  <br />                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  <br />                       c.setTimeInMillis(getMillis(date)  +  ((long)  day)  *  24  *  3600  *  1000);  <br />                       return  c.getTime();  <br />           }  <br /><br />           /**  <br />             *  日期相减  <br />             *    <br />             *  @param  date  <br />             *                        日期  <br />             *  @param  date1  <br />             *                        日期  <br />             *  @return  返回相减后的日期  <br />             */  <br />           public  static  int  diffDate(java.util.Date  date,  java.util.Date  date1)  {  <br />                       return  (int)  ((getMillis(date)  -  getMillis(date1))  /  (24  *  3600  *  1000));  <br />           }              <br />}  <br />------------------------------------------------------------------------------------------------------------------<br /><br />来个简单点的,也许有点用  <br /><br />Calendar  now  =  Calendar.getInstance();  <br />int  year  =  now.get(Calendar.YEAR);  <br />int  date  =  now.get(Calendar.DAY_OF_MONTH);  <br />int  month  =  now.get(Calendar.MONTH)  +  1;  <br />int  hour  =  now.get(Calendar.HOUR);  <br />int  min  =  now.get(Calendar.MINUTE);  <br />int  sec  =  now.get(Calendar.SECOND);  <br /><br /><br /><br />转自<a href="http://www.513pc.net/bbs/dispbbs.asp?boardid=4&amp;id=239&amp;star=1&amp;page=1">http://www.513pc.net/bbs/dispbbs.asp?boardid=4&amp;id=239&amp;star=1&amp;page=1</a></p><img src ="http://www.blogjava.net/hopeshared/aggbug/38846.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-04-03 10:56 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38846.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：轻量级的xml parser: KXML</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38841.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 02:34:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38841.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38841.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38841.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38841.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38841.html</trackback:ping><description><![CDATA[k<a class="" title="" href="http://dev.21tx.com/web/xml/" target=""><font color="#3366cc">XML</font></a> is a small XML pull parser, specially designed for constrained environments such as <a href="http://dev.21tx.com/java/applet/" target="_blank"><font color="#3366cc">Applet</font></a>s, Personal <a href="http://dev.21tx.com/java/" target="_blank"><font color="#3366cc">Java</font></a> or MIDP devices. <br /><br />最小的版本只有11k,比那些庞大的xml解析起确实小好多。当你对xml解析不需要很严格时可以使用它。<br /><br />下面是使用kxml的一段示例代码：<br /><pre><b><font color="#a020f0">import</font></b> org.xmlpull.v1.*;

<b><font color="#a020f0">import</font></b> java.util.*;
<b><font color="#a020f0">import</font></b> java.io.*;
<b><font color="#a020f0">import</font></b> java<a href="http://dev.21tx.com/dotnet/" target="_blank"><font face="宋体" color="#3366cc">.net</font></a>.*;

<i><font color="#b22222">/** 
 * A simple example illustrationg some differences of the XmlPull API 
 * and SAX. For the corresponding SAX based implementation, please refer to 
 * http://www.cafeconleche.org/slides/sd2001east/xmlandjava/81.html ff. */</font></i><b><font color="#a020f0">public</font></b><b><font color="#a020f0">class</font></b><a href="http://dev.21tx.com/web/" target="_blank"><font face="宋体" color="#3366cc">Web</font></a>logs {

    <b><font color="#a020f0">static</font></b> List listChannels()
        <b><font color="#a020f0">throws</font></b> IOException, XmlPullParserException {
        <b><font color="#a020f0">return</font></b> listChannels(<font color="#bc8f8f"><b>"http://static.userland.com/weblogMonitor/logs.xml"</b></font>);
    }

    <b><font color="#a020f0">static</font></b> List listChannels(String uri)
        <b><font color="#a020f0">throws</font></b> IOException, XmlPullParserException {

        Vector result = <b><font color="#a020f0">new</font></b> Vector();

        InputStream is = <b><font color="#a020f0">new</font></b> URL(uri).openStream();
        XmlPullParser parser =
            XmlPullParserFactory.newInstance().newPullParser();

        parser.setInput(is, <b><font color="#a020f0">null</font></b>);

        parser.nextTag();
        parser.require(XmlPullParser.START_TAG, <font color="#bc8f8f"><b>""</b></font>, <font color="#bc8f8f"><b>"weblogs"</b></font>);

        <b><font color="#a020f0">while</font></b> (parser.nextTag() == XmlPullParser.START_TAG) {
            String url = readSingle(parser);
            <b><font color="#a020f0">if</font></b> (url != <b><font color="#a020f0">null</font></b>)
                result.addElement(url);
        }
        parser.require(XmlPullParser.END_TAG, <font color="#bc8f8f"><b>""</b></font>, <font color="#bc8f8f"><b>"weblogs"</b></font>);

        parser.next();
        parser.require(XmlPullParser.END_DOCUMENT, <b><font color="#a020f0">null</font></b>, <b><font color="#a020f0">null</font></b>);

		is.close ();
		parser.setInput (<b><font color="#a020f0">null</font></b>);

        <b><font color="#a020f0">return</font></b> result;
    }

    <b><font color="#a020f0">public</font></b><b><font color="#a020f0">static</font></b> String readSingle(XmlPullParser parser)
        <b><font color="#a020f0">throws</font></b> IOException, XmlPullParserException {

        String url = <b><font color="#a020f0">null</font></b>;
        parser.require(XmlPullParser.START_TAG, <font color="#bc8f8f"><b>""</b></font>, <font color="#bc8f8f"><b>"log"</b></font>);

        <b><font color="#a020f0">while</font></b> (parser.nextTag() == XmlPullParser.START_TAG) {
            String name = parser.getName();
            String content = parser.nextText();
            <b><font color="#a020f0">if</font></b> (name.equals(<font color="#bc8f8f"><b>"url"</b></font>))
                url = content;
            parser.require(XmlPullParser.END_TAG, <font color="#bc8f8f"><b>""</b></font>, name);
        }
        parser.require(XmlPullParser.END_TAG, <font color="#bc8f8f"><b>""</b></font>, <font color="#bc8f8f"><b>"log"</b></font>);
        <b><font color="#a020f0">return</font></b> url;
    }

    <b><font color="#a020f0">public</font></b><b><font color="#a020f0">static</font></b><b><font color="#a020f0">void</font></b> main(String[] args)
        <b><font color="#a020f0">throws</font></b> IOException, XmlPullParserException {

        List urls =
            args.length &gt; 0
                ? listChannels(args[0])
                : listChannels();

        <b><font color="#a020f0">for</font></b> (Iterator i = urls.iterator(); i.hasNext();)
            System.out.println(i.next());
    }
}
<br /><br /><br /><br />摘自<a href="http://www.21tx.com/dev/2004/11/27/11901.html">http://www.21tx.com/dev/2004/11/27/11901.html</a></pre><img src ="http://www.blogjava.net/hopeshared/aggbug/38841.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-04-03 10:34 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38841.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：Java 中的 XML：Java 文档模型的用法</title><link>http://www.blogjava.net/hopeshared/archive/2006/04/03/38840.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Mon, 03 Apr 2006 02:30:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/04/03/38840.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/38840.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/04/03/38840.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/38840.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/38840.html</trackback:ping><description><![CDATA[在本系列的第一篇文章中，我研究了一些用 Java 编写的主要的 XML 文档模型的性能。但是，在开始选择这种类型的技术时，性能只是问题的一部分。使用方便至少是同样重要的，并且它已是一个主要理由，来支持使用 Java 特定的模型，而不是与语言无关的 DOM 。 <br /><br />为切实了解哪个模型真正的作用，您需要知道它们在可用性程度上是如何排名的。本文中，我将尝试进行这个工作，从样本代码开始，来演示如何在每个模型中编码公共类型的操作。并对结果进行总结来结束本文，而且提出了促使一种表示比另一种更容易使用的一些其它因素。 <br /><br />请参阅以前的文章（请参阅参考资料或本文“内容”下的便捷链接）来获取这个对比中使用的各个模型的背景资料，包含实际的版本号。还可以参阅“参考资料”一节中关于源代码下载、到模型主页的链接以及其它相关信息。 <br /><br />代码对比 <br />在对不同文档表示中用法技术的这些对比中，我将显示如何在每种模型中实现三种基本操作： <br /><br />根据输入流构建文档 <br />遍历元素和内容，并做一些更改： <br />从文本内容中除去前导和尾随的空白。 <br />如果结果文本内容为空，就删除它。 <br />否则，将它包装到父元素的名称空间中一个名为“text”的新元素中。 <br />将已修改的文档写入输出流 <br /><br />这些示例的代码是以我在上篇文章中使用的基准程序为基础的，并进行了一些简化。基准程序的焦点是为了显示每个模型的最佳性能；对于本文，我将尝试显示在每种模型中实现操作的最简便方法。 <br /><br />我已经将每个模型的示例结构化为两个独立的代码段。第一段是读取文档、调用修改代码和编写已修改文档的代码。第二段是真正遍历文档表示和执行修改的递归方法。为避免分散注意力，我已在代码中忽略了异常处理。 <br /><br />您可以从本页底部参考资料一节链接到下载页，以获取所有样本的完整代码。样本的下载版本包括一个测试驱动程序，还有一些添加的代码用于通过计算元素、删除和添加的个数来检查不同模型的操作。 <br /><br />即使您不想使用 DOM 实现，但还是值得浏览下面对 DOM 用法的描述。因为 DOM 示例是第一个示例，所以与后面的模型相比，我用它来探究有关该示例的一些问题和结构的更详细信息。浏览这些内容可以补充您想知道的一些细节，如果直接阅读其它模型之一，那么将错过这些细节。 <br /><br />DOM <br />DOM 规范涵盖了文档表示的所有类型的操作，但是它没有涉及例如对文档的语法分析和生成文本输出这样的问题。包括在性能测试中的两种 DOM 实现，Xerces 和 Crimson，对这些操作使用不同的技术。清单 1 显示了 Xerces 的顶级代码的一种形式。 <br /><br />清单 1. Xerces DOM 顶级代码 <br />1 // parse the document from input stream ("in") <br />2 DOMParser parser = new DOMParser(); <br />3 parser.setFeature("http://xml.org/sax/features/namespaces", true); <br />4 parser.parse(new InputSource(in)); <br />5 Document doc = parser.getDocument(); <br /><br />6 // recursively walk and modify document <br />7 modifyElement(doc.getDocumentElement()); <br /><br />8 // write the document to output stream ("out") <br />9 OutputFormat format = new OutputFormat(doc); <br />10 XMLSerializer serializer = new XMLSerializer(out, format); <br />11 serializer.serialize(doc.getDocumentElement()); <br /><br /><br /><br />正如我在注释中指出的，清单 1 中的第一块代码（第 1-5 行）处理对输入流的语法分析，以构建文档表示。Xerces 定义了 DOMParser 类，以便从 Xerces 语法分析器的输出构建文档。InputSource 类是 SAX 规范的一部分，它能适应供 SAX 分析器使用的几种输入形式的任何之一。通过单一调用进行实际的语法分析和文档构造，如果成功完成了这一操作，那么应用程序就可以检索并使用已构造的 Document。 <br /><br />第二个代码块（第 6-7 行）只是将文档的根元素传递给我马上要谈到的递归修改方法。这些代码与本文中所有文档模型的代码在本质上是相同的，所以在剩余的示例中我将跳过它，不再做任何讨论。 <br /><br />第三个代码块（第 8-11 行）处理将文档作为文本写入输出流。这里，OutputFormat 类包装文档，并为格式化生成的文本提供了多种选项。XMLSerializer 类处理输出文本的实际生成。 <br /><br />Xerces 的 modify 方法只使用标准 DOM 接口，所以它还与任何其它 DOM 实现兼容。清单 2 显示了代码。 <br /><br />清单 2. DOM Modify 方法 <br />1 protected void modifyElement(Element element) { <br /><br />2 // loop through child nodes <br />3 Node child; <br />4 Node next = (Node)element.getFirstChild(); <br />5 while ((child = next) != null) { <br /><br />6 // set next before we change anything <br />7 next = child.getNextSibling(); <br /><br />8 // handle child by node type <br />9 if (child.getNodeType() == Node.TEXT_NODE) { <br /><br />10 // trim whitespace from content text <br />11 String trimmed = child.getNodeValue().trim(); <br />12 if (trimmed.length() == 0) { <br /><br />13 // delete child if nothing but whitespace <br />14 element.removeChild(child); <br /><br />15 } else { <br /><br />16 // create a "text" element matching parent namespace <br />17 Document doc = element.getOwnerDocument(); <br />18 String prefix = element.getPrefix(); <br />19 String name = (prefix == null) ? "text" : (prefix + ":text"); <br />20 Element text = <br />21 doc.createElementNS(element.getNamespaceURI(), name); <br /><br />22 // wrap the trimmed content with new element <br />23 text.appendChild(doc.createTextNode(trimmed)); <br />24 element.replaceChild(text, child); <br /><br />25 } <br />26 } else if (child.getNodeType() == Node.ELEMENT_NODE) { <br /><br />27 // handle child elements with recursive call <br />28 modifyElement((Element)child); <br /><br />29 } <br />30 } <br />31 } <br /><br /><br /><br />清单 2 中显示的方法所使用的基本方法与所有文档表示的方法相同。 通过一个元素调用它，它就依次遍历那个元素的子元素。如果找到文本内容子元素，要么删除文本（如果它只是由空格组成的），要么通过与包含元素相同的名称空间中名为“text”的新元素来包装文本（如果有非空格的字符）。如果找到一个子元素，那么这个方法就使用这个子元素，递归地调用它本身。 <br /><br />对于 DOM 实现，我使用一对引用：child 和 next 来跟踪子元素排序列表中我所处的位置。在对当前子节点进行任何其它处理之前，先装入下个子节点的引用（第 7 行）。这样做使得我能够删除或替代当前的子节点，而不丢失我在列表中的踪迹。 <br /><br />当我创建一个新元素来包装非空白的文本内容（第 16-24 行）时，DOM 接口开始有点杂乱。用来创建元素的方法与文档关联并成为一个整体，所以我需要在所有者文档中检索当前我正在处理的元素（第 17 行）。我想将这个新元素放置在与现有的父元素相同的名称空间中，并且在 DOM 中，这意味着我需要构造元素的限定名称。根据是否有名称空间的前缀，这个操作会有所不同（第 18-19 行）。利用新元素的限定名称，以及现有元素中的名称空间 URI，我就能创建新元素（第 20-21 行）。 <br /><br />一旦创建了新元素，我只要创建和添加文本节点来包装内容 String，然后用新创建的元素来替代原始文本节点（第 22-24 行）。 <br /><br />清单 3. Crimson DOM 顶级代码 <br />1 // parse the document from input stream <br />2 System.setProperty("javax.xml.parsers.DocumentBuilderFactory", <br />3 "org.apache.crimson.jaxp.DocumentBuilderFactoryImpl"); <br />4 DocumentBuilderFactory dbf = DocumentBuilderFactoryImpl.newInstance(); <br />5 dbf.setNamespaceAware(true); <br />6 DocumentBuilder builder = dbf.newDocumentBuilder(); <br />7 Document doc = builder.parse(in); <br /><br />8 // recursively walk and modify document <br />9 modifyElement(doc.getDocumentElement()); <br /><br />10 // write the document to output stream <br />11 ((XmlDocument)doc).write(out); <br /><br /><br /><br />清单 3 中的 Crimson DOM 示例代码使用了用于语法分析的 JAXP 接口。JAXP 为语法分析和转换 XML 文档提供了一个标准化的接口。本示例中的语法分析代码还可以用于 Xerces（对文档构建器类名称的特性设置有适当的更改）来替代较早给定的 Xerces 特定的示例代码。 <br /><br />在本示例中，我首先在第 2 行到第 3 行中设置系统特性来选择要构造的 DOM 表示的构建器工厂类（JAXP 仅直接支持构建 DOM 表示，不支持构建本文中讨论的任何其它表示）。仅当想选择一个要由 JAXP 使用的特定 DOM 时，才需要这一步；否则，它使用缺省实现。出于完整性起见，我在代码中包含了设置这个特性，但是更普遍的是将它设置成一个 JVM 命令行参数。 <br /><br />接着我在第 4 行到第 6 行中创建构建器工厂的实例，对使用那个工厂实例构造的构建器启用名称空间支持，并从构建器工厂创建文档构建器。最后（第 7 行），我使用文档构建器来对输入流进行语法分析并构造文档表示。 <br /><br />为了写出文档，我使用 Crimson 中内部定义的基本方法。不保证在 Crimson 未来版本中支持这个方法，但是使用 JAXP 转换代码来将文档作为文本输出的替代方法需要诸如 Xalan 那样的 XSL 处理器的。那超出了本文的范围，但是要获取详细信息，可以查阅 Sun 中的 JAXP 教程。 <br /><br />JDOM <br />使用 JDOM 的顶级代码比使用 DOM 实现的代码稍微简单一点。为构建文档表示（第 1-3 行），我使用带有由参数值禁止验证的 SAXBuilder。通过使用提供的 XMLOutputter 类，将已修改的文档写入输出流同样简单（第 6-8 行）。 <br /><br />清单 4. JDOM 顶级代码 <br />1 // parse the document from input stream <br />2 SAXBuilder builder = new SAXBuilder(false); <br />3 Document doc = builder.build(in); <br /><br />4 // recursively walk and modify document <br />5 modifyElement(doc.getRootElement()); <br /><br />6 // write the document to output stream <br />7 XMLOutputter outer = new XMLOutputter(); <br />8 outer.output(doc, out); <br /><br /><br /><br />清单 5 中 JDOM 的 modify 方法也比 DOM 的同一方法简单。我获取包含元素所有内容的列表并扫描了这张列表，检查文本（象 String 对象那样的内容）和元素。这张列表是“活的”，所以我能直接对它进行更改，而不必调用父元素上的方法。 <br /><br />清单 5. JDOM modify 方法 <br />1 protected void modifyElement(Element element) { <br /><br />2 // loop through child nodes <br />3 List children = element.getContent(); <br />4 for (int i = 0; i &lt; children.size(); i++) { <br /><br />5 // handle child by node type <br />6 Object child = children.get(i); <br />7 if (child instanceof String) { <br /><br />8 // trim whitespace from content text <br />9 String trimmed = child.toString().trim(); <br />10 if (trimmed.length() == 0) { <br /><br />11 // delete child if only whitespace (adjusting index) <br />12 children.remove(i--); <br /><br />13 } else { <br /><br />14 // wrap the trimmed content with new element <br />15 Element text = new Element("text", element.getNamespace()); <br />16 text.setText(trimmed); <br />17 children.set(i, text); <br /><br />18 } <br />19 } else if (child instanceof Element) { <br /><br />20 // handle child elements with recursive call <br />21 modifyElement((Element)child); <br /><br />22 } <br />23 } <br />24 } <br /><br /><br /><br />创建新元素的技术（第 14-17 行）非常简单，而且与 DOM 版本不同，它不需要访问父文档。 <br /><br />dom4j <br />dom4j 的顶级代码比 JDOM 的稍微复杂些，但是它们的代码行非常类似。这里的主要区别是我保存了用来构建 dom4j 文档表示的 DocumentFactory（第 5 行），并在输出已修改的文档文本之后刷新了 writer（第 10 行）。 <br /><br />清单 6. dom4j 的顶级代码 <br />1 // parse the document from input stream <br />2 SAXReader reader = new SAXReader(false); <br />3 Document doc = reader.read(in); <br /><br />4 // recursively walk and modify document <br />5 m_factory = reader.getDocumentFactory(); <br />6 modifyElement(doc.getRootElement()); <br /><br />7 // write the document to output stream <br />8 XMLWriter writer = new XMLWriter(out); <br />9 writer.write(doc); <br />10 writer.flush(); <br /><br /><br /><br />正如您在清单 6 中看到的，dom4j 使用一个工厂方法来构造文档表示（从语法分析构建）中包含的对象。根据接口来定义每个组件对象，所以实现其中一个接口的任何类型的对象都能包含在表示中（与 JDOM 相反，它使用具体类：这些类在某些情况中可以划分子类和被继承，但是在文档表示中使用的任何类都需要以原始 JDOM 类为基础）。通过使用不同工厂进行 dom4j 文档构建，您能获取不同系列的组件中构造的文档。 <br /><br />在样本代码（第 5 行）中，我检索了用于构建文档的（缺省）文档工厂，并将它存储在一个实例变量（m_factory）中以供 modify 方法使用。并不严格需要这一步 — 可以在一个文档中同时使用来自不同工厂的组件，或者可以绕过工厂而直接创建组件的实例 — 但在该例中，我只想创建与在文档其余部分中使用的同一类型的组件，并且使用相同的工厂来确保完成这个步骤。 <br /><br />清单 7. dom4j modify 方法 <br />1 protected void modifyElement(Element element) { <br /><br />2 // loop through child nodes <br />3 List children = element.content(); <br />4 for (int i = 0; i &lt; children.size(); i++) { <br /><br />5 // handle child by node type <br />6 Node child = (Node)children.get(i); <br />7 if (child.getNodeType() == Node.TEXT_NODE) { <br /><br />8 // trim whitespace from content text <br />9 String trimmed = child.getText().trim(); <br />10 if (trimmed.length() == 0) { <br /><br />11 // delete child if only whitespace (adjusting index) <br />12 children.remove(i--); <br /><br />13 } else { <br /><br />14 // wrap the trimmed content with new element <br />15 Element text = m_factory.createElement <br />16 (QName.get("text", element.getNamespace())); <br />17 text.addText(trimmed); <br />18 children.set(i, text); <br /><br />19 } <br />20 } else if (child.getNodeType() == Node.ELEMENT_NODE) { <br /><br />21 // handle child elements with recursive call <br />22 modifyElement((Element)child); <br /><br />23 } <br />24 } <br />25 } <br /><br /><br /><br />清单 7 中 dom4j modify 方法与 JDOM 中使用的方法非常类似。不通过使用 instanceof 运算符来检查内容项的类型，我可以通过 Node 接口方法 getNodeType 来获取类型代码（也可以使用 instanceof，但类型代码方法看起来更清晰）。通过使用 QName 对象来表示元素名称和通过调用已保存的工厂的方法来构建元素可以区别新元素的创建技术（第 15-16 行）。 <br /><br />Electric XML <br />清单 8 中 Electric XML（EXML）的顶级代码是任何这些示例中最简单的一个，通过单一方法调用就可以读取和编写文档。 <br /><br />清单 8. EXML 顶级代码 <br />1 // parse the document from input stream <br />2 Document doc = new Document(in); <br /><br />3 // recursively walk and modify document <br />4 modifyElement(doc.getRoot()); <br /><br />5 // write the document to output stream <br />6 doc.write(out); <br /><br /><br /><br />清单 9 中 EXML modify 方法尽管与 JDOM 一样，需要使用 instanceof 检查，但它与 DOM 方法最相似。在 EXML 中，无法创建一个带名称空间限定的名称的元素，所以取而代之，我创建新元素，然后设置其名称来达到相同的效果。 <br /><br />清单 9. EXML modify 方法 <br />1 protected void modifyElement(Element element) { <br /><br />2 // loop through child nodes <br />3 Child child; <br />4 Child next = element.getChildren().first(); <br />5 while ((child = next) != null) { <br /><br />6 // set next before we change anything <br />7 next = child.getNextSibling(); <br /><br />8 // handle child by node type <br />9 if (child instanceof Text) { <br /><br />10 // trim whitespace from content text <br />11 String trimmed = ((Text)child).getString().trim(); <br />12 if (trimmed.length() == 0) { <br /><br />13 // delete child if only whitespace <br />14 child.remove(); <br /><br />15 } else { <br /><br />16 // wrap the trimmed content with new element <br />17 Element text = new Element(); <br />18 text.addText(trimmed); <br />19 child.replaceWith(text); <br />20 text.setName(element.getPrefix(), "text"); <br /><br />21 } <br />22 } else if (child instanceof Element) { <br /><br />23 // handle child elements with recursive call <br />24 modifyElement((Element)child); <br /><br />25 } <br />26 } <br />27 } <br /><br /><br /><br />XPP <br />XPP 的顶级代码（在清单 10 中）是所有示例中最长的一个，与其它模型相比，它需要相当多的设置。 <br /><br />清单 10. XPP 顶级代码 <br />1 // parse the document from input stream <br />2 m_parserFactory = XmlPullParserFactory.newInstance(); <br />3 m_parserFactory.setNamespaceAware(true); <br />4 XmlPullParser parser = m_parserFactory.newPullParser(); <br />5 parser.setInput(new BufferedReader(new InputStreamReader(in))); <br />6 parser.next(); <br />7 XmlNode doc = m_parserFactory.newNode(); <br />8 parser.readNode(doc); <br /><br />9 // recursively walk and modify document <br />10 modifyElement(doc); <br /><br />11 // write the document to output stream <br />12 XmlRecorder recorder = m_parserFactory.newRecorder(); <br />13 Writer writer = new OutputStreamWriter(out); <br />14 recorder.setOutput(writer); <br />15 recorder.writeNode(doc); <br />16 writer.close(); <br /><br /><br /><br />因为使用 JAXP 接口，所以我必须首先创建分析器工厂的实例并在创建分析器实例之前启用名称空间处理（第 2-4 行）。一旦获取了分析器实例，我就能将输入设置到分析器中，并真正构建文档表示（第 5-8 行），但是这涉及比其它模型更多的步骤。 <br /><br />输出处理（第 11-16 行）也涉及比其它模型更多的步骤，主要因为 XPP 需要 Writer 而不是直接将 Stream 作为输出目标接受。 <br /><br />清单 11 中 XPP modify 方法尽管需要更多代码来创建新元素（第 13-21 行），但它与 JDOM 方法最类似。名称空间处理在这里有点麻烦。我首先必须创建元素的限定名称（第 15-16 行），然后创建元素，最后在稍后设置名称和名称空间 URI（第 18-21 行）。 <br /><br />清单 11. XPP modify 方法 <br />1 protected void modifyElement(XmlNode element) throws Exception { <br /><br />2 // loop through child nodes <br />3 for (int i = 0; i &lt; element.getChildrenCount(); i++) { <br /><br />4 // handle child by node type <br />5 Object child = element.getChildAt(i); <br />6 if (child instanceof String) { <br /><br />7 // trim whitespace from content text <br />8 String trimmed = child.toString().trim(); <br />9 if (trimmed.length() == 0) { <br /><br />10 // delete child if only whitespace (adjusting index) <br />11 element.removeChildAt(i--); <br /><br />12 } else { <br /><br />13 // construct qualified name for wrapper element <br />15 String prefix = element.getPrefix(); <br />16 String name = (prefix == null) ? "text" : (prefix + ":text"); <br /><br />17 // wrap the trimmed content with new element <br />18 XmlNode text = m_parserFactory.newNode(); <br />19 text.appendChild(trimmed); <br />20 element.replaceChildAt(i, text); <br />21 text.modifyTag(element.getNamespaceUri(), "text", name); <br /><br />22 } <br />23 } else if (child instanceof XmlNode) { <br /><br />24 // handle child elements with recursive call <br />25 modifyElement((XmlNode)child); <br /><br />26 } <br />27 } <br />28 } <br /><br /><br /><br />结束语 <br />DOM、dom4j 和 Electric XML 都得到这些几乎同样易于使用的代码样本，其中 EXML 可能最简单，而 dom4j 受一些小条件限制而较困难。DOM 提供了与语言无关的非常实在的好处，但是如果你只使用 Java 代码，那么通过与 Java 特定的模型相比较，它看上去有点麻烦。我认为这表明 Java 特定的模型通常成功地实现简化 Java 代码中的 XML 文档处理这个目标。 <br /><br />超越基础：真实世界可用性 <br />代码样本显示 JDOM 和 EXML 为基本文档操作（使用元素、属性和文本）提供了简单和清晰的接口。根据我的经验，它们的方法并不能很好地完成处理整个文档表示的编程任务。要完成这些类型的任务，DOM 和 dom4j 使用的组件方法 — 其中从属性到名称空间的所有文档组件实现一些公共接口 — 工作得更好。 <br />相关的例子是最近我为 JDOM 和 dom4j 实现的 XML 流型（XML Streaming (XMLS) ）编码。这个代码遍历整个文档并编码每个组件。JDOM 实现比 dom4j 实现复杂得多，主要是因为 JDOM 使用一些没有公共接口的独特类来表示每个组件。 <br /><br />因为 JDOM 缺少公共接口，所以即使处理 Document 对象的代码与处理 Element 对象的代码都有一些诸如子组件那样相同类型的组件，但是它们必须有所不同。还需要特殊方法来检索与其它类型的子组件相对的 Namespace 组件。甚至当处理被认为是内容的子组件类型时，您需要在组件类型上使用多个带 instanceof 检查的 if 语句，而不是使用一条更清晰更快速的 switch 语句。 <br /><br />具有讽刺意味的可能是 JDOM 的最初目标之一是利用 Java Collection 类，这些类本身在很大程度上以接口为基础。库中接口的使用增加了许多灵活性，而这是以增加了一些复杂性为代价的，并且这对于为重用而设计的代码来说，通常是一个很好的折衷。这可能还主要归功于 dom4j，它达到一个成熟并且稳定的状态，比 JDOM 要快得多。 <br /><br />尽管如此，对于使用多种语言的开发人员来说，DOM 仍是一个非常好的选择。DOM 实现广泛应用于多种编程语言。它还是许多其它与 XML 相关的标准的基础，所以即使您使用 Java 特定的模型，也还有一个您逐步熟悉 DOM 所需要的好机会。因为它正式获得 W3C 推荐（与基于非标准的 Java 模型相对），所以在某些类型的项目中可能也需要它。 <br /><br />就使用方便这一范畴而言，在 JDOM、dom4j 和 Electric XML 这三个主要竞争者中，dom4j 与其它两个的区别在于它使用带有多个继承层的基于接口的方法。这会使得遵循 API JavaDocs 更为困难些。例如，您正在寻找的一个方法（例如 content()，在我们 dom4j 的 modify 方法示例的第 3 行中使用的）可能是 Element 扩展的 Branch 接口的一部分，而不是 Element 接口本身的一部分。尽管如此，这种基于接口的设计添加了许多灵活性（请参阅侧栏超越基础：真实世界可用性）。考虑到 dom4j 的性能、稳定性和特性设置的优点，您应把它当作多数项目中的一个有力的候选者。 <br /><br />在任一 Java 特定的文档模型之中，JDOM 可能拥有最广泛的用户基础，并且它的确是使用起来最简单的模型之一。尽管如此，作为项目开发的一个选择，它还是必须容忍 API 的不固定性和从一个版本到下一个版本的更新，在性能对比中它也表现得很糟糕。基于当前实现，我愿为着手新项目的人们推荐 dom4j，而不是 JDOM。 <br /><br />除了 XPP 以外，EXML 比其它任何模型占用的资源都要少得多，并且考虑到 EXML 易于使用的优点，您应肯定会认为它适用于 jar 文件大小很重要的应用程序。但是，EXML 的 XML 支持的局限性和受限的许可证，以及在较大文件上所表现出的相对拙劣的性能，不得不在许多应用程序中放弃使用它。 <br /><br />XPP 在语法分析和编写文本文档时需要更多步骤，并且在处理名称空间时也需要更多步骤。如果 XPP 打算添加一些便利的方法来处理其中一些常见情况，那么在对比中它可能会更胜一筹。正如它现在所表现的，上篇文章中性能方面的领先者却成了本文中的可用性方面的失败者。尽管如此，因为 XPP 性能方面的优势，所以对于需要较小的 jar 文件大小的应用程序还是值得将它作为 EXML 的替代方法。 <br /><br />下一次... <br />到目前为止在我写的两篇文章中，涉及到用 Java 编写的 XML 文档模型的性能和可用性。在本系列的后两篇文章中，我将讨论用 Java 技术进行 XML 数据绑定的方法。这些方法与文档模型的方法有许多相似处，但是它们更进一步将 XML 文档映射到实际应用程序数据结构中。我们将看到这一操作在使用的简便性和提高性能方面是如何做得如此好的。 <br /><br />回到 developerWorks，检查 Java 代码的 XML 数据绑定的实质。同时，您可以通过下面链接的论坛，给出您对本文的评论和问题。 <br /><br />参考资料 <br /><br />单击本文顶部或底部的讨论，参与本文的论坛。 <br />如果您需要背景资料，尝试 developerWorks 的 XML Java 编程 教程、理解 SAX 教程和 理解 DOM 教程。 <br />从下载页面下载本文中使用的测试程序和文档模型库。 <br />查找有关 Java APIs for XML Processing (JAXP) 或者阅读 JAXP Tutorial。 <br />获取作者有关 XML Streaming 的著作的详细信息，它作为程序间传送 XML 文档的 Java 序列化的另一种选择。 <br />回顾作者以前的文章：XML in Java: Document models, part 1。 <br />根据 Tony Darugar 的团队对几个大型 XML 项目的分析，参考他对 Effective DOM with Java 的建议。 <br />Java 的 XML 文档模型： <br />Xerces Java <br />Crimson <br />JDOM <br />dom4j <br />Electric XML <br />XML Pull Parser (XPP) <br /><br /><br /><br />原文：<a href="http://www.daima.com.cn/Info/127/Info37907/">http://www.daima.com.cn/Info/127/Info37907/</a><img src ="http://www.blogjava.net/hopeshared/aggbug/38840.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-04-03 10:30 <a href="http://www.blogjava.net/hopeshared/archive/2006/04/03/38840.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：MSN Messenger 6.0标签的自定义与开发</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/21/36599.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Tue, 21 Mar 2006 04:32:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/21/36599.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/36599.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/21/36599.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/36599.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/36599.html</trackback:ping><description><![CDATA[MSN Messenger 6.0 这个版本中相比以前增加了很多激动人心的功能，如：更漂亮的界面，更多的表情符号，可设置个人头像，聊天窗口的背景图片支持，P2P功能支持，联机游戏等，在这个版本中增强了的标签功能，对于广大 MSN Messenger 爱好者来说，尤其带来了许多乐趣，本文将主要对 MSN Messenger 的标签的使用和开发进行探讨。<br /><br /><br />什么是 MSN Messenger 标签？ <br /><br /><br />MSN Messenger 标签是对 MSN Messenger 功能的一种扩展，如图1所示。MSN Messenger 已经内置了许多功能扩展，如 MSN Alerts等 .NET My Services ，除此之外，还将 www.msn.com 提供的许多个性服务都集成到 MSN Messenger 里面了，如股票行情、新闻、旅游、汽车、购物、游戏等。 <br /><br /><br /><br />除了这些内置的标签之外，我们还可以增加自己特殊的标签，将常用的一些在线功能（阅读新闻、查看天气预报等）集成到 MSN Messenger 中，将其变为一个功能丰富而强大的客户端工具。笔者曾经见到过 Microsoft 演示新加坡政府的电子政务系统，就是将许多客户端的功能置入 MSN Messenger 的标签中，为工作人员提供了一个使用极其方便的工具。 <br /><br /><br /><br />（注：默认情况下，MSN Messenger 标签只有两个，要想显示全部内置标签，用户需要在 www.passport.net 登录，并在Member Services 中修改 Profile 文件，将自己的 Country/Region 修改为美国，或者直接下载此文件： <img alt="::URL::" hspace="2" src="http://www.blogcn.com/images/aurl.gif" align="absBottom" border="0" /><a href="http://www.china-actuary.com/temp/configcache.zip" target="_blank">http://www.china-actuary.com/temp/configcache.zip</a>  ，解开后覆盖原配置文件即可，原配置文件的路径在下文列出） <br /><br /><br /><br /><br />如何增加标签 <br /><br /><br /><br /><br />MSN Messenger 中所有标签都是定义在一个 XML 配置文件中，这个文件位于 C:\Documents and Settings\[User]\Application Data\Microsoft\MSN Messenger\[Passport ID] 目录下，如果安装的是 MSN Messenger 6.0中文版，则可能位于 C:\Documents and Settings\[USER]\Application Data\Microsoft\MSN Messenger下面的目录名是一串数字的子目录中，文件名为 ConfigCache.xml（注： [User] 是登录 Windows 系统的用户名，Passport ID 是指登录 MSN Messenger 的ID，如 muslem@msn.com，另外，如果你的 Windows 系统安装在 C: 以外的其它驱动器，则上述路径要做相应改变）。<br /><br /><br />打开这个 XML 文件，就可以看到在 \MsgrConfig\TabConfig\msntabdata 节点内就是所有标签的定义，现举例对其中的主要元素加以说明： <br /><br /><br /><br />&lt;tab&gt;<br />  &lt;image&gt;res:moneyimage&gt;<br />  &lt;name&gt;CNBC on MSN Moneyname&gt;<br />  &lt;tooltip&gt;CNBC on MSN Moneytooltip&gt;<br />  &lt;contenturl&gt;<img alt="::URL::" hspace="2" src="http://www.blogcn.com/images/aurl.gif" align="absBottom" border="0" /><a href="http://moneycentral.msn.com/messenger/tab.aspxcontenturl&gt;" target="_blank">http://moneycentral.msn.com/messenger/tab.aspxcontenturl&gt;</a><br />&lt;/tab&gt; <br /><br /><br /><br /><br />了解了标签的定义方式后，我们就可以定义自己的标签了，考虑到 MSN Messenger 窗口的大小和 PocketPC 或 Palm 等PDA 类似，所以适合 PDA 的网站置入到标签中比较合适。例如我们把新浪站点的 PDA版（<img alt="::URL::" hspace="2" src="http://www.blogcn.com/images/aurl.gif" align="absBottom" border="0" /><a href="http://pda.sina.com.cn）做成一个标签，则应定义如下一个/" target="_blank">http://pda.sina.com.cn）做成一个标签，则应定义如下一个</a>  XML 结点：<br /><br /><br />&lt;tab&gt;<br />   &lt;image&gt;res:bellimage&gt;<br />   &lt;name&gt;SINA PDAname&gt;<br />   &lt;tooltip&gt;SINA PDA Sitetooltip&gt;<br />   &lt;contenturl&gt;<img alt="::URL::" hspace="2" src="http://www.blogcn.com/images/aurl.gif" align="absBottom" border="0" /><a href="http://pda.sina.com.cncontenturl&gt;/" target="_blank">http://pda.sina.com.cncontenturl&gt;</a><br />&lt;/tab&gt;<br /><br /><br /><br /><br />然后将结点定义的全部文本插入到配置文件的 \MsgrConfig\TabConfig\msntabdata 节点里面，注销并重新登录 MSN Messenger ，则效果图2所示。<br /><br />除此之外，在 PDA 上很受欢迎的 AvantGO 频道都可以作为标签内容的来源，在 <img alt="::URL::" hspace="2" src="http://www.blogcn.com/images/aurl.gif" align="absBottom" border="0" /><a href="http://www.avantgo.com/" target="_blank">http://www.avantgo.com</a>  中注册登录后，会列出各种各样的频道及其 URL 地址，然后就可以自己的需要选择利用了。<br /><br /><br />如何开发标签<br /><br /><br /><br /><br />除了引用已有的站点，如AvantGO频道或PDA站点外，能不能制做自己的功能标签呢？答案是完全肯定的，可以看到，标签配置中的 ContentURL 既可以引用外部 URL 资源，也可以引用本地资源，最常用的就是引用本地的 HTML 文件。<br /><br />利用 HTML 文件，你的所有与 HTML 有关的开发技术/技巧都能派上用场，如CSS、HTML Component、VBScript/JavaScript、Java Applet、DHTML，ActiveX，MSXML（包括XMLHttp组件），XSLT等。如果是开发一个内容采集（如新闻或天气预报信息抓取）的标签，则一般要经过以下步骤：<br /><br />1、寻找内容来源，此页面一般要求格式相对固定，如新浪的滚动新闻页面等 <br /><br /><br /><br />2、利用 MSXML 包中的 XMLHttp 对象来获取内容 <br /><br /><br /><br />3、在本地利于脚本结合 HTML DOM（文档对象模型）技术对获取的内容进行分析和过滤，取出有用内容 <br /><br /><br /><br />4、以适合的方式显示这些的内容，并可以结合 CSS 进行内容的格式化 <br /><br /><br /><br /><br />另外，如果引用本地的 HTML 文件，则脚本安全性级别较低，对本地文件系统进行读写是允许的，因此开发一些具有高级特性的应用，如本地缓存内容，显示本计算机的某些信息（如 Outlook 的邮件，联系人列表等）。<br /><br /><br />下图是笔者开发的一个标签的实例，主要功能是查阅新闻、电视节目单、天气预报等，可以从 <img alt="::URL::" hspace="2" src="http://www.blogcn.com/images/aurl.gif" align="absBottom" border="0" /><a href="http://www.china-actuary.com/temp/rordown.asp" target="_blank">http://www.china-actuary.com/temp/rordown.asp</a>  下载，通过阅读里面的内容和程序脚本来进一步了解如何开发标签。<br /><br /><br />除此之外，可以利于 Windows 系统和第三方丰富的组件和控件，尝试进行一些MSN Messenger 标签的高级开发，如利用 JMail 组件来读取 POP3 邮箱的内容或发送邮件；利用通讯网关发送手机短消息；利用 XML 解析器来读取和显示 RSS 内容；甚至可以通过 MSXML 中的 XMLHTTP 来读取 Hotmail/MSN 邮箱中的内容，使用 ADO 来存取数据库信息等，当然开发人员也可以将此功能延伸到企业应用开发中，如小组协作，日程安排等。<br /><br /><br />开发标签注意事项<br /><br /><br /><br /><br />1、图标的引用<br /><br />标签的图标即可以采用内置的资源图标，也可以引用外部（URL形式，如 <img alt="::URL::" hspace="2" src="http://www.blogcn.com/images/aurl.gif" align="absBottom" border="0" /><a href="http://www.china-actuary.com/temp/news.png）的" target="_blank">http://www.china-actuary.com/temp/news.png）的</a>  PNG 文件（必须是32位色），图标大小为 32X32，也可以使用 ResHacker 等工具将自己的图标加入到 MSN Messenger 可执行文件 MsnMsgr.exe 中，然后使用 res:[ID] 的方式来引用。 <br /><br /><br /><br />2、界面开发<br /><br />由于标签的内容显示在 MSN Messenger 窗口的内部，所以在界面开发时要注意以下原则：<br /><br />1) 标签中元素的采用相对大小，而非绝对大小。这样当 MSN Messenger 窗口缩放的时候，里面的内容能够自适应地进行调整。此外如果可能，对超过窗口宽度的内容进行手工缩放（在笔者的例子里，对新闻中的图片进行了缩放，以便能够在任意大小的窗口都看到全图），从而避免出现水平滚动条。<br /><br />2) 使用小字体，以显示更多内容<br /><br />3) 使用元素的边框和间隔不要太大，不要采用较大的图片按钮<br /><br />4) 使用简单控件<br /><br />5) 尽量避免重新刷新整个页面<br /><br />3、程序的开发与调试<br /><br />可以使用常用的 HTML/VBScript/Jscript 开发工具，结合 Internet Explorere 进行测试，测试无误后再加入到 MSN Messenger 的标签中，要不然每次更改页面后都要重新登录 MSN Messenger ，无疑是一件很痛苦的事。 <br /><br /><br /><br />4、配置文件与标签图标<br /><br />经常发现登录 MSN Messenger 后，除了最上面好友列表的标签外，其它标签到都不见了，此问题一般都是错误地修改 ConfigCache.xml 文件而使其成为一个不合法的 XML 文件导致的。所以在修改此文件后，最好使用 Internet Explorer 打开查看一下，以确认 XML 文件的格式无误。 <br /><br /><br /><br />另外，有时候即使不修改配置文件，标签也会“消失”，这是由于 MSN Messenger 自动更新配置文件而覆盖和自定义的标签，要防止此类情况，最好将配置文件中的过期设置元素的值改为0，即：0。<br /><br /><br />另外，msn shell也可以实现此功能，而且功能更为强大<br /><br /><br /><br />原文地址：<a href="http://www.blogcn.com/user31/nhdj/blog/7190344.html">http://www.blogcn.com/user31/nhdj/blog/7190344.html</a><img src ="http://www.blogjava.net/hopeshared/aggbug/36599.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-03-21 12:32 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/21/36599.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>概述：在线园艺零售商 Web service 方案</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/15/35417.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Wed, 15 Mar 2006 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/15/35417.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/35417.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/15/35417.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/35417.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/35417.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文档取自IBM Websphere的帮助文档														此方案集由名为				 Plants by WebSphere 				的在线零售商设计。				Plants by WebSphere 				使用				 WebSphere Application Server 				中的				 Web service 			...&nbsp;&nbsp;<a href='http://www.blogjava.net/hopeshared/archive/2006/03/15/35417.html'>阅读全文</a><img src ="http://www.blogjava.net/hopeshared/aggbug/35417.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-03-15 15:34 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/15/35417.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转:使用Google的Web Service</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/15/35401.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Wed, 15 Mar 2006 05:35:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/15/35401.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/35401.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/15/35401.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/35401.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/35401.html</trackback:ping><description><![CDATA[一、简介 <BR>Google搜索引擎提供了基于SOAP的Web Service。这意味着不同的开发语言、开发环境都能够使用这种服务，另外，Google为了简化Java程序员的开发，它还提供了一套Java API接口，用于访问Web Serivce，这使得开发一套支持Google搜索功能的程序变得十分容易。开发人员可以将Google的搜索功能嵌入到他们的应用程序当中。本文将介绍如何使用这些Java API，以及如何使用google的Web Service。 <BR>目前，Google的API还是处于Beta测试阶段，因此，在API的使用上还有一些限制。例如，使用这些API需要申请一个帐号。对于免费申请的帐号，为了防止开发人员不正当的使用，限制每个帐号、每天最多只能查询1000次。目前，Google还没有提出正式的运营模式和收费方式。 <BR>Google API目前主要提供三种服务，分别如下： <BR>1)搜索服务。用户提交一个搜索内容请求，Google服务器将查找超过20亿的网页内容，并且将满足用户需求的目标返回给用户，一般来说这种处理只需要几秒的时间。 <BR>2)缓存服务(Cache)。用户提交一个URL，Google服务器将返回搜索器访问该URL的最新记录。这样，用户很容易复用Google的搜索成果。 <BR>3)拼写检查。该功能用于检查用户的查询请求是否有效。用户提交一个检查内容，Google服务器将返回有效的、最接近该内容的查询请求，返回的查询请求将符合Google的查询规则。 <BR><BR>二、相关准备工作 <BR>为了开发基于Java的程序，我们需要作如下准备工作。 <BR>1)建立Java开发环境，Java SDK 1.3.1 或更新的版本。相关地址http:.//java.sun.com/J2SE <BR>2)下载Google API的Jar文件，Googleapi.jar。开发人员可以到<A href="http://www.google.com/apis">http://www.google.com/apis</A> 下载。<BR>3)申请Google访问帐号，目前Google支持免费申请的方式，用户需要使用一个email帐号来获取新帐号。注意，目前免费帐号支持每天1000条的最大查询量。申请地址：<A href="http://www.google.com/apis">http://www.google.com/apis</A> 。<BR>4)(可选) 如果用户需要通过Java程序直接发送SOAP请求，开发人员需要下载相关的Java 软件包，JAXM。下载地址：<A href="http://java.sun.com/xml" target=_blank>http://java.sun.com/xml </A><BR><BR>三、SOAP的使用 <BR>Google提供了基于SOAP的Web Service，因此用户可以向Google服务器提交SOAP的查询请求，而后Google服务器将处理这些请求，并返回SOAP格式的查询结果。以下就是一个查询请求和查询结果。 <BR>查询：例如，有一个查询请求，请求类型为搜索，查询的内容为“world cup”，帐号为“123456789”，如下为该查询的一个例子。 <BR>&lt;?xml version='1.0' encoding='UTF-8'?&gt; <BR>&lt;SOAP-ENV:Envelope <BR>xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" <BR>xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" <BR>xmlns:xsd="http://www.w3.org/1999/XMLSchema"&gt; <BR>&lt;SOAP-ENV:Body&gt; <BR>&lt;ns1: doGoogleSearch xmlns:ns1="urn:GoogleSearch" <BR>SOAP-ENV:encodingStyle= <BR>"http://schemas.xmlsoap.org/soap/encoding/"&gt; <BR>&lt;key xsi:type="xsd:string"&gt;123456789&lt;/key&gt; <BR>&lt;q xsi:type="xsd:string"&gt;"World Cup"&lt;/q&gt; <BR>&lt;start xsi:type="xsd:int"&gt;0&lt;/start&gt; <BR>&lt;maxResults xsi:type="xsd:int"&gt;10&lt;/maxResults&gt; <BR>&lt;filter xsi:type="xsd:boolean"&gt;true&lt;/filter&gt; <BR>&lt;restrict xsi:type="xsd:string"&gt;&lt;/restrict&gt; <BR>&lt;safeSearch xsi:type="xsd:boolean"&gt;false&lt;/safeSearch&gt; <BR>&lt;lr xsi:type="xsd:string"&gt;&lt;/lr&gt; <BR>&lt;ie xsi:type="xsd:string"&gt;latin1&lt;/ie&gt; <BR>&lt;oe xsi:type="xsd:string"&gt;latin1&lt;/oe&gt; <BR>&lt;/ns1: doGoogleSearch &gt; <BR>&lt;/SOAP-ENV:Body&gt; <BR>&lt;/SOAP-ENV:Envelope&gt; <BR>查询结果：如果我们执行以上查询，可以得到如下的查询结果。其中，查询结果总共有约2660000记录，使用时间为0.125012秒。Item标记代表一条查询结果，在第一条查询结果中，网站的URL为<A href="http://www.fifaworldcup.com">http://www.fifaworldcup.com</A> 。本例子仅仅列举了一条查询结果。<BR><BR>&lt;?xml version="1.0" encoding="UTF-8"?&gt; <BR>&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" <BR>xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" <BR>xmlns:xsd="http://www.w3.org/1999/XMLSchema"&gt; <BR>&lt;SOAP-ENV:Body&gt; <BR>&lt;ns1:doGoogleSearchResponse <BR>xmlns:ns1="urn:GoogleSearch" <BR>SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt; <BR>&lt;return xsi:type="ns1:GoogleSearchResult"&gt; <BR>&lt;documentFiltering xsi:type="xsd:boolean"&gt;false&lt;/documentFiltering&gt; <BR>&lt;estimatedTotalResultsCount xsi:type="xsd:int"&gt; <BR>2660000&lt;/estimatedTotalResultsCount&gt; <BR>&lt;directoryCategories <BR>xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/" <BR>xsi:type="ns2:Array" <BR>ns2:arrayType="ns1:DirectoryCategory[0]"&gt; <BR>&lt;/directoryCategories&gt; <BR>&lt;searchTime xsi:type="xsd:double"&gt;0.125012&lt;/searchTime&gt; <BR>&lt;resultElements <BR>xmlns:ns3="http://schemas.xmlsoap.org/soap/encoding/" <BR>xsi:type="ns3:Array" ns3:arrayType="ns1:ResultElement[10]"&gt; <BR><BR>&lt;item xsi:type="ns1:ResultElement"&gt; <BR>&lt;cachedSize xsi:type="xsd:string"&gt;10k&lt;/cachedSize&gt; <BR>&lt;hostName xsi:type="xsd:string"/&gt; <BR>&lt;snippet xsi:type="xsd:string"&gt;&lt;/snippet&gt; <BR>&lt;directoryCategory xsi:type="ns1:DirectoryCategory"&gt; <BR>&lt;specialEncoding xsi:type="xsd:string"/&gt; <BR>&lt;/directoryCategory&gt; <BR>&lt;relatedInformationPresent xsi:type="xsd:boolean"&gt; <BR>true <BR>&lt;/relatedInformationPresent&gt; <BR>&lt;summary xsi:type="xsd:string"&gt; <BR>The official site from FIFA, made by Yahoo. Daily news updates and loads of relevant information. <BR>&lt;/summary&gt; <BR>&lt;URL xsi:type="xsd:string"&gt; <BR>http://www.fifaworldcup.com <BR>&lt;/URL&gt; <BR>&lt;title xsi:type="xsd:string"&gt; <BR>2002 FIFA &lt;b&gt;World&lt;/b&gt;b&gt;Cup&lt;/b&gt;(tm) <BR>&lt;/title&gt; <BR>&lt;/item&gt; <BR>... <BR>&lt;/resultElements&gt; <BR>&lt;endIndex xsi:type="xsd:int"&gt;10&lt;/endIndex&gt; <BR>&lt;searchTips xsi:type="xsd:string"/&gt; <BR>&lt;searchComments xsi:type="xsd:string"/&gt; <BR>&lt;startIndex xsi:type="xsd:int"&gt;1&lt;/startIndex&gt; <BR>&lt;estimateIsExact xsi:type="xsd:boolean"&gt;false&lt;/estimateIsExact&gt; <BR>&lt;searchQuery xsi:type="xsd:string"&gt; <BR>"science fiction" <BR>&lt;/searchQuery&gt; <BR>&lt;/return&gt; <BR>&lt;/ns1:doGoogleSearchResponse&gt; <BR>&lt;/SOAP-ENV:Body&gt; <BR>&lt;/SOAP-ENV:Envelope&gt; <BR><BR><BR>四、使用Google API <BR>Google为了提高开发人员的开发效率，提供了一套基于Java的API，用于直接访问Google服务器。该API包装了Google的Web Service，使用起来比Web Service更加方便。该API 包主要包括如下几个类： <BR>GoogleSearch ：该类封装了对于Google服务器的访问，支持搜索和缓存(cache)功能。 <BR>GoogleSearchDirectoryCategory ：表示Google中的一个分类目录 <BR>GoogleSearchFault ：该类为Exception的一个子类，用于表示API使用的错误。 <BR>GoogleSearchResult ：该类封装了搜索的结果。 <BR>GoogleSearchResultElement：该类表示搜索结果中的每一条记录。 <BR><BR>如下是一个例子的源代码，用于一个简单查询，查询请求为”world Cup”，查询的帐号为”123456789”。该例子将把查询结果打印出来。如果用户需要近一步解析查询结果，可以使用GoogleSearchResult和GoogleSearchResultElement类。 <BR><BR>import com.google.soap.search.*; <BR>import java.io.*; <BR><BR>public class GoogleAPITest { <BR><BR>public static void main(String[] args) { <BR><BR>String clientKey="123456789"; <BR>String query="Word Cup"; <BR>// Create a Google Search object, set our authorization key <BR>GoogleSearch s = new GoogleSearch(); <BR>s.setKey(clientKey); <BR>try { <BR>s.setQueryString(query); <BR>GoogleSearchResult r = s.doSearch(); <BR>System.out.println("Google Search Results:"+t.toString()); <BR>} catch (GoogleSearchFault f) { <BR>System.out.println("The call to the Google Web APIs failed:"+f.toString()); <BR>} <BR>} <BR>} <BR><BR>五、总结 <BR>Google搜索引擎提供了搜索、缓存和拼写简单功能，通过Web Service可以在不同的平台、不同的语言使用，为了简化SOAP的开发，Google还提供了包装了SOAP服务的Java API，这也大大提高了Java程序员的开发效率。随着搜索内容、形式的丰富，更多的开发人员可以将这种技术运用在自己应用程序中，扩展应用程度的功能。 <BR><img src ="http://www.blogjava.net/hopeshared/aggbug/35401.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-03-15 13:35 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/15/35401.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>将String转换成InputStream</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/14/35224.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Tue, 14 Mar 2006 05:46:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/14/35224.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/35224.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/14/35224.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/35224.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/35224.html</trackback:ping><description><![CDATA[String &nbsp; str &nbsp; = &nbsp; "";//add &nbsp; your &nbsp; string &nbsp; content<BR>InputStream &nbsp; inputStream &nbsp; = &nbsp; new &nbsp; ByteArrayInputStream(str.getBytes());<BR><img src ="http://www.blogjava.net/hopeshared/aggbug/35224.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-03-14 13:46 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/14/35224.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>读入文件流，转换成String</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/14/35222.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Tue, 14 Mar 2006 05:42:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/14/35222.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/35222.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/14/35222.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/35222.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/35222.html</trackback:ping><description><![CDATA[<P>FileInputStream in = new FileInputStream (wordPath);<BR>int i = in.available();&nbsp;<BR>byte abyte0[] = new byte[i];&nbsp;<BR>in.read(abyte0);&nbsp;<BR>in.close();&nbsp;<BR><BR>String s = new String(abyte0);&nbsp;</P><img src ="http://www.blogjava.net/hopeshared/aggbug/35222.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-03-14 13:42 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/14/35222.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：java开发中第三方包的使用方法</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/14/35212.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Tue, 14 Mar 2006 04:57:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/14/35212.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/35212.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/14/35212.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/35212.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/35212.html</trackback:ping><description><![CDATA[<P>java开发过程中，如果用到第三方的包文件，只有放置到正确的文件夹下，该包文件才能被正确的使用，下面是学习实践过程的总结<BR>如果在java程序开发的过程中用到第三方的包，如果已经打包成jar文件，则将该文件放到jdk\jre\lib\ext文件夹下，如果没有打包，则将文件夹放到jdk\lib文件夹下。<BR><BR>原文地址：<A href="http://jacksun.blogchina.com/595055.html">http://jacksun.blogchina.com/595055.html</A></P><img src ="http://www.blogjava.net/hopeshared/aggbug/35212.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-03-14 12:57 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/14/35212.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：网格基础概念FAQ</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/09/34446.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 09 Mar 2006 03:53:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/09/34446.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/34446.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/09/34446.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/34446.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/34446.html</trackback:ping><description><![CDATA[<P>网格基础概念，大家一起商榷。这是任浩、pphu、数字中国、Rose(王跃宣)等网友的共同工作成果，在此代表网格社区对他们表示感谢!</P>
<P><STRONG>1.什么是网格，网格的目标？<BR></STRONG>网格是从电力网格中借鉴过来的一个概念，原本是希望计算力和计算资源能够象电力一样，“打开电源开关就可以使用”，不用去关心是谁、如何提供的这些服务。<BR>网格的目标是抽象并量化计算资源，随时随地能够通过网络“定额”完成“定量”的计算相关的工作。</P>
<P><STRONG>2.网格的本质特征是什么？<BR></STRONG>网格的本质特征是：<BR>分布与资源共享：分布是网格最本源的特征，网格是通过集中分散的资源来完成计算的，资源的共享是一种集中资源的手段<BR>高度抽象：把计算力和所有的计算资源高度抽象成为用户可见的“电源接线板”，其它的东西对用户透明。<BR>自相似：在大尺度上和小尺度上有相同或者类似的规律<BR>动态性和多样性：和电力网格一样，用户的需求是变化的，所以动态性是网格需要考虑的一个基本问题<BR>自治性与管理的多重性：网格节点内部的自治和外部的受控整合是网格的一个特征，分层的资源需要层次化的管理，而分层来自于网格节点的归属问题和性能方面的考虑。</P>
<P><STRONG>3.网格计算中有哪些基础概念？<BR></STRONG>资源发现：资源发现是网格主动去发现可用的资源，并注册、管理这些资源的过程<BR>资源调度：如何充分利用网格收集的计算资源进行高效率地合理地使用是资源调度的研究范畴<BR>异构：是抽象的本源动力<BR>元计算：是关于“计算”的计算，就像“元语言”是描述语言的语言一样，元计算是为了进行计算而展开的计算活动，是网格对资源进行抽象的过程<BR>计算网格：网格的重心在解决高密度的计算问题的网格结构<BR>数据网格：重心在高密度、海量数据的储存和管理、使用过程的网格结构</P>
<P><STRONG>4.如何判断是否是网格<BR></STRONG>还没有网格的确切定义出炉，只能根据网格的一些基本特征来判断。P2P严格来说也是一种网格。<BR>根据Globus toolkit的创始人的观点，Globus就是网格，这个观点有些偏颇，姑且这么认为吧。</P>
<P><STRONG>5.网格目前有标准吗，有人在制定标准吗？<BR></STRONG>OGSI和OGSA是目前关于网格的一个标准</P>
<P><STRONG>6.能用非常简单的例子说明什么是网格吗</STRONG><BR>例如P2P，目前已经实现了储存共享，以解决服务器资源限制（带宽和服务能力）的问题，但是目前的P2P 计算还在热门研究中。它的特点是通过分布式的共享储存和带宽资源，也包括部分计算资源的共享来提高服务的可用性，已经包括了网格的几个主要特征。<BR>又如，国外一些如抗癌项目，充分发掘PC 的计算力来寻找治疗癌症的新药，也具有相应的特征，只不过其资源调度的粒度很粗，各节点的耦合很松散，只要计算任务和结果，节点只与中央节点交换数据，结构比较简单。<BR>&nbsp;<BR><STRONG>7.如何在应用领域体现网格的优势<BR></STRONG>高性能（大计算吞吐量）、低成本</P>
<P><STRONG>8.为什么要把网格比做电网</STRONG><BR>不要考虑计算力从什么地方来，只管用就行了。</P>
<P><STRONG>9.网格如何分类</STRONG></P>
<P>分为计算网格、数据网格、访问网格、信息网格、服务网格等</P>
<P><STRONG>10.何为元计算，与网格是什么关系</STRONG></P>
<P>元计算技术是将一组通过广域网连接起来的性质不同的计算资源集合起来，作为一个单独的计算环境向用户提供计算服务。元计算系统的目标是使用户能够共享其中的计算资源并以合作的方式进行计算。元计算前端主要解决最终用户通过统一的界面来使用广域网上各类计算资源的问题；元计算内核主要解决计算任务在广域网中各种超级计算机上协作完成的问题，提供一个完整的程序开发和运行环境。当用户提出计算请求时，计算问题的执行代码在系统内部的计算资源上进行合理的调度和管理，最后得出运行结果并通过元计算前端反馈给最终用户。一个良好的元计算系统主要由三个部分组成：一是尽量简单而又可靠的使用界面；二是资源管理系统;三是良好的编程模型和高效可靠的运行时环境。其中资源管理系统处于核心地位，它负责整个系统中各个资源的描述、变动、分配与保护，与运行时系统密切合作，保证整个系统的正常运行。资源管理系统需要为不同级别的系统管理员提供强有力的工具或界面来对这些资源进行控制和保护。<BR>元计算是网格计算的初级形态。</P>
<P><STRONG>11.什么是计算网格</STRONG><BR>强调计算力获取、管理等的网格</P>
<P><STRONG>12.什么是数据网格</STRONG><BR>强调数据存储、管理、传输、处理的网格</P>
<P><BR><STRONG>13.什么是信息网格<BR></STRONG>强调信息存储、管理、传输、处理的网格</P>
<P><STRONG>14.什么是存储网格</STRONG></P>
<P>强调数据存储的网格</P>
<P><STRONG>15.什么是知识网格<BR></STRONG>强调知识存储、管理、传输、处理的网格</P>
<P><STRONG>16.什么是语义网格</STRONG><BR>强调语义解析的网格，实现语义互操作</P>
<P><STRONG>17.什么是access&nbsp; grid <BR></STRONG>简单来讲，是一个项目。目的是为网格用户提供简单有效的视频、音频会议功能，为e-science提供基础的工具。 提供单一访问通道获取网格资源的网格。</P>
<P><STRONG>18.什么是grid portal</STRONG><BR>格网门户，提供格网单一访问通道</P>
<P><STRONG>19.什么是服务网格</STRONG><BR>强调应用服务集成的网格</P>
<P><STRONG>20.目前有哪些应用网格</STRONG><BR>美国科学格网（DOE Science Grid）、远程分布式计算与通信（Distance and Distributed Computing and Communication，DisCom2）和地球系统格网（Earth System Grid II，ESG）、TeraGrid和国家地震工程仿真格网（Network for Earthquake Engineering Simulation Grid，NEES Grid）、CrossGrid、天体物理虚拟天文台（Astronomical Virtual Observatory，***O）、英国国家格网（U.K. National Grid）、德国的计算资源统一接口项目（Uniform Interface to Computing Resources，UNICORE）、亚太地区格网（APGrid）</P>
<P><BR><STRONG>21.什么是P2P计算，与网格有什么关系<BR></STRONG>对等（P2P）计算是在Internet上实施分布式计算的新模式。在这种模式下，服务器与客户端的界限消失了，网络上的所有节点都可以“平等”共享其他节点的计算资源。IBM为对等下了如下定义：对等系统由若干互联协作的计算机构成，且至少具有如下特征之一：系统依存于边缘化（非中央式服务器）设备的主动协作，每个成员直接从其他成员而不是从服务器的参与中受益；系统中成员同时扮演服务器与客户机的角色；系统应用的用户能够意识到彼此的存在，构成一个虚拟或实际的群体。</P>
<P>网格采用P2P计算体系结构</P>
<P><STRONG>22.网格与分布式计算是什么关系</STRONG></P>
<P>格网计算是一种新的高性能的分布式计算方法。格网计算作为新一代的分布式计算方法，与传统分布式计算的主要区别在于在没有集中控制机制的情况下，通过对计算资源进行大规模共享，满足应用对高性能计算要求，并且这种对计算资源进行大规模共享是动态的、柔性的、安全的和协作式的。</P>
<P><STRONG>23.网格与中间件是什么关系<BR></STRONG>网格中间件（Gridware）就是一种在网格中应用的中间件<BR>现在的globus toolkit就是一种网格中间件，连接异质的主机节点构成globus网格</P>
<P><STRONG>24.什么是web service，网格与web service是什么关系。</STRONG><BR>OGSA吸纳了许多Web服务标准，如Web服务描述语言（WSDL）、简单对象访问协议（SOAP）、轻目录访问协议（LDAP）、Web服务探测（WS-Inspection）等。这些标准用于定位、调度计算资源并保证它们的安全。<BR>OGSA采用Web服务框架的优点是：一方面，通过注册和发现接口定义和终端（endpoint）实现描述以及动态产生特定接口绑定的代理，在异构环境中能够支持服务的动态发现和合成。另一方面，由于Web服务机制在商业应用领域广泛采用，OGSA采用Web服务框架使人们能够利用许多现有的开发工具和扩展服务，如产生语言绑定的WSDL处理器。</P>
<P><STRONG>25.网格跟.net, corba, jini,java的rmi，J2EE都是什么关系。</STRONG><BR>网格融合了.net, corba, jini或java的rmi，J2EE思想，并从软件集成扩展到CPU、存储设备、仪器等硬件的集成。</P>
<P><STRONG>26.网格和CSCW是什么关系</STRONG><BR>CSCW可以定义为地域分散的一个群体借助计算机及其网络技术，共同协调与协作来完成一项任务。网格促进了这种工作模式。</P>
<P><STRONG>27.网格跟IBM提出的自主计算有什么关系<BR></STRONG>“自主计算”的设想是将日益复杂的IT系统管理后台化、自动化和智能化，通过强大的中间件将异构网络改造成真正意义上的和谐自主的数字神经系统。<BR>网格本身就是一种自主计算</P>
<P><STRONG>28.什么是网格的体系结构</STRONG></P>
<P>网格体系结构就是关于如何建造网格的技术描述。它给出了网格的基本组成与功能，描述了网格各组成部分的关系以及它们集成的方式或方法，刻画了支持网格有效运转的机制。</P>
<P><STRONG>29.网格的主要有那些体系结构，他们目前发展状况如何。<BR></STRONG>美国Globus项目提出的格网体系结构模型采用本地服务层、核心服务层、高层服务与工具层、应用层四层结构。在此基础上，美国的Argonne国家实验室、芝加哥大学、南加州大学和IBM公司共同提出了开放式格网服务体系结构(Open Grid Services Architecture，OGSA)。OGSA采用纤维层、联络层、资源层、协作层、应用层五层结构。</P>
<P><STRONG>30.网格的起源是什么</STRONG><BR>“网格”（Grid）一词来源于人们熟悉的电力网（Power Grid）。目前对计算力的需求正在持续不断地增长。高速网络、功能和性能更加强大的大规模计算系统、高性能存储系统以及高级数据分析软件的应用为解决商业领域和科学领域的计算问题带来了希望。但是利用这些新的能力意味着应付不断增加的工作负荷要求所带来的空前挑战。当一个组织机构在努力保持竞争力的时候他们面临许多前所未有的挑战：减少计算费用、增加计算吞吐量、加快对市场的响应、改进生产服务质量和产品创新等都是非常重要的。一个组织机构在硬件方面的投资需要谨慎地进行权衡，并且必须找到利用可得资源完成更多工作的方法。<BR>尽管对计算力的需求是非常大的，但是许多现有的系统都没有得到充分利用。其中一些单个服务器也许满负荷工作，但是大多数系统却不是满负荷工作的，使许多计算周期（computing cycles）没有使用，而实际上这些计算周期可以用于满足不断增长的计算资源需求。格网计算使组织机构可以更加有效地和灵活地用他们的分布式计算资源，从现有的系统中获取更加有用的计算力，帮助组织机构获得竞争优势。</P>
<P><STRONG>31.网格目前的发展状况如何</STRONG></P>
<P>研究、开发、炒作，即处于预热期。</P>
<P><STRONG>32.国内的网格有那些项目，每个项目的目标如何，目前进展如何。</STRONG></P>
<P>ACI工程的目标是把分布在不同地理位置的高性能计算机、贵重仪器、数据库等用高速网络连接在一起构成一台虚拟计算机，用户通过ACI共享资源、共同讨论、合作开展科研项目。<BR>织女星计划（Vega计划以元数据、构件框架、智能体、格网公共信息协议和格网计算协议为主要研究突破点。</P>
<P><STRONG>33.介绍一些国外的主要网格项目的状况</STRONG><BR>美国科学格网项目的目标是建立基于格网中间件（Gridware）和系统工具的高级分布式计算基础设施（ADCI）使能源部科学计算体系的可伸缩性满足能源部内部科学研究任务要求。<BR>远程分布式计算与通信项目的目标是创建一个用于访问美国能源部三个武器实验室的具有可操作性的格网，以支持国防计划中远程计算和分布式计算这两个关键战略领域复杂的分析、设计、制造、认证功能。<BR>地球系统格网（Earth System Grid II，ESG）项目由阿贡国家实验室（Argonne National Laboratory）等五个国家实验室的科学家联合承担。主要目标是解决从全球地球系统模型分析和发现知识所面临的巨大挑战，为下一代气候研究提供一个无缝的强大的虚拟协同环境。 </P>
<P>国家地震工程仿真格网的目标使地震研究人员能够进行各种地震工程实验数据分析的大规模计算任务。<BR>此外，目前美国正在进行的格网计算研发项目还包括Globus、美国航空航天局（NASA）的信息动力格网（Information Power Grid，IPG）、美国国家技术格网（National Technology Grid）、虚拟实验室项目（Virtual Laboratory Project）、天体物理仿真合作实验室（Astrophysics Simulation Collaboratory，ASC）、国际虚拟数据格网实验室（International Virtual Data Grid Laboratory，iVDGL）等。Globus目标构建一个格网软件基础设施。研究内容包括资源管理、数据访问、应用开发、格网安全等。目前全球许多用户利用Globus工具包创建格网和开发格网应用。信息动力格网的目标是为国家航空航天局科学研究任务提供持续、可靠的计算动力源。美国国家技术格网项目的目标是创建一个无缝集成的协同计算环境原型系统。虚拟实验室项目致力于研究、设计、开发能够帮助解决数据密集的、涉及大规模计算的分子生物学问题的格网技术。天体物理仿真合作实验室项目的主要目标利用Cactus和Globus格网计算的研究成果为高级可视化交互和元计算提供大规模并行计算能力，实现在相对论天体物理学领域的大规模仿真。国际虚拟数据格网实验室项目由欧盟的数据格网（DataGrid）、美国的格网物理网络（Grid Physics Network）和粒子物理数据格网（Particle Physics Data Grid）协作创建。</P>
<P><STRONG>34.网格的发展有哪些困难</STRONG><BR>信息安全、商业模式等</P>
<P><STRONG>35.网格面临哪些技术问题<BR></STRONG>格网资源管理、注册、预订、分配、调度、安全、计费等</P>
<P><STRONG>36.GGF主要的工作是什么</STRONG><BR>应用程序及编程模型和环境（APME）。 <BR>架构。 <BR>数据。 <BR>安全性。 <BR>信息系统和性能（ISP）。 <BR>端到端应用（P2P）。 <BR>调度和资源管理（SRM）。 </P>
<P><BR><STRONG>37.国内有类似GGF的组织吗</STRONG><BR>没有，至少没有成规模、成气候、分工协作。</P>
<P><STRONG>38. OGSA,OGSI,Globus,WSRF有什么关系<BR></STRONG>OGSI（Open Grid Service Ieterface，开放网格服务接口）规定了向网格发送处理请求时所使用的接口，相当于Web服务中的WSDL（Web服务描述语言），在网格计算相关标准中处于核心地位。<BR>开放式格网服务体系结构(Open Grid Services Architecture，OGSA)采用纤维层、联络层、资源层、协作层、应用层五层结构，格网是通过调用服务来构造应用的，这就涉及接口问题。<BR>OGSA是一个抽象的东西,里面没有具体的内容,OGSA的推出要比OGSI早,OGSI是对OGSA的主要方面具体化,规范化.Globus是基于OGSI标准的一个实现. WSRF是和OGSI同一个层次的东西,是吸收了Web Service最新成果后,对OGSI的重构和发展.<BR>&nbsp;<BR><STRONG>39.网格是一种新技术，任何新技术或多或少都会带有泡沫的成分？你是否赞成？网格技术是否也带有泡沫的色彩？</STRONG><BR>技术本身没有泡沫，泡沫是商业炒作产生的，现在已经开始商业炒作了。</P>
<P><STRONG>40.从总体上看，网格技术目前的发展，是处在一个什么状况？</STRONG><BR>技术研究开发阶段</P>
<P><STRONG>41.网格离实际应用，离商业应用还有多远？<BR></STRONG>2到3年时间</P>
<P><STRONG>42.一个新技术，从理论性的研究，到实际的商业的推广需要经历几个阶段，你认为“网格”需要经历几个阶段，才能走向真正的商用？</STRONG><BR>原型应用系统开发、格网标准化（规范、协议）、成熟的商业模式</P>
<P><STRONG>43.网格想要得到广泛的商业应用，是否会遇到一些阻碍？</STRONG><BR>是的，例如安全问题、计费问题等。</P>
<P><STRONG>44.任何一个新技术的发展趋势，都应该受到身在政府、行业、企业中的CIO所关注。你认为作为“网格”技术，这些CIO应该从哪几方面进行关注？<BR></STRONG>网格在政府、行业、企业中的应用</P>
<P><STRONG>45.什么才算是Ian Foster三个checklist中的非平凡服务？</STRONG></P>
<P>大吞吐量、服务透明、高可靠性</P>
<P><STRONG>46.学习网格要学习哪些基础知识<BR></STRONG>计算机原理、计算机网络、数据库、编程语言等<BR><STRONG>&nbsp;<BR>47：什么是制造网格</STRONG><BR>是现代集成制造哲理和敏捷制造模式在网络化经济环境下的一种具体物化形式，目标是快速设计、快速制造、快速检测、快速响应和快速重组，克服空间上的距离给不同企业间的协同带来的障碍，在最大程度上实现设计、制造、信息、技术资源的共享，以及协同制造过程中物流、信息流、价值流的优化运行，它能为实现敏捷制造和虚拟企业的运作提供支持，形成具有数字化、柔性化、敏捷化等基本特征的优势互补的协同企业。而在这样的制造网络支持下，整个制造网格系统中的制造企业群体将以较低的成本、较短的开发周期，制造出符合市场需求的高质量产品。<BR></P><img src ="http://www.blogjava.net/hopeshared/aggbug/34446.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-03-09 11:53 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/09/34446.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：用Axis开发基于Java的Web服务</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/09/34439.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 09 Mar 2006 03:24:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/09/34439.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/34439.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/09/34439.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/34439.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/34439.html</trackback:ping><description><![CDATA[<SPAN class=postbody><A href="http://www.yesky.com/SoftChannel/72342371961929728/20030821/1723269.shtml" target=_blank><FONT color=#002c99>http://www.yesky.com/SoftChannel/72342371961929728/20030821/1723269.shtml</FONT></A> <BR><BR>概述 <BR><BR>　　本文介绍了一个比较简单实用的基于Java的SOAP引擎——Apache组织推出的Axis——的 <BR>安装、配置和应用开发方法，希望对有兴趣做基于Java的Web Services应用开发的朋友有所 <BR>帮助。 <BR><BR>　　Axis简介 <BR><BR>　　Axis是Apache组织推出的SOAP引擎，Axis项目是Apache组织著名的SOAP项目的后继项目 <BR>，目前最新版本是采用Java开发的1.1版本，C++的版本正在开发之中。Axis v1.1软件包可 <BR>以从http://ws.apache.org/axis/dist/1_1/下载得到。 <BR><BR>　　Axis的安装 <BR><BR><BR><BR>　　应用Axis开发Web Services，你需要有一个支持Servlet的服务器引擎，比如广为人知 <BR><BR>的Tomcat（Tomcat也是由Apache组织所提供的，目前的最新版本为4.1.24，下载地址为http <BR><BR>://jakarta.apache.org/builds/jakarta-tomcat-4.0/release/v4.1.24/）。在此之前当然 <BR><BR>你机器必须已经装好了JDK1.3以上的版本，当安装好Tomcat之后，只需将下载的Axis软件包 <BR>解压缩，将其中的“webapps”目录下的“axis”目录整个拷贝到Tomcat安装目录下的“web <BR>apps”目录下即可。 <BR><BR>　　Axis的配置 <BR><BR>　　Axis基于Java开发，可以部署于多种操作系统，使用前需要配置一系列的系统变量，在 <BR>此假定你已经在本机上装好了Tomcat 4.0以上的版本，需要配置的系统变量如下表所示： <BR><BR>CATALINA_HOME <BR>C:\Tomcat_4_1 <BR><BR>（此处应为Tomcat的安装位置，注意路径名中不要有空格） <BR><BR>AXIS_HOME <BR>%CATALINA_HOME%\webapps\axis <BR><BR>AXIS_LIB <BR>%AXIS_HOME%\lib <BR><BR>AXISCLASSPATH <BR>%AXIS_LIB%\axis.jar;%AXIS_LIB%\commons-discovery.jar; %AXIS_LIB%\commons-logging <BR><BR>.jar;%AXIS_LIB%\jaxrpc.jar; %AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar; %AXI <BR>S_LIB%\xml-apis.jar;%AXIS_LIB%\xercesImpl.jar <BR><BR><BR>　　如果你不愿做这么繁琐的类路径设置，可以直接将axis软件包中“lib”目录下的所有. <BR>jar文件加入到系统环境变量CLASSPATH中即可。 <BR><BR>　　Axis的测试 <BR><BR>　　安装配置完毕后，应测试一下是否Axis可以正确运行了。 <BR><BR>　　启动Tomcat服务器，在浏览器中访问http://localhost:8080/axis/happyaxis.jsp，如 <BR>果页面显示有错误，则需要回头检查一下相关配置是否正确，如果浏览页面能正确显示出系 <BR>统组件、属性等参数配置信息，则表示安装成功，如下图所示： <BR><BR>　　服务的发布 <BR><BR>　　Axis提供了两种服务发布方式，一种是即时发布（Instant Deployment），一种是定制 <BR>发布（Custom Deployment）。 <BR><BR>　　使用即时发布 <BR><BR>　　对即时发布的支持是Axis的特色之一，使用即时发布使用户只需有提供服务的Java类的 <BR>源代码，即可将其迅速发布成Web服务。每当用户调用这类服务的时候，Axis会自动进行编 <BR>译，即使服务器重启了也不必对其做任何处理，使用非常简单快捷。 <BR><BR>　　使用即时发布首先需要一个实现服务功能的Java源文件，将其扩展名改为.jws（Java W <BR>eb Service的缩写），然后将该文件放到“……\webapps\axis”目录下即可。 <BR><BR>　　在此给出一个从英里到公里的长度单位转换的服务，其源码如下： <BR><BR>文件Distance.jws <BR><BR>/** <BR><BR>* @author 飞鹰 <BR><BR>*/ <BR><BR>public class Distance <BR><BR>{ <BR><BR>public double convertMile2Kilometre( double mile ) <BR><BR>{ <BR><BR>//实现英里到公里的距离转换 <BR><BR>return mile * 1.609; <BR><BR>}//convertMile2Kilometre() <BR><BR>}/* Distance */ <BR><BR><BR>　　将其放到“……\webapps\axis”目录，通过访问http://localhost:8080/axis/Distan <BR>ce.jws?wsdl可以看到这个服务的WSDL描述文件，这说明Distance服务被成功发布了。如下 <BR>图所示： <BR>使用定制发布 <BR><BR>　　即时发布是一项令人激动的技术，它使Web服务的开发变得如此简单；然而即时发布并 <BR>不总是最好的选择，比如有些应用系统是第三方提供的，我们没有购买源代码，只有.clas <BR>s文件，但我们又希望将这个应用系统的一些功能对外发布成Web服务，使其能够在更大范围 <BR>内产生作用，这个时候即时发布技术就无能为力了。此外，即时发布技术并不灵活，无法进 <BR>行更多的服务配置，这使得它并不能满足一些特定系统的需求。 <BR><BR>　　因此，Axis提供了另一种服务发布方式，这就是定制发布。 <BR><BR>　　在此给出一个从加仑到升的容积单位转换的服务，其源码如下： <BR><BR>　　文件Capacity.java <BR><BR>package samples.capacity; <BR><BR><BR>/** <BR><BR>* @author 飞鹰 <BR><BR>*/ <BR><BR>public class Capacity <BR><BR>{ <BR><BR>public double convertGallon2Litre( double gallon ) <BR><BR>{ <BR><BR>//实现加仑到升的容积转换 <BR><BR><BR>return gallon * 4.546; <BR><BR>}//convertGallon2Litre() <BR><BR>}/* Capacity */ <BR><BR><BR>　　将其编译成.class文件，放置到“……\webapps\axis\samples\capacity”目录下，即 <BR>可着手进行发布。 <BR><BR>　　定制发布不需要构建.jws文件，但必须创建服务发布描述文件deploy.wsdd（Web Servi <BR>ce Deployment Descriptor的缩写），这个文件负责描述服务的名称、入口等信息，其内容 <BR>如下： <BR><BR>　　文件deploy.wsdd <BR><BR>&lt;deployment xmlns="http://xml.apache.org/axis/wsdd/" <BR><BR>xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&gt; <BR><BR><BR>&lt;service name="Capacity" provider="java:RPC"&gt; <BR><BR>&lt;parameter name="className" value="samples.capacity.Capacity"/&gt; <BR><BR>&lt;parameter name="allowedMethods" value="*"/&gt; <BR><BR>&lt;/service&gt; <BR><BR><BR>&lt;/deployment&gt; <BR><BR><BR>　　将该文件也放到“……\webapps\axis\samples\capacity”目录下，然后可以采用Axi <BR>s提供的一个客户端管理工具——AdminClient来完成服务的定制发布。 <BR><BR>　　在“……\webapps\axis\samples\capacity”目录下，运行： <BR><BR>java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient deploy.wsdd <BR><BR><BR>　　可以看到以下运行结果： <BR><BR>Processing file deploy.wsdd <BR><BR>&lt;Admin&gt;Doneprocessing&lt;/Admin&gt; <BR><BR><BR>　　这表明Capacity服务定制发布完成。 <BR><BR>　　通过访问http://localhost:8080/axis/services/Capacity?wsdl可以看到这个服务的W <BR>SDL描述文件，这说明Capacity服务被成功发布了。如下图所示： <BR><BR><BR>服务的访问 <BR><BR>　　GET方式的服务访问 <BR><BR>　　一般的SOAP消息都是采用POST方式实现传递，但也可以通过GET方式来访问。比如以下 <BR>给出的一个服务——“HelloWorld”，其源码如下： <BR><BR>文件HelloWorld.jws <BR><BR>/** <BR><BR>* @author 飞鹰 <BR><BR>*/ <BR><BR>public class HelloWorld <BR><BR>{ <BR><BR>public String helloWorld() <BR><BR>{ <BR><BR>//在服务器端打印输出 <BR><BR>System.out.println( "Hello World!" ); <BR><BR><BR>//返回相应字符串 <BR><BR>return "Hello World!"; <BR><BR>}//helloWorld() <BR><BR><BR>}/* HelloWorld */ <BR><BR><BR>　　这个服务给出一个名位“helloWorld”的无入口参数的操作，返回一个内容为“Hello <BR>World！的字符串”，同时在服务器端打印“Hello World！”，将该文件放到“……\webap <BR>ps\axis”目录下，即可通过GET方法直接访问该服务，访问的地址为http://localhost:808 <BR>0/axis/HelloWorld.jws?method=helloWorld，可以看到返回的SOAP信封消息，同时服务器 <BR>端给出了相应的显示信息“Hello World！”这表明HelloWorld服务被成功访问了。如下图 <BR>所示： <BR><BR><BR><BR>　　客户端服务访问编程 <BR><BR>　　Axis提供了一套API来实现SOAP，从http://localhost:8080/axis/docs/apiDocs/index <BR>.html可以看到Axis的API文档。 <BR><BR>　　其中，org.apache.axis.client.Call和org.apache.axis.client.Service是两个比较 <BR>常用的类，一般的客户端程序欲访问一个Web Service时，都要生成一个客户端的Service对 <BR>象和Call对象，在访问服务之前，首先要对Call对象设置相应的参数，包括服务的位置、操 <BR>作名、入口参数、返回值类型等，最后调用Call对象的invoke方法访问服务。 <BR><BR>　　以下给出了一个客户端访问Web服务的例程——AXISTest.java： <BR><BR>　　文件AXISTest.java <BR><BR>package axisexercise; <BR><BR><BR>import org.apache.axis.client.Call; <BR><BR>import org.apache.axis.client.Service; <BR><BR>import org.apache.axis.encoding.XMLType; <BR><BR><BR>import javax.xml.rpc.ParameterMode; <BR><BR><BR>/** <BR><BR>* @author 飞鹰 <BR><BR>*/ <BR><BR>public class AXISTest <BR><BR>{ <BR><BR>public static void main( String[] args ) throws Exception <BR><BR>{ <BR><BR>Service service = new Service(); <BR><BR>Call call = ( Call ) service.createCall(); <BR><BR><BR><BR>//////////访问即时发布的Distance服务 <BR><BR>//设置访问点 <BR><BR>call.setTargetEndpointAddress( "http://localhost:8080/axis/Distance.jws" ); <BR><BR><BR>//设置操作名 <BR><BR>call.setOperationName( "convertMile2Kilometre" ); <BR><BR><BR>//设置入口参数 <BR><BR>call.addParameter( "op1", XMLType.XSD_DOUBLE, ParameterMode.IN ); <BR><BR>call.setReturnType( XMLType.XSD_DOUBLE ); <BR><BR>Double d1 = new Double( 190 ); <BR><BR><BR>//调用服务 <BR><BR>System.out.println( d1 + " 英里相当于 " + <BR><BR>call.invoke( new Object[] {d1} ) + " 公里！" ); <BR><BR><BR><BR><BR>//////////访问定制发布的Capacity服务 <BR><BR>call = ( Call ) service.createCall(); <BR><BR>//设置访问点 <BR><BR>call.setTargetEndpointAddress( "http://localhost:8080/axis/services/Capacity" ); <BR><BR><BR>//设置操作名 <BR><BR>call.setOperationName( "convertGallon2Litre" ); <BR><BR><BR>//设置入口参数 <BR><BR>call.addParameter( "op1", XMLType.XSD_DOUBLE, ParameterMode.IN ); <BR><BR>call.setReturnType( XMLType.XSD_DOUBLE ); <BR><BR>d1 = new Double( 10.00 ); <BR><BR><BR>//调用服务 <BR><BR>System.out.println( d1 + " 加仑相当于 " + <BR><BR>call.invoke( new Object[] {d1} ) + " 升！" ); <BR><BR>} //main() <BR><BR>}/* AXISTest */ <BR><BR><BR>　　编译运行后运行可以看到以下的结果： <BR><BR>　　190.0英里相当于305.71公里！ <BR><BR>　　10.0加仑相当于45.46升！ <BR>　 <BR>　　注意程序在访问即时发布的Distance服务和定制发布的Capacity服务时的不同，前者的 <BR>服务访问点地址为http://localhost:8080/axis/HelloWorld.jws，而后者的则为http://lo <BR>calhost:8080/axis/services/Capacity。 <BR><BR>　　总结 <BR><BR>　　Web Services是未来网络应用的发展方向，SOAP和WSDL是Web Services的核心协议，Ax <BR>is给出了一个很好的SOAP实现，它使得开发Web Services应用变得轻松而有趣。</SPAN><SPAN class=postbody></SPAN><SPAN class=gensmall></SPAN> <img src ="http://www.blogjava.net/hopeshared/aggbug/34439.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-03-09 11:24 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/09/34439.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：一个soap的例子</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/09/34438.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 09 Mar 2006 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/09/34438.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/34438.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/09/34438.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/34438.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/34438.html</trackback:ping><description><![CDATA[<SPAN class=postbody>CSDN - 专家门诊 - Java J2SE / 基础类问题 <BR><BR>回复人： panq(漫随天外) ( ) 信誉：100 2001-10-25 13:40:12Z 得分:300 <BR><BR>简单对象访问协议(SOAP): <BR>用于Internet上的分布式计算,可以实现跨平台跨编程语言的对象访问和方法调用. <BR>它通过HTTP协议实现参数的传输,同时以特定的XML格式表示参数.这样,只需要一个支持H <BR>TTP协议的WEB服务器和一个XML解析器就可以实现简单的SOAP功能.由于成为了标准,所以 <BR>会有越来越多的软件支持它.大家若有兴趣的话可以去看SOAP1.1标准. <BR>同时,还有许多SOAP的开发工具,以简化SOAP的开发.随着这些开发工具的完善,我们 <BR>甚至可以不用管具体的SOAP格式就可以开发SOAP应用. <BR>下面以APACHE的SOAP包为例,举个例子: <BR>服务器端的Class: <BR>package samples.soap; <BR>public class SOAPCal { <BR>double rate; <BR>public SOAPCal() { <BR>rate = 8.7; <BR>} <BR>public void setRate(double rate) { <BR>this.rate = rate; <BR>} <BR>public Double calculate(int direction, double value) { <BR>Double retVal; <BR>switch(direction) { <BR>case 0: <BR>retVal = new Double(value * rate); <BR>break; <BR>case 1: <BR>retVal = new Double(value / rate); <BR>break; <BR>default: <BR>retVal = null; <BR>} <BR>return retVal; <BR>} <BR>} <BR>客户端Class: <BR>package samples.soap; <BR>import java.io.*; <BR>import java.util.*; <BR>import java.net.*; <BR>import org.w3c.dom.*; <BR>import org.apache.soap.util.xml.*; <BR>import org.apache.soap.*; <BR>import org.apache.soap.encoding.*; <BR>import org.apache.soap.encoding.soapenc.*; <BR>import org.apache.soap.rpc.*; <BR>public class SOAPCalUser { <BR>public static void main(String[] args) throws Exception { <BR>if (args.length != 3 <BR>&amp;&amp; (args.length != 4 || !args[0].startsWith("-"))) { <BR>System.err.println("Usage:"); <BR>System.err.println(" java " + SOAPCalUser.class.getName() + <BR>" [-encodingStyleURI] SOAP-router-URL name " + <BR>"(0: dollor to yuan | 1: yuan to dollor) value"); <BR>System.exit (1); <BR>} <BR>int offset = 4 - args.length; <BR>String encodingStyleURI = args.length == 11 <BR>? args[0].substring(1) <BR>: Constants.NS_URI_SOAP_ENC; <BR>URL url = new URL(args[1 - offset]); <BR>int direction = Integer.parseInt(args[2 - offset]); <BR>double value = Double.parseDouble(args[3 - offset]); <BR>Call call = new Call(); <BR>call.setTargetObjectURI("urn:SOAPCal"); <BR>call.setMethodName("calculate"); <BR>call.setEncodingStyleURI(encodingStyleURI); <BR><BR>Vector params = new Vector(); <BR>params.add(new Parameter("direction", int.class, new <BR>Integer(direction), null)); <BR>params.add(new Parameter("value", double.class, new Double(value), <BR>null)); <BR>call.setParams(params); <BR>Response resp; <BR>try { <BR>resp = call.invoke(url, ""); <BR>} catch (SOAPException e) { <BR>System.err.println("Caught SOAPException (" + <BR>e.getFaultCode() + "): " + <BR>e.getMessage()); <BR>return; <BR>} <BR>// Check the response. <BR>if (!resp.generatedFault()) { <BR>Parameter ret = resp.getReturnValue(); <BR>Object retVal = ret.getValue(); <BR>System.out.println(retVal != null ? "\n" + retVal : "I don't <BR>know."); <BR>} else { <BR>Fault fault = resp.getFault(); <BR>System.err.println("Generated fault: "); <BR>System.out.println (" Fault Code = " + <BR>fault.getFaultCode()); <BR>System.out.println (" Fault String = " + <BR>fault.getFaultString()); <BR>} <BR>} <BR>} <BR>和 <BR>package samples.soap; <BR>import java.io.*; <BR>import java.util.*; <BR>import java.net.*; <BR>import org.w3c.dom.*; <BR>import org.apache.soap.util.xml.*; <BR>import org.apache.soap.*; <BR>import org.apache.soap.encoding.*; <BR>import org.apache.soap.encoding.soapenc.*; <BR>import org.apache.soap.rpc.*; <BR><BR>public class SOAPCalAdmin { <BR>public static void main(String[] args) throws Exception { <BR>if (args.length != 2 <BR>&amp;&amp; (args.length != 3 || !args[0].startsWith("-"))) { <BR>System.err.println("Usage:"); <BR>System.err.println(" java " + SOAPCalAdmin.class.getName() <BR>+ " [-encodingStyleURI] SOAP-router-URL name " + <BR>"rate "); <BR>System.exit (1); <BR>} <BR>int offset = 3 - args.length; <BR>String encodingStyleURI = args.length == 11 <BR>? args[0].substring(1) <BR>: Constants.NS_URI_SOAP_ENC; <BR>URL url = new URL(args[1 - offset]); <BR>double rate = Double.parseDouble(args[2 - offset]); <BR>Call call = new Call(); <BR>call.setTargetObjectURI("urn:SOAPCal"); <BR>call.setMethodName("setRate"); <BR>call.setEncodingStyleURI(encodingStyleURI); <BR>Vector params = new Vector(); <BR>params.add(new Parameter("rate", double.class, new Double(rate), <BR>null)); <BR>call.setParams(params); <BR>Response resp; <BR>try { <BR>resp = call.invoke(url, ""); <BR>} catch (SOAPException e) { <BR>System.err.println("Caught SOAPException (" + <BR>e.getFaultCode() + "): " + <BR>e.getMessage()); <BR>return; <BR>} <BR>// Check the response. <BR>if (!resp.generatedFault()) { <BR>System.out.println("The rate has been changed."); <BR>} else { <BR>Fault fault = resp.getFault(); <BR>System.err.println("Generated fault: "); <BR>System.out.println (" Fault Code = " + <BR>fault.getFaultCode()); <BR>System.out.println (" Fault String = " + <BR>fault.getFaultString()); <BR>} <BR>} <BR>} <BR>发布用的XML文件dd.xml: <BR>&lt;isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" <BR>id="urn:SOAPCal"&gt; <BR>&lt;isd:provider type="java" <BR>scope="Application" <BR>methods="setRate calculate"&gt; <BR>&lt;isd:java class="samples.soap.SOAPCal"/&gt; <BR>&lt;/isd:provider&gt; <BR>&lt;/isd:service&gt; <BR>这个程序的功能是设置和计算美元/人民币的兑换价格,用 <BR>java org.apache.soap.server.ServiceManagerClient <BR><A href="http://sb3server:8080/soap/servlet/rpcrouter" target=_blank>http://sb3server:8080/soap/servlet/rpcrouter</A> deploy dd.xml <BR>把服务器端程序发布到TOMCAT上,然后在客户端用: <BR>java samples.soap.SOAPCalAdmin <A href="http://sb3server:8080/soap/servlet/rpcrouter" target=_blank>http://sb3server:8080/soap/servlet/rpcrouter</A> <BR>XX.XX <BR>来设置汇率(1美元兑换多少人民币), 用 <BR>java samples.soap.SOAPCalUser <A href="http://sb3server:8080/soap/servlet/rpcrouter" target=_blank>http://sb3server:8080/soap/servlet/rpcrouter</A> <BR>(1|0) XX.XX <BR>来换算,其中1和0代表从人民币到美元和从美元到人民币,下一个参数是要换算的钱数. <BR>另外,在http://www.xmethods.com/gettingstarted/apache.html <BR>有一个简单的教程介绍怎样用APACHE的SOAP包写SOAP客户端程序.讲解比较详细. <BR>注意点: <BR>*如果SOAP传回来的参数是一个CLASS,这个CALSS会被序列化(Serializer)为一段XML <BR>代码,在客户端接这个CLASS时,仍然需要一个同名CLASS,而且那些被传递过来的数据的SE <BR>T和GET方法名和参数名要相同.如果是自定义的CLASS,就要做maping声明.声明方法见APA <BR>CHE SOAP包的samples\addressbook\DeploymentDescriptor.xml文件中isd:mappings的 <BR>写法. <BR>在客户端调用时要加上 <BR>SOAPMappingRegistry smr = new SOAPMappingRegistry(); <BR>BeanSerializer beanSer = new BeanSerializer(); <BR>smr.mapTypes(/*encodingStyle*/Constants.NS_URI_SOAP_ENC, <BR>new QName(/*Namespace URI*/"urn:xml-soap-address-demo", /*Local <BR>Part*/"address"), <BR>/*Class type*/Address.class, /*java2XMLClassName*/beanSer, <BR>/*xml2JavaClassName*/beanSer); <BR>call.setSOAPMappingRegistry(smr); <BR>*目前不是所有的CLASS都能被序列化,如Hashtable就不行,所以最好用简单的数据类 <BR>型,如String,String,Integer,Double及其数组来传递变量,对复杂的参数,可以直接做成 <BR>XML Document传递(使用Constants.NS_URI_LITERAL_XML作为EncodingStyleURI),这样就 <BR>省得客户端专门做个CLASS来接参数,也方便跨语言的调用.在使用APACHE的SOAP包时,XML <BR>Document在客户端会被转换为XML DOM的Element class. <BR>现在唯一的方法是自己写进行序列化的程序,不过以后也许会有标准的序列化方法. <BR><BR>*CLASS被序列化时CLASS中的静态的变量是不会被加入的来的,这一点要注意.</SPAN><SPAN class=postbody></SPAN><SPAN class=gensmall></SPAN> <BR><img src ="http://www.blogjava.net/hopeshared/aggbug/34438.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-03-09 11:23 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/09/34438.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：在P2P网络中使用Web服务的好处</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/09/34437.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 09 Mar 2006 03:20:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/09/34437.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/34437.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/09/34437.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/34437.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/34437.html</trackback:ping><description><![CDATA[<SPAN class=postbody>作者： ZDNet China <BR><BR>现在是钻研专业术语的时候了。我们将谈谈在P2P网 <BR>络体系中XML以及其他Web服务的使用情况，并从技术的角度指出尝试将各种“新”技术嫁接 <BR>在一起的好处。 <BR><BR><BR><BR>从巴赫开始 <BR><BR><BR>巴赫——伟大的作曲家——以他的“为了艺术而艺术”而闻名于世。换句话说，他创作音乐 <BR>并并不需要特殊的原因或者动机，仅仅是因为他觉得自己需要这么做。 <BR><BR>有时候，技术领域也遵循着同样的道路——我们创造并使用新技术仅仅是因为我们觉得我们 <BR><BR><BR>需要这么做。当然，在巴赫的年代，不会每年有五千万份广告来告诉他“你需要创作大提琴 <BR><BR>协奏曲来紧跟时代”。 <BR><BR><BR>把多种技术集合在一起实际上会创造新的技术。例如，电技术加上麦克风和扬声器就导致了 <BR>一系列新技术的出现，如电话和收音机；再加上视频技术又出现了更复杂的技术，如电视和 <BR>可视电话。那么根据这一原则，Web技术加上P2P会产生更复杂的东东，这些东东或许会派上 <BR>用场，那么它们究竟是些什么呢？ <BR><BR><BR><BR>Web服务已经P2P了么？ <BR><BR>或许Web服务本身就是P2P的。实际上，已经有几篇文章在探讨Web服务与P2P的“收敛（conv <BR>ergence）”。这里的“收敛”指的是Web服务与P2P共同使用。 <BR><BR>或许看到这里你会问：“如何在Web服务中利用P2P技术？”呵呵，这个问题的提法本身就不 <BR>对。你应该这么问自己：“我如何在我的P2P网络中利用Web服务呢？” <BR><BR><BR><BR>P2P网络 <BR><BR>开发者常常把P2P技术称为P2P网络。这是因为“peer”实际上就是网络中的“节点”。由于 <BR><BR>P2P是一个高层次的技术，实际的底层网络对这个概念并不重要。尽管实际上，网络常常是T <BR>CP/IP网络，并利用Internet来从一个peer到达另一个peer，如同IP地址中的节点那样。 <BR><BR><BR>P2P网络的应用程序导致了若干种类型的应用程序。从即时消息到文件共享以及分布式计算 <BR>的每一样事物都用到了P2P概念和技术。和P2P应用程序一样，它们会遇到各种问题，而Web <BR>服务技术可以解决这些问题。 <BR><BR><BR><BR>Web服务对P2P的贡献 <BR><BR>P2P的一个主要问题就是定位P2P服务以及理解底层通信协议。一般的P2P途径在Internet上 <BR>搜索适当的P2P应用程序并安装定制应用程序。在更集成的环境下，常常用客户API或者工具 <BR>包提供了封装，也就是隐藏了应用程序协议的细节。 <BR><BR><BR>Web服务为定位应用程序提供了UDDI注册服务。每一个注册到UDDI数据库的应用程序都包含 <BR>语言（Web Services Description Language ，WSDL）的格式进行标准化的。 <BR><BR>WSDL描述指出了访问服务的实际方法和参数。此外，它还提供了可以被服务器识别的传输和 <BR>绑定协议。大多数Web服务通过XML和HTTP、用SOAP来绑定Web服务来传输SOAP消息。 <BR><BR>简而言之，Web服务技术（无论故意使用还是无意的）可以解决设计、建立和配置P2P应用程 <BR><BR>序中的许多常见问题。联合使用XML、SOAP、WSDL、HTTP和UDDI，使用Web服务的P2P应用程 <BR>序将会更加标准、更易于访问、更易于理解并更容易集成。</SPAN><SPAN class=postbody></SPAN><SPAN class=gensmall></SPAN> <BR><img src ="http://www.blogjava.net/hopeshared/aggbug/34437.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-03-09 11:20 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/09/34437.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：Java使用技巧两则---J2EE 1.4简介，JSTL </title><link>http://www.blogjava.net/hopeshared/archive/2006/03/09/34435.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 09 Mar 2006 03:10:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/09/34435.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/34435.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/09/34435.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/34435.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/34435.html</trackback:ping><description><![CDATA[作者： <A id=HyperAuthor href="http://kb.csdn.net/ArticleAuthor.aspx?author=高科华">高科华</A>∣来源：<A id=HyFromWhere href="http://www.matrix.org.cn/article.shtml" target=_blank>Matrix</A><BR><BR>J2EE 1.4简介<BR>2003年11月24日，J2EE的JCP(Java Community Process)委员会投票一致通过了J2EE 1.4，这是过去两年内发布的第一个主要版本。这个最新版的企业Java标准开放平台增加了许多新的服务，J2EE 1.4 SDK支持这些服务，现在可以下载这个SDK了。这个SDK包括J2EE 1.4 应用服务器、J2SE 1.4.2和J2EE 1.4示例代码。你可以下载整个SDK或单独下载应用服务器或示例代码。<BR>下面的技巧介绍了J2EE 1.4的一些新特点。也包括如何在Solaris、 Windows、 Linux、和 Mac OS X上安装J2EE 1.4 SDK。但是没有完全覆盖这个平台的所有新内容。关于J2EE 1.4新内容的更全面的介绍，请看J2EE v1.4 Application Server Release Notes。<BR>对Web Service的支持<BR>J2EE 1.4最重要、最受争议的特点是对标准的Web Service的端对端的支持。有了J2EE 1.4，现在可以用HTTP之上的SOAP(Simple Object Access Protocol)使得J2EE组件（例如企业Bean和servlet）成为Web Service。<BR>J2EE 1.4要求应用服务器必须遵守WS-I Basic Profile 1.0，这个规范定义了Web service的传输和协议。这使得Web service是可以互操作的。<BR>与J2EE中Web service有关的关键技术如下：<BR>· Web Services for J2EE 1.1。结合单个J2EE技术创建Web service的主要是Web Services for J2EE。它定义了客户端、service端以及容器之间的部署协议。新的部署描述符文件，webservices.xml，为每一种组件定义了部署模式。Web Services for J2EE 还定义了编程模型（JAX-RPC）、 使用JNDI查找提供service的对象、和安全。最后，它还提供了一个新的Handler对象，开发者可以用这种对象阻止和操作SOAP消息。<BR>· JAX-RPC 1.1 (Java API for XML-Remote Procedure Calls)。JAX-RPC是一个标准的远程过程调用接口，它使用XML作为方法调用和返回值的序列化协议。JAX-RPC 把servlet和无状态会话Beans定义为在SOAP和 WSDL （Web Services Description Language）的基础上的XML Web service的service端。Service端是实现了Web service逻辑的软件组件。JAX-RPC 的client接口定义了对Web service的远程调用是如何发生的，以及参数和返回值（包括collection）是如何序列化的。JAX-RPC 1.1 还支持对安全Web services的基于SSL的验证。 <BR>· JAXR 1.0 （Java API for XML Registries）。 JAXR 把不同的XML注册表统一到单个模型。程序员可以利用XML注册表发现和查找service，访问它们的内容和元数据。例如，你可以用应用创建工具访问指定服务器上的注册表，找出服务器提供了哪些service，设计SOAP客户端使用其中的一个service。JARX还使得外部Web service客户端可以访问J2EE Web service。<BR>· SAAJ 1.2 (SOAP with Attachments API for Java)。SAAJ用具有MIME附件的SOAP消息处理客户端和Web service之间的SOAP消息传递。MIME附件既可以用来发送文档（对基于文档的Web service而言）又可以用来发送传递大量数据的参数（对 RPC式的Web service）。 SAAJ还为带附件的SOAP消息定义了DOM（Document Object model，文档对象模型）。它允许用户对如何编码SOAP消息进行控制， 为重复或复杂的任务提供了automation hook。<BR>· JAXP 1.2 （Java API for XML Parsing）。这个包统一了解析和处理XML的各种方法。负责解析的类可以在部署时与运行时之间切换。JAXP 1.2包含了SAX 2.0、 DOM Level 2、和XSLT。<BR>Web层增强<BR>不是所有的J2EE 1.4中的Web层增强都与Web service有关。现有的核心技术也得到了更新。<BR>J2EE 1.4中的Web层最大的增强在于JSP 2.0。JSTL（JavaServer Pages Standard Tag Library）提供了范围广泛的通用tag，这些tag极大地方便了Web层的开发。JSTL还定义了新的表达式语言，这种语言可以用来编写存储在任何范围内的Web层JavaBean组件的表达式。下一个技巧说明了如何使用几种这样的tag。<BR>JSP另一个主要的增强是方便地定义定制tag的能力。在JSP 2.0以前的版本中，定义定制tag要求编写实现Tag接口的类。JSP 2.0仍然支持这种方法，但是它还允许用JSP代码片断定义定制tag（在JSP文件中定义），然后在多个地方使用所定义的定制tag。<BR>EJB 2.1<BR>J2EE 1.4的另一个重要特点是EJB 2.1，EJB 2.1提供了几个新的增强。这些包括：<BR>l 无状态会话Bean可以用作Web service端。<BR>l 新的Timer service可以设置成定时调用企业Bean的ejbTimeout()方法。可以用这个service定时调用商业逻辑。<BR>l EJB-QL增加了对结果集排序的ORDER BY子句和新的合计函数（AVG、MAX、MIN、SUM和COUNT）。<BR>l 现在消息驱动的Bean可以接受任何类型的消息，而不只是JMS消息（以前的版本只接受JMS消息）。现在连接器可以直接与MDB通讯，通过适配器把从外部系统传来的消息翻译成对MDB的调用。<BR>企业Java BluePrint for J2EE 1.4<BR>Java BluePrint示例应用是J2EE平台的必要部分。示例应用展示了应用设计和开发的最佳实践，提供了如何使用J2EE技术的例子。J2EE 1.4 SDK开发版本包含了AdventureBuilder，这是新增的J2EE的Web service示例应用。它也包含了原有的Java Pet Store和无线游戏示例应用。<BR>其他变化<BR>J2EE平台其他的一些重要变化包括：<BR>l JACC 1.0（Java Authorization Contract for Containers ）：允许自由选择安全和基于角色的授权提供者。<BR>l Java Deployment API 1.1: 独立于服务器的部署工具。<BR>l Java Message Service (1.1) 增强<BR>l J2EE Connectors 1.5：包括了对异步、双向、多线程连接器的完全支持。<BR>l J2EE Deployment 1.1：包括了可代替DTD的用XML模式定义的部署描述符，为了向后兼容保留了DTD.<BR>l J2EE Management 1.0：用Java工具或非Java工具管理企业资源。<BR>l 新版JavaMail （1.3），Java Transaction API (JTA 版本1.01B)， 和JDBC 3.0 <BR>安装服务器<BR>用J2EE 1.4新的GUI安装程序安装服务器很简单。所支持的操作系统如下：<BR>l Solaris SPARC 8 <BR>l Solaris SPARC 9 <BR>l Windows 2000 Pro SP3+ <BR>l Windows XP Pro SP1+ <BR>l Windows 2000 Server SP3+ <BR>l Windows Server 2003 <BR>l Linux RedHat 8 <BR>除了对Linux RedHat的支持以外，只需作很少的修改，甚至不需修改，服务器就能在许多其他的Linux版本环境运行。在Linux上安装的主要要求是J2RE 1.4.1以上的版本。<BR>在所支持的任意一个操作系统上安装服务器的步骤是：<BR>1. 下载适当的软件包（http://java.sun.com/j2ee/1.4/download-dr.html）。<BR>2. 运行下载的可执行程序。<BR>在Macintosh OS X 或Darwin上安装J2EE 1.4 SDK是可能的，尽管没有宣称对它们的支持。要在OS X 或Darwin上安装J2EE 1.4 SDK，你必须有root访问权限，J2RE必须是1.4.1以上的版本。<BR>在OS X 或Darwin上安装J2EE 1.4 SDK的步骤如下：<BR>1. 下载SDK的Linux版本（http://java.sun.com/j2ee/1.4/download-dr.html）。下载的文件将是ELF二进制文件。<BR>2. 用unzip命令行实用程序将压缩文件解压到一个空目录。必须用unzip，用象StuffIt这样的程序是不行的。在提示符（‘$’）后输入：<BR>$ unzip j2eesdk-1_4-dr-linux-eval<BR>3. 开始安装，输入：<BR>$ java -cp package/PackageFormat.jar:. appserv<BR>安装程序会开始安装SDK。如果偶尔不能看到安装时显示的提示，这时可以调整窗口的大小。<BR>4. 安装程序要求输入Java路径时，输入：<BR>/Library/Java/Home<BR>安装程序会给出一个提示信息“server requires JDK 1.4.2. Java 2 version 1.4.1 will work properly”，选择&lt;continue&gt;。<BR>5. 安装程序完成后，还必须做些工作。第一件要做的工作是改变安装目录img/bin的访问权限。输入：<BR>$ chmod a+x /Users/yourname/SUNWappserver/imq/bin<BR>这里yourname是你的用户名。（这假定服务器安装在/Users/yourname。）<BR>6. 最后一步要求root访问权限。服务器在/Library/Java/Home/jre/bin内查找java和javac，这个目录还不存在。你必须创建这个目录，创建这些程序的二进制代码的符号连接。输入：<BR>$ sudo mkdir -p /Library/Java/Home/jre/bin<BR>$ cd /Library/Java/Home/jre/bin<BR>$ sudo ln -s /Library/Java/Home/bin/java java<BR>$ sudo ln -s /Library/Java/Home/bin/javac javac<BR>sudo命令在第一次运行时会要求你输入口令。如果你不是系统管理员，你必须以root身份登录或su到root才能完成这些工作。<BR>7. 安装到此已经完成。启动应用服务器，输入：<BR>$ cd /Users/yourname/SUNWappserver/bin<BR>$ asadmin start-domain<BR><BR>JSTL（THE JAVA STANDARD TAG LIBRARY）<BR>过去几年，我们一直自己编写定制tag实现JSP页面的显示逻辑。已经建立了几个流行的tag库，用来执行迭代、处理XML、调用辅助函数和访问Web层JavaBean。这些库的功能有许多重叠，更主要的是它们互不兼容。<BR>为了解决这个不兼容问题，JCP就一个共用逻辑和现实tag集达成了一致。这些tag通常在创建Web应用的视图时非常有用。这个新的tag集被称之为JSTL（JavaServer Pages Standard Tag Library）。<BR>JSTL不是J2EE 1.4必需的部分，因此，不要求应用服务器供应商对它的支持。然而，Sun的J2EE 1.4 SDK还是包含了JSTL，其他的供应商也很可能支持它。JSTL并不都是新的---J2EE 1.3容器早就有了JSTL 1.0。JSTL 1.1对JSTL进行了升级，以便利用JSP 2.0和J2EE 1.4的新特点。<BR>这个技巧说明了如何使用几个新的JSTL tag。为了使用这些tag，必须了解新的JSP表达式语言（JSP Expression Language，JSP EL）。<BR>JSP表达式语言<BR>JSTL的基本思想是,JSP页面格式化的大多数数据都是JSP Page状态的。这些状态包括：<BR>l page、request、session或application范围的对象。<BR>l Request参数<BR>l JSP隐含对象（例如，pageContext）<BR>l HTTP header、cookie、和request 参数<BR>l Servlet上下文初始化参数<BR>关于JSP页面可以使用的数据的完整列表，请看J2EE 1.4教程（http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html）。<BR>用户定义的JavaBean（包括JavaBean和对企业Bean的引用）可以绑定到任何范围的状态变量。如果你在一个页面上用代码片断创建了一个新的Bean，你只需使用&lt;jsp:useBean&gt;tag。否则，只能在EL表达式中用名称访问Bean。JSP 2.0表达式语言使用JavaBean的命名机制操纵所连接的Bean。这个语言也为JSP tag中所用的表达式定义了语法。JSP页面编译器把分隔符${和}之间的JSP语句看成表达式。<BR>解释JSP表达式是如何工作的最容易的方法是用例子，下面我们就看一看这个技巧的示例代码。<BR>示例代码<BR>示例代码是一个使用了两个JavaBean的Web应用。JavaBean ColorScheme是一个映照，这个映照的键名是颜色名称，键值是ColorBean对象。ColorBean有名称（用这个名称注册到ColorScheme），red、green和blue颜色组件。ColorScheme继承了javax.util.TreeMap。对ColorScheme迭代按名称顺序取得它的所有ColorBean对象。ColorBean继承了java.awt.Color，因此，在JSP EL中可以使用它继承的所有方法（包括JavaBean属性）。<BR>应用的欢迎页面，index.jsp，操作有几百个ColorBean对象的ColorScheme对象。这些对象的数据来源于Web归档文件中的文本文件（rgb.txt）。文件中的数据有下面的格式：<BR>R G B Name<BR>240 248 255 AliceBlue<BR>index.jsp前面的一段代码初始化ColorScheme Bean，以名称“scheme”用session状态存储这个Bean，如下所示：<BR>&lt;jsp:useBean id="scheme"<BR>class="com.elucify.tips.dec2003.ColorScheme"<BR>scope="session"&gt;<BR><BR>&lt;!-- Initialize this bean --&gt;<BR>&lt;%<BR>// Open a stream to the init file<BR>InputStream stream =<BR>application.getResourceAsStream("/rgb.txt");<BR><BR>// Get a reference to the scheme bean<BR>ColorScheme colorScheme =<BR>(ColorScheme)pageContext.findAttribute(<BR>"scheme");<BR><BR>// Load colors from stream<BR>try {<BR>colorScheme.load(stream);<BR>} catch (IOException iox) {<BR>throw new JspException(iox);<BR>}<BR>%&gt;<BR>&lt;/jsp:useBean&gt;<BR><BR>（注意，如果使用tag文件代替代码片断，这个应用会更好。Tag文件是JSP 2.0的新特点，将在以后的使用技巧中介绍。）<BR>上面的代码典型地使用了&lt;jsp:useBean&gt;tag初始化这个tag所创建的Bean。在index.jsp对scheme Bean初始化并把它放在session范围内以后，其他JSP页面就可以对这个scheme Bean的数据进行格式化。<BR>打印ColorScheme集大小的JSP代码可能是下面这个样子：<BR>&lt;%<BR>ColorScheme scheme =<BR>(ColorScheme)session.getAttribute("scheme");<BR>out.println("The current color scheme has " +<BR>scheme.size() + " colors.");<BR>%&gt;<BR>使用JSTL和JSP表达式语言，这个任务会更简单，可读性也更强。示例页面Count.jsp就是这样做的：<BR>&lt;%@ taglib uri="http://java.sun.com/jsp/jstl/functions"<BR>prefix="fn" %&gt;<BR><BR>The current color scheme has ${fn:length(scheme)} colors.<BR><BR>第一行为JSTL函数包中的tag定义了前缀fn（关于这个包的更多信息，请看教程）。第二行是包含了表达式${fn:length(scheme)}的模板文本。JSP页面在运行时用scheme对象的长度代换这个表达式。因为scheme是一个映照，其长度返回的是其中条目的个数。Length函数对所有Collection类型都是可用的。<BR>更有趣的示例是示例页面ListColorNames.jsp，它列出了ColorScheme bean中的所有颜色的名称: <BR>&lt;%@ taglib uri="http://java.sun.com/jsp/jstl/core"<BR>prefix="c" %&gt;<BR><BR>This is a list of the names of all of the colors<BR>in the current color scheme:<BR><BR>&lt;ol&gt;<BR>&lt;c:forEach var="entry" items="${scheme}"&gt;<BR>&lt;li&gt;${entry.key}&lt;/li&gt;<BR>&lt;/c:forEach&gt;<BR>&lt;/ol&gt;<BR>行&lt;%@taglib%&gt;定义c为JSTL Core tag的前缀。这个页面的其他部分定义了一个有序列表。使用&lt;c:forEach&gt;对scheme集中的所有条目迭代。所有Collection类都可用ForEach tag。接著把在var属性中给出名称的page变量赋给这个集中的每个值。<BR>当forEach tag的item属性中的表达式的值是java.util.Map时，在var中命名的变量被设置为类型是java.util.Map.Entry的item。这时，迭代变量被称为entry,因此，用表达式${entry.key}取得键名。<BR>在下面的例子中你会看到，可以用表达式${entry.value}得到每个entry的值。这是因为java.util.Map.Entry对象有getKey和getValue方法，表达式语言遵守JavaBean的命名约定。<BR>通常，JSP EL表达式${a.b.c.d}是用代码a.getB().getC().getD()来计算的。这种表达式是对JavaBean属性的相继调用的简化。<BR>示例页面ShowColors.jsp显示了scheme中包含的颜色，如下所示：<BR><BR>This table demonstrates all of the colors <BR>in the current scheme.<BR><BR>&lt;TABLE BORDER="1" BGCOLOR="WHITE"&gt;<BR>&lt;TR&gt;&lt;TH&gt;Name&lt;/TH&gt;&lt;TH&gt;Sample Text&lt;/TH&gt;&lt;TH&gt;Color Swatch&lt;/TH&gt;<BR>&lt;TH&gt;Hex Code&lt;/TH&gt;&lt;TH&gt;Opposite Color&lt;/TH&gt;&lt;/TR&gt;<BR><BR>&lt;!-- Loop over color entries in the scheme --&gt;<BR>&lt;c:forEach var="entry" items="${scheme}"&gt;<BR><BR>&lt;!-- Set two variables to improve --&gt;<BR>&lt;!-- readability of HTML below --&gt;<BR>&lt;c:set var="color" <BR>value="${entry.value.rgbHex}"/&gt;<BR>&lt;c:set var="name" <BR>value="${entry.key}"/&gt;<BR><BR>&lt;TR&gt;<BR>&lt;TD&gt;${name}&lt;/TD&gt;<BR>&lt;TD&gt;&lt;FONT COLOR="${color}"&gt;${name}&lt;/FONT&gt;&lt;/TD&gt;<BR>&lt;TD BGCOLOR="${color}"&gt;${name}&lt;/FONT&gt;&lt;/TD&gt;<BR>&lt;TD&gt;${color}&lt;/TD&gt;<BR>&lt;TD BGCOLOR="${entry.value.rgbComplement}"&gt;<BR>${name}&lt;/TD&gt;<BR>&lt;/TR&gt;<BR><BR>&lt;/c:forEach&gt;<BR><BR>如上所示，这个例子使用了&lt;c:forEach&gt;。两个 &lt;c:set&gt; tag 把page属性color和name分别设置到颜色和颜色名称的hex表示。按照JavaBean命名约定，下面的行： <BR>&lt;c:set var="color" value="${entry.value.rgbHex}"/&gt;<BR>等效于Java代码 <BR>ColorBean cb = (ColorBean)entry.getValue();<BR>pageContext.setAttribute("color", cb.getRgbHex());<BR>模板文本中使用的color和name变量用来创建JSP页面输出中的table行。不用使用&lt;c:get&gt;tag，因为EL变量的值总是可以作为表达式引用，就象${color}这样。<BR>最后一个示例页面，ColorNameGrid.jsp，给出了JSTL tag更多的用法。它说明了如何使用JSTL tag进行下面的工作：<BR>l 访问request参数<BR>l 用两个不同的方法执行有条件输出<BR>l 使用EL比较和运算<BR>l 使用EL算术函数<BR>ColorNameGrid.jsp以多列的table打印ColorScheme中所有颜色的名称。列数在1到10之间，根据request参数cols确定。如果列数超出了范围就产生错误信息。<BR>ColorNameGrid.jsp的代码如下：<BR>&lt;!-- Initialize local variables --&gt;<BR>&lt;c:set var="cols" value="${param['cols']}"/&gt;<BR>"c:set var="col" value="0"/&gt;<BR><BR>&lt;!-- use the "choose" element to limit --&gt;<BR>&lt;!-- number of columns --&gt;<BR>&lt;c:choose&gt;<BR>&lt;c:when test="${cols &gt; 0 &amp;&amp; cols &lt;= 10}"&gt;<BR><BR>This is a grid of the names of all of the <BR>colors in the current color scheme:<BR>&lt;p&gt;<BR><BR>&lt;TABLE BORDER="0" BGCOLOR="#404040"&gt;<BR>&lt;TR&gt;<BR>&lt;c:forEach var="entry" items="${scheme}"&gt;<BR>&lt;c:set var="color" value="${entry.value}"/&gt; <BR><BR>&lt;TD&gt;&lt;FONT COLOR="${color.rgbHex}"&gt;${entry.key}<BR>&lt;/FONT&gt;&lt;/TD&gt;<BR><BR>&lt;c:set var="col" value="${col+1}"/&gt;<BR><BR>&lt;c:if test="${(col % cols) == 0}"&gt;<BR>&lt;/TR&gt;&lt;TR&gt;<BR>&lt;/c:if&gt;<BR><BR>&lt;/c:forEach&gt;<BR>&lt;/TR&gt;<BR><BR>&lt;/TABLE&gt;<BR>&lt;/c:when&gt;<BR><BR>&lt;c:otherwise&gt;<BR>&lt;!-- Complain about unreasonable input --&gt;<BR><BR>${cols} is an unreasonable number of columns.<BR>&lt;p&gt;<BR>Click &lt;a href="index.jsp"&gt;here&lt;/a&gt; to try again.<BR>&lt;/c:otherwise&gt;<BR><BR>&lt;/c:choose&gt;<BR>第一个&lt;c:set&gt;tag取得request参数cols，把它赋给page属性（属性名也是cols），如下所示：<BR>&lt;c:set var="cols" value="${param['cols']}"/&gt;<BR>（这个页面也可以不用page属性cols，在需要的地方使用${param['cols']}。定义page属性是为了使该页面的可读性更强。）<BR>第二个&lt;c:set&gt;tag用值1对page属性col初始化。这个变量用来分隔列。<BR>&lt;c:choose&gt;tag很象Java中的if-else语句。&lt;c:when&gt;tag就象if and else if语句，&lt;c:otherwise&gt;tag就象最后的else语句。&lt;c:choose&gt;tag包含了一系列的&lt;c:when&gt;tag，每一个&lt;c:when&gt;tag都有一个包含表达式的test属性。第一个&lt;c:when&gt;tag体的test表达式返回true，这个tag体执行后包含在输出中。如果没有一个&lt;c:when&gt;的test表达式返回true，就执行&lt;c:otherwise&gt;tag体，将结果送到输出。<BR>在这个例子中，第一个&lt;c:when&gt;tag的test表达式是逻辑表达式：<BR>&lt;c:when test="${cols &gt; 0 &amp;&amp; cols &lt;= 10}"&gt;<BR>这里所用的变量cols是在上面讨论的第一个&lt;c:set&gt;tag中定义的page属性。如果所要求的列数在1到10之间，这个页面就会产生一个颜色的table。否则（在&lt;c:otherwise&gt; tag中），这个页面就显示一个提示：<BR>&lt;c:otherwise&gt;<BR>&lt;!-- Complain about unreasonable input --&gt;<BR>${cols} is an unreasonable number of columns.&lt;p&gt;<BR>Click &lt;a href="index.jsp"&gt;here&lt;/a&gt; to try again.<BR>&lt;/c:otherwise&gt;<BR>在第一个&lt;c:when&gt;tag体内的是建table的代码。&lt;c:forEach&gt;tag对table内的所有color迭代，把page属性color赋给ColorScheme中的每个ColorBean:<BR>&lt;c:if test="${(col % cols) == 0}"&gt;<BR>&lt;/TR&gt;&lt;TR&gt;<BR>&lt;/c:if&gt;<BR>每个颜色名用它所代表的颜色打印出来，用表达式(${color.rgbHex})从color Bean得到颜色的hex表示。变量col在循环的末尾加1，以便正确分行：<BR>&lt;c:set var="col" value="${col+1}"/&gt;<BR>最后，循环在一行结束时用算术表达式和&lt;c:if&gt;tag强迫分行。<BR>上面的例子仅仅使我们对JSTL tag能做些什么有个初步印象。JSTL tag可以用来处理URL、传递请求、包含其他Web资源的内容、处理XML、创建国际化内容、执行SQL查询，还有很多。利用JSTL提供的强大的新工具可以使JSP的开发更容易。<BR><BR>运行示例代码<BR>下载以上两个技巧的示例代码的归档文件（http://java.sun.com/developer/EJTechTips/download/ttdec2003.war）。应用上下文的root是ttdec2003。下载的war文件还包含了这个示例应用的全部源代码。<BR>你可以用deploytool程序或admin console在J2EE 1.4应用服务器上部署应用归档文件（ttdec2003.war）。你也可以用asadmin命令部署应用，如下所示：<BR>asadmin deploy install_dir/ttdec2003.war<BR>用war文件所在的目录代替install_dir。<BR>可以用http:// ://localhost:8000/ttdec2003访问应用。<BR>对于与J2EE 1.4兼容的其他应用服务器，用你所用的服务器部署工具把应用部署到你的平台上。<BR>你应该在启动应用时看到下面的页面：<BR><BR><BR><img src ="http://www.blogjava.net/hopeshared/aggbug/34435.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-03-09 11:10 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/09/34435.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：WSDL文件详解</title><link>http://www.blogjava.net/hopeshared/archive/2006/03/09/34426.html</link><dc:creator>hopeshared</dc:creator><author>hopeshared</author><pubDate>Thu, 09 Mar 2006 02:25:00 GMT</pubDate><guid>http://www.blogjava.net/hopeshared/archive/2006/03/09/34426.html</guid><wfw:comment>http://www.blogjava.net/hopeshared/comments/34426.html</wfw:comment><comments>http://www.blogjava.net/hopeshared/archive/2006/03/09/34426.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hopeshared/comments/commentRss/34426.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hopeshared/services/trackbacks/34426.html</trackback:ping><description><![CDATA[在本文寫作時，並無任何 SOAP 實作，能進行作業名稱的多載動作。這對使用 Java 的客戶端是很重要的，因為使用 Java 的伺服端所使用的介面，會利用 Java 的多載功能。但這對使用 COM 的客戶端倒無所謂，因為 COM 並不支援多載。<BR><BR>&lt;binding&gt; 與 &lt;operation&gt; 元素<BR>Binding 區段是，指定通訊協定、序列化、與編碼的地方。若 Types、Messages、與 PortType 等區段負責抽象的資料內容，那麼 Binding 區段便負責處理資料傳輸的具體細節。Binding 區段負責使前三個區段的抽象具體化。<BR><BR>將繫結規格從資料與訊息宣告中分離出來的意義是，執行相同業務的服務提供者，可將一組作業 (portType) 標準化。若欲凸顯自身的差異，每個提供者可另外提供自訂繫結。讓 WSDL 擁有匯入 construct 是有好處的；因為如此一來，即可遠離 Bindings 與 Services 區段，將抽象定義置於自身的檔案中；並可將其散佈於服務提供者間，讓他們把抽象定義設定成標準。例如，銀行可標準化，一組抽象 WSDL 文件詳盡說明的銀行作業。但每家銀行還是可以自由地，「自訂」基礎通訊協定、序列最佳化、與編碼。<BR><BR>下列為 Binding 區段的 WSDL 多載範例，於此重複以方便詳細討論：<BR><BR>&lt;binding name="fooSampleBinding" type="wsdlns:fooSamplePortType"&gt;<BR>&lt;stk:binding preferredEncoding="UTF-8" /&gt;<BR>&lt;soap:binding style="rpc" <BR>transport="http://schemas.xmlsoap.org/soap/http"/&gt;<BR>&lt;operation name="foo"&gt;<BR>&lt;soap:operation soapAction="http://tempuri.org/action/foo1"/&gt;<BR>&lt;input name="foo1"&gt;<BR>&lt;soap:body use="encoded" <BR>namespace="http://tempuri.org/message/" <BR>encodingStyle=<BR>"http://schemas.xmlsoap.org/soap/encoding/" /&gt;<BR>&lt;/input&gt;<BR>&lt;/operation&gt;<BR>&lt;operation name="foo"&gt;<BR>&lt;soap:operation soapAction="http://tempuri.org/action/foo2"/&gt;<BR>&lt;input name="foo2"&gt;<BR>&lt;soap:body use="encoded" <BR>namespace="http://tempuri.org/message/" <BR>encodingStyle=<BR>"http://schemas.xmlsoap.org/soap/encoding/" /&gt;<BR>&lt;/input&gt;<BR>&lt;/operation&gt;<BR>&lt;/binding&gt;<BR><BR>&lt;binding&gt; 元素會有個名稱 (本例中為「fooSampleBinding」)，以方便 Services 區段的 &lt;port&gt; 元素參照。它有個參照 &lt;portType&gt; 的「type」屬性，在本例中為「wsdlns:fooSamplePortType」。第二行是 MSTK2 擴充元素 &lt;stk:binding&gt;，它可指定 preferredEncoding，或稱「UTF-8」。<BR><BR>&lt;soap:binding&gt; 元素可指定，使用的樣式 (rpc) 與傳輸。傳輸屬性會參照命名空間，以確定所使用的 HTTP SOAP 通訊協定。<BR><BR>有兩個 &lt;operation&gt; 元素具有相同的名稱「foo」。這兩個作業不同之處在於，兩個不同的 &lt;input&gt; 名稱：「foo1」與「foo2」。在兩個 &lt;operation&gt; 元素中，&lt;soap:operation&gt; 元素的「soapAction」屬性皆相同，都是一個 URI。soapAction 屬性是 SOAP 專屬的 URI，並逐字依 SOAP 訊息使用。以此方式取得的 SOAP 訊息，會有個 SOAPAction 標頭；而且還會以 &lt;soap:operation&gt; 元素中的 URI 為其值。雖然 HTTP 繫結需要 soapAction 屬性，但非 HTTP 繫結則不用。本文刊出時，其用途仍然不明。就本範例而言，它似乎可用來區別兩個不同的「foo」作業。SOAP 1.1 宣稱，soapAction 可用來識別訊息的「目的 (intent)」。它更建議伺服端，可用此屬性路由訊息，而不用剖析整個訊息。實務上，其用途差異很大。&lt;soap:operation&gt; 元素也可以含另一個名為「style」的屬性；若特定作業需要覆寫 &lt;soap:binding&gt; 元素中所指定的樣式，則可使用此屬性。<BR><BR>&lt;operation&gt; 元素所含的 &lt;input&gt;、&lt;output&gt;、與 &lt;fault&gt; 元素，可對應 PortTypes 區段的相同元素。在上例中出現的只有 &lt;input&gt; 元素。在本例中，這三個元素都各有一個選擇性的「name」屬性，可用以區別名稱相同的作業。在範例的 &lt;input&gt; 元素中，有個 &lt;soap:body&gt; 元素，它可指定輸入結果 SOAP 訊息之 &lt;body&gt; 的內容。此元素有下列屬性：<BR><BR>Use<BR>它是用以指定資料採「編碼 (encoded)」或「常值 (literal)」。「常值」的意義是，結果 SOAP 訊息所含資料的格式，完全依照抽象定義 (Types、Messages、與 PortTypes 區段) 的指定規格 。「編碼」的意義是，「encodingStyle」屬性 (參考下文) 會指定編碼方式。 <BR>Namespace<BR>每個 SOAP 訊息的主體，都有自己的命名空間，以防止名稱衝突。此屬性所指定的 URI，會逐字使用於結果 SOAP 訊息之中。 <BR>EncodingStyle<BR>以 SOAP 的編碼方式而言，URI 的值應該是「http://schemas.xmlsoap.org/soap/encoding」。 <BR>文件樣式繫結<BR>在上一區段中，&lt;soap:binding&gt; 元素有個類型屬性被設定成「rpc」。若將此屬性設定成「document」，即可改變訊息在線上的排序。此時這些訊息便成了文件傳輸，而不再是函數簽章。在此繫結類型中，&lt;message&gt; 元素是定義文件格式，而非函數簽章。請參考下例中的 WSDL 片段：<BR><BR>&lt;definitions<BR>xmlns:stns="(SchemaTNS)"<BR>xmlns:wtns="(WsdlTNS)"<BR>targetNamespace="(WsdlTNS)"&gt;<BR><BR>&lt;schema targetNamespace="(SchemaTNS)"<BR>elementFormDefault="qualified"&gt;<BR>&lt;element name="SimpleElement" type="xsd:int"/&gt;<BR>&lt;element name="CompositElement" type="stns:CompositeType"/&gt;<BR>&lt;complexType name="CompositeType"&gt;<BR>&lt;all&gt;<BR>&lt;element name='a' type="xsd:int"/&gt;<BR>&lt;element name='b' type="xsd:string"/&gt;<BR>&lt;/all&gt;<BR>&lt;/complexType&gt;<BR>&lt;/schema&gt;<BR><BR>&lt;message...&gt;<BR>&lt;part name='p1' type="stns:CompositeType"/&gt;<BR>&lt;part name='p2' type="xsd:int"/&gt;<BR>&lt;part name='p3' element="stns:SimpleElement"/&gt;<BR>&lt;part name='p4' element="stns:CompositeElement"/&gt;<BR>&lt;/message&gt;<BR>?&lt;/definitions&gt;<BR><BR>此結構描述有 SimpleElement 與 CompositeElement 兩個元素，以及一個宣告類型 (CompositeType)。所宣告的唯一 &lt;message&gt; 元素有四個部分 (part)：p1 是 CompositeType 類型；p2 是 int 類型；p3 是 SimpleElement；而 p4 是 CompositeElement。下表是四種繫結的比較，依照「使用/類型」分別為：rpc/literal、document/literal、rpc/encoded、與 document/encoded。本表可說明，每種繫結方式反映在線上的實際情況。<BR><BR>rpc / literal<BR>&lt;operation name="method1" style="rpc" ...&gt;<BR>&lt;input&gt;<BR>&lt;soap:body parts="p1 p2 p3 p4" <BR>use="literal"<BR>namespace="(MessageNS)"/&gt;<BR>&lt;/input&gt;<BR>&lt;/operation&gt;<BR><BR>線上情形：<BR>&lt;soapenv:body... xmlns:mns="(MessageNS)"<BR>xmlns:stns="(SchemaTNS)"&gt;<BR>&lt;mns:method1&gt;<BR>&lt;mns:p1&gt;<BR>&lt;stns:a&gt;123&lt;/stns:a&gt;<BR>&lt;stns:b&gt;hello&lt;/stns:b&gt;<BR>&lt;/mns:p1&gt;<BR>&lt;mns:p2&gt;123&lt;/mns:p2&gt;<BR>&lt;mns:p3&gt;<BR><BR>&lt;stns:SimpleElement&gt;<BR><BR>123<BR><BR>&lt;/stns:SimpleElement&gt;<BR>&lt;/mns:p3&gt;<BR>&lt;mns:p4&gt;<BR>&lt;stns:CompositeElement&gt;<BR>&lt;stns:a&gt;123&lt;/stns:a&gt;<BR>&lt;stns:b&gt;hello&lt;/stns:b&gt;<BR>&lt;/stns:CompositeElement&gt;<BR>&lt;/mns:p4&gt;<BR>&lt;/mns:method1&gt;<BR>&lt;/soapenv:body&gt;<BR>document / literal / type=<BR>&lt;operation name="method1" <BR>style="document" ...&gt;<BR>&lt;input&gt;<BR>&lt;soap:body parts="p1" use="literal"&gt;<BR>&lt;/input&gt;<BR>&lt;/operation&gt;<BR><BR>線上情形：<BR><BR>&lt;soapenv:body... xmlns:stns="(SchemaTNS)"&gt;<BR>&lt;stns:a&gt;123&lt;/stns:a&gt;<BR>&lt;stns:b&gt;hello&lt;/stns:b&gt;<BR>&lt;/soapenv:body&gt;<BR><BR>rpc / encoded <BR>&lt;operation name="method1" style="rpc" ...&gt;<BR>&lt;input&gt;<BR>&lt;soap:body parts="p1 p2" use="encoded" <BR>encoding= <BR>"http://schemas.xmlsoap.org/soap/encoding/"<BR>namespace="(MessageNS)"/&gt;<BR>&lt;/input&gt;<BR>&lt;/operation&gt;<BR><BR>線上情形：<BR>&lt;soapenv:body... xmlns:mns="(MessageNS)"&gt;<BR>&lt;mns:method1&gt;<BR>&lt;p1 TARGET="_self" HREF="#1"/&gt;<BR>&lt;p2&gt;123&lt;/p2&gt;<BR>&lt;/mns:method1&gt;<BR>&lt;mns:CompositeType id="#1"&gt;<BR>&lt;a&gt;123&lt;/a&gt;<BR>&lt;b&gt;hello&lt;/b&gt;<BR>&lt;/mns:CompositeType&gt;<BR>&lt;/soapenv:body&gt;<BR>document / literal / element=<BR>&lt;operation name="method1" <BR>style="document" ...&gt;<BR>&lt;input&gt;<BR>&lt;soap:body parts="p3 p4" <BR><BR>use="literal"&gt;<BR>&lt;/input&gt;<BR>&lt;/operation&gt;<BR><BR>線上情形：<BR><BR>&lt;soapenv:body... xmlns:stns="(SchemaTNS)"&gt;<BR>&lt;stns:SimpleElement&gt;<BR><BR>123<BR><BR>&lt;/stns:SimpleElement&gt;<BR>&lt;stns:CompositeElement&gt;<BR>&lt;stns:a&gt;123&lt;/stns:a&gt;<BR>&lt;stns:b&gt;hello&lt;/stns:b&gt;<BR>&lt;/stns:CompositeElement&gt;<BR>&lt;/soapenv:body&gt;<BR><BR>document / encoded<BR>&lt;operation name="method1" <BR>style="document" ...&gt;<BR>&lt;input&gt;<BR>&lt;soap:body parts="p1 p2" use="encoded" <BR>encoding=<BR><BR>"http://schemas.xmlsoap.org/soap/encoding/"<BR>namespace="(MessageNS)"/&gt;<BR>&lt;/input&gt;<BR>&lt;/operation&gt;<BR><BR>線上情形：<BR>&lt;soapenv:body... xmlns:mns="(MessageNS)"&gt;<BR>&lt;mns:CompositeType&gt;<BR>&lt;a&gt;123&lt;/a&gt;<BR>&lt;b&gt;hello&lt;/b&gt;<BR>&lt;/mns:CompositeType&gt;<BR>&lt;soapenc:int&gt;123&lt;/soapenc:int&gt;<BR>&lt;/soapenv:body&gt;<BR><BR><BR>&lt;service&gt; 與 &lt;port&gt; 元素<BR>服務即是一組 &lt;port&gt; 元素。每個 &lt;port&gt; 元素，都以一對一的方式，關連一個位置與 &lt;binding&gt;。若有一個以上的 &lt;port&gt; 元素，與相同的 &lt;binding&gt; 關連，便可以使用其它 URL 作為替換。<BR><BR>在 WSDL 文件中，可以有一個以上的 &lt;service&gt; 元素。多個 &lt;service&gt; 元素，可以有許多用途。其中之一便是，根據 URL 的目的地，將傳輸埠群組化。因此，我只要使用另一個 &lt;service&gt;，就可以重新導向所有股票報價要求；而且我的客戶端程式依然可以正常運作，這是因為在這類的服務群組化中，通訊協定完全不會更動其它的服務。多個 &lt;service&gt; 服務的另一個用途是為了，根據基礎通訊協定，對傳輸埠進行分類。例如，可將所有的 HTTP 傳輸埠，置於一個 &lt;service&gt;；所有的 SMTP 傳輸埠置於另一個 &lt;service&gt;。客戶端便可根據它能處理的通訊協定，搜尋相對的 &lt;service&gt;。<BR><BR>&lt;service name="FOOService"&gt;<BR>&lt;port name="fooSamplePort" binding="fooSampleBinding"&gt;<BR>&lt;soap:address <BR>location="http://carlos:8080/fooService/foo.asp"/&gt;<BR>&lt;/port&gt;<BR>&lt;/service&gt;<BR><BR>在一份 WSDL 文件中，&lt;service&gt; 的「name」屬性，可區分出個別服務的不同。因為一個服務可能會有幾個傳輸埠，所以傳輸埠也必須有「name」屬性。<BR><BR>總結<BR>本文已經說明了 WSDL 文件最顯著的 SOAP 特色。但應該聲明的是，WSDL 絕不僅只能在 HTTP 上說明 SOAP 而已。在說明 SOAP 上，WSDL 的表達能力絕對足以使用 HTTP-POST、HTTP-GET、SMTP、與其它通訊協定。只要使用 WSDL，不論是程式開發人員或一般使用者，都可輕鬆處理 SOAP。個人深信，WSDL 與 SOAP 的結合，將可引進全新的應用程式類別，以更徹底地利用各種網路服務。<BR><BR>在 WSDL 的命名空間，有各種 XML 元素。下表整理了這些元素、屬性、與內容，以供參考：<BR><BR>元素 屬性 內容 (子項) <BR>&lt;definitions&gt; name<BR>targetNamespace<BR>xmlns (其它命名空間) &lt;types&gt;<BR>&lt;message&gt;<BR>&lt;portType&gt;<BR>&lt;binding&gt;<BR>&lt;service&gt; <BR>&lt;types&gt; (無) &lt;xsd:schema&gt; <BR>&lt;message&gt; name &lt;part&gt; <BR>&lt;portType&gt; name &lt;operation&gt; <BR>&lt;binding&gt; name<BR>type &lt;operation&gt; <BR>&lt;service&gt; name &lt;port&gt; <BR>&lt;part&gt; name<BR>type (空值) <BR>&lt;operation&gt; name<BR>parameterOrder &lt;input&gt;<BR>&lt;output&gt;<BR>&lt;fault&gt; <BR>&lt;input&gt; name<BR>message (空值) <BR>&lt;output&gt; name<BR>message (空值) <BR>&lt;fault&gt; name<BR><BR>message (空值) <BR>&lt;port&gt; name<BR>binding &lt;soap:address&gt; <BR><BR>資源：<BR>WSDL 1.1 <BR>SOAP 1.1 <BR>XML 結構描述入門 <BR>MS SOAP Toolkit 下載網站 <BR>IDL 至 WSDL 的轉譯工具 <BR>含 WSDL 至 VB 之 Proxy 產生器的免費網路資源 <BR>PocketSOAP：SOAP 之相關元件、工具、與原始程式碼 <BR><BR><BR>原文地址：<A href="http://stu.timeover.net/article.php?articleid=842">http://stu.timeover.net/article.php?articleid=842</A><img src ="http://www.blogjava.net/hopeshared/aggbug/34426.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-03-09 10:25 <a href="http://www.blogjava.net/hopeshared/archive/2006/03/09/34426.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>