﻿<?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-Sealyu-随笔分类-Java基础</title><link>http://www.blogjava.net/sealyu/category/30669.html</link><description>--- The devil's in the Details</description><language>zh-cn</language><lastBuildDate>Fri, 11 Jun 2010 18:16:35 GMT</lastBuildDate><pubDate>Fri, 11 Jun 2010 18:16:35 GMT</pubDate><ttl>60</ttl><item><title>Java Annotation手册（转）</title><link>http://www.blogjava.net/sealyu/archive/2010/03/29/316829.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 29 Mar 2010 06:51:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/03/29/316829.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/316829.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/03/29/316829.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/316829.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/316829.html</trackback:ping><description><![CDATA[<span  style="border-collapse: collapse; color: #333333; font-family: verdana, sans-serif; font-size: 13px; line-height: 17px; ">作者:cleverpig(作者的Blog:<a target="_new" href="http://blog.matrix.org.cn/page/cleverpig" style="color: #444444; text-decoration: none; ">http://blog.matrix.org.cn/page/cleverpig</a>)<br />
原文:<a target="_new" href="http://www.matrix.org.cn/resource/article/44/44055_Java+Annotation+Reflect.html" style="color: #444444; text-decoration: none; ">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 target="_new" href="http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html" style="color: #444444; text-decoration: none; ">《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的&#8220;name=value&#8221;结构对中。 annotation类型是一种接口，能够通过java反射API的方式提供对其信息的访问。<br />
<br />
annotation能被用来为某个程序元 素（类、方法、成员变量等）关联任何的信息。需要注意的是，这里存在着一个基本的潜规则：annotaion不能影响程序代码的执行，无论增加、删除 annotation，代码都始终如一的执行。另外，尽管一些annotation通过java的反射api方法在运行时被访问，而java语言解释器在 工作时忽略了这些annotation。正是由于java虚拟机忽略了annotation，导致了annotation类型在代码中是&#8220;不起作用&#8221;的； 只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理。本文中将涵盖标准的annotation和meta- annotation类型，陪伴这些annotation类型的工具是java编译器（当然要以某种特殊的方式处理它们）。<br />
<br />
由于上述原 因，annotation在使用时十分简便。一个本地变量可以被一个以NonNull命名的annotation类型所标注，来作为对这个本地变量不能被 赋予null值的断言。而我们可以编写与之配套的一个annotation代码分析工具，使用它来对具有前面变量的代码进行解析，并且尝试验证这个断言。 当然这些代码并不必自己编写。在JDK安装后，在JDK/bin目录中可以找到名为&#8220;apt&#8221;的工具，它提供了处理annotation的框架：它启动后 扫描源代码中的annotation，并调用我们定义好的annotation处理器完成我们所要完成的工作（比如验证前面例子中的断言）。说到这里， annotation的强大功能似乎可以替代XDoclet这类的工具了，随着我们的深入，大家会更加坚信这一点。<br />
注：详细描述请参看jsr250规范：<br />
<a target="_new" href="http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/" style="color: #444444; text-decoration: none; ">http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/</a><br />
<br />
<span style="color: blue; ">二、Annotation的定义：</span><br />
<br />
这 段文字开始介绍annotation相关技术。在此大家将看到java5.0的标准annotation类型，这种标准类型就是前文中所说的&#8220;内建&#8221;类 型，它们可以直接被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的&#8220;生命周期&#8221; 限制。<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 />
<strong>清单1：</strong><br />
<pre><br />
@Override<br />
public String toSting() {&nbsp;&nbsp; // 注意方法名拼写错了<br />
&nbsp;&nbsp;&nbsp;&nbsp;return "[" + super.toString() + "]";<br />
}<br />
</pre>
<br />
<br />
<span style="color: green; ">B.Deprecated：</span><br />
同 样Deprecated也是一个marker annotation。当一个类型或者类型成员使用@Deprecated修饰的话，编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 &#8220;延续性&#8221;：如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员，虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated，但编译器仍然要报警。<br />
值得注意，@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的：前者是java编译器识别的，而后者是被javadoc工具所识别用来生成文档（包含程序成员为什么已经过 时、它应当如何被禁止或者替代的描述）。<br />
在java5.0，java编译器仍然象其从前版本那样寻找@deprecated这个javadoc tag，并使用它们产生警告信息。但是这种状况将在后续版本中改变，我们应在现在就开始使用@Deprecated来修饰过时的方法而不是 @deprecated javadoc tag。<br />
<strong>清单2：</strong><br />
<pre><br />
下面是一段使用@Deprecated的代码：<br />
/**<br />
* 这里是javadoc的@deprecated声明.<br />
* @deprecated No one has players for this format any more.&nbsp;&nbsp;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 />
<strong>清单3：</strong><br />
<pre><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 />
<strong>清单4：</strong><br />
<pre><br />
@SuppressWarnings({"unchecked","fallthrough"})<br />
</pre>
<br />
如果SuppressWarnings所声明的被禁止警告个数为一个时，可以省去大括号：<br />
<pre><br />
@SuppressWarnings("unchecked")<br />
</pre>
<br />
<br />
<span style="color: blue; ">3。Annotation语法：</span><br />
<br />
在上一个章节中，我们看到书写marker annotation和单一成员annotation的语法。下面本人来介绍一下完整的语法：<br />
<br />
annotation 由&#8220;@+annotation类型名称+(..逗号分割的name-value对...)&#8221;组成。其中成员可以按照任何的顺序。如果annotation 类型定义了某个成员的默认值，则这个成员可以被省略。成员值必须为编译时常量、内嵌的annotation或者数组。<br />
<br />
下面我们将定义一个 annotation类型名为Reviews，它有一个由@Review annotation数组构成的成员。这个@Review annotation类型有三个成员："reviewer"是一个字符串，"comment" 是一个具有默认值的可选的字符串，"grade"是一个Review.Grade枚举类型值。<br />
<strong>清单5：</strong><br />
<pre><br />
@Reviews({&nbsp;&nbsp;// Single-value annotation, so "value=" is omitted here<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Review(grade=Review.Grade.EXCELLENT,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reviewer="df"),<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Review(grade=Review.Grade.UNSATISFACTORY,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reviewer="eg",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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 />
<strong>清单6：</strong><br />
<pre><br />
@UncheckedExceptions({<br />
&nbsp;&nbsp;&nbsp;&nbsp;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 />
<strong>清单7：</strong><br />
<pre><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 />
<strong>清单8：</strong><br />
<pre><br />
import java.lang.reflect.*;<br />
<br />
Class c = WhizzBangClass.class;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
Method m = c.getMethod("whizzy", int.class, int.class);&nbsp;&nbsp;<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 />
<strong>清单9：</strong><br />
<pre><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 />
&nbsp;&nbsp;&nbsp;&nbsp;Review.Grade grade = r.grade();<br />
&nbsp;&nbsp;&nbsp;&nbsp;String reviewer = r.reviewer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;String comment = r.comment();<br />
&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf("%s assigned a grade of %s and comment '%s'%n",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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 />
<strong>清单10：</strong><br />
<pre><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 />
<strong>清单11：</strong><br />
<pre><br />
/**<br />
* 使用Author这个annotation定义在程序中指出代码的作者<br />
*/<br />
public @interface Author {<br />
&nbsp;&nbsp;&nbsp;&nbsp;/** 返回作者名 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;String value();<br />
}<br />
</pre>
<br />
<br />
以 下的example更加复杂。Reviews annotation类型只有一个成员，但是这个成员的类型是复杂的：由Review annotation组成的数组。Review annotation类型有3个成员：枚举类型成员grade、表示Review名称的字符串类型成员Reviewer、具有默认值的字符串类型成员 Comment。<br />
<strong>清单12：</strong><br />
<pre><br />
import java.lang.annotation.*;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
/**<br />
* Reviews annotation类型只有一个成员，<br />
* 但是这个成员的类型是复杂的：由Review annotation组成的数组<br />
*/<br />
@Retention(RetentionPolicy.RUNTIME)<br />
public @interface Reviews {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Review[] value();<br />
}<br />
<br />
/**<br />
* Review annotation类型有3个成员： <br />
* 枚举类型成员grade、<br />
&nbsp;&nbsp;* 表示Review名称的字符串类型成员Reviewer、<br />
&nbsp;&nbsp;* 具有默认值的字符串类型成员Comment。<br />
*/<br />
public @interface Review {<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 内嵌的枚举类型<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY };<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 下面的方法定义了annotation的成员<br />
&nbsp;&nbsp;&nbsp;&nbsp;Grade grade();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;String reviewer();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;String comment() default "";&nbsp;&nbsp;<br />
}<br />
</pre>
<br />
<br />
最 后，我们来定义一个annotation方法用于罗列出类运行中所有的unchecked异常（上文已经提到这种情况不一定是错误）。这个 annotation类型将一个数组作为了唯一的成员。数组中的每个元素都是异常类。为了加强对未检查的异常（此类异常都是在运行时抛出）进行报告，我们 可以在代码中对异常的类型进行限制：<br />
<strong>清单13：</strong><br />
<pre><br />
public @interface UncheckedExceptions {<br />
&nbsp;&nbsp;&nbsp;&nbsp;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 />
<br />
凡是有该标志的文章，都是该blog博主Caoer（草儿）原创，凡是索引、收藏<br />
、转载请注明来处和原文作者。非常感谢。<br />
</span>
<img src ="http://www.blogjava.net/sealyu/aggbug/316829.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-03-29 14:51 <a href="http://www.blogjava.net/sealyu/archive/2010/03/29/316829.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java路径问题总结(转)</title><link>http://www.blogjava.net/sealyu/archive/2010/02/06/312178.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Sat, 06 Feb 2010 03:00:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/02/06/312178.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/312178.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/02/06/312178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/312178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/312178.html</trackback:ping><description><![CDATA[<div>
<p>平时写程序的时候,很多时候提示文件找不到,而抛出了异常,现在整理如下</p>
<p>一 相对路径的获得<br />
&nbsp;&nbsp;&nbsp; 说明:相对路径(即不写明时候到底相对谁)均可通过以下方式获得（不论是一般的java项目还是web项目）<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; String relativelyPath=<span style="color: #ff0000;">System.getProperty("user.dir");</span>
<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;上述相对路径中，java项目中的文件是相对于项目的根目录<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;web项目中的文件路径视不同的web服务器不同而不同（tomcat是相对于 tomcat安装目录"bin）<br />
<br />
<br />
二 类加载目录的获得(即当运行时某一类时获得其装载目录)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.1)通用的方法一(不论是一般的java项目还是web项目,先定位到能看到包路径的第一级目录)<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #ff0000;">InputStream is=TestAction.class.getClassLoader().getResourceAsStream("test.txt");</span>
<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (test.txt文件的路径为&nbsp; 项目名"src"test.txt;类TestAction所在包的第一级目录位于src目录下)<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 上式中将TestAction，test.txt替换成对应成相应的类名和文件名字即可<br />
<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<span style="color: #ff0000;"> 1.2)通用方法二&nbsp; (此方法和1.1中的方法类似,不同的是此方法必须以'/'开头) <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; InputStream is=Test1.class.getResourceAsStream("/test.txt");</span>
<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (test.txt文件的路径为&nbsp; 项目名"src"test.txt,类Test1所在包的第一级目录位于src目录下)<br />
<br />
&nbsp;&nbsp;&nbsp; &nbsp; <br />
<br />
<br />
三 web项目根目录的获得(发布之后)<br />
&nbsp;&nbsp;&nbsp; 1 从servlet出发 </p>
<p>&nbsp;&nbsp;&nbsp; 可建立一个servlet在其的init方法中写入如下语句<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletContext s1=this.getServletContext();<br />
&nbsp;&nbsp;<span style="color: #ff0000;">&nbsp; &nbsp;String temp=s1.getRealPath("/"); (关键)</span>
<br />
&nbsp;&nbsp;&nbsp; &nbsp;结果形如：D:"工具"Tomcat-6.0"webapps"002_ext"&nbsp; (002_ext为项目名字)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000;">如果是调用了s1.getRealPath("")则输出D:"工具"Tomcat-6.0"webapps"002_ext(少了一个""")</span>
</p>
<p>&nbsp;&nbsp; 2 从httpServletRequest出发</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String cp11111=<span style="color: #ff0000;">request.getSession().getServletContext().getRealPath("/");</span>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 结果形如:D:"工具"Tomcat-6.0"webapps"002_ext"</p>
<p>四&nbsp; classpath的获取(在Eclipse中为获得src或者classes目录的路径)</p>
<p>&nbsp;&nbsp;&nbsp; 方法一&nbsp;&nbsp;<span style="color: #ff0000;"> Thread.currentThread().getContextClassLoader().getResource("").getPath()</span>
</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; eg: String t=Thread.currentThread().getContextClassLoader().getResource("").getPath();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; System.out.println("t---"+t);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输出:t---/E:/order/002_ext/WebRoot/WEB-INF/classes/</p>
<p>&nbsp;&nbsp; 方法二 &nbsp;&nbsp;&nbsp;<span style="color: #ff0000;">&nbsp; JdomParse.class.getClassLoader().getResource("").getPath() </span>
&nbsp; (JdomParse为src某一个包中的类,下同)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eg:String p1=JdomParse.class.getClassLoader().getResource("").getPath();<br />
&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; System.out.println("JdomParse.class.getClassLoader().getResource--"+p1);</p>
<p>&nbsp;&nbsp; 输出: JdomParse.class.getClassLoader().getResource--/E:/order/002_ext/WebRoot/WEB-INF/classes/</p>
<p>另外,如果想把文件放在某一包中,则可以 通过以下方式获得到文件(先定位到该包的最后一级目录)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eg&nbsp; String p2=<span style="color: #ff0000;">JdomParse.class.getResource("").getPath();</span>
<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; System.out.println("JdomParse.class.getResource---"+p2); </p>
<p>&nbsp;&nbsp; 输出: JdomParse.class.getResource---/E:/order/002_ext/WebRoot/WEB-INF/classes/jdom/ (JdomParse为src目录下jdom包中的类)</p>
<p>四&nbsp;&nbsp; 属性文件的读取:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 方法 一</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>InputStream&nbsp;in&nbsp;=&nbsp;lnew&nbsp;BufferedInputStream(
new
&nbsp;FileInputStream(name));
</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Properties&nbsp;p&nbsp;=&nbsp;<span>new
&nbsp;Properties();&nbsp;
</span>
p.load(in);&nbsp;
</p>
<p>&nbsp;&nbsp;&nbsp; 注意路径的问题,做执行之后就可以调用p.getProperty("name")得到对应属性的值</p>
<p>方法二</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Locale locale = Locale.getDefault();&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #ff0000;"> ResourceBundle localResource = ResourceBundle.getBundle("test/propertiesTest", locale);&nbsp; </span>
<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String value = localResource.getString("test");&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; System.out.println("ResourceBundle: " + value);</p>
<p>&nbsp; &nbsp;&nbsp; 工程src目录下propertiesTest.properties(名字后缀必须为properties)文件内容如下:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test=hello word</p>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/312178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-02-06 11:00 <a href="http://www.blogjava.net/sealyu/archive/2010/02/06/312178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java(Web)中相对路径，绝对路径问题总结 （转）</title><link>http://www.blogjava.net/sealyu/archive/2010/02/06/312179.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Sat, 06 Feb 2010 03:00:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/02/06/312179.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/312179.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/02/06/312179.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/312179.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/312179.html</trackback:ping><description><![CDATA[<div>
<p>1.基本概念的理解</p>
<p>　　绝对路径：绝对路径就是你的主页上的文件或目录在硬盘上真正的路径，(URL和物理路径)例如：<br />
C:"xyz"test.txt&nbsp;代表了test.txt文件的绝对路径。<a href="http://www.sun.com/index.htm">http://www.sun.com/index.htm</a>也代表了一个<br />
URL绝对路径。</p>
<p>　　相对路径：相对与某个基准目录的路径。包含Web的相对路径（HTML中的相对目录），例如：在<br />
Servlet中，"/"代表Web应用的跟目录。和物理路径的相对表示。例如："./" 代表当前目录,<br />
"../"代表上级目录。这种类似的表示，也是属于相对路径。</p>
<p>另外关于URI，URL,URN等内容，请参考RFC相关文档标准。</p>
<p>RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax, <br />
(<a href="http://www.ietf.org/rfc/rfc2396.txt">http://www.ietf.org/rfc/rfc2396.txt</a>)</p>
<p><br />
2.关于JSP/Servlet中的相对路径和绝对路径。</p>
<p>2.1服务器端的地址 </p>
<p>　　&nbsp;服务器端的相对地址指的是相对于你的web应用的地址，这个地址是在服务器端解析的<br />
（不同于html和javascript中的相对地址，他们是由客户端浏览器解析的）也就是说这时候<br />
在jsp和servlet中的相对地址应该是相对于你的web应用，即相对于<a href="http://192.168.0.1/webapp/">http://192.168.0.1/webapp/</a>的。 </p>
<p>　　其用到的地方有： <br />
&nbsp;forward：servlet中的request.getRequestDispatcher(address);这个address是<br />
在服务器端解析的，所以，你要forward到a.jsp应该这么写：<br />
request.getRequestDispatcher(&#8220;/user/a.jsp&#8221;)这个/相对于当前的web应用webapp，<br />
其绝对地址就是：<a href="http://192.168.0.1/webapp/user/a.jsp">http://192.168.0.1/webapp/user/a.jsp</a>。 <br />
sendRedirect：在jsp中&lt;%response.sendRedirect("/rtccp/user/a.jsp");%&gt; </p>
<p>2.22、客户端的地址 <br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有的html页面中的相对地址都是相对于服务器根目录(<a href="http://192.168.0.1/">http://192.168.0.1/</a>)的，<br />
而不是(跟目录下的该Web应用的目录)http://192.168.0.1/webapp/的。 <br />
&nbsp;Html中的form表单的action属性的地址应该是相对于服务器根目录(<a href="http://192.168.0.1/">http://192.168.0.1/</a>)的，<br />
所以，如果提交到a.jsp为：action＝"/webapp/user/a.jsp"或action="&lt;%=request.getContextPath()%&gt;"/user/a.jsp；<br />
提交到servlet为actiom＝"/webapp/handleservlet" &nbsp;<br />
&nbsp;　　Javascript也是在客户端解析的，所以其相对路径和form表单一样。 <br />
&nbsp;</p>
<p>　　因此，一般情况下，在JSP/HTML页面等引用的CSS,Javascript.Action等属性前面最好都加上<br />
&lt;%=request.getContextPath()%&gt;,以确保所引用的文件都属于Web应用中的目录。<br />
另外，应该尽量避免使用类似".","./","../../"等类似的相对该文件位置的相对路径，这样<br />
当文件移动时，很容易出问题。</p>
<p><br />
3. JSP/Servlet中获得当前应用的相对路径和绝对路径<br />
3.1 JSP中获得当前应用的相对路径和绝对路径<br />
&nbsp;根目录所对应的绝对路径:request.getRequestURI()<br />
&nbsp;文件的绝对路径&nbsp;&nbsp;&nbsp; 　:application.getRealPath(request.getRequestURI());<br />
&nbsp;当前web应用的绝对路径 :application.getRealPath("/");<br />
&nbsp;取得请求文件的上层目录:new File(application.getRealPath(request.getRequestURI())).getParent()</p>
<p>3.2 Servlet中获得当前应用的相对路径和绝对路径<br />
&nbsp;根目录所对应的绝对路径:request.getServletPath();<br />
&nbsp;文件的绝对路径&nbsp;&nbsp;&nbsp; :request.getSession().getServletContext().getRealPath<br />
(request.getRequestURI())&nbsp;&nbsp;&nbsp;<br />
&nbsp;当前web应用的绝对路径 :servletConfig.getServletContext().getRealPath("/");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(ServletContext对象获得几种方式：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.servlet.http.HttpSession.getServletContext() <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.servlet.jsp.PageContext.getServletContext() <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.servlet.ServletConfig.getServletContext() <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)</p>
<p>4.java 的Class中获得相对路径，绝对路径的方法<br />
4.1单独的Java类中获得绝对路径<br />
根据java.io.File的Doc文挡，可知:<br />
&nbsp;默认情况下new File("/")代表的目录为：System.getProperty("user.dir")。<br />
&nbsp;一下程序获得执行类的当前路径<br />
package org.cheng.file;<br />
import java.io.File;</p>
<p>public class FileTest {<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) throws Exception {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;System.out.println(FileTest.class.getClassLoader().getResource(""));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>　　System.out.println(ClassLoader.getSystemResource(""));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;System.out.println(FileTest.class.getResource(""));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;System.out.println(FileTest.class.getResource("/")); //Class文件所在路径&nbsp; <br />
&nbsp;&nbsp;System.out.println(new File("/").getAbsolutePath());&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;System.out.println(System.getProperty("user.dir"));&nbsp;&nbsp;&nbsp; <br />
&nbsp;}<br />
}</p>
<p>4.2服务器中的Java类获得当前路径（来自网络）<br />
(1).Weblogic</p>
<p>WebApplication的系统文件根目录是你的weblogic安装所在根目录。<br />
例如：如果你的weblogic安装在c:"bea"weblogic700.....<br />
那么，你的文件根路径就是c:".<br />
所以，有两种方式能够让你访问你的服务器端的文件：<br />
a.使用绝对路径：<br />
比如将你的参数文件放在c:"yourconfig"yourconf.properties，<br />
直接使用 new FileInputStream("yourconfig/yourconf.properties");<br />
b.使用相对路径：<br />
相对路径的根目录就是你的webapplication的根路径，即WEB-INF的上一级目录，将你的参数文件放</p>
<p>在yourwebapp"yourconfig"yourconf.properties，<br />
这样使用：<br />
new FileInputStream("./yourconfig/yourconf.properties");<br />
这两种方式均可，自己选择。</p>
<p>(2).Tomcat</p>
<p>在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin</p>
<p>(3).Resin</p>
<p>不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET<br />
的路径为根.比如用新建文件法测试File f = new File("a.htm");<br />
这个a.htm在resin的安装目录下 </p>
<p>(4).如何读相对路径哪？</p>
<p>在Java文件中getResource或getResourceAsStream均可</p>
<p>例：getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web</p>
<p>发布根路径下WEB-INF/classes</p>
<p>默认使用该方法的路径是：WEB-INF/classes。已经在Tomcat中测试。</p>
</div>
<script type="text/javascript"><!--
google_ad_client = "pub-4348265167276910";
/* 468x60, 个人博客 */
google_ad_slot = "2046406163";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script>google_protectAndRun("ads_core.google_render_ad", google_handleError, google_render_ad);</script>
<img src ="http://www.blogjava.net/sealyu/aggbug/312179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-02-06 11:00 <a href="http://www.blogjava.net/sealyu/archive/2010/02/06/312179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Pragmatic Programmer --- My Notes</title><link>http://www.blogjava.net/sealyu/archive/2009/12/21/306776.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 21 Dec 2009 03:43:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/21/306776.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/306776.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/21/306776.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/306776.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/306776.html</trackback:ping><description><![CDATA[<br />
Chapter 1. A Pragmatic Philosophy<br />
<ol>
    <li>Pragmatic Programmer - An attitude, a style, a philosophy of approaching problems and their solutions. They think beyond the immediate problem, always trying to place it in its larger context, always trying to be aware of the bigger picture. <br />
    </li>
    <li>Take responsibility for everything they do. Don't sit idly by and watch their projects fall apart through neglect.<br />
    One of the cornerstones of the pragmatic philosophy is the idea of taking responsibility for yourself and your actions. Don't be afraid to admit ignorance or error. Be honest about our shortcomings-our ignorance as well as our mistakes. Don't blame someone else or make up an excuse. Have a contingency plan. <strong>Provide Options, Don't Make Lame Excuses.</strong><br />
    </li>
    <li>Learning is a continuous and ongoing process.</li>
    <li><strong>Don't Live with Broken Windows。</strong><br />
    Don't leave bad designs,wrong decisions, or poor code unrepaired. It's easy to slip into the mindset of "All the rest of this code is crap, I'll just follow suit." But if you find yourself on a team and a project where the code is pristinely beautiful-clean written, well designed, and elegant, you will likely take extra special care not to mess it up. Everyone dont't want to be the first one to make a mess.<br />
    </li>
</ol>
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/306776.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-21 11:43 <a href="http://www.blogjava.net/sealyu/archive/2009/12/21/306776.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈SUN JVM内存管理与应用服务器的优化之 服务器内存分配与优化(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/12/21/306755.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 21 Dec 2009 01:06:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/21/306755.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/306755.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/21/306755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/306755.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/306755.html</trackback:ping><description><![CDATA[作者：Jason S.H.Chen
<br />
<br />
上篇给大家介绍了SUN JVM的内存管理机制。本篇主要讲解与性能相关的JVM参数，怎样使用工具监控JVM的内存分配使用情况和怎样调整JVM参数让系统在特定硬件配置下达到最优化的性能。
<br />
通过上篇SUN JVM内存管理机制的介绍，大家都知道了SUN JVM内存分为永久存储区，伊甸园，幸存者1区，幸存者2区和养老区等几个区域。他们的作用以及垃圾回收处理过程在上篇也做了详细介绍。下面我们就来看看和这些内存分区相关的JVM参数。
<br />
<br />
JVM相关参数：
<br />
参数名	参数说明
<br />
-server 	启用能够执行优化的编译器, 显著提高服务器的性能，但使用能够执行优化的编译器时，服务器的预备时间将会较长。生产环境的服务器强烈推荐设置此参数。
<br />
-Xss	单个线程堆栈大小值；JDK5.0以后每个线程堆栈大小为1M，以前每个线程堆栈大小为256K。在相同物理内存下，减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的，不能无限生成，经验值在3000~5000左右。
<br />
-XX:+UseParNewGC
可用来设置年轻代为并发收集【多CPU】，如果你的服务器有多个CPU，你可以开启此参数；开启此参数，多个CPU可并发进行垃圾回收，可提高垃圾回收的
速度。此参数和+UseParallelGC，-XX:ParallelGCThreads搭配使用。
<br />
+UseParallelGC	选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下，年轻代使用并发收集，而年老代仍旧使用串行收集 。可提高系统的吞吐量。
<br />
-XX:ParallelGCThreads	年轻代并行垃圾收集的前提下（对并发也有效果）的线程数，增加并行度，即：同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
<br />
<br />
永久存储区相关参数：
<br />
参数名	参数说明
<br />
-Xnoclassgc 	每次永久存储区满了后一般GC算法在做扩展分配内存前都会触发一次FULL GC，除非设置了-Xnoclassgc.
<br />
-XX:PermSize	应用服务器启动时，永久存储区的初始内存大
<br />
-XX:MaxPermSize	应用运行中，永久存储区的极限值。为了不消耗扩大JVM永久存储区分配的开销，将此参数和-XX:PermSize这个两个值设为相等。
<br />
<br />
堆空间相关参数
<br />
参数名	参数说明
<br />
-Xms	启动应用时，JVM堆空间的初始大小值。
<br />
-Xmx	应用运行中，JVM堆空间的极限值。为了不消耗扩大JVM堆控件分配的开销，将此参数和-Xms这个两个值设为相等，考虑到需要开线程，讲此值设置为总内存的80%.
<br />
-Xmn	此参数硬性规定堆空间的新生代空间大小，推荐设为堆空间大小的1/4。
<br />
<br />
上面所列的JVM参数关系到系统的性能，而其中-XX:PermSize，-XX:MaxPermSize，-Xms，-Xmx和-Xmn这5个参数更是直接关系到系统的性能，系统是否会出现内存溢出。
<br />
-XX:PermSize和-XX:MaxPermSize分别设置应用服务器启动时，永久存储区的初始大小和极限大小；在生成环境中强烈推荐将
这个两个值设置为相同的值，以避免分配永久存储区的开销，具体的值可取系统&#8220;疲劳测试&#8221;获取到的永久存储区的极限值；如果不进行设置
-XX:MaxPermSize默认值为64M,一般来说系统的类定义文件大小都会超过这个默认值。
<br />
-Xms和-Xmx分别是服务器启动时，堆空间的初始大小和极限值。-Xms的默认值是物理内存的1/64但小于1G，-Xmx的默认值是物理
内存的1/4但小于1G.在生产环境中这些默认值是肯定不能满足我们的需要的。也就是你的服务器有8g的内存，不对JVM参数进行设置优化，应用服务器启
动时还是按默认值来分配和约束JVM对内存资源的使用，不会充分的利用所有的内存资源。
<br />
到此我们就不难理解上文提到的&#8220;我的服务器有8g内存，系统也就100M左右，居然出现内存溢出&#8221;这个&#8220;怪现象&#8221;了。在上文我曾提到&#8220;永久存储
区溢出（java.lang.OutOfMemoryError: Java Permanent
Space）&#8221;和&#8220;JVM堆空间溢出（java.lang.OutOfMemoryError: Java heap
space）&#8221;这两种溢出错误。现在大家都知道答案了：&#8220;永久存储区溢出（java.lang.OutOfMemoryError: Java
Permanent
Space）&#8221;乃是永久存储区设置太小，不能满足系统需要的大小，此时只需要调整-XX:PermSize和-XX:MaxPermSize这两个参数即
可。&#8220;JVM堆空间溢出（java.lang.OutOfMemoryError: Java heap
space）&#8221;错误是JVM堆空间不足，此时只需要调整-Xms和-Xmx这两个参数即可。
<br />
到此我们知道了，当系统出现内存溢出时，是哪些参数设置不合理需要调整。但我们怎么知道服务器启动时，到底JVM内存相关参数的值是多少呢。在
实践中，经常遇到对JVM参数进行设置了，并且自己心里觉得应该不会出现内存溢出了；但不幸的是内存溢出还是发生了。很多人百思不得其解，那我可以肯定地
告诉你，你设置的JVM参数并没有起作用（本文咱不探讨没有起作用的原因）。不信我们就去看看，下面介绍如何使用SUN公司的内存使用监控工具
jvmstat.
<br />
本文只介绍如何使用jvmstat查看内存使用，不介绍其安装配置。有兴趣的读者，可到SUN公司的官方网站下载一个，他本身已经带有非常详细
的安装配置文档了。这里假设你已经在你的应用服务器上配置好了jvmstat了。那我们就开始使用他来看看我们的服务器到底是有没有按照我们设置的参数启
动。
<br />
首先启动服务器，等服务器启动完。开启DOS窗口（此例子是在windows下完成，linux下同样），在dos窗口中输入jps这个命令。如下图
<br />
<br />
<img src="http://dl.javaeye.com/upload/attachment/182125/9a771bcf-9624-39cd-81ed-257dbdf3a6e2.jpg"  alt="" />
<br />
<br />
窗口中会显示所有JAVA应用进程列表，列表的第一列为应用的进程ID,第二列为应用的名字。在列表中找到你的应用服务器的进程ID,比如我这里的应用服务器进程ID为1856.在命令行输入visualgc 1856回车。进入jvmstat的主界面，如下图：
<br />
<br />
<img title="点击查看原始大小图片" src="http://dl.javaeye.com/upload/attachment/182127/3c20a080-a446-35da-8228-116e1f8a058e.jpg" width="760" height="416"  alt="" />
<br />
<br />
上图分别标注了伊甸园，幸存者0区，幸存者1区，养老区和永久存储区。图上直观的反应出各存储区的大小，已经使用的大小，剩下的空间大小，并用数
字标出了各区的大小；如果你这上面的数字和你设置的JVM参数相同的话，那么恭喜你，你设置的参数已经起作用，如果和你设置的不一致的话，那么你设置的参
数没有起作用（可能是服务器的启动方式没有载入你的JVM参数设置。）
<br />
在优化服务器的时候，这个工具很有用，他占用资源少。可以随应用服务器一直保持开启状态，如果系统发生内粗溢出，可以一眼就看出是那个区发生了溢出。根据观察结果进行进一步优化。
<img src ="http://www.blogjava.net/sealyu/aggbug/306755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-21 09:06 <a href="http://www.blogjava.net/sealyu/archive/2009/12/21/306755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Joda-Time 简介(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/12/18/306568.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 18 Dec 2009 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/18/306568.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/306568.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/18/306568.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/306568.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/306568.html</trackback:ping><description><![CDATA[<p>级别： 中级</p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#author">J Steven Perry</a>, 首席顾问, Makoto Consulting Group, Inc.<br />
</p>
<p>2009 年  12 月  14 日</p>
<blockquote>任
何企业应用程序都需要处理时间问题。应用程序需要知道当前的时间点和下一个时间点，有时它们还必须计算这两个时间点之间的路径。使用 JDK
完成这项任务将非常痛苦和繁琐。现在来看看 Joda Time，一个面向 Java&#8482;
平台的易于使用的开源时间/日期库。正如您在本文中了解的那样，Joda-Time 轻松化解了处理日期和时间的痛苦和繁琐。</blockquote>
<p>在编写企业应用程序时，我常常需要处理日期。并且在我的最新项目中 — 保险行业 — 纠正日期计算尤其重要。使用 <code>java.util.Calendar</code> 让我有些不安。如果您也曾使用这个类处理过日期/时间值，那么您就知道它使用起来有多麻烦。因此当我接触到 Joda-Time — 面向 Java 应用程序的日期/时间库的替代选择 — 我决定研究一下。其结果是：我很庆幸我这么做了。 </p>
<p>Joda-Time
令时间和日期值变得易于管理、操作和理解。事实上，易于使用是 Joda
的主要设计目标。其他目标包括可扩展性、完整的特性集以及对多种日历系统的支持。并且 Joda 与 JDK
是百分之百可互操作的，因此您无需替换所有 Java 代码，只需要替换执行日期/时间计算的那部分代码。 </p>
<table width="40%" align="right" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td>
            <table width="100%" border="1" cellpadding="5" cellspacing="0">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee">
                        <a name="N1008D"><strong>Joda 大型项目</strong></a><br />
                        <p>Joda
                        实际上是涵盖众多用于 Java 语言的替代 API 的大型项目，因此从技术上讲，使用 Joda 和 Joda-Time
                        名称表示相同的意思是一种误称。但在撰写本文之际，Joda-Time API 目前似乎是唯一处于活跃开发状态下的 Joda API。考虑到
                        Joda 大型项目的当前状态，我想将 Joda-Time 简称为 Joda 应该没什么问题。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>本文将介绍并展示如何使用它。我将介绍以下主题： </p>
<ul>
    <li>日期/时间替代库简介</li>
    <li>Joda 的关键概念</li>
    <li>创建 Joda-Time 对象</li>
    <li>以 Joda 的方式操作时间  style</li>
    <li>以 Joda 的方式格式化时间</li>
</ul>
<p>您可以 <a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#download">下载</a> 演示这些概念的样例应用程序的源代码。</p>
<p><a name="N100B3">Joda 简介</a></p>
<p>为什么要使用 Joda？考虑创建一个用时间表示的某个随意的时刻 — 比如，2000 年 1 月 1 日 0 时 0 分。我如何创建一个用时间表示这个瞬间的 JDK 对象？使用 <code>java.util.Date</code>？事实上这是行不通的，因为自 JDK 1.1 之后的每个 Java 版本的 Javadoc 都声明应当使用 <code>java.util.Calendar</code>。<code>Date</code> 中不赞成使用的构造函数的数量严重限制了您创建此类对象的途径。 </p>
<p>然而，<code>Date</code> 确实有一个构造函数，您可以用来创建用时间表示某个瞬间的对象（除 &#8220;现在&#8221; 以外）。该方法使用距离 1970 年 1 月 1 日子时格林威治标准时间（也称为 <em>epoch</em>）以来的毫秒数作为一个参数，对时区进行校正。考虑到 Y2K 对软件开发企业的重要性，您可能会认为我已经记住了这个值 — 但是我没有。<code>Date</code> 也不过如此。</p>
<p>那么 <code>Calendar</code> 又如何呢？我将使用下面的方式创建必需的实例： </p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>Calendar calendar = Calendar.getInstance();<br />
            calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>使用 Joda，代码应该类似如下所示： </p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这一行简单代码没有太大的区别。但是现在我将使问题稍微复杂化。假设我希望在这个日期上加上 90 天并输出结果。使用 JDK，我需要使用清单 1 中的代码： </p>
<br />
<a name="listing1"><strong>清单 1. 以 JDK 的方式向某一个瞬间加上 90 天并输出结果</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            Calendar calendar = Calendar.getInstance();<br />
            calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);<br />
            SimpleDateFormat sdf =<br />
            new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS");<br />
            calendar.add(Calendar.DAY_OF_MONTH, 90);<br />
            System.out.println(sdf.format(calendar.getTime()));<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>使用 Joda，代码如清单 2 所示： </p>
<br />
<a name="listing2"><strong>清单 2. 以 Joda 的方式向某一个瞬间加上 90 天并输出结果 </strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);<br />
            System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>两者之间的差距拉大了（Joda 用了两行代码，JDK 则是 5 行代码）。</p>
<p>现在假设我希望输出这样一个日期：距离 Y2K 45 天之后的某天在下一个月的当前周的最后一天的日期。坦白地说，我甚至不想使用 <code>Calendar</code> 处理这个问题。使用 JDK 实在太痛苦了，即使是简单的日期计算，比如上面这个计算。正是多年前的这样一个时刻，我第一次领略到 Joda-Time 的强大。使用 Joda，用于计算的代码如清单 3 所示： </p>
<br />
<a name="listing3"><strong>清单 3. 改用 Joda</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);<br />
            System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek()<br />
            .withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 3 的输出为：</p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>Sun 03/19/2000 00:00:00.000</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>如果您正在寻找一种易于使用的方式替代 JDK 日期处理，那么您真的应该考虑 Joda。如果不是这样的话，那么继续痛苦地使用 <code>Calendar</code> 完成所有日期计算吧。当您做到这一点后，您完全可以做到使用几把剪刀修建草坪并使用一把旧牙刷清洗您的汽车。 </p>
<p><a name="N10125">Joda 和 JDK 互操作性</a></p>
<p>JDK <code>Calendar</code> 类缺乏可用性，这一点很快就能体会到，而 Joda 弥补了这一不足。Joda 的设计者还做出了一个决定，我认为这是它取得成功的构建：JDK 互操作性。Joda 的类能够生成（但是，正如您将看到的一样，有时会采用一种比较迂回的方式）<code>java.util.Date</code> 的实例（和 <code>Calendar</code>）。这使您能够保留现有的依赖 JDK 的代码，但是又能够使用 Joda 处理复杂的日期/时间计算。 </p>
<p>例如，完成 <a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#listing3">清单 3</a> 中的计算后。我只需要做出如清单 4 所示的更改就可以返回到 JDK 中：</p>
<br />
<a name="listing4"><strong>清单 4. 将 Joda 计算结果插入到 JDK 对象中</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            Calendar calendar = Calendar.getInstance();<br />
            DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);<br />
            System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek()<br />
            .withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");<br />
            calendar.setTime(dateTime.toDate());<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>就是这么简单。我完成了计算，但是可以继续在 JDK 对象中处理结果。这是 Joda 的一个非常棒的特性。 </p>
<br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" height="6" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" width="16" border="0" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1014D">Joda 的关键日期/时间概念</a></p>
<p>Joda 使用以下概念，它们可以应用到任何日期/时间库： </p>
<ul>
    <li>不可变性（Immutability）</li>
    <li>瞬间性（Instant）</li>
    <li>局部性（Partial）</li>
    <li>年表（Chronology）</li>
    <li>时区（Time zone）</li>
</ul>
<p>我将针对 Joda 依次讨论每一个概念。 </p>
<p><a name="N1016B">不可变性</a></p>
<p>我
在本文讨论的 Joda 类具有不可变性，因此它们的实例无法被修改。（不可变类的一个优点就是它们是线程安全的）。我将向您展示的用于处理日期计算的
API 方法全部返回一个对应 Joda 类的新实例，同时保持原始实例不变。当您通过一个 API 方法操作 Joda
类时，您必须捕捉该方法的返回值，因为您正在处理的实例不能被修改。您可能对这种模式很熟悉；比如，这正是 <code>java.lang.String</code> 的各种操作方法的工作方式。 </p>
<p><a name="N10178">瞬间性</a></p>
<p><code>Instant</code> 表示时间上的某个精确的时刻，使用从 epoch 开始计算的毫秒表示。这一定义与 JDK 相同，这就是为什么任何 Joda <code>Instant</code> 子类都可以与 JDK <code>Date</code> 和 <code>Calendar</code> 类兼容的原因。</p>
<p>更通用一点的定义是：一个<em>瞬间</em> 就是指时间线上只出现一次且唯一的一个时间点，并且这种日期结构只能以一种有意义的方式出现一次。 </p>
<p><a name="N10196">局部性</a></p>
<p>一个局部时间，正如我将在本文中将其称为局部时间片段一样，它指的是时间的一部分片段。瞬间性指定了与 epoch 相对的时间上的一个精确时刻，与此相反，局部时间片段指的是在时间上可以来回 &#8220;移动&#8221; 的一个时刻，这样它便可以应用于多个实例。比如，<em>6 月 2 日</em> 可以应用于任意一年的 6 月份（使用 Gregorian 日历）的第二天的任意瞬间。同样，<em>11:06 p.m.</em> 可以应用于任意一年的任意一天，并且每天只能使用一次。即使它们没有指定一个时间上的精确时刻，局部时间片段仍然是有用的。 </p>
<p>我喜欢将局部时间片段看作一个重复周期中的一点，这样的话，如果我正在考虑的日期构建可以以一种有意义的方式出现多次（即重复的），那么它就是一个局部时间。 </p>
<p><a name="N101A8">年表</a></p>
<p>Joda 本质 — 以及其设计核心  — 的关键就是<em>年表</em>（它的含义由一个同名抽象类捕捉）。从根本上讲，年表是一种日历系统 — 一种计算时间的特殊方式 — 并且是一种在其中执行日历算法的框架。受 Joda 支持的年表的例子包括：</p>
<ul>
    <li>ISO（默认）</li>
    <li>Coptic</li>
    <li>Julian</li>
    <li>Islamic</li>
</ul>
<p>Joda-Time 1.6 支持 8 种年表，每一种都可以作为特定日历系统的计算引擎。 </p>
<p><a name="N101CE">时区</a></p>
<p>时
区是值一个相对于英国格林威治的地理位置，用于计算时间。要了解事件发生的精确时间，还必须知道发生此事件的位置。任何严格的时间计算都必须涉及时区（或
相对于 GMT），除非在同一个时区内发生了相对时间计算（即时这样时区也很重要，如果事件对于位于另一个时区的各方存在利益关系的话）。</p>
<p><code>DateTimeZone</code> 是 Joda 库用于封装位置概念的类。许多日期和时间计算都可以在不涉及时区的情况下完成，但是仍然需要了解 <code>DateTimeZone</code> 如何影响 Joda 的操作。默认时间，即从运行代码的机器的系统时钟检索到的时间，在大部分情况下被使用。 </p>
<br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" height="6" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" width="16" border="0" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="jto">创建 Joda-Time 对象</a></p>
<p>现在，我将展示在采用该库时会经常遇到的一些 Joda 类，并展示如何创建这些类的实例。 </p>
<table width="40%" align="right" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td>
            <table width="100%" border="1" cellpadding="5" cellspacing="0">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee">
                        <a name="N101ED"><strong>可变的 Joda 类</strong></a><br />
                        <p>我并不是可变实用类的粉丝；我只是认为它们的用例并不适合广泛使用。但是如果您认为您的确需要使用可变 Joda 类的话，本节的内容应当会对您的项目有帮助。<code>Readable</code> 和 <code>ReadWritable</code> API 之间的唯一区别在于 <code>ReadWritable</code> 类能够改变封装的日期/时间值，因此我在这里将不再介绍这一点。 </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>本节中介绍的所有实现都具有若干构造函数，允许您初始化封装的日期/时间。它们可以分为 4 个类别： </p>
<ul>
    <li>使用系统时间。</li>
    <li>使用多个字段指定一个瞬间时刻（或局部时间片段），达到这个特定实现所能支持的最细粒度的精确度。 </li>
    <li>指定一个瞬间时刻（或局部时间片段），以毫秒为单位。</li>
    <li>使用另一个对象（例如，<code>java.util.Date</code>，或者是另一个 Joda 对象）。</li>
</ul>
<p>我将在第一个类中介绍这些构造函数：<code> DateTime</code>。当您使用其他 Joda 类的相应构造函数时，也可以使用这里介绍的内容。</p>
<table width="40%" align="right" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td>
            <table width="100%" border="1" cellpadding="5" cellspacing="0">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee">
                        <a name="N10223"><strong>重载方法</strong></a><br />
                        <p>如果您创建了一个 <code>DateTime</code> 的实例，并且没有提供 <code>Chronology</code> 或 <code>DateTimeZone</code>，Joda 将使用 <code>ISOChronology</code>（默认）和 <code>DateTimeZone</code>（来自系统设置）。然而，Joda <code>ReadableInstant</code> 子类的所有构造函数都包含一个超载方法，该方法以一个 <code>Chronology</code> 或 <code>DateTimeZone</code> 为参数。本文附带的应用程序的的样例代码展示了如何使用这些超载方法（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#download">下载</a>）。我在这里不会再详细介绍它们，因为这些方法使用起来非常简单。然而，我建议您试着使用一下这个样例应用程序，看看编写您的应用程序代码有多么简单，这样您就可以随意地在 Joda 的 <code>Chronology</code> 和 <code>DateTimeZone</code> 之间切换，同时不会影响到代码的其余部分。 </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p><a name="N10259"><strong><code>ReadableInstant</code></strong></a></p>
<p>Joda 通过 <code>ReadableInstant</code> 类实现了瞬间性这一概念。表示时间上的不可变瞬间的 Joda 类都属于这个类的子类。（将这个类命名为 <code>ReadOnlyInstant</code> 可能更好，我认为这才是设计者需要传达的意思）。换句话说，<code>ReadableInstant</code> 表示时间上的某一个不可修改的瞬间）。其中的两个子类分别为 <code>DateTime</code> 和
<code>DateMidnight</code>：</p>
<ul>
    <li><strong><code>DateTime</code></strong>：这是最常用的一个类。它以毫秒级的精度封装时间上的某个瞬间时刻。<code>DateTime</code> 始终与 <code>DateTimeZone</code> 相关，如果您不指定它的话，它将被默认设置为运行代码的机器所在的时区。
    <p>可以使用多种方式构建 <code>DateTime</code> 对象。这个构造函数使用系统时间：</p>
    <table width="60%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>DateTime dateTime = new DateTime();<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    一般来讲，我会尽量避免使用系统时钟来初始化应用程序的实际，而是倾向于外部化设置应用程序代码使用的系统时间。样例应用程序执行以下代码：
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>DateTime dateTime = SystemFactory.getClock().getDateTime();<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    这使得使用不同日期/时间测试我的代码变得更加简单：我不需要修改代码来在应用程序中运行不同的日期场景，因为时间是在 <code>SystemClock</code> 实现的内部设置的，而不是在应用程序的内部。（我可以修改系统时间，但是那实在太痛苦了！）
    <p>下面的代码使用一些字段值构建了一个 <code>DateTime</code> 对象：</p>
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>DateTime dateTime = new DateTime(<br />
                2000, //year<br />
                1,    // month<br />
                1,    // day<br />
                0,    // hour (midnight is zero)<br />
                0,    // minute<br />
                0,    // second<br />
                0     // milliseconds<br />
                );<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <p>正如您所见，Joda 可以使您精确地控制创建 <code>DateTime</code> 对象的方式，该对象表示时间上的某个特定的瞬间。每一个 Joda 类都有一个与此类似的构造函数，您在此构造函数中指定 Joda 类可以包含的所有字段。您可以用它快速了解特定类在哪一种粒度级别上操作。 </p>
    <p>下一个构造函数将指定从 epoch 到某个时刻所经过的毫秒数。它根据 JDK <code>Date</code> 对象的毫秒值创建一个 <code>DateTime</code> 对象，其时间精度用毫秒表示，因为 epoch 与 Joda 是相同的：</p>
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>java.util.Date jdkDate = obtainDateSomehow();<br />
                long timeInMillis = jdkDate.getTime();<br />
                DateTime dateTime = new DateTime(timeInMillis);<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <p>并且这个例子与前例类似，唯一不同之处是我在这里将 <code>Date</code> 对象直接传递给构造函数：</p>
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>java.util.Date jdkDate = obtainDateSomehow();<br />
                dateTime = new DateTime(jdkDate);<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <p>Joda 支持使用许多其他对象作为构造函数的参数，用于创建 <code>DateTime</code>，如清单 5 所示：</p>
    <br />
    <br />
    <a name="listing5"><strong>清单 5. 直接将不同对象传递给 <code>DateTime</code> 的构造函数</strong></a><br />
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>						<br />
                // Use a Calendar<br />
                java.util.Calendar calendar = obtainCalendarSomehow();<br />
                dateTime = new DateTime(calendar);<br />
                // Use another Joda DateTime<br />
                DateTime anotherDateTime = obtainDateTimeSomehow();<br />
                dateTime = new DateTime(anotherDateTime);<br />
                // Use a String (must be formatted properly)<br />
                String timeString = "2006-01-26T13:30:00-06:00";<br />
                dateTime = new DateTime(timeString);<br />
                timeString = "2006-01-26";<br />
                dateTime = new DateTime(timeString);<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <p>注意，如果您准备使用 <code>String</code>（必须经过解析），您必须对其进行精确地格式化。参考 Javadoc，获得有关 Joda 的 <code>ISODateTimeFormat</code> 类的更多信息（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#resources">参考资料</a>）。</p>
    </li>
    <li><strong><code>DateMidnight</code></strong>：这个类封装某个时区（通常为默认时区）在特定年/月/日的午夜时分的时刻。它基本上类似于 <code>DateTime</code>，不同之处在于时间部分总是为与该对象关联的特定 <code>DateTimeZone</code> 时区的午夜时分。</li>
</ul>
<p>您将在本文看到的其他类都遵循与 <code>ReadableInstant</code> 类相同的模式（Joda Javadoc 将显示这些内容），因此为了节省篇幅，我将不会在以下小节介绍这些内容。 </p>
<p><a name="N10304"><strong><code>ReadablePartial</code></strong></a></p>
<p>应用程序所需处理的日期问题并不全部都与时间上的某个完整时刻有关，因此您可以处理一个局部时刻。例如，有时您比较关心年/月/日，或者一天中的时间，甚至是一周中的某天。Joda 设计者使用 <code>ReadablePartial</code> 接口捕捉这种表示局部时间的概念，这是一个不可变的局部时间片段。用于处理这种时间片段的两个有用类分别为 <code>LocalDate</code> 和 <code>LocalTime</code>：</p>
<ul>
    <li><strong><code>LocalDate</code></strong>：该类封装了一个年/月/日的组合。当地理位置（即时区）变得不重要时，使用它存储日期将非常方便。例如，某个特定对象的<em>出生日期</em> 可能为 <em>1999 年 4 月 16 日</em>，但是从技术角度来看，在保存所有业务值的同时不会了解有关此日期的任何其他信息（比如这是一周中的星期几，或者这个人出生地所在的时区）。在这种情况下，应当使用 <code>LocalDate</code>。
    <p>样例应用程序使用 <code>SystemClock</code> 来获取被初始化为系统时间的 <code>LocalDate</code> 的实例： </p>
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>LocalDate localDate = SystemFactory.getClock().getLocalDate();<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <p>也可以通过显式地提供所含的每个字段的值来创建 <code>LocalDate</code>：</p>
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>LocalDate localDate = new LocalDate(2009, 9, 6);// September 6, 2009<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <p><code>LocalDate</code> 替代了在早期 Joda 版本中使用的 <code>YearMonthDay</code>。</p>
    </li>
    <li><strong><code>LocalTime</code></strong>：
    这个类封装一天中的某个时间，当地理位置不重要的情况下，可以使用这个类来只存储一天当中的某个时间。例如，晚上 11:52
    可能是一天当中的一个重要时刻（比如，一个 cron
    任务将启动，它将备份文件系统的某个部分），但是这个时间并没有特定于某一天，因此我不需要了解有关这一时刻的其他信息。
    <p>样例应用程序使用 <code>SystemClock</code> 获取被初始化为系统时间的 <code>LocalTime</code> 的一个实例：</p>
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>LocalTime localTime = SystemFactory.getClock().getLocalTime();<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <p>也可以通过显式地提供所含的每个字段的值来创建 <code>LocalTime</code>：</p>
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td>
                <pre>LocalTime localTime = new LocalTime(13, 30, 26, 0);// 1:30:26PM<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    </li>
</ul>
<p><a name="N10375">时间跨度</a></p>
<p>了解特定的时刻或是某个局部时间片段将非常有用，但是如果能够表达一段时间跨度的话，通常也很有用。Joda 提供了三个类来简化这个过程。您可以选择用于表示不同跨度的类：</p>
<ul>
    <li><strong><code>Duration</code></strong>：这个类表示一个绝对的精确跨度，使用毫秒为单位。这个类提供的方法可以用于通过标准的数学转换（比如 1 分钟 = 60 秒，1 天 = 24 小时），将时间跨度转换为标准单位（比如秒、分和小时）。
    <p>您只在以下情况使用 <code>Duration</code> 的实例：您希望转换一个时间跨度，但是您并不关心这个时间跨度在何时发生，或者使用毫秒处理时间跨度比较方便。</p>
    </li>
    <li><strong><code>Period</code></strong>：这个类表示与 <code>Duration</code> 相同的概念，但是以人们比较熟悉的单位表示，比如年、月、周。
    <p>您可以在以下情况使用 <code>Period</code>：您并不关心这段时期必须在何时发生，或者您更关心检索单个字段的能力，这些字段描述由 <code>Period</code> 封装的时间跨度。  </p>
    </li>
    <li><strong><code>Interval</code></strong>：这个类表示一个特定的时间跨度，将使用一个明确的时刻界定这段时间跨度的范围。<code>Interval</code> 为<em>半开</em> 区间，这表示由 <code>Interval</code> 封装的时间跨度包括这段时间的起始时刻，但是不包含结束时刻。
    <p>可以在以下情况使用 <code>Interval</code>：需要表示在时间连续区间中以特定的点开始和结束的一段时间跨度。  </p>
    </li>
</ul>
<br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" height="6" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" width="16" border="0" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N103BE">以 Joda 的方式处理时间</a></p>
<p>现在，您已经了解了如何创建一些非常有用的 Joda 类，我将向您展示如何使用它们执行日期计算。接着您将了解到 Joda 如何轻松地与 JDK 进行互操作。</p>
<p><a name="N103C7">日期计算</a></p>
<p>如果您只是需要对日期/时间信息使用占位符，那么 JDK 完全可以胜任，但是它在日期/时间计算方面的表现十分糟糕，而这正是 Joda 的长处。我将向您展示一些简单的例子。 </p>
<p>假设在当前的系统日期下，我希望计算上一个月的最后一天。对于这个例子，我并不关心一天中的时间，因为我只需要获得年/月/日，如清单 6 所示： </p>
<br />
<a name="listing6"><strong>清单 6. 使用 Joda 计算日期</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            LocalDate now = SystemFactory.getClock().getLocalDate();<br />
            LocalDate lastDayOfPreviousMonth ="<br />
            now.minusMonths(1).dayOfMonth().withMaximumValue(); <br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>您可能对清单 6 中的 <code>dayOfMonth()</code> 调用感兴趣。这在 Joda 中被称为<em>属性（property）</em>。它相当于 Java 对象的属性。属性是根据所表示的常见结构命名的，并且它被用于访问这个结构，用于完成计算目的。属性是实现 Joda 计算威力的关键。您目前所见到的所有 4 个 Joda 类都具有这样的属性。一些例子包括： </p>
<ul>
    <li><code>yearOfCentury</code></li>
    <li><code>dayOfYear</code></li>
    <li><code>monthOfYear</code></li>
    <li><code>dayOfMonth</code></li>
    <li><code>dayOfWeek</code></li>
</ul>
<p>我将详细介绍清单 6 中的示例，以向您展示整个计算过程。首先，我从当前月份减去一个月，得到 &#8220;上一个月&#8221;。接着，我要求获得 <code>dayOfMonth</code> 的最大值，它使我得到这个月的最后一天。注意，这些调用被连接到一起（注意 Joda <code>ReadableInstant</code> 子类是不可变的），这样您只需要捕捉调用链中最后一个方法的结果，从而获得整个计算的结果。 </p>
<p>当计算的中间结果对我不重要时，我经常会使用这种计算模式。（我以相同的方式使用 JDK 的 <code>BigDecimal</code>）。假设您希望获得任何一年中的第 11 月的第一个星期二的日期，而这天必须是在这个月的第一个星期一之后。清单 7 展示了如何完成这个计算： </p>
<br />
<a name="listing7"><strong>清单 7. 计算 11 月中第一个星期一之后的第一个星期二</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            LocalDate now = SystemFactory.getClock().getLocalDate();<br />
            LocalDate electionDate = now.monthOfYear()<br />
            .setCopy(11)        // November<br />
            .dayOfMonth()       // Access Day Of Month Property<br />
            .withMinimumValue() // Get its minimum value<br />
            .plusDays(6)        // Add 6 days<br />
            .dayOfWeek()        // Access Day Of Week Property<br />
            .setCopy("Monday")  // Set to Monday (it will round down)<br />
            .plusDays(1);       // Gives us Tuesday<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 7 的注释帮助您了解代码如何获得结果。<code>.setCopy("Monday")</code> 是整个计算的关键。不管中间 <code>LocalDate</code> 值是多少，将其 <code>dayOfWeek</code> 属性设置为 Monday 总是能够四舍五入，这样的话，在每月的开始再加上 6 天就能够让您得到第一个星期一。再加上一天就得到第一个星期二。Joda 使得执行此类计算变得非常容易。 </p>
<p>下面是其他一些因为使用 Joda 而变得超级简单的计算： </p>
<p>以下代码计算从现在开始经过两个星期之后的日期： </p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>DateTime now = SystemFactory.getClock().getDateTime();<br />
            DateTime then = now.plusWeeks(2);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>您可以以这种方式计算从明天起 90 天以后的日期：</p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>DateTime now = SystemFactory.getClock().getDateTime();<br />
            DateTime tomorrow = now.plusDays(1);<br />
            DateTime then = tomorrow.plusDays(90);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>（是的，我也可以向 <code>now</code> 加 91 天，那又如何呢？）</p>
<p>下面是计算从现在起 156 秒之后的时间：</p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>DateTime now = SystemFactory.getClock().getDateTime();<br />
            DateTime then = now.plusSeconds(156);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>下面的代码将计算五年后的第二个月的最后一天：</p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>DateTime now = SystemFactory.getClock().getDateTime();<br />
            DateTime then = now.minusYears(5) // five years ago<br />
            .monthOfYear()     // get monthOfYear property<br />
            .setCopy(2)        // set it to February<br />
            .dayOfMonth()      // get dayOfMonth property<br />
            .withMaximumValue();// the last day of the month<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这样的例子实在太多了，我向您已经知道了如何计算。尝试操作一下样例应用程序，亲自体验一下使用 Joda 计算任何日期是多么有趣。</p>
<p><a name="N10455">JDK 互操作性</a></p>
<p>我的许多代码都使用了 JDK <code>Date</code> 和 <code>Calendar</code> 类。但是幸亏有 Joda，我可以执行任何必要的日期算法，然后再转换回 JDK 类。这将两者的优点集中到一起。您在本文中看到的所有 Joda 类都可以从 JDK <code>Calendar</code> 或 <code>Date</code> 创建，正如您在 <a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#jto">创建 Joda-Time 对象</a> 中看到的那样。出于同样的原因，可以从您所见过的任何 Joda 类创建 JDK <code>Calendar</code> 或 <code>Date</code>。</p>
<p>清单 8 展示了从 Joda <code>ReadableInstant</code> 子类转换为 JDK 类有多么简单： </p>
<br />
<a name="listing8"><strong>清单 8. 从 Joda <code>DateTime</code> 类创建 JDK 类</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            DateTime dateTime = SystemFactory.getClock().getDateTime();<br />
            Calendar calendar = dateTime.toCalendar(Locale.getDefault());<br />
            Date date = dateTime.toDate();<br />
            DateMidnight dateMidnight = SystemFactory.getClock()<br />
            .getDateMidnight();<br />
            date = dateMidnight.toDate();<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>对于 <code>ReadablePartial</code> 子类，您还需要经过额外一步，如清单 9 所示： </p>
<br />
<a name="listing9"><strong>清单 9. 创建表示 <code>LocalDate</code> 的 <code>Date</code> 对象</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            LocalDate localDate = SystemFactory.getClock().getLocalDate();<br />
            Date date = localDate.toDateMidnight().toDate();<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>要创建 <code>Date</code> 对象，它表示从清单 9 所示的 <code>SystemClock</code> 中获得的 <code>LocalDate</code>，您必须首先将它转换为一个 <code>DateMidnight</code> 对象，然后只需要将 <code>DateMidnight</code> 对象作为 <code>Date</code>。（当然，产生的 <code>Date</code> 对象将把它自己的时间部分设置为午夜时刻）。 </p>
<p>JDK 互操作性被内置到 Joda API 中，因此您无需全部替换自己的接口，如果它们被绑定到 JDK 的话。比如，您可以使用 Joda 完成复杂的部分，然后使用 JDK 处理接口。</p>
<br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" height="6" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" width="16" border="0" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N104C8">以 Joda 方式格式化时间</a></p>
<p>使用 JDK 格式化日期以实现打印是完全可以的，但是我始终认为它应该更简单一些。这是 Joda 设计者进行了改进的另一个特性。要格式化一个 Joda 对象，调用它的 <code>toString()</code> 方法，并且如果您愿意的话，传递一个标准的 ISO-8601 或一个 JDK 兼容的控制字符串，以告诉 JDK 如何执行格式化。不需要创建单独的 <code>SimpleDateFormat</code> 对象（但是 Joda 的确为那些喜欢自找麻烦的人提供了一个 <code>DateTimeFormatter</code> 类）。调用 Joda 对象的 <code>toString()</code> 方法，仅此而已。我将展示一些例子。 </p>
<p>清单 10 使用了 <code>ISODateTimeFormat</code> 的静态方法：</p>
<br />
<a name="listing10"><strong>清单 10. 使用 ISO-8601</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            DateTime dateTime = SystemFactory.getClock().getDateTime();<br />
            dateTime.toString(ISODateTimeFormat.basicDateTime());<br />
            dateTime.toString(ISODateTimeFormat.basicDateTimeNoMillis());<br />
            dateTime.toString(ISODateTimeFormat.basicOrdinalDateTime());<br />
            dateTime.toString(ISODateTimeFormat.basicWeekDateTime());<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 10 中的四个 <code>toString()</code> 调用分别创建了以下内容：</p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>20090906T080000.000-0500<br />
            20090906T080000-0500<br />
            2009249T080000.000-0500<br />
            2009W367T080000.000-0500<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>您也可以传递与 <code>SimpleDateFormat</code> JDK 兼容的格式字符串，如清单 11 所示： </p>
<br />
<a name="listing11"><strong>清单 11. 传递 <code>SimpleDateFormat</code> 字符串</strong></a><br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>
            <pre>				<br />
            DateTime dateTime = SystemFactory.getClock().getDateTime();<br />
            dateTime.toString("MM/dd/yyyy hh:mm:ss.SSSa");<br />
            dateTime.toString("dd-MM-yyyy HH:mm:ss");<br />
            dateTime.toString("EEEE dd MMMM, yyyy HH:mm:ssa");<br />
            dateTime.toString("MM/dd/yyyy HH:mm ZZZZ");<br />
            dateTime.toString("MM/dd/yyyy HH:mm Z");<br />
            <br />
            09/06/2009 02:30:00.000PM<br />
            06-Sep-2009 14:30:00<br />
            Sunday 06 September, 2009 14:30:00PM<br />
            09/06/2009 14:30 America/Chicago<br />
            09/06/2009 14:30 -0500<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>查看 Javadoc 中有关 <code>joda.time.format.DateTimeFormat</code> 的内容，获得与 JDK <code>SimpleDateFormat</code> 兼容的格式字符串的更多信息，并且可以将其传递给 Joda 对象的 <code>toString()</code> 方法。 </p>
<br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" height="6" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" width="16" border="0" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1051F">结束语</a></p>
<p>谈
到日期处理，Joda 是一种令人惊奇的高效工具。无论您是计算日期、打印日期，或是解析日期，Joda
都将是工具箱中的便捷工具。在本文中，我首先介绍了 Joda，它可以作为 JDK 日期/时间库的替代选择。然后介绍了一些 Joda
概念，以及如何使用 Joda 执行日期计算和格式化。 </p>
<p>Joda-Time
衍生了一些相关的项目，您可能会发现这些项目很有用。现在出现了一个针对 Grails Web 开发框架的 Joda-Time
插件。joda-time-jpox 项目的目标就是添加一些必需的映射，以使用 DataNucleus 持久化引擎持久化 Joda-Time
对象。并且，一个针对 Google Web Toolkit（也称为 Goda-Time）的 Joda-Time
实现目前正在开发当中，但是在撰写本文之际因为许可问题而被暂停。访问 <a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#resources">参考资料</a> 获得更多信息。</p>
<br />
<br />
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" height="6" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" width="16" border="0" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/java/j-jodatime.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="download">下载</a></p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <th scope="col">描述</th><th scope="col">名字</th><th scope="col">大小</th><th scope="col">下载方法</th>
        </tr>
        <tr>
            <th scope="row">源代码</th>
            <td nowrap="nowrap">j-jodatime.zip</td>
            <td nowrap="nowrap">812KB</td>
            <td nowrap="nowrap"><a onclick="sa_onclick(this.href)" onkeypress="sa_onclick(this.href)" href="http://download.boulder.ibm.com/ibmdl/pub/software/dw/java/j-jodatime.zip"><strong>HTTP</strong></a></td>
        </tr>
    </tbody>
</table>
<table border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr valign="top">
            <td colspan="5"><img alt="" src="http://www.ibm.com/i/c.gif" width="12" border="0" height="12" /></td>
        </tr>
        <tr>
            <td><img alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width="16" height="16" /></td>
            <td><a href="http://www.ibm.com/developerworks/cn/whichmethod.html">关于下载方法的信息</a></td>
            <td><img alt="" src="http://www.ibm.com/i/c.gif" width="50" height="1" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="resources">参考资料 </a></p>
<strong>学习</strong><br />
<ul>
    <li>
    <a href="http://joda-time.sourceforge.net/">Joda</a>：在 SourceForge 中访问 Joda 项目，并查阅 <a href="http://joda-time.sourceforge.net/api-release/index.html">Javadoc</a> 获得有关 Joda-Time 库的内容。<br />
    <br />
    </li>
    <li>
    <a href="http://grails.org/JodaTime+Plugin">Joda-Time Plugin for Grails</a>：了解面向 Grails 的 Joda-Time 插件。<br />
    <br />
    </li>
    <li>
    <a href="http://code.google.com/p/joda-time-jpox/">joda-time-jpox</a>：了解有关 joda-time-jpox 项目的更多内容。<br />
    <br />
    </li>
    <li>
    <a href="http://code.google.com/p/goda-time/">Goda-Time</a>：关注针对 Google Web Toolkit 的 Joda-Time 移植项目。<br />
    <br />
    </li>
    <li>
    <a href="http://www.ibm.com/developerworks/apps/SendTo?bookstore=safari">技术书店</a>：浏览有关这些主题和其他技术主题的图书。 <br />
    <br />
    </li>
    <li>
    <a href="http://www.ibm.com/developerworks/cn/java/">developerWorks Java 技术专区</a>：提供了数百篇有关 Java 编程各个方面的文章。 <br />
    <br />
    </li>
</ul>
<br />
<strong>获得产品和技术</strong><br />
<ul>
    <li>
    <a href="http://sourceforge.net/projects/joda-time/files/joda-time/">Joda-Time</a>：下载 Joda-Time 库。<br />
    <br />
    </li>
</ul>
<br />
<strong>讨论</strong><br />
<ul>
    <li>
    <a href="http://joda-time.sourceforge.net/mail-lists.html">Joda-Time 邮件列表</a>：订阅 Joda-Time 邮件列表或查看列表归档。<br />
    <br />
    </li>
    <li>加入 <a href="http://www.ibm.com/developerworks/mydeveloperworks" onmouseover="linkQueryAppend(this)">My developerWorks 社区</a>。 </li>
</ul>
<br />
<br />
<p><a name="author">关于作者</a></p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td colspan="3"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="5" /></td>
        </tr>
        <tr valign="top" align="left">
            <td>
            <p><img alt="J Steven Perry 的照片" src="http://www.ibm.com/developerworks/i/p-jsperry.jpg" valign="top" align="left" /></p>
            </td>
            <td><img alt="" src="http://www.ibm.com/i/c.gif" width="4" height="5" /></td>
            <td width="100%">
            <p>J
            Steven Perry 是一名软件开发人员、架构师和全能 Java 专家，他从 1991 年起就从事专业的软件开发。他的专业兴趣包括 JVM
            的内部工作原理和 UML 建模，以及介于两者之间的所有内容。Steve 编写了从技术文档到 Java
            代码等各种内容，并且对教学和培训也充满了热情。Steve 是 <em>Java Management Extensions</em>（O&#8217;Reilly）的作者，<em>Java Enterprise Best Practices</em>（O&#8217;Reilly）的合著者，并撰写了有关软件开发主题和 O'Reilly ShortCut: Log4J 的杂志文章。</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/sealyu/aggbug/306568.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-18 15:34 <a href="http://www.blogjava.net/sealyu/archive/2009/12/18/306568.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的几种常见接口用法</title><link>http://www.blogjava.net/sealyu/archive/2009/12/10/305442.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Dec 2009 08:15:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/10/305442.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305442.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/10/305442.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305442.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305442.html</trackback:ping><description><![CDATA[今天在看阎宏的《Java与模式》，里面对java的几种接口的常用方法的总结：<br />
<ol>
    <li>单方法接口：只含有一个方法，做特定的事情。例子：Runnable接口的run（）方法</li>
    <li>标记接口：没有任何方法和属性，不对实现他的类有任何语义上的要求，仅仅表明实现他的类属于特定的类型。例子：java.io.Serializable/java.rmi.Remote接口</li>
    <li>常量接口：在接口中声明一些常量，实现他的类就可以调用这些常量，不过不推荐使用。</li>
</ol>
<br />
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/305442.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-10 16:15 <a href="http://www.blogjava.net/sealyu/archive/2009/12/10/305442.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>overload和override的区别</title><link>http://www.blogjava.net/sealyu/archive/2009/12/10/305434.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Dec 2009 07:49:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/10/305434.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305434.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/10/305434.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305434.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305434.html</trackback:ping><description><![CDATA[<font size="2">
<font color="#c60a00">
<strong>
<font size="3">override（重写）</font>
</strong>
<br />
</font>1、方法名、参数、返回值相同。<br />
2、子类方法不能缩小父类方法的访问权限。<br />
3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。<br />
4、存在于父类和子类之间。<br />
5、方法被定义为final不能被重写。<br />
<font size="3"><strong><font color="#c60a00">overload（重载）<br />
</font></strong></font></font>
<font size="2">1、参数类型、个数、顺序至少有一个不相同。&nbsp;&nbsp;<br />
2、不能重载只有返回值不同的方法名。<br />
3、存在于同类中。</font>
<img src ="http://www.blogjava.net/sealyu/aggbug/305434.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-10 15:49 <a href="http://www.blogjava.net/sealyu/archive/2009/12/10/305434.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UML中的几种关系</title><link>http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Dec 2009 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305420.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305420.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305420.html</trackback:ping><description><![CDATA[UML中的各种关系： <br />
&nbsp;&nbsp;&nbsp;&nbsp; (1)依赖(dependency)是两个事物间的语义关系，其中一个事物(独立事物)发生变化会影响另一个事物(依赖事物)的语义。在图形上，把一个依赖画成一条可能有方向的虚线。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;
(2)关联(association)是一种结构关系，连接模型元素及链接实例，用一条实线来表示。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;
(3)泛化(generalization)是一种特殊／一般关系，特殊元素(子元素)的对象可替代一般元素(父元素)的对象，用这种方法，子元素共享了
父元素的结构和行为。在图形上，把一个泛化关系画成一条带有空心箭头的实线，它指向父元素。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;
(4)实现(realization)是类元之间的语义关系，其中一个类元指定了由另一个类元保证执行的契约。在两种地方要遇到实现关系：一种是在接口和
实现它们的类或构件之间；另一种是在用例和实现它们的协作之间。在图形上，把一个实现关系画成一条带有空心箭头的虚线。&nbsp; <br />
&nbsp;
&nbsp;&nbsp; (5)聚集(aggregation)表示整体与部分的关系，用一条实线加空心菱形来表示；<br />
&nbsp;&nbsp;&nbsp;&nbsp;
(6)组成(Composition)表示整体与部分的有一关系，用一条实线加实心菱形来表示；
<p><br />
</p>
<p>UML中几种类间关系：继承、实现、依赖、关联、聚合、组合的联系与区别</p>
<h3>继承</h3>
<p>指的是一个类（称为子类、子接口）继承另外的一个类（称为父类、父接口）的功能，并可以增加它自己的新功能的能力，继承是类与类或者接口与接口之间最常见的关系；在Java中此类关系通过关键字extends明确标识，在设计时一般没有争议性；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Generalization.jpg" height="204" width="293" border="0" /></p>
<h3>实现</h3>
<p>指的是一个class类实现interface接口（可以是多个）的功能；实现是类与接口之间最常见的关系；在Java中此类关系通过关键字implements明确标识，在设计时一般没有争议性；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Realization.jpg" height="203" width="121" border="0" /></p>
<h3>依赖</h3>
<p>可以简单的理解，就是一个类A使用到了另一个类B，而这种使用关系是具有偶然性的、、临时性的、非常弱的，但是B类的变化会影响到A；比如某人要过河，需要借用一条船，此时人与船之间的关系就是依赖；表现在代码层面，为类B作为参数被类A在某个method方法中使用；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Dependence.jpg" height="97" width="430" border="0" /></p>
<h3>关联</h3>
<p>他体现的是两个类、或者类与接口之间语义级别的一种强依赖关系，比如我和我的朋友；这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性
的，一般是长期性的，而且双方的关系一般是平等的、关联可以是单向、双向的；表现在代码层面，为被关联类B以类属性的形式出现在关联类A中，也可能是关联
类A引用了一个类型为被关联类B的全局变量；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Association.jpg" height="105" width="430" border="0" /></p>
<h3>聚合</h3>
<p>聚合是关联关系的一种特例，他体现的是整体与部分、拥有的关系，即has-a的关系，此时整体与部分之间是可分离的，他们可以具有各自的生命周期，
部分可以属于多个整体对象，也可以为多个整体对象共享；比如计算机与CPU、公司与员工的关系等；表现在代码层面，和关联关系是一致的，只能从语义级别来
区分；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Aggregation.jpg" height="108" width="430" border="0" /></p>
<h3>组合</h3>
<p>组合也是关联关系的一种特例，他体现的是一种contains-a的关系，这种关系比聚合更强，也称为强聚合；他同样体现整体与部分间的关系，但此
时整体与部分是不可分的，整体的生命周期结束也就意味着部分的生命周期结束；比如你和你的大脑；表现在代码层面，和关联关系是一致的，只能从语义级别来区
分；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Composition.jpg" height="106" width="430" border="0" /></p>
<p>对于继承、实现这两种关系没多少疑问，他们体现的是一种类与类、或者类与接口间的纵向关系；其他的四者关系则体现的是类与类、或者类与接口间的引
用、横向关系，是比较难区分的，有很多事物间的关系要想准备定位是很难的，前面也提到，这几种关系都是语义级别的，所以从代码层面并不能完全区分各种关
系；但总的来说，后几种关系所表现的强弱程度依次为：组合&gt;聚合&gt;关联&gt;依赖；</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/305420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-10 14:47 <a href="http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DCL（双检测锁定-Double Checked Lock）安全性分析(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 09 Dec 2009 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305301.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305301.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305301.html</trackback:ping><description><![CDATA[<div>
<script type="text/javascript">
document.body.oncopy = function() {
if (window.clipboardData) {
setTimeout(function() {
var text = clipboardData.getData("text");
if (text && text.length > 300) {
text = text + ""r"n"n本文来自CSDN博客，转载请标明出处：" + location.href;
clipboardData.setData("text", text);
}
}, 100);
}
}
</script>
<script type="text/javascript">                        function StorePage() { d = document; t = d.selection ? (d.selection.type != 'None' ? d.selection.createRange().text : '') : (d.getSelection ? d.getSelection() : ''); void (keyit = window.open('http://www.365key.com/storeit.aspx?t=' + escape(d.title) + '&u=' + escape(d.location.href) + '&c=' + escape(t), 'keyit', 'scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes')); keyit.focus(); }</script>
<p>在
水木上看到一篇分析DCL（双检测锁定-Double Checked
Lock）安全性的文章。记得以前也讨论过这个问题，但是为什么DCL也存在隐患，至今没弄明白，这篇分析做了比较详尽的解释，概括起来原因有两点：一是
因为编译器或者处理器并不是严格按照程序顺序进行指令调度。二是java中同步机制的存在。</p>
<p>原文来自：</p>
<p>发信人: wyxzellux (I still believe...), 信区: Java<br />
标 &nbsp;题: Singleton模式与双检测锁定(DCL)<br />
发信站: 水木社区 (Mon Apr &nbsp;7 23:42:14 2008), 站内</p>
<p>看OOP教材时，提到了一个双检测锁定(Double-Checked Lock, DCL)的问题，但是书上没有多介绍，只是说这是一个和底层内存机制有关的漏洞。查阅了下相关资料，对这个问题大致有了点了解。<br />
从头开始说吧。<br />
在多线程的情况下Singleton模式会遇到不少问题，一个简单的例子<br />
<br />
&nbsp;&nbsp; 1: &nbsp;class Singleton { &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 2: &nbsp; &nbsp; &nbsp;private static Singleton instance = null; &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 3: &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp;&nbsp; 4: &nbsp; &nbsp; &nbsp;public static Singleton instance() { &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 5: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (instance == null) { &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 6: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;instance = new Singleton(); &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 7: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 8: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return instance; &nbsp; &nbsp; <br />
&nbsp;&nbsp; 9: &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; <br />
&nbsp;&nbsp; 10: &nbsp;} <br />
&nbsp;&nbsp; <br />
假
设这样一个场景，有两个线程调用Singleton.instance()，首先线程一判断instance是否等于null，判断完后一瞬间虚拟机把线
程二调度为运行线程，线程二再次判断instance是否为null，然后创建一个Singleton实例，线程二的时间片用完后，线程一被唤醒，接下来
它执行的代码依然是instance = new Singleton(); <br />
两次调用返回了不同的对象，出现问题了。<br />
<br />
最简单的方法自然是在类被载入时就初始化这个对象：private static Singleton instance = new Singleton();<br />
<br />
JLS(Java Language Specification)中规定了一个类只会被初始化一次，所以这样做肯定是没问题的。<br />
<br />
但是如果要实现延迟初始化(Lazy initialization)，比如这个实例初始化时的参数要在运行期才能确定，应该怎么做呢？<br />
<br />
依然有最简单的方法：使用synchronized关键字修饰初始化方法：<br />
<br />
&nbsp;&nbsp; &nbsp;public synchronized static Singleton instance() { &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if (instance == null) {<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;instance = new Singleton();<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;return instance;<br />
&nbsp;&nbsp; &nbsp;} <br />
&nbsp;&nbsp; &nbsp;<br />
这里有一个性能问题：多个线程同时访问这个方法时，会因为同步而导致每次只有一个线程运行，影响程序性能。而事实上初始化完毕后只需要简单的返回instance的引用就行了。<br />
<br />
DCL是一个&#8220;看似&#8221;有效的解决方法，先把对应代码放上来吧：<br />
<br />
&nbsp;&nbsp; &nbsp;1 : &nbsp; class Singleton { &nbsp; <br />
&nbsp;&nbsp; &nbsp;2 : &nbsp; &nbsp; &nbsp; private static Singleton instance = null ; &nbsp; <br />
&nbsp;&nbsp; &nbsp;3 : &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;4 : &nbsp; &nbsp; &nbsp; public static Singleton instance() { &nbsp; <br />
&nbsp;&nbsp; &nbsp;5 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (instance == null ) {<br />
&nbsp;&nbsp; &nbsp;6 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; synchronized (this) { &nbsp; <br />
&nbsp;&nbsp; &nbsp;7 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (instance == null)<br />
&nbsp;&nbsp; &nbsp;8 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;instance = new Singleton();<br />
&nbsp;&nbsp; &nbsp;9 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;10 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;11 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return instance;<br />
&nbsp;&nbsp; &nbsp;12 : &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;13 : &nbsp;} <br />
<br />
用JavaWorld上对应文章的标题来评论这种做法就是smart, but broken。来看原因：<br />
<br />
Java
编译器为了提高程序性能会进行指令调度，CPU在执行指令时同样出于性能会乱序执行（至少现在用的大多数通用处理器都是out-of-order的），另
外cache的存在也会改变数据回写内存时的顺序[2]。JMM(Java Memory Model,
见[1])指出所有的这些优化都是允许的，只要运行结果和严格按顺序执行所得的结果一样即可。<br />
<br />
Java假设每个线程都跑在自己的处理器
上，享有自己的内存，和共享的主存交互。注意即使在单核上这种模型也是有意义的，考虑到cache和寄存器会保存部分临时变量。理论上每个线程修改自己的
内存后，必须立即更新对应的主存内容。但是Java设计师们认为这种约束会影响程序性能，他们试着创造了一套让程序跑得更快、但又保证线程之间的交互与预
期一致的内存模型。<br />
<br />
synchronized关键字便是其中一把利器。事实上，synchronized块的实现和Linux中的信号量
(semaphore)还是有区别的，前者过程中锁的获得和释放都会都会引发一次Memory
Barrier来强制线程本地内存和主存之间的同步。通过这个机制，Java中的同步机制保证了synchronized块中指令的原子性
(atomic)。<br />
<br />
好了，回过头来看DCL问题。看起来访问一个未同步的instance字段不会产生什么问题，我们再次来假设一个场景：<br />
<br />
线程一进入同步块，执行instance = new Singleton(); 线程二刚开始执行getResource();<br />
<br />
按照顺序的话，接下来应该执行的步骤是 1) 分配新的Singleton对象的内存 2) 调用Singleton的构造器，初始化成员字段 3) instance被赋为指向新的对象的引用。<br />
<br />
前
面说过，编译器或处理器都为了提高性能都有可能进行指令的乱序执行，线程一的真正执行步骤可能是1) 分配内存 2) instance指向新对象
3)
初始化新实例。如果线程二在2完成后3执行前被唤醒，它看到了一个不为null的instance，跳出方法体走了，带着一个还没初始化的
Singleton对象。<br />
<br />
错误发生的一种情形就是这样，关于更详细的编译器指令调度导致的问题，可以参看这个网页 [4]。<br />
<br />
[3] 中提供了一个编译器指令调度的证据<br />
<br />
instance = new Singleton(); 这条命令在Symantec JIT中被编译成<br />
<br />
0206106A &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; eax,0F97E78h<br />
0206106F &nbsp; call &nbsp; &nbsp; &nbsp; &nbsp;01F6B210 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 分配空间<br />
02061074 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ebp],eax &nbsp; &nbsp; &nbsp; ; EBP中保存了instance的地址 <br />
<br />
02061077 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; ecx,dword ptr [eax] &nbsp; &nbsp; &nbsp; ; 解引用，获得新的指针地址 <br />
<br />
02061079 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx],100h &nbsp; &nbsp; &nbsp;; 接下来四行是inline后的构造器<br />
0206107F &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx+4],200h &nbsp; &nbsp;<br />
02061086 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx+8],400h<br />
0206108D &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx+0Ch],0F84030h <br />
<br />
可以看到，赋值完成在初始化之前，而这是JLS允许的。<br />
&nbsp;<br />
另
一种情形是，假设线程一安稳地完成Singleton对象的初始化，退出了同步块，并同步了和本地内存和主存。线程二来了，看到一个非空的引用，拿走。注
意线程二没有执行一个Read Barrier，因为它根本就没进后面的同步块。所以很有可能此时它看到的数据是陈旧的。<br />
<br />
还有很多人根据已知的几种提出了一个又一个fix的方法，但最终还是出现了更多的问题。可以参阅[3]中的介绍。<br />
<br />
[5]中还说明了即使把instance字段声明为volatile还是无法避免错误的原因。<br />
<br />
由此可见，安全的Singleton的构造一般只有两种方法，一是在类载入时就创建该实例，二是使用性能较差的synchronized方法。<br />
<br />
(by ZelluX &nbsp;<a href="../../zellux" target="_blank">http://www.blogjava.net/zellux</a> )<br />
<br />
参考资料： <br />
<br />
[1] Java Language Specification, Second Edition, 第17章介绍了Java中线程和内存交互关系的具体细节。<br />
[2] out-of-order与cache的介绍可以参阅Computer System, A Programmer's Perspective的第四、五章。<br />
[3] The "Double-Checked Locking is Broken" Declaration, <a href="http://www.cs.umd.edu/%7Epugh/java/memoryModel/DoubleCheckedLocking.html" target="_blank">http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html</a><br />
[4] Synchronization and the Java Memory Model, <a href="http://gee.cs.oswego.edu/dl/cpj/jmm.html" target="_blank">http://gee.cs.oswego.edu/dl/cpj/jmm.html</a><br />
[5] Double-checked locking: Clever, but broken, <a href="http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1" target="_blank">http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1</a><br />
[6] Holub on Patterns, Learning Design Patterns by Looking at Code</p>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/305301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-09 17:01 <a href="http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一道Java笔试面试题(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/12/09/305287.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 09 Dec 2009 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/09/305287.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305287.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/09/305287.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305287.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305287.html</trackback:ping><description><![CDATA[用这题目吸引大家注意，请见谅！<br />
以下代码有些朋友可能会似曾熟识，可能也算不上&#8220;原创&#8221;，请见谅！<br />
不过，我出的这题，确定有点意思，考考大家。<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;*&nbsp;父类<br />
&nbsp;*&nbsp;</span><span style="color: #808080;">@author</span><span style="color: #008000;">&nbsp;rongxinhua<br />
&nbsp;*<br />
&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Father{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">FATHER</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Father(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;whoAmI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tellName(name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;whoAmI(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">Father&nbsp;says,&nbsp;I&nbsp;am&nbsp;</span><span style="color: #000000;">"</span>&nbsp;<span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;tellName(String&nbsp;name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">Father's&nbsp;name&nbsp;is&nbsp;</span><span style="color: #000000;">"</span>&nbsp;<span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;*&nbsp;子类<br />
&nbsp;*&nbsp;</span><span style="color: #808080;">@author</span><span style="color: #008000;">&nbsp;rongxinhua<br />
&nbsp;*<br />
&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Son&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;Father{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">SON</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Son(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;whoAmI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tellName(name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;whoAmI(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">Son&nbsp;says,&nbsp;I&nbsp;am&nbsp;</span><span style="color: #000000;">"</span>&nbsp;<span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;tellName(String&nbsp;name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">Son's&nbsp;name&nbsp;is&nbsp;</span><span style="color: #000000;">"</span>&nbsp;<span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
问题：当执行 <span style="color: #ff0000;"><span style="color: red;"><span style="color: red;">Father who = new Son();</span></span></span> 时，会输出什么？<br />
如果你去参加一个公司的笔试或面试时问了这么一道题，在没有环境测试的情况下，你会猜测什么答案？<br />
<br />
这个题目其实是一个小Trick，要注意whoAmI和tellName函数的区别：一个是打印field的值，而另一个是打印参数。<br />
其实过程是这样的：<br />
1.
创建Son的时候先创建Father，而Father中的whoAmI是已经被Son覆盖了，因此这里打印的name这个field是Son中的
field，而此时还没有构造Son，因此Son中的name的值是null(这里即使是写了String name =
"SON"也是没有用的，因为父类没有构造结束之前，这里是不会被执行的)。<br />
2.Father在执行tellName的时候，传递的参数name是Father自身的name这个field值，这个值是已经被赋值为"FATHER"的，因此会打印出&#8220;Son's name is FATHER&#8221;<br />
3.Father构造完毕后开始构造Son，这里的打印结果就可以按照常规方式来解释了。<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/305287.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-09 15:36 <a href="http://www.blogjava.net/sealyu/archive/2009/12/09/305287.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>   Fix Out of Memory errors by Increasing Available Memory   </title><link>http://www.blogjava.net/sealyu/archive/2009/10/30/300352.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 30 Oct 2009 06:40:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/30/300352.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/300352.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/30/300352.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/300352.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/300352.html</trackback:ping><description><![CDATA[<table>
    <tbody>
        <tr>
            <td valign="top"><img src="http://confluence.atlassian.com/images/icons/emoticons/warning.gif" alt="" width="16" align="absmiddle" border="0" height="16" /></td>
            <td><strong>JDK 1.4</strong> does not provide information why the <tt>OutOfMemory</tt> error occurred. Since Confluence version 2.8, this JDK is not supported any longer.<br />
            <strong>JDK 1.5</strong> or <strong>1.6</strong> are the <a href="http://confluence.atlassian.com/display/DOC/List+Of+Supported+Operating+Systems" class="external-link" rel="nofollow">recommended JDK</a> to be used and they do provide a description of the OOM error. JDK 1.6 also has around a 20% performance improvement over 1.5</td>
        </tr>
    </tbody>
</table>
<p>Since the default memory setting usually is around 64 or 128MB
(256MB in Confluence 2.2 and later), you might have to adjust the
settings to run a bigger Confluence instance with sufficient memory.</p>
<p><strong>On this page:</strong></p>
<div>
<ul>
    <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-DiagnosisandCommonCauses">Diagnosis and Common Causes</a></li>
    <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-Determiningthevariouscausesofmemoryerrors">Determining the various causes of memory errors</a></li>
    <ul>
        <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-java.lang.OutOfMemoryError%3APermGenspace"> java.lang.OutOfMemoryError: PermGen space</a></li>
        <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-java.lang.OutOfMemoryError">java.lang.OutOfMemoryError</a></li>
        <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-OutOfMemoryError%3Aunabletocreatenewnativethread">OutOfMemoryError: unable to create new native thread</a></li>
        <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-OutOfMemoryError%3AGCoverheadlimitexceeded">OutOfMemoryError: GC overhead limit exceeded</a></li>
        <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-OutOfMemoryError%3ARequestedarraysizeexceedsVMlimit">OutOfMemoryError: Requested array size exceeds VM limit</a></li>
    </ul>
    <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-SettingtheMemorySettings">Setting the Memory Settings</a></li>
    <ul>
        <ul>
            <li><a href="http://confluence.atlassian.com/display/DOC/Fix+Out+of+Memory+errors+by+Increasing+Available+Memory#FixOutofMemoryerrorsbyIncreasingAvailableMemory-RELATEDTOPICS">RELATED TOPICS</a></li>
        </ul>
    </ul>
</ul>
</div>
<h1>Diagnosis and Common Causes</h1>
<p>There are several reasons that out of memory exceptions can be
thrown. Either the virtual machine Confluence is using has hit its
allocated memory limit, the system on which Confluence is running has
run out of physical and virtual memory, or Confluence is consuming too
much memory. In the first case, you should modify the maximum heap size
of the virtual machine, per the instructions in this document; in the
second or third cases, the solution is to identify the culprit of the
memory leak.</p>
<p>For help determining which memory settings to choose, consult <a href="http://confluence.atlassian.com/display/DOC/Managing+Application+Server+Memory+Settings" title="Managing Application Server Memory Settings">Managing Application Server Memory Settings</a>.</p>
<p>If you have not yet set your memory settings and your usage has
increased, it's likely that you must set your memory, described below.
If your usage patterns have not changed but you've added a plugin or
done an upgrade, it's likely that there is a memory leak in a plugin.</p>
<ol>
    <li>If you're using the in-memory database (HSQLDB), migrate to an <a href="http://confluence.atlassian.com/display/DOC/Database+Setup+For+Any+External+Database" title="Database Setup For Any External Database">external database</a>. The in-memory database can use a lot of memory.</li>
    <li>If you are using XML backups, disable them and move to the <a href="http://confluence.atlassian.com/display/DOC/Alternative+Backup+Strategy" title="Alternative Backup Strategy">Alternative Backup Strategy</a>. The XML backup process can be a memory hog.</li>
</ol>
<p>To troubleshoot potential memory leaks, enter <a href="http://confluence.atlassian.com/display/DOC/Plugin+Support+Mode" title="Plugin Support Mode">Plugin Support Mode</a>. Take <a href="http://confluence.atlassian.com/display/DOC/Generating+a+Thread+Dump" title="Generating a Thread Dump">thread dumps</a> during normal operations and during an outage, and submit this information in a <a href="http://confluence.atlassian.com/pages/viewpage.action?pageId=12" title="Troubleshooting Problems &amp; Requesting Technical Support">support ticket</a>.</p>
<h1>Determining the various causes of memory errors</h1>
<p>There are different kinds of memory limits inside the Java Virtual
Machine. Each limit can be configured independently. But you must first
find out which limit you have reached.</p>
<h4><a href="http://confluence.atlassian.com/pages/viewpage.action?pageId=126910596" class="external-link" rel="nofollow">java.lang.OutOfMemoryError: PermGen space</a></h4>
<p>If you get the error message: <tt><a href="http://confluence.atlassian.com/pages/viewpage.action?pageId=126910596" class="external-link" rel="nofollow">java.lang.OutOfMemoryError: PermGen space</a></tt>
this means that you have exceeded Java's default 64Mb block for loading
class files. This can happen if many plugins are installed. You may
want to increase the PermGen memory size to suit your needs.</p>
<p>In the following sample, the blue parameter shows how the PermGen
Memory has been set to 192 megabytes. This value should be set
depending on your memory requirements. 192m should be sufficient for
Confluence even when many plugins are installed.</p>
<div style="border-width: 1px;">
<div>
<p>JAVA_OPTS="-Xms128m -Xmx1024m <font color="blue">-XX:MaxPermSize=192m</font> $JAVA_OPTS -Djava.awt.headless=true "</p>
</div>
</div>
<p>Note: The other parameters in this sample are just shown to give you some context, and are not part of this example.</p>
<h4>java.lang.OutOfMemoryError</h4>
<p>Heap space memory errors occur when the application has to deal with
large amounts of data or users. These errors will contain only a <tt>java.lang.OutOfMemoryError</tt>,
e.g. without the reference to PermGen space as above. You should try to
increase the heap size to solve this problem. This requires configuring
the Xmx and Xms parameters. In the following example, the maximum heap
size is set to 1024 megabytes. This should be enough for small to
medium deployments.</p>
<div style="border-width: 1px;">
<div>
<p>JAVA_OPTS="<font color="blue">-Xms128m -Xmx1024m</font> -XX:MaxPermSize=256m $JAVA_OPTS -Djava.awt.headless=true "</p>
</div>
</div>
<p>Note: The other parameters are only shown to give you context and are not part of this example.</p>
<p>Deployments with high usage patterns may require additional memory.
For high-usage deployments, it is recommended to set both Xms and Xmx
as the same value (eg. -Xms1024m -Xmx1024m), _provided the memory is
available_. On the other hand, adding too much memory can also cause
problems (see below), so you should increment memory carefully, for
example in increments of 128 megabytes.</p>
<h4>OutOfMemoryError: unable to create new native thread</h4>
<p>This error occurs when the operating system is unable to create new
threads. This is due to the JVM Heap taking up the available RAM.</p>
<blockquote>
<p> <cite>Big heaps take away from the space that can be allocated for the stack of a new thread</cite></p>
</blockquote>
<p>For 32bit Linux generally the maximum heap size of the JVM cannot be
greater than 2GB. Windows systems will typically split the available
physical memory 50:50 as Application and Kernel/System space, so please
do not allocate an amount exceeding or approaching that split.<br />
The size of the stack per thread can also contribute to this problem.
The stack size can reduce the number of threads that can be created.</p>
<p>To fix this problem, you should reduce the size of your JVM Heap and also the size of the stack per thread.<br />
The stack size can be changed with the following (example) parameter:</p>
<p>"-Xss512k"</p>
<p>Please refer to <a href="http://goobsoft.homeip.net/Wiki.jsp?page=JavaDebianTuning" class="external-link" rel="nofollow">this guide</a> as a reference for JVM tuning.</p>
<h4>OutOfMemoryError: GC overhead limit exceeded</h4>
<p>This error indicates that the JVM took too long to free up memory
during its GC process. This error can be thrown from the Serial,
Parallel or Concurrent collectors. It often means that the Xmx value is
<em>too high</em> - you might consider lowering it. See <a href="http://confluence.atlassian.com/display/DOC/Garbage+Collector+Performance+Issues" title="Garbage Collector Performance Issues">Garbage Collector Performance Issues</a>
for more details. For more severe and persistent performance issues
relating to GC, it is recommended to change to a parallel collector,
and to ensure that Confluence has access to the memory demanded by its
users.</p>
<p>The parallel collector will throw an OutOfMemoryError if too much
time is being spent in garbage collection: if more than 98% of the
total time is spent in garbage collection and less than 2% of the heap
is recovered, an OutOfMemoryError will be thrown. This feature is
designed to prevent applications from running for an extended period of
time while making little or no progress because the heap is too small.
If necessary, this feature can be disabled by adding the option
-XX:-UseGCOverheadLimit to the command line.</p>
<p>This kind of OutOfMemoryError can be caused if user requests drown
the available resources in the JVM. When this occurs, performance will
degrade aggressively. This will eventually require a restart or the
application may recover. </p>
<h4>OutOfMemoryError: Requested array size exceeds VM limit</h4>
<p>This is a rare error and indicates that Confluence attempted to
allocate an array that is larger than the Java heap size. More details
regarding this error can be found <a href="http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/gbywc.html#gbyvi" class="external-link" rel="nofollow">here</a>.<br />
This is due to a known limitation of the JVM as documented <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4880587" class="external-link" rel="nofollow">here</a>. We have a <a href="http://jira.atlassian.com/browse/CONF-12509" class="external-link" rel="nofollow">bug</a> lodged against this, to better handle this exception in Confluence.</p>
<h1>Setting the Memory Settings</h1>
<p>How to set the heap or permanent generation memory depends on your
distribution, platform, and how you start Confluence. Refer to <a href="http://confluence.atlassian.com/display/DOC/Configuring+System+Properties" title="Configuring System Properties">Configuring System Properties</a>.</p>
<div>
<table>
    <colgroup><col width="24"><col></colgroup>
    <tbody>
        <tr>
            <td valign="top"><img src="http://confluence.atlassian.com/images/icons/emoticons/check.gif" alt="" width="16" align="absmiddle" border="0" height="16" /></td>
            <td>
            <p>If you're starting Confluence from a Windows Service, make sure you add the properties through the <a href="http://confluence.atlassian.com/display/DOC/Editing+the+Windows+Registry" title="Editing the Windows Registry">registry settings</a>.</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div>
<table>
    <colgroup><col width="24"><col></colgroup>
    <tbody>
        <tr>
            <td valign="top"><img src="http://confluence.atlassian.com/images/icons/emoticons/check.gif" alt="" width="16" align="absmiddle" border="0" height="16" /></td>
            <td>
            <p>To verify if your settings have been picked up, check <a href="http://confluence.atlassian.com/display/DOC/Displaying+System+Properties" title="Displaying System Properties">Displaying System Properties</a>.</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div>
<table>
    <colgroup><col width="24"><col></colgroup>
    <tbody>
        <tr>
            <td valign="top"><img src="http://confluence.atlassian.com/images/icons/emoticons/warning.gif" alt="" width="16" align="absmiddle" border="0" height="16" /></td>
            <td>Other factors such as system load and allocating too much memory to your JVM Heap can also cause <ins>OutOfMemory</ins> Errors. For more information, you can refer to the JIRA documentation on <a href="http://confluence.atlassian.com/display/JIRA/Causes+of+OutOfMemoryErrors" title="Causes of OutOfMemoryErrors">Causes of OutOfMemoryErrors</a>.</td>
        </tr>
    </tbody>
</table>
</div>
<h5>RELATED TOPICS</h5>
<p><a href="http://confluence.atlassian.com/display/DOC/Managing+Application+Server+Memory+Settings" title="Managing Application Server Memory Settings">Managing Application Server Memory Settings</a><br />
<a href="http://confluence.atlassian.com/display/DOC/Installing+the+Confluence+EAR-WAR+Edition" title="Installing the Confluence EAR-WAR Edition">Installing the Confluence EAR-WAR Edition</a><br />
<a href="http://confluence.atlassian.com/display/DOC/Confluence+FAQ" title="Confluence FAQ">FAQ Home</a><br />
Tomcat <a href="http://www.chemaxon.com/jchem/doc/admin/tomcat.html" class="external-link" rel="nofollow">JVM options</a> and <a href="http://tomcat.apache.org/tomcat-3.2-doc/uguide/tomcat_ug.html" class="external-link" rel="nofollow">Modify the Default JVM Settings</a><br />
Websphere <a href="http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=/com.ibm.websphere.nd.doc/info/ae/ae/tprf_tunejvm_v61.html" class="external-link" rel="nofollow">Tuning JVM</a><br />
<a href="http://confluence.atlassian.com/display/DOC/Generating+a+Thread+Dump" title="Generating a Thread Dump">Logging A Thread Dump</a></p>
<img src ="http://www.blogjava.net/sealyu/aggbug/300352.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-30 14:40 <a href="http://www.blogjava.net/sealyu/archive/2009/10/30/300352.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>慎用java.util.Collections.copy()方法</title><link>http://www.blogjava.net/sealyu/archive/2009/10/27/299934.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 27 Oct 2009 08:24:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/27/299934.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/299934.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/27/299934.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/299934.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/299934.html</trackback:ping><description><![CDATA[今天发现单独的将一个ArrayList的对象添加到另外一个ArrayList的时候，总是源列表和目的列表相同的内存地址。原因如下：<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">偶然看到了Collections的copy（List&nbsp;desc,List&nbsp;src）方法.当时就想这个方法和初始化一个List&nbsp;desc</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList(List&nbsp;c)【参数必须实现Collection接口】的区别。<br />
<br />
两者的差别很大，后者是一个浅拷贝，只是对源list的元素进行拷贝，拷贝的只是引用。拷贝后两个list的元素（引用）不同，但是引用所指向的对象是一样的。即是两个list的每个元素指向的还是通一内存。然而前者是深拷贝，不光拷贝的是src的元素（引用），src内每个元素的所指向的对象都进行一次拷贝。即是两个list的每个元素所指向的不是同一内存。</span></div>
<br />
<p>所以使用了Collections.copy()方法来进行拷贝，但是这样就接触到了此方法所报出的异常：</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">使用后者进行拷贝的结果是：当你的desc链表发生改变时，src也将会随之改变。<br />
使用前者进行拷贝时你又必须要注意目标链表的长度必须要比源链表的长度大或者相等。<br />
<br />
举例如下：<br />
List&nbsp;src1</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList(</span><span style="color: #000000;">3</span><span style="color: #000000;">)<br />
src1.add(</span><span style="color: #000000;">"</span><span style="color: #000000;">a</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
src2.add(</span><span style="color: #000000;">"</span><span style="color: #000000;">b</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
src3.add(</span><span style="color: #000000;">"</span><span style="color: #000000;">c</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
<br />
如果你使用下面方法copy链表<br />
</span><span style="color: #008000;">/**</span><span style="color: #008000;">****************************</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
List&nbsp;des1</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList(</span><span style="color: #000000;">3</span><span style="color: #000000;">);<br />
Collections.copy(des1,src1);<br />
</span><span style="color: #008000;">/**</span><span style="color: #008000;">****************************</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
将会出错，抛出数组越界异常。<br />
当时我怎么想都想不明白为什么，明明已经设置了长度为3，为什么还会出错！<br />
后来打印出des1.size()才知道des1的长度为0；3表示的是这个List的容纳能力为3，并不是说des1中就有了3个元素。查看api才知道，它的capacity（容纳能力大小）可以指定（最好指定）。而初始化时size的大小永远默认为0，只有在进行add和remove等相关操作时，size的大小才变化。然而进行copy()时候，首先做的是将desc1的size和src1的size大小进行比较，只有当desc1的size&nbsp;大于或者等于src1的size时才进行拷贝，否则抛出IndexOutOfBoundsException异常。<br />
<br />
所以可以通过下面的方法指定目标desc的大小<br />
</span><span style="color: #008000;">/**</span><span style="color: #008000;">****************************</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
List&nbsp;des1</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList(Array.asList(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;object[src1.size]));</span><span style="color: #008000;">//</span><span style="color: #008000;">注意：new&nbsp;ArrayList(Collection&nbsp;col)参数必须要实现Collection&nbsp;接口。</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">Collections.copy(des1,src1);<br />
</span><span style="color: #008000;">/**</span><span style="color: #008000;">****************************</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
执行第一句后size的大小是3，其实它是对一个空数组的浅拷贝。</span></div>
<p>使用这位仁兄的方法，我这边一直报错说找不到此构造函数，估计是因为我使用了jdk6的原因，后来使用了替代方法，用apache的CollectionUtils：</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">dest1&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList();<br />
CollectionUtils.addAll(dest1,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Object[src1.size()]);<br />
Collections.copy(dest1,&nbsp;src1);</span></div>
<br />
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/299934.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-27 16:24 <a href="http://www.blogjava.net/sealyu/archive/2009/10/27/299934.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Apache Cocoon入门（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/10/15/298317.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 15 Oct 2009 01:39:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/15/298317.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/298317.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/15/298317.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/298317.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/298317.html</trackback:ping><description><![CDATA[<div>
<p>作者： 周靖 译 <br />
<a href="http://www.zdnet.com.cn/developer/tech/story/0,3800067013,39139224-1,00.htm">http://www.zdnet.com.cn/developer/tech/story/0,3800067013,39139224-1,00.htm</a><br />
<br />
<br />
在
某种意义上，可将Cocoon视为一个基于XML的内容管理系统，因其提供了一种机制来描述Web信息项目（内容）的结构和语义、这些项目的关系、它们如
何随着时间的推移而发生改变（逻辑）以及在请求时如何向用户呈现（它们的样式）。事实上，Cocoon项目作为Apache
Web服务器项目的一部分，它的作用之一就是组织和控制Apache麾下众多项目的文档化过程。</p>
<p>作为对其真实用途的一个比喻，Cocoon（茧）这个名字具有深远的意义。它代表一种包裹结构，其中的某些东西将从幼虫阶段成长为美丽的蝴蝶，并准备展翅飞翔。但在Apache Cocoon的<a href="http://xml.apache.org/cocoon/" target="_target">主页</a>上，
没有对这个巧妙的比喻进行说明。相反，它只是对这个项目进行了准确的技术性解释：&#8220;Apache
Cocoon是一个XML发布框架，它将XML和XSLT技术在服务器应用程序中的应用提升到一个新级别。Cocoon的宗旨是提升管道化SAX处理的性
能和扩展性，通过对内容、逻辑和样式的分离来营造一个灵活的环境。&#8221;<br />
</p>
<h5>到底什么是Cocoon?</h5>
<p>Cocoon最初的型态是一个简单的Java
servlet，全部使用标准W3C组件：用文档对象模型（DOM）来解析文档，用XML来捕捉和格式化数据，用XSLT来转换数据和合并/操纵XML文
档，并用XSL来管理文档的表示以便通过Web传送。但人们很快就要求它为其他类型的内容提供服务（比如程序和文档），所以Cocoon逐渐发展成为一个
完整的、基于XML的发布框架及系统。<br />
</p>
<p>随着时间的推移，几个新的XML组件问世了，比如SAX。另外，XSL也逐渐细分为几个不同的组件，分别提供Transforms、
Formatting Objects和XPath功能。这些新标准直接促成了Cocoon
2在2002年的问世，它定义了一个标准的（而且仍在发展的）内容管理系统，并面向公众开放。<br />
<br />
就目前来说，我们可采取几种不同的方式来描述Cocoon：一个XML发布框架，一个数据源聚合器以及一系列管道和组件的集合。</p>
<h5>作为发布框架的Cocoon <br />
</h5>
<p>Cocoon基于对SAX事件的管道式处理。Web应用程序如果围绕这个框架来构建，将获得较好的扩展性和性能。利用一个集中式的配置系统，你可以
方便地创建、部署和维护Web应用程序。Cocoon使用了一个缓存系统，所有组件都可根据需要进行动态配置。接收到用户请求后，会自动检查缓存，判断请
求的URI（统一资源标识符）是否存在。如果存在，就直接传递缓存的内容，不需要通过一个管道来处理它。<br />
<br />
</p>
<h5>Cocoon作为数据源聚合器<br />
</h5>
<p>Cocoon可作为一个抽象的引擎使用（通过一个Java servlet），它基于自定义的协议处理程序，能通过标准URI来访问外部数据源。Cocoon甚至能递归调用自身，使数据流能在多个管道化阶段同时处理，从而提高处理速度和效率。</p>
<p><br />
管道和组件</p>
<p>Cocoon体系结构的核心设计思想是模块化和抽象处理。Cocoon管道在概念上和UNIX系统中使用的管道差不多，只是Cocoon管道中的所
有元素都是通过解析XML文档而创建的SAX事件。Cocoon识别3种类型的管道元素（组件），分别是生成器（generators）、转换器
（transformers）和序列化器（serializers）。其中，生成器使用一个请求的URI来生成SAX事件；转换器使用SAX事件并生成其
他SAX事件；序列化器使用SAX事件并生成一些响应。</p>
<h5>Cocoon的用途<br />
</h5>
<p>简单地说，Cocoon管道用于生成和递送内容。内容采用的是某种可识别的形式，由一个生成器和一个序列化器构成。在较典型的Cocoon管道中，
生成器之后可能紧接着一个或多个转换器链，而序列化器用于生成不同格式的输出。采取这种方式，同一个源文档可为Web浏览器生成HTML格式的内容，为
WAP设备生成WML格式的内容，为打印输出生成PDF格式的内容。<br />
<br />
作为一个开发平台，Cocoon最吸引人的地方在于它提供了品种丰富的生成器、转换器和序列化器。人们为Cocoon 2项目开发和捐赠了许多这样的组件。一经捐赠，这些组件就可进行定制，以添加新功能或者进行扩展，从而创建出新的组件。<strong>表</strong><strong>A</strong>总结了适用于Cocoon的一些常用生成器、转换器和序列化器。<br />
<br />
</p>
<p>表A</p>
<table cellpadding="0" cellspacing="0" width="535" border="1">
    <tbody>
        <tr>
            <td valign="bottom" width="69"><strong>类别</strong></td>
            <td valign="bottom" width="154"><strong>名称</strong></td>
            <td valign="bottom" width="304"><strong>说明</strong></td>
        </tr>
        <tr>
            <td valign="top" width="69">生成器</td>
            <td valign="top" width="154">DirectoryGenerator</td>
            <td valign="top" width="304">将目录列表转换成XML格式，以便从中生成SAX事件</td>
        </tr>
        <tr>
            <td valign="top" width="69">生成器</td>
            <td valign="top" width="154">FileGenerator</td>
            <td valign="top" width="304">解析一个文件或URI，并生成SAX事件</td>
        </tr>
        <tr>
            <td valign="top" width="69">生成器</td>
            <td valign="top" width="154">JSPGenerator</td>
            <td valign="top" width="304">根据JSP页来生成XML和SAX事件</td>
        </tr>
        <tr>
            <td valign="top" width="69">生成器</td>
            <td valign="top" width="154">ServerPagesGenerator</td>
            <td valign="top" width="304">根据XSP页来生成XML和SAX</td>
        </tr>
        <tr>
            <td valign="top" width="69">转换器</td>
            <td valign="top" width="154">i18nTransformer</td>
            <td valign="top" width="304">使用i18n字典和语言参数值来转换SAX事件</td>
        </tr>
        <tr>
            <td valign="top" width="69">转换器</td>
            <td valign="top" width="154">XincludeTransformer</td>
            <td valign="top" width="304">通过为现有的SAX流添加SAX事件，从而处理<em>xinclude</em><em>命名空间，并包括外部源</em></td>
        </tr>
        <tr>
            <td valign="top" width="69">转换器</td>
            <td valign="top" width="154">XSLTransformer</td>
            <td valign="top" width="304">根据XSLT样式表定义来转换SAX事件流</td>
        </tr>
        <tr>
            <td valign="top" width="69">序列化器</td>
            <td valign="top" width="154">HTMLSerializer</td>
            <td valign="top" width="304">根据SAX事件来生成HTML响应</td>
        </tr>
        <tr>
            <td valign="top" width="69">序列化器</td>
            <td valign="top" width="154">PDFSerializer</td>
            <td valign="top" width="304">根据SAX事件，使用Apache FOP（格式输出处理器）来生成PDF</td>
        </tr>
        <tr>
            <td valign="top" width="69">序列化器</td>
            <td valign="top" width="154">SVG2JPGSerializer</td>
            <td valign="top" width="304">根据SVG SAX事件，使用Apache Batik来生成JPEG图像</td>
        </tr>
        <tr>
            <td valign="top" width="69">序列化器</td>
            <td valign="top" width="154">TextSerializer</td>
            <td valign="top" width="304">根据SAX事件来生成纯文本输出，适用于非XML文本，比如CSS或程序语言代码</td>
        </tr>
        <tr>
            <td valign="top" width="69">序列化器</td>
            <td valign="top" width="154">XMLSerializer</td>
            <td valign="top" width="304">根据SAX事件来生成XML响应</td>
        </tr>
    </tbody>
</table>
<h5>可由Cocoon使用的部分组件<br />
</h5>
<p>如所你见，Cocoon的功能并非只是简单地将数据格式化成HTML。在未来的文章中，我们将研究如何在Cocoon环境中创建和处理文档，并更详
细地介绍在这个环境中工作所牵涉的问题。就目前来说，你只需理解Cocoon是针对多种目的来捕捉、呈现和递送Web内容的一种方式。</p>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/298317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-15 09:39 <a href="http://www.blogjava.net/sealyu/archive/2009/10/15/298317.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Class.isAssignableFrom(Class clz)方法 与 instanceof 关键字区别（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/09/21/295876.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 21 Sep 2009 07:46:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/09/21/295876.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/295876.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/09/21/295876.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/295876.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/295876.html</trackback:ping><description><![CDATA[<font color="#000000"><font>isAssignableFrom 是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的超类或接口。&nbsp;&nbsp; <br />
&nbsp; 通常调用格式是&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class1.isAssignableFrom(Class2)&nbsp;&nbsp; <br />
&nbsp; 调用者和参数都是&nbsp;&nbsp; java.lang.Class&nbsp;&nbsp; 类型。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp; 而&nbsp;&nbsp; instanceof&nbsp;&nbsp; 是用来判断一个对象实例是否是一个类或接口的或其子类子接口的实例。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 格式是：&nbsp;&nbsp; oo&nbsp;&nbsp; instanceof&nbsp;&nbsp; TypeName&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 第一个参数是对象实例名，第二个参数是具体的类名或接口名</font></font>
<p><font color="#000000">具体例子如下：</font></p>
<div><font color="#000000">java 代码</font></div>
<div>
<div>
<ol><font color="#000000">
    <li>package&nbsp;test; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp; </li>
    <li>public&nbsp;class&nbsp;Test2&nbsp;{ &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;&nbsp;void&nbsp;&nbsp;testIsAssignedFrom1()&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;&nbsp;String.class.isAssignableFrom(Object.class)&nbsp;)&nbsp;;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;&nbsp;void&nbsp;&nbsp;testIsAssignedFrom2()&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;&nbsp;Object.class.isAssignableFrom(Object.class)&nbsp;);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;&nbsp;void&nbsp;&nbsp;testIsAssignedFrom3()&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;Object.class.isAssignableFrom(String.class)&nbsp;);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;&nbsp;void&nbsp;&nbsp;testInstanceOf1()&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;&nbsp;ss&nbsp;&nbsp;=&nbsp;&nbsp;"";&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;&nbsp;ss&nbsp;&nbsp;instanceof&nbsp;Object&nbsp;);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;&nbsp;void&nbsp;&nbsp;testInstanceOf2()&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;&nbsp;o&nbsp;=&nbsp;new&nbsp;&nbsp;&nbsp;Object();&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;&nbsp;o&nbsp;&nbsp;instanceof&nbsp;&nbsp;Object&nbsp;);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args){ &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test2&nbsp;test&nbsp;=&nbsp;new&nbsp;Test2(); &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.testIsAssignedFrom1(); &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.testIsAssignedFrom2(); &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.testIsAssignedFrom3(); &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.testInstanceOf1(); &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.testInstanceOf2(); &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp; </li>
    <li>} &nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp; </li>
    <li>打印结果为： &nbsp;&nbsp; </li>
    <li>false&nbsp;&nbsp; </li>
    <li>true&nbsp;&nbsp; </li>
    <li>true&nbsp;&nbsp; </li>
    <li>true&nbsp;&nbsp; </li>
    <li>true&nbsp;&nbsp; </li>
    </font></ol>
    </div>
    </div>
<img src ="http://www.blogjava.net/sealyu/aggbug/295876.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-09-21 15:46 <a href="http://www.blogjava.net/sealyu/archive/2009/09/21/295876.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Servlet 3.0 介绍（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/09/21/295859.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 21 Sep 2009 06:21:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/09/21/295859.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/295859.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/09/21/295859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/295859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/295859.html</trackback:ping><description><![CDATA[<a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/keys/java/index.html" target="_blank">Java</a>
servlets 是一项被普遍接受的技术，用于构建基于 web 应用程序的动态内容;Servlet 3.0
规范早期草案版本的发行让该技术在特性和应用程序接口(Application Program
Interface，API)方面得到了极大增强。Java Specification Request(JSR)已经以 JSR 315
的形式得到了批准，并计划成为 Java Enterprise Edition 6(JSR
316)或更高版本的组成部分。与之前仅仅是维护发行版(maintenance releases)的一些版本规范不同，Servlet 3.0
规范随带了许多 web 开发新时代所需的最令人兴奋的特性。在本文中，我们将研究新版 Java servlets
中引入的主要特性。值得注意的是，本规范仍处于草案版本阶段，因此在本文中所讨论的技术细节可能会发生变化。
<p>　　新规范主要交付了以下新特性：</p>
<p>　　开发的简易性<br />
可插拔性和可扩展性<br />
异步支持<br />
安全性增强<br />
其他杂项变化</p>
<p>　　很明显，与其他技术相比，servlets 在 Java Enterprise Edition
家族中有着更广泛的应用。Servlets 保留了其简洁性和能够处理 HTTP 请求并向 web 客户机传回响应的优点。Servlets
可以用于实现简单和小型应用程序的业务逻辑。在 web 框架中，servlets 作为所有传入请求的入口点(即 controller
servlet);因此，所有流行框架都是在原始的 servlets 上建立的。Servlet 3.0 中的新增特性旨简化 servlet
应用程序的开发，并让 servlet
开发人员和框架开发人员从中受益。在以下章节中，我们将详细介绍每个新增特性，并讨论如何使用它们来开发更优秀的应用程序。</p>
<p>　<strong>　开发的简易性</strong></p>
<p>　　开发的简易性是任何技术成功的关键因素。Servlet 3.0 API 通过使用 JSR 175 注释
集中解决开发简易性问题，允许开发人员采用声明式的编程方式。这意味着您可以通过使用像 @Servlet 或者 @ServletFilter
这样的适当注释对类进行注释来快速开发一个 servlet 或者过滤器类。注释不仅使
servlet、过滤器和侦听器类的编码更容易，而且，即使应用程序存档可能有 servlet、过滤器或者上下文侦听器类也可以选择用于 web
应用程序的开发部署描述符。Web 容器负责处理各种注释，其位置在 WEB-INF/classes 目录下的各个类中、WEB-INF/lib
目录下的 .jar 文件中、或者应用程序类路径中任何可以找到的类中。</p>
<p><strong>　　注释与部署描述符</strong></p>
<p>　　值得注意的是，部署描述符优先于注释。换句话说，部署描述符覆盖通过注释机制所规定的<a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/z/sys/safe-seting/index.html" target="_blank">配置</a>信
息。Web 部署描述符的 3.0 版本在 web-app 元素上包含一种名为 metadata-complete 的新属性。该属性定义了
web 描述符是否完整，或者 web 应用程序的类文件是否针对指定部署信息的注释而进行检查。如果该属性被设置为
true，则部署工具必须忽略类文件中所存在的任何 servlet
注释，并只使用描述符中所提及的配置细节。否则，如果没有指定该值或者该值被设置为
false，容器必须针对注释而扫描应用程序的所有类文件。这个属性提供了在应用程序启动阶段启用或者禁用注释扫描以及对注释的处理。</p>
<p>　　在 Servlet 3.0 中所引入的所有注释都可以在 javax.servlet.http.annotation 和
javax.servlet.http.annotation.jaxrs 软件包中找到。以下章节阐述 Servlet 3.0 中注释的完整集合：</p>
<p>　　@Servlet：javax.servlet.http.annotation.Servlet
是一个类级别的注释，确认经过注释的类为一个 servlet 并保存关于所声明的 servlet 的元数据。urlMappings 属性是指定
URL 模式(调用该 servlet)的 @Servlet 的强制属性。当接收到了一个请求时，容器将请求中的 URL 与 servlet 的
urlMappings 进行匹配，且如果 URL 模式匹配，则调用相应的 servlet
以响应该项请求。该注释的所有其他属性都是可选的，并带有合理的默认值。Servlet 类中必须有一种使用像 GET、PUT、POST、HEAD
或者 DELETE 这样的 HttpMethod 注释进行注释的方法。这些方法应将 HttpServletRequest 和
HttpServletResponse 作为方法参数。与以前的版本相反，servlets 3.0 的版本可以作为简单传统 Java
对象(Plain Old Java Objects，POJOs)而实现;也就是 servlets 不必再扩展像 HTTPServlet 或者
GenericServlet 这样的基础 servlet 实现类。</p>
<p>　　为了进行比较，在此给出了使用传统 Servlet 2.5 API 编写的 Java servlet 代码片段，如下所示。在 Servlet 2.5 中，只要在部署描述符中配置了 servlet 的详细信息，web 容器就将初始化 servlet。</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>public&nbsp;class&nbsp;MyServlet&nbsp;extends&nbsp;HttpServlet&nbsp;{&nbsp;&nbsp;</li>
    <li>public&nbsp;void&nbsp;doGet&nbsp;(HttpServletRequest&nbsp;req,&nbsp;&nbsp;&nbsp;</li>
    <li>HttpServletResponse&nbsp;res)&nbsp;{&nbsp;&nbsp;</li>
    <li>....&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="java">public class MyServlet extends HttpServlet {
public void doGet (HttpServletRequest req,
HttpServletResponse res) {
....
}
}
</textarea>
<p>　　Deployment descriptor (web.xml)</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>&lt;web-app&gt;&nbsp;&nbsp;</li>
    <li>&lt;servlet&gt;&nbsp;&nbsp;</li>
    <li>&lt;servlet-name&gt;MyServlet&lt;/servlet-name&gt;&nbsp;&nbsp;</li>
    <li>&lt;servlet-class&gt;samples.MyServlet&lt;/servlet-class&gt;&nbsp;&nbsp;</li>
    <li>&lt;/servlet&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;servlet-mapping&gt;&nbsp;&nbsp;</li>
    <li>&lt;servlet-name&gt;MyServlet&lt;/servlet-name&gt;&nbsp;&nbsp;</li>
    <li>&lt;url-pattern&gt;/MyApp&lt;/url-pattern&gt;&nbsp;&nbsp;</li>
    <li>&lt;/servlet-mapping&gt;&nbsp;&nbsp;</li>
    <li>...&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;/web-app&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="xhtml">&lt;web-app&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;MyServlet&lt;/servlet-name&gt;
&lt;servlet-class&gt;samples.MyServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;MyServlet&lt;/servlet-name&gt;
&lt;url-pattern&gt;/MyApp&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
...
&lt;/web-app&gt;</textarea>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里给出的是使用 Servlet 3.0 API 编写的较为简化的版本。当 MyServlet 使用 @Servlet 注释而被注释为一个 servlet 时，则在 web 容器的启动期间对其初始化。注意，在这种情况下部署描述符是可选的。</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>@Servlet(urlMappings={"/MyApp"})&nbsp;&nbsp;</li>
    <li>public&nbsp;class&nbsp;MyServlet&nbsp;{&nbsp;&nbsp;</li>
    <li>@GET&nbsp;&nbsp;</li>
    <li>public&nbsp;void&nbsp;handleGet(HttpServletRequest&nbsp;req,&nbsp;&nbsp;&nbsp;</li>
    <li>HttpServletResponse&nbsp;res)&nbsp;{&nbsp;&nbsp;</li>
    <li>....&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="java">@Servlet(urlMappings={"/MyApp"})
public class MyServlet {
@GET
public void handleGet(HttpServletRequest req,
HttpServletResponse res) {
....
}
}</textarea>
<p><strong><span style="font-size: xx-small;">&nbsp;&nbsp;&nbsp;&nbsp; Deployment descriptor (web.xml)<br />
<br />
</span></strong>&nbsp;&nbsp;&nbsp;&nbsp; optional</p>
<p>　　@ServletFilter 和 @FilterMapping：您可以使用
javax.servlet.http.annotation.ServletFilter 注释来注释过滤器类，从而轻松创建一个 servlet
过滤器。该注释封装正被声明的过滤器的有关元数据。在过滤器类上具有 @FilterMapping 注释也是强制性的。@FilterMapping
注释定义用于过滤器的 URL 模式。@ServletFilter 的所有其他属性都是可选的，并带有合理的默认值。V3.0 过滤器类现在类似
POJO 类，并且没有用于这些类所需的 <strong><span style="color: #16387c;">Filter</span></strong> 接口或者非参数公用构造器。以下给出了使用 Servlet v2.5 API 的过滤器类的代码片段：</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>public&nbsp;class&nbsp;MyFilter&nbsp;implements&nbsp;Filter&nbsp;{&nbsp;&nbsp;</li>
    <li>public&nbsp;void&nbsp;doFilter(ServletRequest&nbsp;req,&nbsp;&nbsp;</li>
    <li>ServletResponse&nbsp;res,&nbsp;&nbsp;</li>
    <li>FilterChain&nbsp;chain)&nbsp;&nbsp;&nbsp;</li>
    <li>throws&nbsp;IOException,&nbsp;ServletException&nbsp;{&nbsp;&nbsp;</li>
    <li>......&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="java">public class MyFilter implements Filter {
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain)
throws IOException, ServletException {
......
}
}
</textarea>
<p><span style="font-size: x-small;"><strong><small>&nbsp;&nbsp;&nbsp; Deployment descriptor (web.xml)</small></strong></span></p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>&lt;web-app&gt;&nbsp;&nbsp;</li>
    <li>&lt;filter&gt;&nbsp;&nbsp;</li>
    <li>&lt;filter-name&gt;My&nbsp;Filter&lt;/filter-name&gt;&nbsp;&nbsp;</li>
    <li>&lt;filter-class&gt;samples.MyFilter&lt;/filter-class&gt;&nbsp;&nbsp;</li>
    <li>&lt;/filter&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;filter-mapping&gt;&nbsp;&nbsp;</li>
    <li>&lt;filter-name&gt;My&nbsp;Filter&lt;/filter-name&gt;&nbsp;&nbsp;</li>
    <li>&lt;url-pattern&gt;/foo&lt;/url-pattern&gt;&nbsp;&nbsp;</li>
    <li>&lt;/filter-mapping&gt;&nbsp;&nbsp;&nbsp;</li>
    <li>...&nbsp;&nbsp;</li>
    <li>&lt;/web-app&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="xhtml">&lt;web-app&gt;
&lt;filter&gt;
&lt;filter-name&gt;My Filter&lt;/filter-name&gt;
&lt;filter-class&gt;samples.MyFilter&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;My Filter&lt;/filter-name&gt;
&lt;url-pattern&gt;/foo&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
...
&lt;/web-app&gt;</textarea>
<p>　　　使用 Servlet 3.0 编写的一个示例过滤器类如下所示。因为该类使用 ServletFilter 注释，所以容器将
MyFilter 标记为一个过滤器类。MyFilter 截取所有收到的请求，其中该请求的 URL 匹配模式 /foo。Servlet 3.0
为过滤器配置提供了可选的部署描述符。</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>@ServletFilter&nbsp;&nbsp;</li>
    <li>@FilterMapping("/foo")&nbsp;&nbsp;</li>
    <li>public&nbsp;class&nbsp;MyFilter&nbsp;{&nbsp;&nbsp;</li>
    <li>public&nbsp;void&nbsp;doFilter(HttpServletRequest&nbsp;req,&nbsp;&nbsp;&nbsp;</li>
    <li>HttpServletResponse&nbsp;res)&nbsp;{&nbsp;&nbsp;</li>
    <li>.....&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="java">@ServletFilter
@FilterMapping("/foo")
public class MyFilter {
public void doFilter(HttpServletRequest req,
HttpServletResponse res) {
.....
}
}</textarea>
<p>　　@InitParam：该注释可以用来定义必须传递给 servlet 或者过滤器类的任意初始化参数。它是 @Servlet 和
@ServletFilter 注释的一个属性。以下代码示例解释了如何将具有 english 值、称作 lang 的初始化参数传递给一个
servlet 类。</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>@Servlet(urlMappings={"/MyApp"},&nbsp;initParams&nbsp;={@InitParam(name="lang",&nbsp;value="english")})&nbsp;&nbsp;</li>
    <li>public&nbsp;class&nbsp;MyServlet&nbsp;{&nbsp;&nbsp;</li>
    <li>@GET&nbsp;&nbsp;</li>
    <li>public&nbsp;void&nbsp;handleGet(HttpServletRequest&nbsp;req,&nbsp;&nbsp;&nbsp;</li>
    <li>HttpServletResponse&nbsp;res)&nbsp;{&nbsp;&nbsp;</li>
    <li>....&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="java">@Servlet(urlMappings={"/MyApp"}, initParams ={@InitParam(name="lang", value="english")})
public class MyServlet {
@GET
public void handleGet(HttpServletRequest req,
HttpServletResponse res) {
....
}
}</textarea>
<p>
@ServletContextListener：javax.servlet.http.annotation.ServletContextListener
注释将该类声明为一个 servlet 上下文侦听器。当 web 容器创建或者销毁 ServletContext
时，该上下文侦听器接收注释。上下文侦听器是一个 POJO 类，且不必实现 ServletContextListener 接口。使用
Servlet 2.5 API 编写的侦听器类如下所示。当且仅当您在部署描述符中配置了该侦听器类，容器才识别它。</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>public&nbsp;class&nbsp;MyListener&nbsp;implements&nbsp;ServletContextListener&nbsp;{&nbsp;&nbsp;</li>
    <li>public&nbsp;void&nbsp;contextInitialized(ServletContextEvent&nbsp;sce)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
    <li>.....&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="java">public class MyListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
}
.....
}
</textarea>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: x-small;"><strong><small>Deployment Descriptor (web.xml)</small></strong></span></p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>&lt;web-app&gt;&nbsp;&nbsp;</li>
    <li>&lt;listener&gt;&nbsp;&nbsp;</li>
    <li>&lt;listener-class&gt;samples.MyListener&lt;/listener-class&gt;&nbsp;&nbsp;</li>
    <li>&lt;/listener&gt;&nbsp;&nbsp;</li>
    <li>....&nbsp;&nbsp;</li>
    <li>&lt;/web-app&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="xhtml">&lt;web-app&gt;
&lt;listener&gt;
&lt;listener-class&gt;samples.MyListener&lt;/listener-class&gt;
&lt;/listener&gt;
....
&lt;/web-app&gt;</textarea>
<p>　　使用 Servlet 3.0 API 编写的一个得到极大简化的侦听器类，如下所示。</p>
<div>
<div>
<div><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://blog.csdn.net/hudixin1987/archive/2009/08/20/4465610.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>@ServletContextListener&nbsp;&nbsp;</li>
    <li>public&nbsp;class&nbsp;MyListener&nbsp;{&nbsp;&nbsp;</li>
    <li>public&nbsp;void&nbsp;contextInitialized&nbsp;(ServletContextEvent&nbsp;sce)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
    <li>.....&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<textarea style="display: none;" cols="50" rows="15" name="code" class="java">@ServletContextListener
public class MyListener {
public void contextInitialized (ServletContextEvent sce) {
}
.....
}</textarea>
<p><span style="font-size: x-small;"><strong><small>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Deployment Descriptor (web.xml)</small></strong><br />
<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp; optional</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/295859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-09-21 14:21 <a href="http://www.blogjava.net/sealyu/archive/2009/09/21/295859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java.util.NavigableSet和NavigableMap</title><link>http://www.blogjava.net/sealyu/archive/2009/08/17/291457.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 17 Aug 2009 04:21:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/08/17/291457.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/291457.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/08/17/291457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/291457.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/291457.html</trackback:ping><description><![CDATA[<div id="message91993" class="t_msgfont">Java 6对Nevigable接口做了一些改进。具体的看以下例子：<br />
<br />
例如一个渡口有一个不定期的时刻表，我们将每日离港的时间保存在TreeSet中，那么，如果我们想获得下午4点前离开的最后一班渡轮（1600）和下午8殿后离开的最早一班渡轮（2000），应该怎么实现呢？<br />
<br />
TreeSet&lt;Integer&gt; times = new TreeSet&lt;Integer&gt;();<br />
times.add(1205);<br />
times.add(1505);<br />
times.add(1545);<br />
times.add(1830);<br />
times.add(2010);<br />
times.add(2100);<br />
<br />
Java 5中可以这样获得：<br />
<br />
TreeSet&lt;Integer&gt; subset = new TreeSet&lt;Integer&gt;();<br />
subset = (TreeSet)times.headSet(1600);<br />
System.out.println("the last before 4pm:" + subset.last());<br />
<br />
TreeSet&lt;Integer&gt; subset2 = new TreeSet&lt;Integer&gt;();<br />
subset2 = (TreeSet)times.tailSet(2000);<br />
System.out.println("the last before 8pm:" + subset.last());<br />
<br />
在Java 6中，可以更加方便：<br />
System.out.println("the last before 4pm:" + times.lower(1600));<br />
System.out.println("the last before 8pm:" + times.higher(2000));<br />
<br />
对NavigableSet有四个方法：lower(), floor(),higher()和ceiling()。
lower和floor的区别是，lower是比给定元素小的元素，而floor是小于或等于的。higher和ceiling类似。对于
NavigableMap也有四个方法： lowerKey(), floorKey(), ceilingKey()和higherKey()。</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/291457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-08-17 12:21 <a href="http://www.blogjava.net/sealyu/archive/2009/08/17/291457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入认识javascript中的eval函数(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/05/19/271465.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 19 May 2009 02:10:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/05/19/271465.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/271465.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/05/19/271465.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/271465.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/271465.html</trackback:ping><description><![CDATA[发现为本文起一个合适的标题还不是那么容易，呵呵，所以在此先说明下本文的两个目的：<br />
（1）介绍javascript中的eval函数的用法<br />
（2）如何在函数内执行全局代码
<div style="font-size: 10pt; position: relative;"><dd>
<div>►先来说eval的用法，内容比较简单，熟悉的可以跳过。<br />
eval函数接收一个参数s，如果s不是字符串，则直接返回s。否则执行s语句。如果s语句执行结果是一个值，则返回此值，否则返回undefined。<br />
需要特别注意的是对象声明语法&#8220;{}&#8221;并不能返回一个值，需要用括号括起来才会返回值，简单示例如下：</div>
<div js="">
<div style="color: #000000;"><font style="background-color: #fff8dc;"><font color="#0000ff">var</font>&nbsp;<font color="#000000">code1</font><font color="#000000">=</font><font color="#ff00ff">'"a"&nbsp;+&nbsp;2'</font><font color="#000000">;</font>&nbsp;&nbsp;&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//表达式<br />
</font><font color="#0000ff">var</font>&nbsp;<font color="#000000">code2</font><font color="#000000">=</font><font color="#ff00ff">'{a:2}'</font><font color="#000000">;</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//语句<br />
</font><font color="#000000">alert</font><font color="#000000">(</font><font color="#0000ff">eval</font><font color="#000000">(</font><font color="#000000">code1</font><font color="#000000">));</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//-&gt;'a2'<br />
</font><font color="#000000">alert</font><font color="#000000">(</font><font color="#0000ff">eval</font><font color="#000000">(</font><font color="#000000">code2</font><font color="#000000">));</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//-&gt;undefined<br />
</font><font color="#000000">alert</font><font color="#000000">(</font><font color="#0000ff">eval</font><font color="#000000">(</font><font color="#ff00ff">'('</font>&nbsp;<font color="#000000">+</font>&nbsp;<font color="#000000">code2</font>&nbsp;<font color="#000000">+</font>&nbsp;<font color="#ff00ff">')'</font><font color="#000000">));</font>&nbsp;&nbsp;&nbsp;&nbsp;<font color="#006600">//-&gt;[object&nbsp;Object]</font></font></div>
</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以看到，对于对象声明语句来说，仅仅是执行，并不能返回值。为了返回常用的&#8220;{}&#8221;这样的对象声明语句，必须用括号括住，以将其转换为表达式，才能返回其值。这也是使用<a href="http://www.x2blog.cn/supnate/?tid=3414" target="_blank">JSON</a>来进行Ajax开发的基本原理之一。在例子中可以清楚的看到，第二个alert语句输出的是undefined，而第三个加了括号后输出的是语句表示的对象。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ►现在来说本文的重点，如何在函数内执行全局代码。为了说明这个问题，先看一个例子：</div>
<div js="" style="color: #000000;" align="left"><font style="background-color: #fffacd;"><font color="#0000ff">var</font>&nbsp;<font color="#000000">s</font><font color="#000000">=</font><font color="#ff00ff">'global'</font><font color="#000000">;</font>&nbsp;&nbsp;&nbsp;&nbsp;</font><font style="background-color: #fffacd;"><font color="#006600">//定义一个全局变量<br />
</font><font color="#0000ff">function</font>&nbsp;<font color="#000000">demo1</font><font color="#000000">(){</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">eval</font><font color="#000000">(</font><font color="#ff00ff">'var&nbsp;s="local"'</font><font color="#000000">);</font><br />
<font color="#000000">}</font><br />
<font color="#000000">demo1</font><font color="#000000">();</font><br />
<font color="#000000">alert</font><font color="#000000">(</font><font color="#000000">s</font><font color="#000000">);</font>&nbsp;&nbsp;&nbsp;&nbsp;<font color="#006600">//-&gt;global</font></font></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很好理解，上面的demo1函数等价于：function demo1(){var s='local';}，其中定义了一个局部变量s。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以最后的输出是global并不是什么奇怪的事情，毕竟大家都能很清楚的区分局部变量和全局变量。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 仔细体会一下，可以发现eval函数的特点，它总是在调用它的上下文变量空间（也称为：包，closure）内执行，无论是变量定义还是函数定义都是如此，所以如下的代码会产生函数未定义的错误：</div>
<div js="">
<div style="color: #000000;"><font style="background-color: #fff8dc;"><font color="#0000ff">var</font>&nbsp;<font color="#000000">s</font><font color="#000000">=</font><font color="#ff00ff">'function&nbsp;test(){return&nbsp;1;}'</font><font color="#000000">;</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//一个函数定义语句<br />
</font><font color="#0000ff">function</font>&nbsp;<font color="#000000">demo2</font><font color="#000000">(){</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">eval</font><font color="#000000">(</font><font color="#000000">s</font><font color="#000000">);</font><br />
<font color="#000000">}</font><br />
<font color="#000000">demo2</font><font color="#000000">();</font><br />
<font color="#000000">alert</font><font color="#000000">(</font><font color="#000000">test</font><font color="#000000">());</font>&nbsp;&nbsp;&nbsp;&nbsp;<font color="#006600">//-&gt;error:test&nbsp;is&nbsp;not&nbsp;defined</font></font></div>
</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是因为test函数在局部空间定义，demo2函数内可以访问到，外面就访问不到了。</div>
<div><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 而在实际的Ajax开发中，有时我们需要从服务器动态获取代码来执行，以减轻一次载入代码过多的问题，或者是一些代码是通过Javascript自身生成的，希望用eval函数来使其执行。<br />
但这样的动态获取代码的工作一般在函数内完成，比如：</div>
<div js="">
<div style="color: #000000;"><font style="background-color: #fff8dc;"><font color="#0000ff">function</font>&nbsp;<font color="#000000">loadCode</font><font color="#000000">(){</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">var</font>&nbsp;<font color="#000000">code</font><font color="#000000">=</font><font color="#000000">getCode</font><font color="#000000">();</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">eval</font><font color="#000000">(</font><font color="#000000">code</font><font color="#000000">);</font><br />
<font color="#000000">}</font></font></div>
</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可见eval不可能在全局空间内执行，这就给开发带来了不少问题，也看到过很多人为此郁闷。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不过现在偶终于找到了解决办法，嘿嘿，可以同时兼容IE和Firefox，方法如下：</div>
<div js="">
<div style="color: #000000;"><font style="background-color: #fff8dc;"><font color="#0000ff">var</font>&nbsp;<font color="#000000">X2</font><font color="#000000">={}</font>&nbsp;&nbsp;&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//my&nbsp;namespace:)<br />
</font><font color="#000000">X2</font><font color="#000000">.</font><font color="#000000">Eval</font><font color="#000000">=</font><font color="#0000ff">function</font><font color="#000000">(</font><font color="#000000">code</font><font color="#000000">){</font><br />
&nbsp;<font color="#0000ff">if</font><font color="#000000">(!!(</font><font color="#000000">window</font><font color="#000000">.</font><font color="#000000">attachEvent</font>&nbsp;<font color="#000000">&amp;&amp;</font>&nbsp;<font color="#000000">!</font><font color="#000000">window</font><font color="#000000">.</font><font color="#000000">opera</font><font color="#000000">)){</font><br />
&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//ie<br />
</font>&nbsp;&nbsp;<font color="#000000">execScript</font><font color="#000000">(</font><font color="#000000">code</font><font color="#000000">);</font>&nbsp;<br />
&nbsp;<font color="#000000">}</font><font color="#0000ff">else</font><font color="#000000">{</font><br />
&nbsp;&nbsp;</font><font style="background-color: #fff8dc;"><font color="#006600">//not&nbsp;ie<br />
</font>&nbsp;&nbsp;<font color="#000000">window</font><font color="#000000">.</font><font color="#0000ff">eval</font><font color="#000000">(</font><font color="#000000">code</font><font color="#000000">);</font><br />
&nbsp;<font color="#000000">}</font><br />
<font color="#000000">}</font></font></div>
</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在如果要想在函数内定义全局代码，就可以通过调用X2.Eval(code)方法，一个例子如下：</div>
<div js="">
<div style="color: #000000;"><font style="background-color: #fff8dc;"><font color="#0000ff">var</font>&nbsp;<font color="#000000">s</font><font color="#000000">=</font><font color="#ff00ff">'global'</font><font color="#000000">;</font><br />
<font color="#0000ff">function</font>&nbsp;<font color="#000000">demo3</font><font color="#000000">(){</font><br />
&nbsp;<font color="#000000">X2</font><font color="#000000">.</font><font color="#000000">Eval</font><font color="#000000">(</font><font color="#ff00ff">'var&nbsp;s="local"'</font><font color="#000000">);</font><br />
<font color="#000000">}</font><br />
<font color="#000000">demo3</font><font color="#000000">();</font><br />
<font color="#000000">alert</font><font color="#000000">(</font><font color="#000000">s</font><font color="#000000">);</font>&nbsp;<font color="#006600">//-&gt;'local'</font></font></div>
</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可见，在demo3函数内重新定义了全局变量s="local"。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 需要注意的是X2.Eval并不返回值，如果要进行表达式的求值，还是用系统的eval函数。X2.Eval设计为仅做全局代码定义用。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实看到这里，或许有人感觉问题也太容易解决了点，呵呵，但发现这个办法倒是需要些运气和技巧的：<br />
（1）对于IE浏览器，默认已经提供了这样的函数：execScript，用于在全局空间执行代码，只是知道的人还不多。<br />
（2）对于Firefox浏览器，直接调用eval函数，则在调用者的空间执行；如果调用&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; window.eval则在全局空间执行。这个知道的人估计就更少了。毕竟alert(eval==window.eval)返回true！</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Firefox的eval函数的特点的确是很令人奇怪的，但从javascript规范中倒也能找到其来源：</div>
<div><font style="background-color: #ffefd5;">If value of the eval property is used in any way other than a direct call (that is, other than by the explicit use of its<br />
name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to,<br />
an EvalError exception may be thrown.</font></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 意思大概就是说eval函数的执行是和调用者相关的，但并没有说其执行上下文的问题。所以IE和Firefox孰是孰非也就很难说了，大家知道解决办法就好。</div>
</dd>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div><font style="background-color: #ffdab9;" color="#a9a9a9">原文地址：</font><a href="http://www.x2blog.cn/supnate/#sid.1735/page.1/"><font style="background-color: #808080;" color="#a9a9a9">[url]http://www.x2blog.cn/supnate/#sid.1735/page.1/[/url]</font></a></div>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/271465.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-05-19 10:10 <a href="http://www.blogjava.net/sealyu/archive/2009/05/19/271465.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JBoss中的TransactionTimeOut的设置</title><link>http://www.blogjava.net/sealyu/archive/2009/05/18/271239.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 18 May 2009 02:01:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/05/18/271239.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/271239.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/05/18/271239.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/271239.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/271239.html</trackback:ping><description><![CDATA[这两天在作seam的时候不时遇到&#8220;org.jboss.util.NestedSQLException: Transaction is not active&#8221;的错误信息，后来到jboss的wiki上发现了问题的原因和解决方法：<br />
<h2> How do I set the Transaction Timeout?</h2>
<p>Transaction timeout (unit is always seconds) can be configured in various ways:</p>
<p>This argument is the same no matter if you are using JBossTM(&lt;=4.0.5) or JBossJTA-Arjuna(&gt;=4.2).</p>
<ul>
    <li type="ul">
    <p>Globally:
    You can change this behavior globally by modifying the
    TransactionManagerService in /conf/jboss-service.xml (or
    /deploy/jta-service.xml for 4.0.3)</p>
    </li>
</ul>
<p><strong>Version &lt;= 4.0.5</strong></p>
<p>This part is the same for either JBossTM or JBossJTA and is the same for ejb2 and ejb3</p>
<ul>
    <li type="ul">
    <p>Per-method basis: Modifying the <span style="font-family: courier new,courier;">&lt;transaction-timeout&gt;</span>
    element inside the &lt;method&gt; element of a session or entity bean.
    This is located in the META-INF/jboss.xml deployment descriptor of a
    session bean. When the transaction timeout is specified at the method
    level, it overrides the default timeout. Further information about this
    element can be found in jboss-x.x.x/docs/dtd/jboss_4_0.dtd. Example
    taken from the testsuite:</p>
    </li>
</ul>
<ul>
    <li type="ul">
    <p>Using
    BMT: Calling
    javax.transaction.UserTransaction.setTransactionTimeout(int seconds).
    Please, be aware that this only applies to transactions started after
    this invocation on the same thread. Example:</p>
    </li>
</ul>
<pre><code jive-java="">@TransactionTimeout(1500)<br />
</code></pre>
<br />
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/271239.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-05-18 10:01 <a href="http://www.blogjava.net/sealyu/archive/2009/05/18/271239.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>serialVersionUID的作用以及设置方法（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/04/21/266759.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 21 Apr 2009 06:57:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/04/21/266759.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/266759.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/04/21/266759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/266759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/266759.html</trackback:ping><description><![CDATA[设置 serialVersionUID默认的生成方式：&nbsp; private static final long serialVersionUID = 1L;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; serialVersionUID的作用：serialVersionUID&nbsp;用来表明类的不同版本间的兼容性。如果你修改了此类,&nbsp;要修改此值。否则以前用老版本的类序列化的类恢复时会出错。
<div id="more" class="asset-more">
在JDK中，可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID，对于Test.class，执行命令：serialver&nbsp;Test。 <br />
为了在反序列化时，确保类版本的兼容性，最好在每个要序列化的类中加入
private&nbsp;static&nbsp;final&nbsp;long&nbsp;serialVersionUID这个属性，具体数值自己定义。这样，即使某个类在与之对应的对象
已经序列化出去后做了修改，该对象依然可以被正确反序列化。否则，如果不显式定义该属性，这个属性值将由JVM根据类的相关信息计算，而修改后的类的计算
结果与修改前的类的计算结果往往不同，从而造成对象的反序列化因为类版本不兼容而失败。 <br />
不显式定义这个属性值的另一个坏处是，不利于程序在不同的JVM之间的移植。因为不同的编译器实现该属性值的计算策略可能不同，从而造成虽然类没有改变，但是因为JVM不同，出现因类版本不兼容而无法正确反序列化的现象出现。
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/266759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-04-21 14:57 <a href="http://www.blogjava.net/sealyu/archive/2009/04/21/266759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>cvc-complex-type.2.4.a错误解决方法(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/03/26/262236.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 26 Mar 2009 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/03/26/262236.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/262236.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/03/26/262236.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/262236.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/262236.html</trackback:ping><description><![CDATA[在web.xml中配置taglib的时候，有时候会碰到这个错误。<br />
解决方法如下：<br />
&lt;jsp-config&gt;<br />
&lt;taglib&gt;<br />
&lt;taglib-uri&gt;/tags/struts-html&lt;/taglib-uri&gt;<br />
&lt;taglib-location&gt;/WEB-INF/struts-html.tld&lt;/taglib-location&gt;<br />
&lt;/taglib&gt;<br />
<br />
&lt;taglib&gt;<br />
&lt;taglib-uri&gt;/tags/struts-bean&lt;/taglib-uri&gt;<br />
&lt;taglib-location&gt;/WEB-INF/struts-bean.tld&lt;/taglib-location&gt;<br />
&lt;/taglib&gt;<br />
<br />
&lt;taglib&gt;<br />
&lt;taglib-uri&gt;/tags/struts-logic&lt;/taglib-uri&gt;<br />
&lt;taglib-location&gt;/WEB-INF/struts-logic.tld&lt;/taglib-location&gt;<br />
&lt;/taglib&gt;<br />
&lt;/jsp-config&gt;<br />
<strong>///////把所有跟jsp设置有关的内容都放在&lt;jsp-config&gt;&lt;/jsp-config&gt;里面////////</strong>
<img src ="http://www.blogjava.net/sealyu/aggbug/262236.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-03-26 20:55 <a href="http://www.blogjava.net/sealyu/archive/2009/03/26/262236.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java相对路径/绝对路径总结 （转）</title><link>http://www.blogjava.net/sealyu/archive/2009/03/24/261728.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 24 Mar 2009 08:35:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/03/24/261728.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/261728.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/03/24/261728.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/261728.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/261728.html</trackback:ping><description><![CDATA[<font size="3"><strong>1.基本概念的理解</strong><br />
<br />
绝对路径：绝对路径就是你的主页上的文件或目录在硬盘上真正的路径，(URL和物理路径)例如：<br />
C:xyz est.txt 代表了test.txt文件的绝对路径。http://www.sun.com/index.htm也代表了一个URL绝对路径。<br />
<br />
相对路径：相对与某个基准目录的路径。包含Web的相对路径（HTML中的相对目录），例如：在<br />
Servlet中，"/"代表Web应用的跟目录。和物理路径的相对表示。例如："./" 代表当前目录,"../"代表上级目录。这种类似的表示，也是属于相对路径。<br />
另外关于URI，URL,URN等内容，请参考RFC相关文档标准。<br />
<br />
RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax,<br />
(http://www.ietf.org/rfc/rfc2396.txt)<br />
<br />
<br />
<strong>2.关于JSP/Servlet中的相对路径和绝对路径。</strong><br />
<br />
2.1服务器端的地址<br />
<br />
服务器端的相对地址指的是相对于你的web应用的地址，这个地址是在服务器端解析的（不同于html和javascript中的相对地址，他们是由客户端
浏览器解析的）也就是说这时候在jsp和servlet中的相对地址应该是相对于你的web应用，即相对于http:
//192.168.0.1/webapp/的。<br />
<br />
其用到的地方有：<br />
forward：servlet中的request.getRequestDispatcher(address);这个address是在服务器端解析
的，所以，你要forward到a.jsp应该这么写：request.getRequestDispatcher(&#8220;/user/a.jsp&#8221;)这个/
相对于当前的web应用webapp，其绝对地址就是：http://192.168.0.1/webapp/user/a.jsp。
sendRedirect：在jsp中&lt;%response.sendRedirect("/rtccp/user/a.jsp");%&gt;<br />
<br />
2.22、客户端的地址<br />
<br />
所有的html页面中的相对地址都是相对于服务器根目录(http://192.168.0.1/)的，而不是(跟目录下的该Web应用的目录)
http://192.168.0.1/webapp/的。
Html中的form表单的action属性的地址应该是相对于服务器根目录(http://192.168.0.1/)的，所以，如果提交到a.jsp
为：action＝"/webapp/user/a.jsp"或action="&lt;%=request.getContextPath()%
&gt;"/user/a.jsp；<br />
提交到servlet为actiom＝"/webapp/handleservlet" Javascript也是在客户端解析的，所以其相对路径和form表单一样。<br />
<br />
<br />
因此，一般情况下，在JSP/HTML页面等引用的CSS,Javascript.Action等属性前面最好都加上<br />
&lt;%=request.getContextPath()%&gt;,以确保所引用的文件都属于Web应用中的目录。另外，应该尽量避免使用类
似".","./","../../"等类似的相对该文件位置的相对路径，这样当文件移动时，很容易出问题。<br />
<br />
<br />
<strong>3. JSP/Servlet中获得当前应用的相对路径和绝对路径<br />
<br />
</strong>3.1 JSP中获得当前应用的相对路径和绝对路径<br />
根目录所对应的绝对路径:request.getRequestURI()<br />
文件的绝对路径 　:application.getRealPath(request.getRequestURI());<br />
当前web应用的绝对路径 :application.getRealPath("/");<br />
取得请求文件的上层目录:new File(application.getRealPath(request.getRequestURI())).getParent()<br />
<br />
3.2 Servlet中获得当前应用的相对路径和绝对路径<br />
根目录所对应的绝对路径:request.getServletPath();<br />
文件的绝对路径 :request.getSession().getServletContext().getRealPath<br />
(request.getRequestURI())<br />
当前web应用的绝对路径 :servletConfig.getServletContext().getRealPath("/");<br />
(ServletContext对象获得几种方式：<br />
javax.servlet.http.HttpSession.getServletContext()<br />
javax.servlet.jsp.PageContext.getServletContext()<br />
javax.servlet.ServletConfig.getServletContext()<br />
)<br />
<br />
<strong>4.java 的Class中获得相对路径，绝对路径的方法<br />
<br />
</strong>4.1单独的Java类中获得绝对路径<br />
根据java.io.File的Doc文挡，可知:<br />
默认情况下new File("/")代表的目录为：System.getProperty("user.dir")。<br />
一下程序获得执行类的当前路径<br />
</font>
<div>
<div><font size="3">&nbsp;</font></div>
<ol>
    <li><font size="3">package&nbsp;org.cheng.file;&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;</font> </li>
    <li><font size="3">import&nbsp;java.io.File;&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;</font> </li>
    <li><font size="3">public&nbsp;class&nbsp;FileTest&nbsp;{&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;throws&nbsp;Exception&nbsp;{&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(FileTest.class.getClassLoader().getResource(""));&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(ClassLoader.getSystemResource(""));&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(FileTest.class.getResource(""));&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(FileTest.class.getResource("/"));</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</font>Class<font size="3">文件所在路径</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(new&nbsp;File("/").getAbsolutePath());&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(System.getProperty("user.dir"));&nbsp;&nbsp;</font> </li>
    <li><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</font> </li>
    <li><font size="3"><span>}&nbsp; <br />
    </span></font></li>
</ol>
</div>
<font size="3"><br />
4.2服务器中的Java类获得当前路径（来自网络）<strong><br />
<br />
</strong>(1).Weblogic<br />
<br />
WebApplication的系统文件根目录是你的weblogic安装所在根目录。<br />
例如：如果你的weblogic安装在c:eaweblogic700.....<br />
那么，你的文件根路径就是c:.<br />
所以，有两种方式能够让你访问你的服务器端的文件：<br />
a.使用绝对路径：<br />
比如将你的参数文件放在c:yourconfigyourconf.properties，<br />
直接使用 new FileInputStream("yourconfig/yourconf.properties");<br />
b.使用相对路径：<br />
相对路径的根目录就是你的webapplication的根路径，即WEB-INF的上一级目录，将你的参数文件放<br />
<br />
在yourwebappyourconfigyourconf.properties，<br />
这样使用：<br />
new FileInputStream("./yourconfig/yourconf.properties");<br />
这两种方式均可，自己选择。<br />
<br />
(2).Tomcat<br />
<br />
在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin<br />
<br />
(3).Resin<br />
<br />
不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET<br />
的路径为根.比如用新建文件法测试File f = new File("a.htm");<br />
这个a.htm在resin的安装目录下<br />
<br />
(4).如何读相对路径哪？<br />
<br />
在Java文件中getResource或getResourceAsStream均可<br />
<br />
例：getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web<br />
<br />
发布根路径下WEB-INF/classes<br />
<br />
默认使用该方法的路径是：WEB-INF/classes。已经在Tomcat中测试。<br />
<br />
5.读取文件时的相对路径，避免硬编码和绝对路径的使用。（来自网络）<br />
5.1 采用Spring的DI机制获得文件，避免硬编码。<br />
参考下面的连接内容：<br />
http://www.javajia.net/viewtopic.php?p=90213&amp;<br />
5.2 配置文件的读取<br />
参考下面的连接内容：<br />
http://dev.csdn.net/develop/article/39/39681.shtm<br />
<br />
<strong>5.3 通过虚拟路径或相对路径读取一个xml文件，避免硬编码<br />
<br />
</strong>参考下面的连接内容：<br />
http://club.gamvan.com/club/clubPage.jsp?iPage=1&amp;tID=10708&amp;ccID=8<br />
<br />
6.Java中文件的常用操作（复制，移动，删除，创建等）（来自网络）<br />
常用 java File 操作类<br />
http://www.easydone.cn/014/200604022353065155.htm<br />
<br />
Java文件操作大全（JSP中）<br />
http://www.pconline.com.cn/pcedu/empolder/gj/java/0502/559401.html<br />
<br />
java文件操作详解（Java中文网）<br />
http://www.51cto.com/html/2005/1108/10947.htm<br />
<br />
JAVA 如何创建删除修改复制目录及文件<br />
http://www.gamvan.com/developer/java/2005/2/264.html<br />
<br />
总结：<br />
通过上面内容的使用，可以解决在Web应用服务器端，移动文件，查找文件，复制<br />
删除文件等操作，同时对服务器的相对地址，绝对地址概念更加清晰。<br />
建议参考URI,的RFC标准文挡。同时对Java.io.File. Java.net.URI.等内容了解透彻<br />
对其他方面的理解可以更加深入和透彻。</font>
<img src ="http://www.blogjava.net/sealyu/aggbug/261728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-03-24 16:35 <a href="http://www.blogjava.net/sealyu/archive/2009/03/24/261728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ant 的最完整build.xml(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/03/21/261264.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Sat, 21 Mar 2009 14:18:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/03/21/261264.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/261264.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/03/21/261264.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/261264.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/261264.html</trackback:ping><description><![CDATA[Ant的概念<br />
Make命令是一个项目管理工具，而Ant所实现功能与此类似。像make，gnumake和nmake这些编译工具都有一定的缺陷，但是Ant却克服了这些工具的缺陷。最初Ant开发者在开发跨平台的应用时，用样也是基于这些缺陷对Ant做了更好的设计。<br />
<br />
Ant 与 makefile<br />
Makefile有一些不足之处，比如很多人都会碰到的烦人的Tab问题。最初的Ant开发者多次强调&#8221;只是我在Tab前面加了一个空格，所以我的命令就不能执行&#8221;。有一些工具在一定程度上解决了这个问题，但还是有很多其他的问题。Ant则与一般基于命令的工具有所不同，它是Java类的扩展。Ant运行需要的XML格式的文件不是Shell命令文件。它是由一个Project组成的，而一个Project又可分成可多target，target再细分又分成很多task，每一个task都是通过一个实现特定接口的java类来完成的。<br />
<br />
Ant的优点<br />
<br />
Ant是Apache软件基金会JAKARTA目录中的一个子项目，它有以下的优点。<br />
&nbsp; &nbsp;跨平台性。Ant是存Java语言编写的，所示具有很好的跨平台性。<br />
&nbsp; &nbsp;操作简单。Ant是由一个内置任务和可选任务组成的。Ant运行时需要一个XML文件(构建文件)。<br />
<br />
Ant通过调用target树，就可以执行各种task。每个task实现了特定接口对象。由于Ant构建文件时XML格式的文件，所以和容易维护和书写，而且结构很清晰。<br />
Ant可以集成到开发环境中。由于Ant的跨平台性和操作简单的特点，它很容易集成到一些开发环境中去。<br />
<br />
Ant 开发<br />
<br />
Ant的构建文件<br />
&nbsp; &nbsp;当开始一个新的项目时，首先应该编写Ant构建文件。构建文件定义了构建过程，并被团队开发中每个人使用。Ant构建文件默认命名为 build.xml，也可以取其他的名字。只不过在运行的时候把这个命名当作参数传给Ant。构建文件可以放在任何的位置。一般做法是放在项目顶层目录中，这样可以保持项目的简洁和清晰。下面是一个典型的项目层次结构。<br />
(1) src存放文件。<br />
(2) class存放编译后的文件。<br />
(3) lib存放第三方JAR包。<br />
(4) dist存放打包，发布以后的代码。<br />
Ant构建文件是XML文件。每个构建文件定义一个唯一的项目(Project元素)。每个项目下可以定义很多目标(target元素)，这些目标之间可以有依赖关系。当执行这类目标时，需要执行他们所依赖的目标。每个目标中可以定义多个任务，目标中还定义了所要执行的任务序列。Ant在构建目标时必须调用所定义的任务。任务定义了Ant实际执行的命令。Ant中的任务可以为3类。<br />
（1） 核心任务。核心任务是Ant自带的任务。<br />
（2） 可选任务。可选任务实来自第三方的任务，因此需要一个附加的JAR文件。<br />
（3） 用户自定义的任务。用户自定义的任务实用户自己开发的任务。<br />
1.&lt;project&gt;标签<br />
&nbsp;&nbsp;每个构建文件对应一个项目。&lt;project&gt;标签时构建文件的根标签。它可以有多个内在属性，就如代码中所示，其各个属性的含义分别如下。<br />
(1) default表示默认的运行目标，这个属性是必须的。<br />
(2) basedir表示项目的基准目录。<br />
(3) name表示项目名。<br />
(4) description表示项目的描述。<br />
每个构建文件都对应于一个项目，但是大型项目经常包含大量的子项目，每一个子项目都可以有自己的构建文件。<br />
<br />
2.&lt;target&gt;标签 <br />
一个项目标签驴梢杂幸桓龌蚨喔?/span&gt;target标签。一个target标签可以依赖其他的target标签。例如，有一个target用于编译程序，另一个target用于声称可执行文件。在生成可执行文件之前必须先编译该文件，因策可执行文件的target依赖于编译程序的 target。Target的所有属性如下。<br />
(1).name表示标明，这个属性是必须的。<br />
(2).depends表示依赖的目标。<br />
(3)if表示仅当属性设置时才执行。<br />
(4)unless表示当属性没有设置时才执行。<br />
(5)description表示项目的描述。<br />
Ant的depends属性指定了target的执行顺序。Ant会依照depends属性中target出现顺序依次执行每个target。在执行之前，首先需要执行它所依赖的target。程序中的名为run的target的depends属性compile，而名为compile的target的 depends属性是prepare，所以这几个target执行的顺序是prepare-&gt;compile-&gt;run。一个target只能被执行一次，即使有多个target依赖于它。如果没有if或unless属性，target总会被执行。<br />
<br />
3.&lt;mkdir&gt;标签<br />
该标签用于创建一个目录，它有一个属性dir用来指定所创建的目录名，其代码如下：<br />
&lt;mkdir dir=&#8221;＄{class.root}&#8221;/&gt;<br />
通过以上代码就创建了一个目录，这个目录已经被前面的property标签所指定。<br />
<br />
4&lt;jar&gt;标签<br />
该标签用来生成一个JAR文件，其属性如下。<br />
(1) destfile表示JAR文件名。<br />
(2) basedir表示被归档的文件名。<br />
(3) includes表示别归档的文件模式。<br />
(4) exchudes表示被排除的文件模式。<br />
<br />
5．&lt;javac标签&gt;<br />
该标签用于编译一个或一组java文件，其属性如下。<br />
(1).srcdir表示源程序的目录。<br />
(2).destdir表示class文件的输出目录。<br />
(3).include表示被编译的文件的模式。<br />
(4).excludes表示被排除的文件的模式。<br />
(5).classpath表示所使用的类路径。<br />
(6).debug表示包含的调试信息。<br />
(7).optimize表示是否使用优化。<br />
(8).verbose 表示提供详细的输出信息。<br />
(9).fileonerror表示当碰到错误就自动停止。<br />
<br />
6．&lt;java&gt;标签<br />
该标签用来执行编译生成的.class文件，其属性如下。<br />
(1).classname 表示将执行的类名。<br />
(2).jar表示包含该类的JAR文件名。<br />
(3).classpath所表示用到的类路径。<br />
(4).fork表示在一个新的虚拟机中运行该类。<br />
(5).failonerror表示当出现错误时自动停止。<br />
(6).output 表示输出文件。<br />
(7).append表示追加或者覆盖默认文件。<br />
<br />
7.&lt;delete&gt;标签<br />
该标签用于删除一个文件或一组文件，去属性如下。<br />
(1)/file表示要删除的文件。<br />
(2).dir表示要删除的目录。<br />
(3).includeEmptyDirs 表示指定是否要删除空目录，默认值是删除。<br />
(4).failonerror 表示指定当碰到错误是否停止，默认值是自动停止。<br />
(5).verbose表示指定是否列出所删除的文件，默认值为不列出。<br />
<br />
8.&lt;copy&gt;标签<br />
该标签用于文件或文件集的拷贝，其属性如下。<br />
(1).file 表示源文件。<br />
(2).tofile 表示目标文件。<br />
(3).todir 表示目标目录。<br />
(4).overwrite 表示指定是否覆盖目标文件，默认值是不覆盖。<br />
(5).includeEmptyDirs 表示制定是否拷贝空目录，默认值为拷贝。<br />
(6).failonerror 表示指定如目标没有发现是否自动停止，默认值是停止。<br />
(7).verbose 表示制定是否显示详细信息，默认值不显示。<br />
<br />
Ant的数据类型<br />
在构建文件中为了标识文件或文件组，经常需要使用数据类型。数据类型包含在org.apache.tool.<span>ant</span>.types包中。下面简单介绍构建文件中常用的数据类型。<br />
<br />
1. argument 类型<br />
由Ant构建文件调用的程序，可以通过&lt;arg&gt;元素向其传递命令行参数，如apply,exec和java任务均可接受嵌套&lt;arg&gt;元素，可以为各自的过程调用指定参数。以下是&lt;arg&gt;的所有属性。<br />
(1).values 是一个命令参数。如果参数种有空格，但又想将它作为单独一个值，则使用此属性。<br />
(2).file表示一个参数的文件名。在构建文件中，此文件名相对于当前的工作目录。<br />
(3).line表示用空格分隔的多个参数列表。<br />
(4).path表示路径。<br />
<br />
2.ervironment 类型<br />
&nbsp;&nbsp;由Ant构建文件调用的外部命令或程序，&lt;env&gt;元素制定了哪些环境变量要传递给正在执行的系统命令，&lt;env&gt;元素可以接受以下属性。<br />
(1).file表示环境变量值得文件名。此文件名要被转换位一个绝对路径。<br />
(2).path表示环境变量的路径。Ant会将它转换为一个本地约定。<br />
(3).value 表示环境变量的一个直接变量。<br />
(4).key 表示环境变量名。<br />
注意&nbsp; &nbsp;&nbsp;&nbsp;file path 或 value只能取一个。<br />
<br />
3.filelist类型<br />
Filelist 是一个支持命名的文件列表的数据类型，包含在一个filelist类型中的文件不一定是存在的文件。以下是其所有的属性。<br />
(1).dir是用于计算绝对文件名的目录。<br />
(2).files 是用逗号分隔的文件名列表。<br />
(3).refid 是对某处定义的一个&lt;filelist&gt;的引用。<br />
注意&nbsp; &nbsp;&nbsp;&nbsp;dir 和 files 都是必要的，除非指定了refid(这种情况下，dir和files都不允许使用)。<br />
<br />
4.fileset类型<br />
Fileset 数据类型定义了一组文件，并通常表示为&lt;fileset&gt;元素。不过，许多ant任务构建成了隐式的fileset,这说明他们支持所有的fileset属性和嵌套元素。以下为fileset 的属性列表。<br />
(1).dir表示fileset 的基目录。<br />
(2).casesensitive的值如果为false，那么匹配文件名时，fileset不是区分大小写的，其默认值为true.<br />
(3).defaultexcludes 用来确定是否使用默认的排除模式，默认为true。<br />
(4).excludes 是用逗号分隔的需要派出的文件模式列表。<br />
(5).excludesfile 表示每行包含一个排除模式的文件的文件名。<br />
(6).includes 是用逗号分隔的，需要包含的文件模式列表。<br />
(7).includesfile 表示每行包括一个包含模式的文件名。<br />
<br />
5.patternset 类型 <br />
Fileset 是对文件的分组，而patternset是对模式的分组，他们是紧密相关的概念。&lt;patternset&gt;支持4个属性：includes excludex includexfile 和 excludesfile,与fileset相同。Patternset 还允许以下嵌套元素：include,exclude,includefile 和 excludesfile.<br />
<br />
6.filterset 类型<br />
Filterset定义了一组过滤器，这些过滤器将在文件移动或复制时完成文件的文本替换。主要属性如下：<br />
(1).begintoken 表示嵌套过滤器所搜索的记号，这是标识其开始的字符串。<br />
(2).endtoken表示嵌套过滤器所搜索的记号这是标识其结束的字符串。<br />
(3).id是过滤器的唯一标志符。<br />
(4).refid是对构建文件中某处定义一个过滤器的引用。<br />
<br />
7.Path类型 <br />
Path元素用来表示一个类路径，不过它还可以用于表示其他的路径。在用作揖个属性时，路经中的各项用分号或冒号隔开。在构建的时候，此分隔符将代替当前平台中所有的路径分隔符，其拥有的属性如下。<br />
(1).location 表示一个文件或目录。Ant在内部将此扩展为一个绝对路径。<br />
(2).refid 是对当前构建文件中某处定义的一个path的引用。<br />
(3).path表示一个文件或路径名列表。<br />
<br />
8.mapper类型<br />
Mapper类型定义了一组输入文件和一组输出文件间的关系，其属性如下。<br />
(1).classname 表示实现mapper类的类名。当内置mapper不满足要求时，用于创建定制mapper.<br />
(2).classpath表示查找一个定制mapper时所用的类型路径。<br />
(3).classpathref是对某处定义的一个类路径的引用。<br />
(4).from属性的含义取决于所用的mapper.<br />
(5).to属性的含义取决于所用的mapper.<br />
(6).type属性的取值为identity，flatten glob merge&nbsp; &nbsp;&nbsp;&nbsp;regexp&nbsp;&nbsp;其中之一，它定义了要是用的内置mapper的类型。<br />
<br />
Ant 的运行<br />
安装好Ant并且配置好路径之后，在命令行中切换到构建文件的目录，输入Ant命令就可以运行Ant.若没有指定任何参数，Ant会在当前目录下查询 build.xml文件。如果找到了就用该文件作为构建文件。如果使用了 &#8211;find 选项，Ant 就会在上级目录中找构建文件，直至到达文件系统得跟目录。如果构建文件的名字不是build.xml ，则Ant运行的时候就可以使用 &#8211;buildfile file ,这里file 指定了要使用的构建文件的名称，示例如下：Ant<br />
如下说明了表示当前目录的构建文件为build.xml 运行 ant 执行默认的目标。Ant &#8211;buildfile&nbsp; &nbsp;&nbsp;&nbsp;test.xml<br />
使用当前目录下的test.xml 文件运行Ant ,执行默认的目标.<br />
<br />
&lt;?xml version="1.0" encoding="GB2312" ?&gt;<br />
&lt;!--<br />
=======================================================================<br />
hello-ant 项目 ,学习ant工具的build file.<br />
<br />
参照ant的jakarta-ant-1.6alpha的build.xml<br />
<br />
Copyright (c) 2002 The Neusoft Software Foundation. All rights<br />
reserved.<br />
<br />
=======================================================================<br />
--&gt;<br />
&lt;!--<br />
文档结构为:<br />
&lt;project&gt;<br />
&lt;property/&gt; 全局变量的定义<br />
&lt;property/&gt;...<br />
<br />
&lt;target name="1"&gt; 任务组(tasks)<br />
&lt;javac&gt;&lt;/javac&gt; 一项javac任务<br />
...<br />
&lt;oneTask&gt;&lt;/ontTask&gt; 一项其它任务<br />
&lt;/target&gt;<br />
<br />
&lt;target name="2"&gt;<br />
&lt;javac&gt;&lt;/javac&gt;<br />
...<br />
&lt;oneTask&gt;&lt;/ontTask&gt;<br />
&lt;/target&gt;<br />
&lt;/project&gt;<br />
<br />
project代表一个项目，<br />
default:运行到名称为"dist"的target(任务组)<br />
basedir:基准路径。<br />
--&gt;<br />
&lt;project default="dist" basedir="."&gt;<br />
<br />
&lt;!--<br />
===================================================================<br />
定义属性（property tasks）<br />
最好把用到的路径呀，名称呀都在这里定义成全局变量<br />
例：定义<br />
&lt;property name="a" value="hello"/&gt;<br />
以后就可以这样用它：<br />
&lt;property name="b" value="${a}/b"/&gt;<br />
现在:b=="hello/b"<br />
===================================================================<br />
--&gt;<br />
<br />
&lt;!--主要的系统环境属性--&gt;<br />
&lt;property environment="env"/&gt;&lt;!--取window,unix...的环境变量--&gt;<br />
&lt;property name="java.home" value="${env.JAVA_HOME}"/&gt;<br />
&lt;property name="ant.home" value="${env.ANT_HOME}"/&gt;<br />
<br />
&lt;!--主要的app环境属性--&gt;<br />
&lt;property name="app.name" value="hello-ant"/&gt;<br />
&lt;property name="app.jar" value="${app.name}.jar"/&gt;<br />
&lt;property name="app.copyright" value=" Copyright (c) 2002 The Neusoft Software Foundation. All rights reserved."/&gt;<br />
<br />
&lt;!--app中src的属性--&gt;<br />
&lt;property name="src.dir" value="src" /&gt;<br />
&lt;property name="src.main" value="${src.dir}/main"/&gt;<br />
&lt;property name="src.script" value="${src.dir}/script"/&gt;<br />
<br />
&lt;!--app用到的lib--&gt;<br />
&lt;property name="lib.dir" value="lib"/&gt;<br />
<br />
&lt;!--app的build目录中--&gt;<br />
&lt;property name="build.dir" value="build" /&gt;<br />
&lt;property name="build.classes" value="${build.dir}/classes"/&gt;<br />
&lt;property name="build.docs" value="${build.dir}/docs"/&gt;<br />
&lt;property name="build.docs.api" value="${build.docs}/api"/&gt;<br />
&lt;property name="build.lib" value="${build.dir}/lib"/&gt;<br />
<br />
&lt;!--app的dist (distribution) 目录中--&gt;<br />
&lt;property name="dist.dir" value="dist"/&gt;<br />
&lt;property name="dist.bin" value="${dist.dir}/bin"/&gt;<br />
&lt;property name="dist.docs" value="${dist.dir}/docs"/&gt;<br />
&lt;property name="dist.lib" value="${dist.dir}/lib"/&gt;<br />
<br />
&lt;!--app的docs目录中--&gt;<br />
&lt;property name="docs.dir" value="docs"/&gt;<br />
<br />
&lt;!--<br />
定义一组路径以后可以通过id重用这组路径 ，例：<br />
&lt;javac srcdir="src/main" destdir="build/classes"&gt;<br />
&lt;classpath refid="classpath"/&gt;<br />
&lt;/javac&gt;<br />
--&gt;<br />
&lt;path id="classpath"&gt;<br />
&lt;!--本项目只有一个java，用不上classpath，这里只是做个例子--&gt;<br />
&lt;pathelement location="${build.classes}"/&gt;<br />
&lt;pathelement path="${java.home}/lib/tools.jar"/&gt;<br />
&lt;/path&gt;<br />
<br />
&lt;!--<br />
===================================================================<br />
init 准备目录(File Tasks)<br />
主要的目录结构通常是不会变的，一起生成他们<br />
===================================================================<br />
--&gt;<br />
&lt;target name="init"&gt;<br />
&lt;!--清除以前目录--&gt;<br />
&lt;delete dir="${build.dir}" failonerror="false" /&gt;<br />
&lt;delete dir="${dist.dir}" failonerror="false"/&gt;<br />
<br />
&lt;!--准备目录--&gt;<br />
&lt;mkdir dir="${build.dir}"/&gt;<br />
&lt;mkdir dir="${build.classes}"/&gt;<br />
&lt;mkdir dir="${build.docs}"/&gt;<br />
&lt;mkdir dir="${build.docs.api}"/&gt;<br />
&lt;mkdir dir="${build.lib}"/&gt;<br />
<br />
&lt;mkdir dir="${dist.dir}"/&gt;<br />
&lt;mkdir dir="${dist.bin}"/&gt;<br />
&lt;mkdir dir="${dist.lib}"/&gt;<br />
<br />
&lt;/target&gt;<br />
<br />
&lt;!--<br />
===================================================================<br />
Build the code (Compile Tasks,File Tasks)<br />
===================================================================<br />
--&gt;<br />
&lt;target name="build" depends="init"&gt;<br />
&lt;!--编译--&gt;<br />
&lt;javac srcdir="${src.main}" destdir="${build.classes}"&gt;<br />
&lt;classpath refid="classpath"/&gt;<br />
&lt;/javac&gt;<br />
&lt;/target&gt;<br />
<br />
&lt;!--<br />
===================================================================<br />
打包文档(Archive Tasks)<br />
Create the project jars: xxx1.jar and xxx2.jar<br />
===================================================================<br />
--&gt;<br />
&lt;target name="jars" depends="build"&gt;<br />
&lt;jar basedir="${build.classes}" jarfile="${build.lib}/${app.jar}"/&gt;<br />
&lt;/target&gt;<br />
<br />
&lt;!--<br />
===================================================================<br />
Creates the API documentation<br />
===================================================================<br />
--&gt;<br />
&lt;target name="javadocs"<br />
depends="jars"<br />
description="--&gt; creates the API documentation"&gt;<br />
&lt;!--copy docs 手册... --&gt;<br />
&lt;copy todir="${build.docs}"&gt;<br />
&lt;fileset dir="${docs.dir}"/&gt;<br />
&lt;/copy&gt;<br />
<br />
&lt;javadoc packagenames="hello.ant.*"<br />
sourcepath="${src.main}"<br />
defaultexcludes="yes"<br />
destdir="${build.docs.api}"<br />
author="true"<br />
version="true"<br />
use="true"<br />
windowtitle="Docs API"&gt;<br />
&lt;doctitle&gt;&lt;![CDATA[&lt;h1&gt;hello ant Docs API&lt;/h1&gt;]]&gt;&lt;/doctitle&gt;<br />
&lt;bottom&gt;&lt;![CDATA[&lt;i&gt;${app.copyright}&lt;/i&gt;]]&gt;&lt;/bottom&gt;<br />
&lt;tag name="todo" scope="all" description="To do:" /&gt;<br />
&lt;/javadoc&gt;<br />
&lt;/target&gt;<br />
<br />
&lt;!--<br />
===================================================================<br />
Create the distribution that can run (Archive Tasks)<br />
主要是从各目录中把该copy的copy上<br />
===================================================================<br />
--&gt;<br />
&lt;target name="dist" depends="javadocs"&gt;<br />
&lt;!--copy bin 执行文件 --&gt;<br />
&lt;copy todir="${dist.bin}"&gt;<br />
&lt;fileset dir="${src.script}/"/&gt;<br />
&lt;/copy&gt;<br />
&lt;copy todir="${dist.docs}"&gt;<br />
&lt;fileset dir="${build.docs}/"/&gt;<br />
&lt;/copy&gt;<br />
&lt;!-- copy lib 文件 --&gt;<br />
&lt;copy todir="${dist.lib}"&gt;<br />
&lt;fileset dir="${build.lib}/"/&gt;<br />
&lt;/copy&gt;<br />
<br />
&lt;/target&gt;<br />
&lt;!--<br />
===================================================================<br />
Cleans everything(File Tasks)<br />
例如可以删除build中的文件<br />
===================================================================<br />
--&gt;<br />
&lt;/project&gt;
<p>&nbsp;</p>
<p>趁热打铁，接着上面的再发一个实例：<br />
&lt;?xml version="1.0"?&gt;<br />
&lt;project name="ssh" basedir="." default="usage"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="name" value="ssh"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="war.dir" value="war"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="src.dir" value="src"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="client.dir" value="client"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="build.dir" value=".classes"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="webcontent.dir" value="WebContent"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="prjlib.dir" value="lib"/&gt;&nbsp; &nbsp; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="webcontentlib.dir" value="${webcontent.dir}/WEB-INF/lib"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="weblib.dir" value="${war.dir}/WEB-INF/lib"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="dist.dir" value="dist"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property environment="env"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="tomcat.home" value="${env.CATALINA_HOME}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;property name="webapp.dist" value="${dist.dir}/webapps"/&gt;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;path id="master-classpath"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;fileset dir="${webcontentlib.dir}"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="hibernate3.jar"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="spring.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="struts.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="struts-el.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="struts-menu-2.4.2.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="acegi-security-1.0.2.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="activation.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="antlr.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="antlr-2.7.6.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="asm.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="aspectjweaver-1.5.2.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="cglib-2.1.3.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-beanutils.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-codec-1.3.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-collections.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-dbcp.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-digester.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-fileupload.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-io.jar"/&gt;&nbsp; &nbsp; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-lang.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-logging-1.1.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-pool.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="commons-validator.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="displaytag-1.1.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="dom4j-1.6.1.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="dwr.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="ehcache-1.2.3.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="itext-1.4.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="jakarta-oro.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="jstl.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="jta.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="log4j-1.2.11.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="mail.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="oscache-2.3.2.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="mysql-connector-java-5.0.3-bin.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="sitemesh-2.2.1.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="standard.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="urlrewrite-3.0-beta.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="velocity-1.4.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="velocity-tools-view-1.1.jar"/&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/fileset&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;fileset dir="${prjlib.dir}/servletapi-2.3"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="servletapi-2.3.jar"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/fileset&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/path&gt;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;target name="usage"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message=""/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message="Spring JPetStore build file"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message="------------------------------------------------------"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message=""/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message="Available targets are:"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message=""/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message="clean&nbsp; &nbsp;&nbsp; &nbsp;--&gt; Clean output dirs"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message="build&nbsp; &nbsp;&nbsp; &nbsp;--&gt; Compile main Java sources and copy libraries"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message="warfile --&gt; Build the web application archive"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message="all&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;--&gt; Clean, build, warfile"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;echo message=""/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/target&gt;&nbsp; &nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;target name="clean" description="Clean output dirs (build, weblib, dist)"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;delete dir="${build.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;delete dir="${weblib.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;delete dir="${war.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;delete dir="${dist.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;delete file="client/${name}.jar"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/target&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;target name="build" description="Compile main source tree java files into class files, generate jar files"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;mkdir dir="${build.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;mkdir dir="${war.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;javac destdir="${build.dir}" source="1.3" target="1.3" debug="true"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;deprecation="false" optimize="false" failonerror="true"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;src path="${src.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;classpath refid="master-classpath"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/javac&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;mkdir dir="${weblib.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;mkdir dir="${war.dir}/WEB-INF/classes"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;jar jarfile="${weblib.dir}/${name}.jar" compress="true" basedir="${build.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;copy todir="${war.dir}" preservelastmodified="true"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;fileset dir="${webcontent.dir}"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;include name="**/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;include name="**.**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;/fileset&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/copy&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;copy todir="${war.dir}/WEB-INF/classes" preservelastmodified="true"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;fileset dir="${src.dir}"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;include name="*.xml"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;include name="**/*.properties"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;include name="**/*.vm"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;exclude name="**/.*"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;/fileset&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/copy&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<br />
&nbsp; &nbsp;&nbsp; &nbsp; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;copy file="${weblib.dir}/${name}.jar" tofile="${client.dir}/${name}.jar"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/target&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;target name="dist" depends="warfile"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;!-- <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Delegate to warfile target by depending on it. dist is just to offer<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;a generic target name across all Spring sample apps that may be used<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;for autobuilds testing.<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; --&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/target&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;target name="warfile" depends="build" description="Build the web application archive"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;mkdir dir="${dist.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;war warfile="${dist.dir}/${name}.war" basedir="${war.dir}" webxml="${webcontent.dir}/WEB-INF/web.xml"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="*"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="images/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="common/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="decorators/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="scripts/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="styles/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="WEB-INF/*.*"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="WEB-INF/lib/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="WEB-INF/pages/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="WEB-INF/classes/**"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;exclude name="WEB-INF/web.xml"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/war&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/target&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;target name="all" depends="clean,build,warfile" description="Clean,build,warfile"/&gt;<br />
&lt;/project&gt;<br />
<font face="Arial "><font size="2"><strong><font size="3"><font color="#000080">在Eclipse中使用Ant</font></font></strong> Ant是Java平台下非常棒的批处理命令执行程序，能非常方便地自动完成编译，测试，打包，部署等等一系列任务，大大提高开发效率。如果你现在还没有开始使用Ant，那就要赶快开始学习使用，使自己的开发水平上一个新台阶。<br />
<br />
Eclipse中已经集成了Ant，我们可以直接在Eclipse中运行Ant。<br />
<br />
以前面建立的Hello工程为例，创建以下目录结构：<br />
<br />
<img title="Ant 的最完整build.xml - 水中的鱼 - 水中的鱼的博客" height="315" alt="" src="http://www.javaeedev.com/upload/6/1/ff80808112e766ee011312dd2e75004d.jpg" width="236" border="0" /><br />
<br />
新建一个build.xml，放在工程根目录下。build.xml定义了Ant要执行的批处理命令。虽然Ant也可以使用其它文件名，但是遵循标准能更使开发更规范，同时易于与别人交流。<br />
通常，src存放Java源文件，classes存放编译后的class文件，lib存放编译和运行用到的所有jar文件，web存放JSP等web文件，dist存放打包后的jar文件，doc存放API文档。<br />
然后在根目录下创建build.xml文件，输入以下内容：<br />
</font></font></p>
<blockquote><font face="Courier New "><font color="#006400">&lt;?xml version="1.0"?&gt;<br />
&lt;project name="Hello world" default="doc"&gt;</font></font><br />
<font face="Courier New "><font color="#006400">&lt;!-- properies --&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;property name="src.dir" value="src" /&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;property name="report.dir" value="report" /&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;property name="classes.dir" value="classes" /&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;property name="lib.dir" value="lib" /&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;property name="dist.dir" value="dist" /&gt;<br />
&lt;property name="doc.dir" value="doc"/&gt;</font></font><br />
<font face="Courier New "><font color="#006400">&nbsp; &nbsp;&nbsp;&nbsp;&lt;!-- 定义classpath --&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;path id="master-classpath"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;fileset file="${lib.dir}/*.jar" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;pathelement path="${classes.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;/path&gt;</font></font><br />
<font face="Courier New "><font color="#006400">&nbsp; &nbsp;&nbsp;&nbsp;&lt;!-- 初始化任务 --&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;target name="init"&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;/target&gt;</font></font><br />
<font face="Courier New "><font color="#006400">&nbsp; &nbsp;&nbsp;&nbsp;&lt;!-- 编译 --&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;target name="compile" depends="init" description="compile the source files"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;mkdir dir="${classes.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;javac srcdir="${src.dir}" destdir="${classes.dir}" target="1.4"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;classpath refid="master-classpath"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/javac&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;/target&gt;</font></font><br />
<font face="Courier New "><font color="#006400">&nbsp; &nbsp;&nbsp;&nbsp;&lt;!-- 测试 --&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;target name="test" depends="compile" description="run junit test"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;mkdir dir="${report.dir}"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;junit printsummary="on"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;haltonfailure="false"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;failureproperty="tests.failed"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;showoutput="true"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;classpath refid="master-classpath" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;formatter type="plain"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;batchtest todir="${report.dir}"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;fileset dir="${classes.dir}"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;include name="**/*Test.*"/&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;/fileset&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/batchtest&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/junit&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;fail if="tests.failed"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;***********************************************************<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;****&nbsp; &nbsp;One or more tests failed!&nbsp; &nbsp;Check the output ...&nbsp; &nbsp;****<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;***********************************************************<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/fail&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;/target&gt;</font></font><br />
<font face="Courier New "><font color="#006400">&nbsp; &nbsp;&nbsp;&nbsp;&lt;!-- 打包成jar --&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;target name="pack" depends="test" description="make .jar file"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;mkdir dir="${dist.dir}" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;jar destfile="${dist.dir}/hello.jar" basedir="${classes.dir}"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;exclude name="**/*Test.*" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;exclude name="**/Test*.*" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/jar&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;/target&gt;</font></font><br />
<font face="Courier New "><font color="#006400">&nbsp; &nbsp;&nbsp;&nbsp;&lt;!-- 输出api文档 --&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;target name="doc" depends="pack" description="create api doc"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;mkdir dir="${doc.dir}" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;javadoc destdir="${doc.dir}"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; author="true"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; version="true"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; use="true"<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; windowtitle="Test API"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;packageset dir="${src.dir}" defaultexcludes="yes"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;include name="example/**" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/packageset&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;doctitle&gt;&lt;![CDATA[&lt;h1&gt;Hello, test&lt;/h1&gt;]]&gt;&lt;/doctitle&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;bottom&gt;&lt;![CDATA[&lt;i&gt;All Rights Reserved.&lt;/i&gt;]]&gt;&lt;/bottom&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;tag name="todo" scope="all" description="To do:" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/javadoc&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp;&lt;/target&gt;<br />
&lt;/project&gt;</font></font><br />
</blockquote>
<p>以上xml依次定义了init（初始化），compile（编译），test（测试），doc（生成文档），pack（打包）任务，可以作为模板。<br />
选中Hello工程，然后选择&#8220;Project&#8221;，&#8220;Properties&#8221;，&#8220;Builders&#8221;，&#8220;New&#8230;&#8221;，选择&#8220;Ant Build&#8221;：<br />
<img title="Ant 的最完整build.xml - 水中的鱼 - 水中的鱼的博客" height="546" alt="" src="http://www.javaeedev.com/upload/15/4/ff80808112e766ee011312dd66cf004e.jpg" width="604" border="0" /><br />
填入Name：Ant_Builder；Buildfile：build.xml；BaseDirectory：${workspace_loc: /Hello}（按&#8220;BrowseWorkspace&#8221;选择工程根目录），由于用到了junit.jar包，搜索Eclipse目录，找到 junit.jar，把它复制到Hello/lib目录下，并添加到Ant的Classpath中：<br />
<img title="Ant 的最完整build.xml - 水中的鱼 - 水中的鱼的博客" height="546" alt="" src="http://www.javaeedev.com/upload/8/14/ff80808112e766ee011312dda926004f.jpg" width="604" border="0" /><br />
然后在Builder面板中钩上Ant_Build，去掉Java Builder：<br />
<img title="Ant 的最完整build.xml - 水中的鱼 - 水中的鱼的博客" height="283" alt="" src="http://www.javaeedev.com/upload/10/12/ff80808112e766ee011312ddd4ef0050.jpg" width="459" border="0" /><br />
再次编译，即可在控制台看到Ant的输出：<br />
</p>
<blockquote><font face="Courier New "><font color="#006400">Buildfile: F:\eclipse-projects\Hello\build.xml</font></font><br />
<font face="Courier New "><font color="#006400">init:</font></font><br />
<font face="Courier New "><font color="#006400">compile:<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[mkdir] Created dir: F:\eclipse-projects\Hello\classes<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[javac] Compiling 2 source files to F:\eclipse-projects\Hello\classes</font></font><br />
<font face="Courier New "><font color="#006400">test:<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[mkdir] Created dir: F:\eclipse-projects\Hello\report<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[junit] Running example.HelloTest<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.02 sec</font></font><br />
<font face="Courier New "><font color="#006400">pack:<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[mkdir] Created dir: F:\eclipse-projects\Hello\dist<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; [jar] Building jar: F:\eclipse-projects\Hello\dist\hello.jar</font></font><br />
<font face="Courier New "><font color="#006400">doc:<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[mkdir] Created dir: F:\eclipse-projects\Hello\doc<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Generating Javadoc<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Javadoc execution<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Loading source files for package example...<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Constructing Javadoc information...<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Standard Doclet version 1.4.2_04<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Building tree for all the packages and classes...<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Building index for all the packages and classes...<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Building index for all classes...<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Generating F:\eclipse-projects\Hello\doc\stylesheet.css...<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Note: Custom tags that could override future standardtags:&nbsp; &nbsp;@todo. To avoid potential overrides, use at least one periodcharacter (.) in custom tag names.<br />
&nbsp; &nbsp;&nbsp; &nbsp;[javadoc] Note: Custom tags that were not seen:&nbsp; &nbsp;@todo<br />
BUILD SUCCESSFUL<br />
Total time: 11 seconds</font></font><br />
</blockquote>
<p>Ant依次执行初始化，编译，测试，打包，生成API文档一系列任务，极大地提高了开发效率。将来开发J2EE项目时，还可加入部署等任务。并且，即使脱离了Eclipse环境，只要正确安装了Ant，配置好环境变量ANT_HOME=&lt;Ant解压目录&amp;gt;， Path=&#8230;;%ANT_HOME%\bin，在命令行提示符下切换到Hello目录，简单地键入ant即可。<br />
<br />
</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/261264.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-03-21 22:18 <a href="http://www.blogjava.net/sealyu/archive/2009/03/21/261264.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BCEL &amp; Javassist 的介绍</title><link>http://www.blogjava.net/sealyu/archive/2009/03/16/260033.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 16 Mar 2009 07:43:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/03/16/260033.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/260033.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/03/16/260033.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/260033.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/260033.html</trackback:ping><description><![CDATA[<p><font color="#ff0000">BCEL&nbsp; 介绍：</font></p>
<p><span>Byte Code Engineering Library (BCEL)，这是Apache Software Foundation 的Jakarta 项目的一部分。<br />
<table align="left" border="0" cellpadding="0" cellspacing="0" width="160">
    <tbody>
        <tr>
            <td id="newbooks1"><font color="red"><strong>正在装载数据&#8230;&#8230;</strong></font></td>
        </tr>
    </tbody>
</table>
BCEL
是 Java classworking 最广泛使用的一种框架,它可以让您深入 JVM 汇编语言进行类操作的细节。BCEL与Javassist
有不同的处理字节码方法，BCEL在实际的JVM 指令层次上进行操作(BCEL拥有丰富的JVM 指令级支持)而Javassist
所强调的源代码级别的工作。<br />
<br />
</span>http://sourceforge.net/projects/bcel</p>
<p><font color="#ff0000">Javassist:使字节码工程变得简单(Zz)　：</font></p>
<div text="" id="articleText4a35ff91010006k8" align="left">javassist
是一个执行字节码操作的强而有力的驱动代码库。它允许开发者自由的在一个已经编译好的类中添加新的方法，或者是修改已有的方法。但是，和其他的类似库不同
的是，Javassist并不要求开发者对字节码方面具有多么深入的了解，同样的，它也允许开发者忽略被修改的类本身的细节和结构。<br />
<p>字节码驱动通常被用来执行对于已经编译好的类的修改，或者由程序自动创建执行类等等等等相关方面的操作。这就要求字节码引擎具备无论是在运行时或是编译时都能修改程序的能力。当下有些<font color="#000033">技术</font>便是使用字节码来强化已经存在的Java类的，也有的则是使用它来使用或者产生一些由系统在运行时动态创建的类。举例而言，JDO1.0规范就使用了字节码<font color="#000033">技术</font>对<a href="http://www.23book.net/DB/Index.htm">数据库</a>中
的表进行处理和预编译，并进而包装成Java类。特别是在面向对象驱动的系统开发中，相当多的框架体系使用字节码以使我们更好的获得程序的范型性和动态
性。而某些EJB容器，比如JBOSS项目，则通过在运行中动态的创建和加载EJB，从而戏剧性的缩短了部署EJB的周期。这项<font color="#000033">技术</font>是如此的引人入胜，以至于在JDK中也有了标准的java.lang.reflect.Proxy类来执行相关的操作。</p>
<br />
<p>但
是，尽管如此，编写字节码对于框架程序开发者们而言，却是一个相当不受欢迎的繁重任务。学习和使用字节码在某种程度上就如同使用汇编语言。这使得于大多数
开发者而言，尽管在程序上可以获得相当多的好处，可攀登它所需要的难度则足以冷却这份热情。不仅如此，在程序中使用字节码操作也大大的降低了程序的可读性
和可维护性。</p>
<br />
<p>这是一块很好的奶油面包，但是我们却只能隔着橱窗流口水&#8230;难道我们只能如此了吗？</p>
<br />
<p>所幸的是，我们还有Javassist。Javassist是一个可以执行字节码操作的函数库，可是尽管如此，它却是简单而便与理解的。他允许开发者对自己的程序自由的执行字节码层的操作，当然了，你并不需要对字节码有多深的了解，或者，你根本就不需要了解。</p>
<br />
<p>API Parallel to the Reflection API</p>
<br />
<p>Javassist
的最外层的API和JAVA的反射包中的API颇为类似。它使你可以在装入ClassLoder之前，方便的查看类的结构。它主要由
CtClass,，CtMethod,，以及CtField几个类组成。用以执行和JDK反射API中java.lang.Class,，
java.lang.reflect.Method,， java.lang.reflect.Method
.Field相同的操作。这些类可以使你在目标类被加载前，轻松的获得它的结构，函数，以及属性。此外，不仅仅是在功能上，甚至在结构上，这些类的执行函
数也和反射的API大体相同。比如getName，getSuperclass，getMethods,，getSignature，等等。如果你对
JAVA的反射机制有所了解的话，使用Javassist的这一层将会是轻松而快乐的。</p>
<br />
<p>接下来我们将给出一个使用Javassist来读取org.geometry.Point.class的相关信息的例子（当然了，千万不要忘记引入javassist.*包）：</p>
<br />
<p>1. ClassPool pool = ClassPool.getDefault();</p>
<br />
<p>2. CtClass pt = pool.get("org.geometry.Point");</p>
<br />
<p>3. System.out.println(pt.getSuperclass().getName());</p>
<br />
<p>其
中，ClassPool是CtClass 的创建工厂。它在class
path中查找CtClass的位置，并为每一个分析请求创建一个CtClass实例。而&#8220;getSuperclass().getName()&#8221;则展示
出org.geometry.Point.class所继承的父类的名字。</p>
<br />
<p>但是，和反射的API不尽相同的是，Javassist
并不提供构造的能力，换句话说，我们并不能就此得到一个
org.geometry.Point.class类的实例。另一方面，在该类没有实例化前，Javassist也不提供对目标类的函数的调用接口和获取
属性的值的方法。在分析阶段，它仅仅提供对目标类的类定义修改，而这点，却是反射API所无法做到的。</p>
<br />
<p>举例如下：</p>
<br />
<p>4. pt.setSuperclass(pool.get("Figure"));</p>
<br />
<p>这样做将修改目标类和其父类之间的关系。我们将使org.geometry.Point.clas改继承自Figure类。当然了，就一致性而言，必须确保Figure类和原始的父类之间的兼容性。</p>
<br />
<p>而往目标类中新增一个新的方法则更加的简单了。首先我们来看字节码是如何形成的：</p>
<br />
<p>5. CtMethod m = CtNewMethod.make("public int xmove(int dx) { x += dx; }", pt);</p>
<br />
<p>6. pt.addMethod(m);</p>
<br />
<p>CtMethod类的让我们要新增一个方法只需要写一段小小的函数。这可是一个天大的好消息，开发者们再也不用为了实现这么一个小小的操作而写一大段的虚拟机指令序列了。Javassist将使用一个它自带的编译器来帮我们完成这一切。</p>
<br />
<p>最后，千万别忘了指示Javassist把已经写好的字节码存入到你的目标类里：</p>
<br />
<p>7. pt.writeFile();</p>
<br />
<p>writeFile方法可以帮我们把修改好了的定义写到目标类的.class文件里。当然了，我们甚至可以在该目标类加载的时候完成这一切，Javassist可以很好的和ClassLoader协同工作，我们不久就将看到这一点。</p>
<br />
<p>Javassist
并不是第一套用以完成从代码到字节码的翻译的函数库。Jakarta的BCEL也是一个比较知名的字节码引擎工具。但是，你却无法使用BCEL来完成代码
级别的字符码操作。如果你需要在一个已经编译好的类中添加一个新的方法，假如你用的是BCEL的话，你只能定义一段由那么一大串字符码所构成的指令序列。
正如上文所说，这并不是我们所希望看到的。因此，就此方面而言，Javassis使用代码的形式来插入新的方法实在是一大福音。</p>
<br />
<p>Instrumenting a Method Body</p>
<br />
<p>和
方法的新增一样，对于一个类的方法的其他操作也是定义在代码层上的。换而言之，尽管这些步骤是必须的，开发者们也同样无须直接对虚拟机的指令序列进行操作
和修改，Javassis将自动的完成这些操作。当然了，如果开发者认为自己有必要对这些步骤进行管理和监控，或者希望由自己来管理这些操作的
话，Javassist同样提供了更加底层的API来实现，不过我们在这篇文章中将不会就此话题再做深入探讨。恩，尽管从结构而言，它和BCEL的字节码
层API差不多。</p>
<br />
<p>设计Javassist对目标类的子函数体的操作API的设想立足与Aspect-Oriented
Programming（AOP）思想。Javassist允许把具有耦合关系的语句作为一个整体，它允许在一个插入语句中调用或获取其他函数或者及属性
值。它将自动的对这些语句进行优先级分解并执行嵌套操作。</p>
<br />
<p>如下例所示，清单1首先包含了一个CtMethod，它主要针对
Screen类的draw方法。然后，我们定义一个Point类，该类有一个
move操作，用来实现该Point的移动。当然了，在移动前，我们希望可以通过draw方法得到该point目前的位置，那么，我们需要对该move方
法加增如下的定义：</p>
<br />
<p>{ System.out.println("move"); $_ = $proceed($$); }</p>
<br />
<p>这样，在执行move之前，我们就可以打印出它的位置了。请注意这里的调用语句，它是如下格式的：</p>
<br />
<p>$_ = $proceed($$);</p>
<br />
<p>这样我们就将使用原CtMethod类中的process()对该point的位置进行追踪了。</p>
<br />
<p>基
与如上情况，CtMethod的关于methord的操作其实被划分成了如下步骤，首先，CtMethod的methord将扫描插入语句（代码）本身。
一旦发现了子函数，则创建一个ExprEditor实例来分析并执行这个子函数的操作。这个操作将在整个插入语句执行之前完成。而假如这个实例存在某个
static的属性，那么methord将率先检测对插入语句进行检测。然后，在执行插入到目标类---如上例的point类---之前，该
static属性将自动的替换插入语句（代码）中所有的相关的部分。不过，值得注意的是，以上的替换操作，将在Javassist把插入语句（代码）转变
为字节码之后完成。</p>
<br />
<p>Special Variables</p>
<br />
<p>在替换的语句（代码）中，我们也有可能需要用到一些
特殊变量来完成对某个子函数的调用，而这个时候我们就需要使用关键字&#8220;$&#8221;了。在
Javassist中，&#8220;$&#8221;用来申明此后的某个词为特殊参数，而&#8220;$_&#8221;则用来申明此后的某个词为函数的回传值。每一个特殊参数在被调用时应该是这个样
子的&#8220;$1,$2,$3&#8230;&#8221;但是，特别的，目标类本身在被调用时，则被表示为&#8220;$0&#8221;。这种使用格式让开发者在填写使用子函数的参数时轻松了许多。比如如
下的例子：</p>
<br />
<p>{ System.out.println("move"); $_ = $proceed($1, 0); }</p>
<br />
<p>请注意，该子函数的第2个参数为0。</p>
<br />
<p>另
外一个特殊类型则是$arg，它实际上是一个容纳了函数所有调用参数的Object队列。当Javassist在扫描该$arg时，如果发现某一个参数为
JAVA的基本类型，则它将自动的对该参数进行包装，并放入队列。比如，当它发现某一个参数为int类型时，它将使用
java.lang.integer
类来包装这个int参数，并存入参数队列。和Java的反射包：java.lang.reflect.Methord类中的invoke方法相比，$
args明显要省事的多。</p>
<br />
<p>Javassist也同样允许开发者在某个函数的头，或者某个函数的尾上插入某段语句（代码）。比如，它有一个insertBefore方法用以在某函数的调用前执行某个操作，它的使用大致是这个样子的：</p>
<br />
<p>1. ClassPool pool = ClassPool.getDefault();<br />
2. CtClass cc = pool.get("Screen");<br />
3. CtMethod cm = cc.getDeclaredMethod("draw", new CtClass[0]);<br />
4. cm.insertBefore("{ System.out.println($1); System.out.println($2); }");<br />
5. cc.writeFile();</p>
<br />
<p>以上例子允许我们在draw函数调用之前执行打印操作---把传递给draw的两个参数打印出来。</p>
<br />
<p>同样的，我们也可以使用关键字$对某一个函数进行修改或者是包装，下面就</p>
<br />
<p>1. CtClass cc = sloader.get("Point");<br />
2. CtMethod m1 = cc.getDeclaredMethod("move");<br />
3. CtMethod m2 = CtNewMethod.copy(m1, cc, null);<br />
4. m1.setName(m1.getName() + "_orig");<br />
5. m2.setBody("{ System.out.println("call"); return $proceed($$);<br />
}", "this", m1.getName());<br />
6. cc.addMethod(m2);<br />
7. cc.writeFile();</p>
<br />
<p>以
上代码的前四行不难理解，Javassist首先对Point中的move方法做了个拷贝，并创建了一个新的函数。然后，它把存在与Point类中的原
move方法更名为&#8220;_orig&#8221;。接下来，让我们关注一下程序第五行中的几个参数：第一个参数指示该函数的在执行的最初部分需要先打印一段信息，然后执
行子函数proceed()并返回结果，这个和move方法差不多，很好理解。第二个参数则只是申明该子函数所在的类的位置。这里为this即为
Point类本身。第三个参数，也就是&#8220;m1.getName()&#8221;则定义了这个新函数的名字。</p>
<br />
Javassist也同样具有其他的操作和类来帮助你实现诸如修改某一个属性的值，改变函数的回值，并在某个函数的执行后补上其他操作的功能。您可以浏览<font color="#cccc66">www.javassist.org</font>以获得相关的信息 </div>
<p>
<table align="center" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td>&nbsp;</td>
        </tr>
    </tbody>
</table>
</p>
<p><font color="#ff0000">使用Javassist对.class文件进行修改(Zz)　：</font></p>
<p>最
近重新再看&lt;Inside
JVM&gt;，对JAVA编译成的字节码结构很感兴趣，希望找个工具能够对.class文件进行的解析和查看。没找到，倒发现javaassist可以
对字节码进行操作和修改。此工具是JBOSS项目的一部分,JBOSS实现AOP的基础。呵呵，开眼界了，原来我们可以直接对字节码文件进行修改，哪怕不
知道源文件（跟反编译完全不同）。一个简单例子：</p>
<p>import javassist.*;<br />
class Hello {<br />
&nbsp;&nbsp;&nbsp; public void say() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Hello");<br />
&nbsp;&nbsp;&nbsp; }<br />
}</p>
<p>public class Test {<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClassPool cp = ClassPool.getDefault();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CtClass cc = cp.get("Hello");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CtMethod m = cc.getDeclaredMethod("say");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.setBody("{System.out.println(""shit"");}");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.insertBefore("System.out.println(""fuck"");");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class c = cc.toClass();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hello h = (Hello)c.newInstance();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h.say();<br />
&nbsp;&nbsp;&nbsp; }<br />
}</p>
<p>编译运行此文件，输出：</p>
<p>fuck</p>
<p>shit</p>
<p>我们在</p>
<p>&nbsp;CtMethod m = cc.getDeclaredMethod("say");<br />
&nbsp; m.setBody("{System.out.println(""shit"");}");</p>
<p>&nbsp; m.insertBefore("System.out.println(""fuck"");");</p>
<p>修改了say()方法，改成了</p>
<p>System.out.println("fuck");</p>
<p>System.out.println("shit");</p>
<p>这里的ClassPool是CtClass的容器，它读取class文件，并根据要求保存CtClass的结构以便日后使用，默认状态下是从当前的类装载器获得，当然你可以指定：</p>
<p>pool.insertClassPath("/usr/local/javalib");</p>
<p>当然，不仅仅是修改方法，你还可以新建一个class,利用makeClass()方法，如：</p>
<p>ClassPool pool = ClassPool.getDefault();<br />
CtClass cc = pool.makeClass("Point");</p>
<p>还可以新增方法，下面是sample里的一个例子，同样的：</p>
<p>package sample;</p>
<p>import javassist.*;<br />
import java.lang.reflect.*;</p>
<p>/*<br />
&nbsp;&nbsp; A very simple sample program</p>
<p>&nbsp;&nbsp; This program overwrites sample/Test.class (the class file of this<br />
&nbsp;&nbsp; class itself) for adding a method g().&nbsp; If the method g() is not<br />
&nbsp;&nbsp; defined in class Test, then this program adds a copy of<br />
&nbsp;&nbsp; f() to the class Test with name g().&nbsp; Otherwise, this program does<br />
&nbsp;&nbsp; not modify sample/Test.class at all.</p>
<p>&nbsp;&nbsp; To see the modified class definition, execute:</p>
<p>&nbsp;&nbsp; % javap sample.Test</p>
<p>&nbsp;&nbsp; after running this program.<br />
*/<br />
public class Test {<br />
&nbsp;&nbsp;&nbsp; public int f(int i) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;i++;<br />
&nbsp;&nbsp; &nbsp;return i;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String[] args) throws Exception {<br />
&nbsp;ClassPool pool = ClassPool.getDefault();</p>
<p>&nbsp;CtClass cc = pool.get("sample.Test");<br />
&nbsp;Test test=new Test();<br />
&nbsp;Class c=test.getClass();<br />
&nbsp;Method []method=c.getDeclaredMethods();<br />
&nbsp;for(int i=0;i&lt;method.length;i++){<br />
&nbsp;&nbsp;System.out.println(method[i]);<br />
&nbsp;}<br />
&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp; cc.getDeclaredMethod("g");<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("g() is already defined in sample.Test.");<br />
&nbsp;}<br />
&nbsp;catch (NotFoundException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp; /* getDeclaredMethod() throws an exception if g()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * is not defined in sample.Test.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp;&nbsp; CtMethod fMethod = cc.getDeclaredMethod("f");<br />
&nbsp;&nbsp;&nbsp;&nbsp; CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);<br />
&nbsp;&nbsp;&nbsp;&nbsp; cc.addMethod(gMethod);<br />
&nbsp;&nbsp;&nbsp;&nbsp; cc.writeFile();&nbsp;// update the class file<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("g() was added.");<br />
&nbsp;}<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
第一次运行时，因为Test里并没有g()方法，所以执行</p>
<p>&nbsp;CtMethod fMethod = cc.getDeclaredMethod("f");<br />
&nbsp;&nbsp;&nbsp;&nbsp; CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);&nbsp; //把f方法复制给g<br />
&nbsp;&nbsp;&nbsp;&nbsp; cc.addMethod(gMethod);<br />
&nbsp;&nbsp;&nbsp;&nbsp; cc.writeFile();&nbsp;//更新class文件</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("g() was added.");<br />
打印：g() was added</p>
<p>第2次运行时，因为以上步骤已经在class文件中增加了一个g方法，所以</p>
<p>&nbsp;System.out.println("g() is already defined in sample.Test.");<br />
打印：g() is already defined in sample.Test</p>
<p>Javassist不仅能修改你自己的class文件，而且可以同样修改JDK自带的类库（废话，类库也是人写的^_^）具体请看它的tutorial。</p>
<p><br />
</p>
<p>Javassist 官方网站： http://www.csg.is.titech.ac.jp/~chiba/javassist/</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/260033.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-03-16 15:43 <a href="http://www.blogjava.net/sealyu/archive/2009/03/16/260033.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> JAVA连接池(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/03/13/259574.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 13 Mar 2009 08:58:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/03/13/259574.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/259574.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/03/13/259574.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/259574.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/259574.html</trackback:ping><description><![CDATA[<p>package com.xinnuo.jdbc;</p>
<p>import java.io.*;<br />
import java.sql.*;<br />
import java.util.*;<br />
import java.util.Date;</p>
<p>&nbsp;/*<br />
&nbsp; * 该类只能创建一个实例，其它对象能够调用其静态方法（也称为类方法）获得该唯一实例的引用。<br />
&nbsp; * DBConnectionManager类的建构函数是私有的，这是为了避免其它对象创建该类的实例.<br />
&nbsp; * DBConnectionManager类的客户程序可以调用getInstance()方法获得对该类唯一实例的引用。<br />
&nbsp; * 类的唯一实例在getInstance()方法第一次被调用期间创建，此后其引用就一直保存在静态变量<br />
&nbsp; * instance中。每次调用getInstance()都增加一个DBConnectionManager的客户程序计数。<br />
&nbsp; * 即，该计数代表引用DBConnectionManager唯一实例的客户程序总数，它将被用于控制连接池的<br />
&nbsp; * 关闭操作。 该类实例的初始化工作私有方法init()完成。其中 getResourceAsStream()方法<br />
&nbsp; * 用于定位并打开外部文件。外部文件的定位方法依赖于类装载器的实现。标准的本地类装载器查找操<br />
&nbsp; * 作总是开始于类文件所在路径，也能够搜索CLASSPATH中声明的路径。db.properties是一个属性<br />
&nbsp; * 文件，它包含定义连接池的键-值对。可供定义的公用属性如下： <br />
&nbsp; * &nbsp;&nbsp;drivers 以空格分隔的JDBC驱动程序类列表"" <br />
&nbsp; * &nbsp;&nbsp;logfile 日志文件的绝对路径 <br />
&nbsp; *&nbsp;&nbsp;其它的属性和特定连接池相关，其属性名字前应加上连接池名字： <br />
&nbsp; *&nbsp;&nbsp;&lt; poolname&gt;.url 数据库的 JDBC URL <br />
&nbsp; *&nbsp;&nbsp;&lt; poolname&gt;.maxconn 允许建立的最大连接数，0表示没有限制 <br />
&nbsp; *&nbsp;&nbsp;&lt; poolname&gt;.user 用于该连接池的数据库帐号 <br />
&nbsp; *&nbsp;&nbsp;&lt; poolname&gt;.password 相应的密码""<br />
&nbsp; *&nbsp;其中url属性是必需的，而其它属性则是可选的。数据库帐号和密码必须合法。用于Windows平台的<br />
&nbsp; *&nbsp;db.properties文件示例如下：<br />
&nbsp; *&nbsp;&nbsp;drivers=com.microsoft.jdbc.sqlserver.SQLServerDriver<br />
&nbsp; *&nbsp;&nbsp;logfile=D:""log.txt<br />
&nbsp; *&nbsp;&nbsp;access.maxconn=20<br />
&nbsp; *&nbsp;&nbsp;access.url=jdbc:microsoft:sqlserver://localhost:1433;databasename=web<br />
&nbsp; *&nbsp;&nbsp;access.user=sa<br />
&nbsp; *&nbsp;&nbsp;access.password=sa&nbsp;<br />
&nbsp; *&nbsp;注意在Windows路径中的反斜杠必须输入2个，这是由于属性文件中的反斜杠同时也是一个转义字符。<br />
&nbsp; *&nbsp;init()方法在创建属性对象并读取db.properties文件之后，就开始检查logfile属性。如果属<br />
&nbsp; *&nbsp;性文件中没有指定日志文件，则默认为当前目录下的DBConnectionManager.log文件。如日志文<br />
&nbsp; *&nbsp;件无法使用，则向System.err输出日志记录。装载和注册所有在drivers属性中指定的JDBC驱动<br />
&nbsp; *&nbsp;程序loadDrivers()方法实现。该方法先用StringTokenizer将drivers属性值分割为对应于驱<br />
&nbsp; *&nbsp;动程序名称的字符串，然后依次装载这些类并创建其实例，最后在DriverManager中注册该实例并把<br />
&nbsp; *&nbsp;它加入到一个私有的向量drivers。向量drivers将用于关闭服务时从DriverManager取消所有<br />
&nbsp; *&nbsp;JDBC 驱动程序的注册。init()方法的最后一个任务是调用私有方法createPools()创建连接池对<br />
&nbsp; *&nbsp;象。createPools()方法先创建所有属性名字的枚举对象（即Enumeration对象，该对象可以想象<br />
&nbsp; *&nbsp;为一个元素系列，逐次调用其nextElement()方法将顺序返回各元素），然后在其中搜索名字以&#8220;.url&#8221;<br />
&nbsp; *&nbsp;结尾的属性。对于每一个符合条件的属性，先提取其连接池名字部分，进而读取所有属于该连接池的属性，<br />
&nbsp; *&nbsp;最后创建连接池对象并把它保存在实例变量pools中。散列表（Hashtable类 ）pools实现连接池名字<br />
&nbsp; *&nbsp;到连接池对象之间的映射，此处以连接池名字为键，连接池对象为值。 为便于客户程序从指定连接池获<br />
&nbsp; *&nbsp;得可用连接或将连接返回给连接池，类DBConnectionManager提供了方法getConnection()和<br />
&nbsp; *&nbsp;freeConnection()。所有这些方法都要求在参数中指定连接池名字，具体的连接获取或返回操作则调<br />
&nbsp; *&nbsp;用对应的连接池对象完成。为实现连接池的安全关闭，DBConnectionManager提供了方法release()。<br />
&nbsp; *&nbsp;在上面我们已经提到，所有DBConnectionManager的客户程序都应该调用静态方法getInstance()<br />
&nbsp; *&nbsp;以获得该管理器的引用，此调用将增加客户程序计数。客户程序在关闭时调用release()可以递减该计数。<br />
&nbsp; *&nbsp;当最后一个客户程序调用release()，递减后的引用计数为0，就可以调用各个连接池的release()方法<br />
&nbsp; *&nbsp;关闭所有连接了。管理类release()方法最后的任务是撤销所有JDBC驱动程序的注册。<br />
&nbsp; */</p>
<p>/**<br />
&nbsp;* 管理类DBConnectionManager支持对一个或多个由属性文件定义的数据库连接<br />
&nbsp;* 池的访问.客户程序可以调用getInstance()方法访问本类的唯一实例.<br />
&nbsp;*/<br />
public class DBConnectionManager {<br />
&nbsp;static private DBConnectionManager instance; // 唯一实例</p>
<p>&nbsp;static private int clients;</p>
<p>&nbsp;private Vector drivers = new Vector();</p>
<p>&nbsp;private PrintWriter log;</p>
<p>&nbsp;private Hashtable pools = new Hashtable();</p>
<p>&nbsp;/**<br />
&nbsp; * 返回唯一实例.如果是第一次调用此方法,则创建实例<br />
&nbsp; * @return DBConnectionManager 唯一实例<br />
&nbsp; */<br />
&nbsp;static synchronized public DBConnectionManager getInstance() {<br />
&nbsp;&nbsp;if (instance == null) {<br />
&nbsp;&nbsp;&nbsp;instance = new DBConnectionManager();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;clients++;<br />
&nbsp;&nbsp;return instance;<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 建构函数私有以防止其它对象创建本类实例<br />
&nbsp; */<br />
&nbsp;private DBConnectionManager() {<br />
&nbsp;&nbsp;init();<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * * 将连接对象返回给由名字指定的连接池<br />
&nbsp; * @param name在属性文件中定义的连接池名字<br />
&nbsp; * @param con连接对象<a>""""r</a><br />
&nbsp; */<br />
&nbsp;public void freeConnection(String name, Connection con) {<br />
&nbsp;&nbsp;DBConnectionPool pool = (DBConnectionPool) pools.get(name);<br />
&nbsp;&nbsp;if (pool != null) {<br />
&nbsp;&nbsp;&nbsp;pool.freeConnection(con);<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数 053 * 限制,则创建并返回新连<br />
&nbsp; * @param name在属性文件中定义的连接池名字 056 *<br />
&nbsp; * @return Connection 可用连接或null 057<br />
&nbsp; */<br />
&nbsp;public Connection getConnection(String name) {<br />
&nbsp;&nbsp;DBConnectionPool pool = (DBConnectionPool) pools.get(name);<br />
&nbsp;&nbsp;if (pool != null) {<br />
&nbsp;&nbsp;&nbsp;return pool.getConnection();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return null;<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制, <br />
&nbsp; * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.<br />
&nbsp; * @param name 连接池名字 071 *<br />
&nbsp; * @param time以毫秒计的等待时间<a>""""r</a><br />
&nbsp; * @return Connection 可用连接或null<br />
&nbsp; */<br />
&nbsp;public Connection getConnection(String name, long time) {<br />
&nbsp;&nbsp;DBConnectionPool pool = (DBConnectionPool) pools.get(name);<br />
&nbsp;&nbsp;if (pool != null) {<br />
&nbsp;&nbsp;&nbsp;return pool.getConnection(time);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return null;<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 关闭所有连接,撤销驱动程序的注册<a>""""r</a><br />
&nbsp; */<br />
&nbsp;public synchronized void release() {<br />
&nbsp;&nbsp;// 等待直到最后一个客户程序调用<br />
&nbsp;&nbsp;if (--clients != 0) {<br />
&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;Enumeration allPools = pools.elements();<br />
&nbsp;&nbsp;while (allPools.hasMoreElements()) {<br />
&nbsp;&nbsp;&nbsp;DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();<br />
&nbsp;&nbsp;&nbsp;pool.release();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;Enumeration allDrivers = drivers.elements();<br />
&nbsp;&nbsp;while (allDrivers.hasMoreElements()) {<br />
&nbsp;&nbsp;&nbsp;Driver driver = (Driver) allDrivers.nextElement();<br />
&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;DriverManager.deregisterDriver(driver);<br />
&nbsp;&nbsp;&nbsp;&nbsp;log("撤销JDBC驱动程序 " + driver.getClass().getName() + "的注册");<br />
&nbsp;&nbsp;&nbsp;} catch (SQLException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;log(e, "无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName());<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 根据指定属性创建连接池实例.<br />
&nbsp; * @param props 连接池属性 113<br />
&nbsp; */<br />
&nbsp;private void createPools(Properties props) {<br />
&nbsp;&nbsp;Enumeration propNames = props.propertyNames();<br />
&nbsp;&nbsp;while (propNames.hasMoreElements()) {<br />
&nbsp;&nbsp;&nbsp;String name = (String) propNames.nextElement();<br />
&nbsp;&nbsp;&nbsp;if (name.endsWith(".url")) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;String poolName = name.substring(0, name.lastIndexOf("."));<br />
&nbsp;&nbsp;&nbsp;&nbsp;String url = props.getProperty(poolName + ".url");<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (url == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log("没有为连接池" + poolName + "指定URL");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;String user = props.getProperty(poolName + ".user");<br />
&nbsp;&nbsp;&nbsp;&nbsp;String password = props.getProperty(poolName + ".password");<br />
&nbsp;&nbsp;&nbsp;&nbsp;String maxconn = props.getProperty(poolName + ".maxconn", "0");<br />
&nbsp;&nbsp;&nbsp;&nbsp;int max;<br />
&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max = Integer.valueOf(maxconn).intValue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;} catch (NumberFormatException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;DBConnectionPool pool = new DBConnectionPool(poolName, url,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user, password, max);<br />
&nbsp;&nbsp;&nbsp;&nbsp;pools.put(poolName, pool);<br />
&nbsp;&nbsp;&nbsp;&nbsp;log("成功创建连接池" + poolName);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 读取属性完成初始化<br />
&nbsp; */<br />
&nbsp;private void init() {<br />
&nbsp;&nbsp;InputStream is = getClass().getResourceAsStream("/db.properties");<br />
&nbsp;&nbsp;Properties dbProps = new Properties();<br />
&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;dbProps.load(is);<br />
&nbsp;&nbsp;} catch (Exception e) {<br />
&nbsp;&nbsp;&nbsp;System.err.println("不能读取属性文件. "+"请确保db.properties在CLASSPATH指定的路径中");<br />
&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;String logFile = dbProps.getProperty("logfile","DBConnectionManager.log");<br />
&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;log = new PrintWriter(new FileWriter(logFile, true), true);<br />
&nbsp;&nbsp;} catch (IOException e) {<br />
&nbsp;&nbsp;&nbsp;System.err.println("无法打开日志文件: " + logFile);<br />
&nbsp;&nbsp;&nbsp;log = new PrintWriter(System.err);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;loadDrivers(dbProps);<br />
&nbsp;&nbsp;createPools(dbProps);<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 装载和注册所有JDBC驱动程序<br />
&nbsp; * @param props属性<br />
&nbsp; */<br />
&nbsp;private void loadDrivers(Properties props) {<br />
&nbsp;&nbsp;String driverClasses = props.getProperty("drivers"); <br />
&nbsp;&nbsp;StringTokenizer st = new StringTokenizer(driverClasses);<br />
&nbsp;&nbsp;while (st.hasMoreElements()) {<br />
&nbsp;&nbsp;&nbsp;String driverClassName = st.nextToken().trim(); <br />
&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Driver driver = (Driver) Class.forName(driverClassName).newInstance();<br />
&nbsp;&nbsp;&nbsp;&nbsp;DriverManager.registerDriver(driver);<br />
&nbsp;&nbsp;&nbsp;&nbsp;drivers.addElement(driver);<br />
&nbsp;&nbsp;&nbsp;&nbsp;log("成功注册JDBC驱动程序" + driverClassName);<br />
&nbsp;&nbsp;&nbsp;} catch (Exception e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 将文本信息写入日志文件<br />
&nbsp; */<br />
&nbsp;private void log(String msg) {<br />
&nbsp;&nbsp;log.println(new Date() + ": " + msg);<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * 将文本信息与异常写入日志文件<br />
&nbsp; */<br />
&nbsp;private void log(Throwable e, String msg) {<br />
&nbsp;&nbsp;log.println(new Date() + ": " + msg);<br />
&nbsp;&nbsp;e.printStackTrace(log);<br />
&nbsp;}</p>
<p>&nbsp;// ///////////////////////////////////////////////////////////////////////////////////////////////////////<br />
&nbsp;// ///////////////////////////////////////////////////////////////////////////////////////////////////////<br />
&nbsp;/*<br />
&nbsp; * DBConnectionPool实现，它表示指向某个数据库的连接池。数据库由JDBC URL标识。一个JDBCURL由三部分组成：协议标识（总是jdbc），<br />
&nbsp; * 驱动程序标识（如odbc、idb、oracle等），数据库标识（其格式依赖于驱动程序）。例如，jdbc:odbc:demo，即是一个指向demo数据 <br />
&nbsp; * 库的JDBCURL，而且访问该数据库要使用JDBC-ODBC驱动程序。每个连接池都有一个供客户程序使用的名字以及可选的用户帐号、密码、最<br />
&nbsp; * 大连接数限制。如果Web应用程序所支持的某些数据库操作可以被所有用户执行，而其它一些操作应由特别许可的用户执行，则可以为两类操作<br />
&nbsp; * 分别定义连接池，两个连接池使用相同的JDBC URL，但使用不同的帐号和密码。类DBConnectionPool的建构函数需要上述所有数据作为其<br />
&nbsp; * 参数。客户程序可以使用DBConnectionPool<br />
&nbsp; * 类提供的两个方法获取可用连接。两者的共同之处在于：如连接池中存在可用连接，则直接返回，否则创建新的连接并返回。如果没有可用连接<br />
&nbsp; * 且已有连接总数等于最大限制数，第一个方法将直接返回null，而第二个方法将等待直到有可用连接为止。所有的可用连接对象均登记在名为<br />
&nbsp; * freeConnections的向量（Vector）中。如果向量中有多于一个的连接，getConnection()总是选取第一个。同时，由于新的可用连接总<br />
&nbsp; * 是从尾部加入向量，从而使得数据库连接由于长时间闲置而被关闭的风险减低到最小程度。 第一个getConnection()在返回可用连接给客户<br />
&nbsp; * 程序之前，调用了isClosed()方法验证连接仍旧有效。如果该连接被关闭或触发异常，getConnection()递归地调用自己以尝试获取另外的<br />
&nbsp; * 可用连接。如果在向量freeConnections中不存在任何可用连接，getConnection()方法检查是否已经指定最大连接数限制。如已经指定，<br />
&nbsp; * 则检查当前连接数是否已经到达极限。此处maxConn为0表示没有限制。如果没有指定最大连接数限制或当前连接数小于该值，该方法尝试创建<br />
&nbsp; * 新的连接。如创建成功，则增加已使用连接的计数并返回，否则返回空值。创建新连接由newConnection()方法实现。<br />
&nbsp; * 创建过程与是否已经指定数据库帐号、密码有关。JDBC的DriverManager类提供多个getConnection()方法，这些方法要用到JDBC URL<br />
&nbsp; * 与其它一些参数，如用户帐号和密码等。DriverManager将使用指定的JDBC URL确定适合于目标数据库的驱动程序及建立连接。<br />
&nbsp; * 第二个getConnection()方法需要一个以毫秒为单位的时间参数，该参数表示客户程序能够等待的最长时间。建立连接的具体操<br />
&nbsp; * 作仍旧由第一个getConnection()方法实现。该方法执行时先将startTime初始化为当前时间。在while循环中尝试获得一个连接。如果失<br />
&nbsp; * 败，则以给定的时间值为参数调用wait()。wait()的返回可能是由于其它线程调用notify()或notifyAll()，也可能是由于预定时间已到。<br />
&nbsp; * 为找出wait()返回的真正原因，程序用当前时间减开始时间（startTime），如差值大于预定时间则返回空值，否则再次调用getConnection()。<br />
&nbsp; * 把空闲的连接登记到连接池由freeConnection()方法实现，它的参数为返回给连接池的连接对象。该对象被加入到freeConnections<br />
&nbsp; * 向量的末尾，然后减少已使用连接计数。调用notifyAll()是为了通知其它正在等待可用连接的线程。 许多Servlet引擎为实现安全关闭提供<br />
&nbsp; * 多种方法。数据库连接池需要知道该事件以保证所有连接能够正常关闭。DBConnectionManager类负协调整个关闭过程，但关闭连接池中所有连<br />
&nbsp; * 接的任务则由DBConnectionPool类负责。release()方法供DBConnectionManager调用。该方法遍历freeConnections向量并关闭所有连接，<br />
&nbsp; * 然后从向量中删除这些连接。<br />
&nbsp; */</p>
<p>&nbsp;/**<br />
&nbsp; * 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最<a>""""r</a><br />
&nbsp; */<br />
&nbsp;class DBConnectionPool {<br />
&nbsp;&nbsp;private int checkedOut;</p>
<p>&nbsp;&nbsp;private Vector freeConnections = new Vector();</p>
<p>&nbsp;&nbsp;private int maxConn;</p>
<p>&nbsp;&nbsp;private String name;</p>
<p>&nbsp;&nbsp;private String password;</p>
<p>&nbsp;&nbsp;private String URL;</p>
<p>&nbsp;&nbsp;private String user;</p>
<p>&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 创建新的连接池<br />
&nbsp;&nbsp; * @param name连接池名字<br />
&nbsp;&nbsp; * @param URL数据库的JDBC URL<br />
&nbsp;&nbsp; * @param user数据库帐号,或 null<br />
&nbsp;&nbsp; * @param password密码,或 null<br />
&nbsp;&nbsp; * @param maxConn此连接池允许建立的最大连接数<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public DBConnectionPool(String name, String URL, String user,<br />
&nbsp;&nbsp;&nbsp;&nbsp;String password, int maxConn) {<br />
&nbsp;&nbsp;&nbsp;this.name = name;<br />
&nbsp;&nbsp;&nbsp;this.URL = URL;<br />
&nbsp;&nbsp;&nbsp;this.user = user;<br />
&nbsp;&nbsp;&nbsp;this.password = password;<br />
&nbsp;&nbsp;&nbsp;this.maxConn = maxConn;<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 将不再使用的连接返回给连接池<br />
&nbsp;&nbsp; * @param con客户程序释放的连接<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public synchronized void freeConnection(Connection con) {<br />
&nbsp;&nbsp;&nbsp;// 将指定连接加入到向量末尾<br />
&nbsp;&nbsp;&nbsp;freeConnections.addElement(con);<br />
&nbsp;&nbsp;&nbsp;checkedOut--;<br />
&nbsp;&nbsp;&nbsp;notifyAll();<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接 数限制,则创建新连接.<br />
&nbsp;&nbsp; * 如原来登记为可用的连接不再有效,则从向量删除之,<br />
&nbsp;&nbsp; * 然后递归调用自己以尝试新的可用连接.<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public synchronized Connection getConnection() {<br />
&nbsp;&nbsp;&nbsp;Connection con = null;<br />
&nbsp;&nbsp;&nbsp;if (freeConnections.size() &gt; 0) {// 获取向量中第一个可用连接<br />
&nbsp;&nbsp;&nbsp;&nbsp;con = (Connection) freeConnections.firstElement();<br />
&nbsp;&nbsp;&nbsp;&nbsp;freeConnections.removeElementAt(0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (con.isClosed()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log("从连接池" + name + "删除一个无效连接");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 递归调用自己,尝试再次获取可用连接<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con = getConnection();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;} catch (SQLException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log("从连接池" + name + "删除一个无效连接");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 递归调用自己,尝试再次获取可用连接<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con = getConnection();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;} else if (maxConn == 0 || checkedOut &lt; maxConn) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;con = newConnection();<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;if (con != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;checkedOut++;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;return con;<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间 参见前一个getConnection()方法.<br />
&nbsp;&nbsp; * @param timeout以毫秒计的等待时间限制<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public synchronized Connection getConnection(long timeout) {<br />
&nbsp;&nbsp;&nbsp;long startTime = new Date().getTime();<br />
&nbsp;&nbsp;&nbsp;Connection con;<br />
&nbsp;&nbsp;&nbsp;while ((con = getConnection()) == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wait(timeout);<br />
&nbsp;&nbsp;&nbsp;&nbsp;} catch (InterruptedException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ((new Date().getTime() - startTime) &gt;= timeout) {// wait()返回的原因是超时<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;return con;<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 关闭所有连接<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public synchronized void release() {<br />
&nbsp;&nbsp;&nbsp;Enumeration allConnections = freeConnections.elements();<br />
&nbsp;&nbsp;&nbsp;while (allConnections.hasMoreElements()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Connection con = (Connection) allConnections.nextElement();<br />
&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log("关闭连接池" + name + "中的一个连接");<br />
&nbsp;&nbsp;&nbsp;&nbsp;} catch (SQLException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log(e, "无法关闭连接池" + name + "中的连接");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;freeConnections.removeAllElements();<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 创建新的连接<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;private Connection newConnection() {<br />
&nbsp;&nbsp;&nbsp;Connection con = null;<br />
&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (user==null||"".equals(user)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con = DriverManager.getConnection(URL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con = DriverManager.getConnection(URL, user, password);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;log("连接池" + name + "创建一个新的连接");<br />
&nbsp;&nbsp;&nbsp;} catch (SQLException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;log(e, "无法创建下列URL的连接: " + URL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return null;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;return con;<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>&nbsp;/////////////////////////////////////////////////////////////////////////////////////////////<br />
&nbsp;/////////////////////////////////////////////////////////////////////////////////////////////<br />
}<br />
<br />
在sqlserver2000，tomcat5.5验证通过</p>
<p>方法调用！</p>
<p>package com.xinnuo.jdbc;</p>
<p>import java.sql.Connection;<br />
import java.sql.ResultSet;<br />
import java.sql.SQLException;<br />
import java.sql.Statement;</p>
public class DBConn {<br />
&nbsp;private DBConnectionManager connMgr = null;<br />
&nbsp;private Connection conn = null;<br />
&nbsp;private Statement stat = null;<br />
&nbsp;private ResultSet rs = null;<br />
&nbsp;public DBConn() {<br />
&nbsp;&nbsp;connMgr = DBConnectionManager.getInstance();<br />
&nbsp;}<br />
&nbsp;public ResultSet executeQuery(String strSQL) throws SQLException {<br />
&nbsp;&nbsp;this.conn = connMgr.getConnection("access");<br />
&nbsp;&nbsp;this.stat = this.conn.createStatement();<br />
&nbsp;&nbsp;this.rs = this.stat.executeQuery(strSQL);<br />
&nbsp;&nbsp;return this.rs;<br />
&nbsp;}<br />
&nbsp;public void execute(String strSQL) throws SQLException {<br />
&nbsp;&nbsp;this.conn = connMgr.getConnection("access");<br />
&nbsp;&nbsp;this.stat = this.conn.createStatement();<br />
&nbsp;&nbsp;this.stat.execute(strSQL);<br />
&nbsp;}<br />
&nbsp;public void executeUpdate(String strSQL) throws SQLException {<br />
&nbsp;&nbsp;this.conn = connMgr.getConnection("access");<br />
&nbsp;&nbsp;this.stat = this.conn.createStatement();<br />
&nbsp;&nbsp;this.stat.executeUpdate(strSQL);<br />
&nbsp;}<br />
&nbsp;public Connection getConnection(){<br />
&nbsp;&nbsp;this.conn = connMgr.getConnection("access");<br />
&nbsp;&nbsp;return this.conn;<br />
&nbsp;}<br />
&nbsp;public void free(){<br />
&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;if (this.rs != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;this.rs.close();<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;if (this.stat != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;this.stat.close();<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;} catch (SQLException e) {<br />
&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;connMgr.freeConnection("access", this.conn);<br />
&nbsp;}<br />
}
<img src ="http://www.blogjava.net/sealyu/aggbug/259574.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-03-13 16:58 <a href="http://www.blogjava.net/sealyu/archive/2009/03/13/259574.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>eclipse 中boolean型变量自动生成Setter、Getter方法的问题</title><link>http://www.blogjava.net/sealyu/archive/2009/02/17/255042.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 17 Feb 2009 03:08:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/02/17/255042.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/255042.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/02/17/255042.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/255042.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/255042.html</trackback:ping><description><![CDATA[今天在使用eclipse自动生成Setter/Getter方法时发现， 如果一个布尔型变量命名以&#8220;is&#8221;开头的话，自动生成的Setter/Getter方法中会将&#8220;is&#8221;自动去掉，例如：<br />
变量：isRequestable，<br />
生成的变量就会为：<br />
&nbsp;&nbsp;&nbsp; public boolean isRequestable() {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return isRequestable;<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
&nbsp;&nbsp; &nbsp;public void setRequestable(boolean isRequestable) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.isRequestable = isRequestable;<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
结果我在使用seam的时候，上下文绑定就出了问题。提示找不到isRequestable这个property.<br />
在代码中自动生成之后，手动将之改写为：<br />
&nbsp;&nbsp;&nbsp; public boolean isIsRequestable() {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return isRequestable;<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
&nbsp;&nbsp; &nbsp;public void setIsRequestable(boolean isRequestable) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.isRequestable = isRequestable;<br />
&nbsp;&nbsp; &nbsp;}<br />
问题就解决了<br />
<br />
另外，还有一个问题，就是变量名如果是第一个为小写字母，第二个为大写字母，也会出错，例如：eOrder,<br />
这样在生成Setter/Getter方法时也会提示找不到变量。所以将变量改名，然后JPA注释的时候映射一下就可以了。<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/255042.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-02-17 11:08 <a href="http://www.blogjava.net/sealyu/archive/2009/02/17/255042.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java程序员的推荐阅读书籍 （转）</title><link>http://www.blogjava.net/sealyu/archive/2009/01/08/250449.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 08 Jan 2009 01:59:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/01/08/250449.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/250449.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/01/08/250449.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/250449.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/250449.html</trackback:ping><description><![CDATA[今天在JavaEye上看到这篇帖子，先记下来，争取里面的书多看看。<br />
<br />
<div>
作为Java程序员来说，最痛苦的事情莫过于可以选择的范围太广，可以读的书太多，往往容易无所适从。我想就我自己读过的技术书籍中挑选出来一些，按照学习的先后顺序，推荐给大家，特别是那些想不断提高自己技术水平的Java程序员们。
<br />
<br />
<em>在这份推荐阅读书籍的名单中，我没有列举流行的软件框架类学习书籍，例如Struts，Hibernate，Spring之类，也没有列举AJAX方面的书籍。是因为这类书籍容易过时，而上述的大半书籍的生命周期都足够长，值得你去购买和收藏。</em>
<br />
<br />
</div>
<div id="wiki_menu">
<h4>目 录 <a href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#" onclick="$$('#wiki_menu ol')[0].toggle();return false;" title="隐藏/显示目录"><small>[ - ]</small></a></h4>
<ol>
    <li><a href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#1468">Java编程入门类 </a></li>
    <li><a href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#1469">Java编程进阶类</a></li>
    <li><a href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#1470">Java架构师之路</a></li>
    <li><a href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#1471">软件开发过程</a></li>
    <li><a href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#1472">软件项目管理</a></li>
</ol>
</div>
<h2>
Java编程入门类
<a name="1468" href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#top"><img alt="Top" src="http://www.javaeye.com/images/wiki/top.gif?1229402183" /></a>
</h2>
<div>
对于没有Java编程经验的程序员要入门，随便读什么入门书籍都一样，这个阶段需要你快速的掌握Java基础语法和基本用法，宗旨就是&#8220;囫囵吞枣不求甚解
&#8221;，先对Java熟悉起来再说。用很短的时间快速过一遍Java语法，连懵带猜多写写代码，要&#8220;知其然&#8221;。
<br />
<br />
<strong><span style="font-size: medium;">1、《Java编程思想》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1959358.jpg"  alt="" />
<br />
在有了一定的Java编程经验之后，你需要&#8220;知其所以然&#8221;了。这个时候《Java编程思想》是一本让你知其所以然的好书，它对于基本的面向对象知
识有比较清楚的交待，对Java基本语法，基本类库有比较清楚的讲解，可以帮你打一个良好的Java编程基础。这本书的缺点是实在太厚，也比较罗嗦，不适
合现代人快节奏学习，因此看这本书要懂得取舍，不是每章每节都值得一看的，挑重点的深入看就可以了。
<br />
<br />
<strong><span style="font-size: medium;">2、《Agile Java》中文版</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s2008093.jpg"  alt="" />
<br />
这本书是出版社送给我的，我一拿到就束之高阁，放在书柜一页都没有翻过，但是前两天整理书柜的时候，拿出来一翻，竟然发现这绝对是一本好书！这本
书一大特点是以单元测试和TDD来贯穿全书的，在教你Java各种重要的基础知识的过程中，潜移默化的影响你的编程思维走向敏捷，走向TDD。另外这本书
成书很新，以JDK5.0的语法为基础讲解，要学习JDK5.0的新语法也不错。还有这本书对于内容取舍也非常得当，Java语言毕竟类库庞大，可以讲的
内容太多，这本书选择的内容以及内容的多寡都很得当，可以让你以最少的时间掌握Java最重要的知识，顺便培养出来优秀的编程思路，真是一本不可多得的好
书。
<br />
<br />
虽然作者自己把这本书定位在入门级别，但我不确定这本书用来入门是不是稍微深了点。
</div>
<h2>
Java编程进阶类
<a name="1469" href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#top"><img alt="Top" src="http://www.javaeye.com/images/wiki/top.gif?1229402183" /></a>
</h2>
<div>
打下一个良好的Java基础，还需要更多的实践经验积累，我想没有什么捷径。有两本书值得你在编程生涯的这个阶段阅读，培养良好的编程习惯，提高你的代码质量。
<br />
<br />
<strong><span style="font-size: medium;">1、《重构 改善既有代码的设计》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1826359.jpg"  alt="" />
<br />
这本书名气很大，不用多介绍，可以在闲暇的时候多翻翻，多和自己的实践相互印证。这本书对你产生影响是潜移默化的。
<br />
<br />
<strong><span style="font-size: medium;">2、《测试驱动开发 by Example》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1441607.jpg"  alt="" />
<br />
本书最大特点是很薄，看起来没有什么负担。你可以找一个周末的下午，一边看，一边照做，一个下午就把书看完，这本书的所有例子跑完了。这本书的作用是通过实战让你培养TDD的思路。
</div>
<h2>
Java架构师之路
<a name="1470" href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#top"><img alt="Top" src="http://www.javaeye.com/images/wiki/top.gif?1229402183" /></a>
</h2>
<div>
到这个阶段，你应该已经非常娴熟的运用Java编程，而且有了一个良好的编程思路和习惯了，但是你可能还缺乏对应用软件整体架构的把握，现在就是你迈向架构师的第一步。
<br />
<br />
<strong><span style="font-size: medium;">1、《Expert One-on-One J2EE Design and Development》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1501574.jpg"  alt="" />
<br />
这本书是Rod Johnson的成名著作，非常经典，从这本书中的代码诞生了springframework。但是好像这本书没有中译本。
<br />
<br />
<strong><span style="font-size: medium;">2、《Expert One-on-One J2EE Development without EJB》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1496227.jpg"  alt="" />
<br />
这本书由gigix组织翻译，多位业界专家参与，虽然署名译者是JavaEye，其实JavaEye出力不多，实在是忝居译者之名。
<br />
<br />
以上两本书都是Rod
Johnson的经典名著，Java架构师的必读书籍。在我所推荐的这些书籍当中，是我看过的最仔细，最认真的书，我当时读这本书几乎是废寝忘食的一气读
完的，有小时候挑灯夜读金庸武侠小说的劲头，书中所讲内容和自己的经验知识一一印证，又被无比精辟的总结出来，读完这本书以后，我有种被打通经脉，功力爆
增的感觉。
<br />
<br />
但是后来我看过一些其他人的评价，似乎阅读体验并没有我那么high，也许是因为每个人的知识积累和经验不同导致的。我那个时候刚好是经验知识积累已经足够丰富，但是还没有系统的整理成型，让这本书一梳理，立刻形成完整的知识体系了。
<br />
<br />
<strong><span style="font-size: medium;">3、《企业应用架构模式》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1607804.jpg"  alt="" />
<br />
Martin的又一本名著，但这本书我只是泛泛的看了一遍，并没有仔细看。这本书似乎更适合做框架的人去看，例如如果你打算自己写一个ORM的
话，这本书是一定要看的。但是做应用的人，不看貌似也无所谓，但是如果有空，我还是推荐认真看看，会让你知道框架为什么要这样设计，这样你的层次可以晋升
到框架设计者的角度去思考问题。Martin的书我向来都是推崇，但是从来都没有像Rod Johnson的书那样非常认真去看。
<br />
<br />
<strong><span style="font-size: medium;">4、《敏捷软件开发 原则、模式与实践》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1671095.jpg"  alt="" />
<br />
Uncle Bob的名著，敏捷的经典名著，这本书比较特别，与其说是讲软件开发过程的书，不如说讲软件架构的书，本书用了很大篇幅讲各种面向对象软件开发的各种模式，个人以为看了这本书，就不必看GoF的《设计模式》了。
<br />
<br />
<br />
</div>
<h2>
软件开发过程
<a name="1471" href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#top"><img alt="Top" src="http://www.javaeye.com/images/wiki/top.gif?1229402183" /></a>
</h2>
<div>
了解软件开发过程不单纯是提高程序员个人的良好编程习惯，也是增强团队协作的基础。
<br />
<br />
<strong><span style="font-size: medium;">1、《UML精粹》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1648691.jpg"  alt="" />
<br />
UML其实和软件开发过程没有什么必然联系，却是软件团队协作沟通，撰写软件文档需要的工具。但是UML真正实用的图不多，看看这本书已经足够了，完全没有必要去啃《UML用户指南》之类的东西。要提醒大家的是，这本书的中译本翻译的非常之烂，建议有条件的看英文原版。
<br />
<br />
<strong><span style="font-size: medium;">2、《解析极限编程 拥抱变化》XP</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s2008432.jpg"  alt="" />
<br />
这是Kent Beck名著的第二版，中英文对照。没什么好说的，必读书籍。
<br />
<br />
<strong><span style="font-size: medium;">3、《统一软件开发过程》UP</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1843342.jpg"  alt="" />
<br />
其实UP和敏捷并不一定冲突，UP也非常强调迭代，测试，但是UP强调的文档和过程驱动却是敏捷所不取的。不管怎么说，UP值得你去读，毕竟在中国真正接受敏捷的企业很少，你还是需要用UP来武装一下自己的，哪怕是披着UP的XP。
<br />
<br />
<strong><span style="font-size: medium;">4、《敏捷建模》AM</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1264986.jpg"  alt="" />
<br />
Scott
Ambler的名著，这本书非常的progmatic，告诉你怎么既敏捷又UP，把敏捷和UP统一起来了，又提出了很多progmatic的建议和做法。
你可以把《解析极限编程
拥抱变化》、《统一软件开发过程》和《敏捷建模》这三本书放在一起读，看XP和UP的不同点，再看AM是怎么统一XP和UP的，把这三种理论融为一炉，形
成自己的理论体系，那么你也可以去写书了。
<br />
<br />
<br />
</div>
<h2>
软件项目管理
<a name="1472" href="http://www.javaeye.com/wiki/Java_Newbie/945-java-programmers-recommendation-books#top"><img alt="Top" src="http://www.javaeye.com/images/wiki/top.gif?1229402183" /></a>
</h2>
如果你突然被领导提拔为项目经理，而你完全没有项目管理经验，你肯定会心里没底；如果你觉得自己管理项目不善，很想改善你的项目管理能力，那么去考PMP肯定是远水不解近渴的。
<br />
<br />
<strong><span style="font-size: medium;">1、《快速软件开发》</span></strong>
<br />
<br />
<img src="http://www.douban.com/lpic/s1696681.jpg"  alt="" />
<br />
这也是一本名著。可以这样说，有本书在手，你就有了一个项目管理的高级参谋给你出谋划策，再也不必担心自己不能胜任的问题了。这本书不是讲管理的
理论的，在实际的项目管理中，讲这些理论是不解决问题的，这本书有点类似于&#8220;软件项目点子大全&#8221;之类的东西，列举了种种软件项目当中面临的各种问题，以及
应该如何解决问题的点子，你只需要稍加变通，找方抓药就行了。
<br />
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/250449.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-01-08 09:59 <a href="http://www.blogjava.net/sealyu/archive/2009/01/08/250449.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TreeSet()零参构造函数引起的问题</title><link>http://www.blogjava.net/sealyu/archive/2009/01/08/250448.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 08 Jan 2009 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/01/08/250448.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/250448.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/01/08/250448.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/250448.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/250448.html</trackback:ping><description><![CDATA[作者： sealyu&nbsp;&nbsp; 日期：2009-1-8<br />
在项目中碰到一个bug，抛出ClassCastException异常，找了半天，终于定位问题所在。<br />
在TreeSet的javadoc里写到：<br />
/**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * Constructs a new, empty set, sorted according to the elements' natural<br />
&nbsp;&nbsp;&nbsp;&nbsp; * order.&nbsp; All elements inserted into the set must implement the<br />
&nbsp;&nbsp;&nbsp;&nbsp; * &lt;tt&gt;Comparable&lt;/tt&gt; interface.&nbsp; Furthermore, all such elements must be<br />
&nbsp;&nbsp;&nbsp;&nbsp; * &lt;i&gt;mutually comparable&lt;/i&gt;: &lt;tt&gt;e1.compareTo(e2)&lt;/tt&gt; must not throw a<br />
&nbsp;&nbsp;&nbsp;&nbsp; * &lt;tt&gt;ClassCastException&lt;/tt&gt; for any elements &lt;tt&gt;e1&lt;/tt&gt; and<br />
&nbsp;&nbsp;&nbsp;&nbsp; * &lt;tt&gt;e2&lt;/tt&gt; in the set.&nbsp; If the user attempts to add an element to the<br />
&nbsp;&nbsp;&nbsp;&nbsp; * set that violates this constraint (for example, the user attempts to<br />
&nbsp;&nbsp;&nbsp;&nbsp; * add a string element to a set whose elements are integers), the<br />
&nbsp;&nbsp;&nbsp;&nbsp; * &lt;tt&gt;add(Object)&lt;/tt&gt; call will throw a &lt;tt&gt;ClassCastException&lt;/tt&gt;.<br />
&nbsp;&nbsp;&nbsp;&nbsp; *<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @see Comparable<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public TreeSet() {<br />
&nbsp;&nbsp; &nbsp;this(new TreeMap&lt;E,Object&gt;());<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
也就是说，在使用零参的构造函数时，你所要插入set的elements必须都声明Comparable接口。<br />
如果没有声明该接口，当你对里面的元素进行排序或者比较操作（所有调用e1.compareTo(e2)的操作），都会抛出一个ClassCastException。同时任何试图插入没有声明该接口的元素也会抛出此异常。<br />
谨记！<br />
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/250448.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-01-08 09:40 <a href="http://www.blogjava.net/sealyu/archive/2009/01/08/250448.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java.util.ConcurrentModificationException </title><link>http://www.blogjava.net/sealyu/archive/2008/11/03/238364.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 03 Nov 2008 08:07:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2008/11/03/238364.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/238364.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2008/11/03/238364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/238364.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/238364.html</trackback:ping><description><![CDATA[ 今日在调试程序时，出现了&nbsp;java.util.ConcurrentModificationException，出错代码如下：<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><img id="Codehighlighter1_71_342_Open_Image" onclick="this.style.display='none'; Codehighlighter1_71_342_Open_Text.style.display='none'; Codehighlighter1_71_342_Closed_Image.style.display='inline'; Codehighlighter1_71_342_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_71_342_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_71_342_Closed_Text.style.display='none'; Codehighlighter1_71_342_Open_Image.style.display='inline'; Codehighlighter1_71_342_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" /><span style="color: #0000ff;">for</span><span style="color: #000000;">(Iterator&nbsp;ite&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;candidateObjDtoList.iterator();&nbsp;ite.hasNext();&nbsp;)&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_71_342_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_71_342_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CandidateObjDto&nbsp;dto&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(CandidateObjDto)ite.next();<br />
<img id="Codehighlighter1_195_266_Open_Image" onclick="this.style.display='none'; Codehighlighter1_195_266_Open_Text.style.display='none'; Codehighlighter1_195_266_Closed_Image.style.display='inline'; Codehighlighter1_195_266_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_195_266_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_195_266_Closed_Text.style.display='none'; Codehighlighter1_195_266_Open_Image.style.display='inline'; Codehighlighter1_195_266_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(dto.getType()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;Constants.CANDIDATE_OBJ_TYPE_SET)&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_195_266_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_195_266_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dto.setVoteType(Constants.VOTE_TYPE_ABSTAIN);<br />
<img id="Codehighlighter1_276_340_Open_Image" onclick="this.style.display='none'; Codehighlighter1_276_340_Open_Text.style.display='none'; Codehighlighter1_276_340_Closed_Image.style.display='inline'; Codehighlighter1_276_340_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_276_340_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_276_340_Closed_Text.style.display='none'; Codehighlighter1_276_340_Open_Image.style.display='inline'; Codehighlighter1_276_340_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_276_340_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_276_340_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;candidateObjDtoList.remove(dto);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<img src="../../Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />}</span></span></div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在网上搜索到资料如下：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><img src="../../Images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000;">在Map或者Collection的时候，不要用它们的API直接修改集合的内容，如果要修改可以用Iterator的remove()方法，例如：<br />
<img id="Codehighlighter1_129_466_Open_Image" onclick="this.style.display='none'; Codehighlighter1_129_466_Open_Text.style.display='none'; Codehighlighter1_129_466_Closed_Image.style.display='inline'; Codehighlighter1_129_466_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_129_466_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_129_466_Closed_Text.style.display='none'; Codehighlighter1_129_466_Open_Image.style.display='inline'; Codehighlighter1_129_466_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setReparation(&nbsp;Reparation&nbsp;reparation&nbsp;)&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_129_466_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_129_466_Open_Text"><span style="color: #000000;">{<br />
<img id="Codehighlighter1_203_461_Open_Image" onclick="this.style.display='none'; Codehighlighter1_203_461_Open_Text.style.display='none'; Codehighlighter1_203_461_Closed_Image.style.display='inline'; Codehighlighter1_203_461_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_203_461_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_203_461_Closed_Text.style.display='none'; Codehighlighter1_203_461_Open_Image.style.display='inline'; Codehighlighter1_203_461_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Iterator&nbsp;it&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.reparations.iterator();it.hasNext();)<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_203_461_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_203_461_Open_Text"><span style="color: #000000;">{&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">reparations为Collection</span><span style="color: #008000;"><br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reparation&nbsp;repa&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Reparation)it.next();<br />
<img id="Codehighlighter1_340_451_Open_Image" onclick="this.style.display='none'; Codehighlighter1_340_451_Open_Text.style.display='none'; Codehighlighter1_340_451_Closed_Image.style.display='inline'; Codehighlighter1_340_451_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_340_451_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_340_451_Closed_Text.style.display='none'; Codehighlighter1_340_451_Open_Image.style.display='inline'; Codehighlighter1_340_451_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(repa.getId()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;reparation.getId())<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_340_451_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_340_451_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.reparations.remove(repa);<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.reparations.add(reparation);<br />
<img src="../../Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
<img src="../../Images/OutliningIndicators/None.gif" align="top"  alt="" />如上写会在运行期报ConcurrentModificationException，可以如下修改：<br />
<img src="../../Images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
<img id="Codehighlighter1_577_1004_Open_Image" onclick="this.style.display='none'; Codehighlighter1_577_1004_Open_Text.style.display='none'; Codehighlighter1_577_1004_Closed_Image.style.display='inline'; Codehighlighter1_577_1004_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_577_1004_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_577_1004_Closed_Text.style.display='none'; Codehighlighter1_577_1004_Open_Image.style.display='inline'; Codehighlighter1_577_1004_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setReparation(&nbsp;Reparation&nbsp;reparation&nbsp;)&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_577_1004_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_577_1004_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;flag&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br />
<img id="Codehighlighter1_681_923_Open_Image" onclick="this.style.display='none'; Codehighlighter1_681_923_Open_Text.style.display='none'; Codehighlighter1_681_923_Closed_Image.style.display='inline'; Codehighlighter1_681_923_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_681_923_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_681_923_Closed_Text.style.display='none'; Codehighlighter1_681_923_Open_Image.style.display='inline'; Codehighlighter1_681_923_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Iterator&nbsp;it&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.reparations.iterator();it.hasNext();)<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_681_923_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_681_923_Open_Text"><span style="color: #000000;">{&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">reparations为Collection</span><span style="color: #008000;"><br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reparation&nbsp;repa&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Reparation)it.next();<br />
<img id="Codehighlighter1_818_913_Open_Image" onclick="this.style.display='none'; Codehighlighter1_818_913_Open_Text.style.display='none'; Codehighlighter1_818_913_Closed_Image.style.display='inline'; Codehighlighter1_818_913_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_818_913_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_818_913_Closed_Text.style.display='none'; Codehighlighter1_818_913_Open_Image.style.display='inline'; Codehighlighter1_818_913_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(repa.getId()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;reparation.getId())<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_818_913_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_818_913_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;it.remove();<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br />
<img src="../../Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img id="Codehighlighter1_944_998_Open_Image" onclick="this.style.display='none'; Codehighlighter1_944_998_Open_Text.style.display='none'; Codehighlighter1_944_998_Closed_Image.style.display='inline'; Codehighlighter1_944_998_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_944_998_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_944_998_Closed_Text.style.display='none'; Codehighlighter1_944_998_Open_Image.style.display='inline'; Codehighlighter1_944_998_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(flag)<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_944_998_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_944_998_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.reparations.add(reparation);<br />
<img src="../../Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
&nbsp;&nbsp;&nbsp;&nbsp; 成功解决了所遇问题，成功后的代码如下：<br />
&nbsp; <br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><img id="Codehighlighter1_71_333_Open_Image" onclick="this.style.display='none'; Codehighlighter1_71_333_Open_Text.style.display='none'; Codehighlighter1_71_333_Closed_Image.style.display='inline'; Codehighlighter1_71_333_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_71_333_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_71_333_Closed_Text.style.display='none'; Codehighlighter1_71_333_Open_Image.style.display='inline'; Codehighlighter1_71_333_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" /><span style="color: #0000ff;">for</span><span style="color: #000000;">(Iterator&nbsp;ite&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;candidateObjDtoList.iterator();&nbsp;ite.hasNext();&nbsp;)&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_71_333_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_71_333_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CandidateObjDto&nbsp;dto&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(CandidateObjDto)ite.next();<br />
<img id="Codehighlighter1_197_280_Open_Image" onclick="this.style.display='none'; Codehighlighter1_197_280_Open_Text.style.display='none'; Codehighlighter1_197_280_Closed_Image.style.display='inline'; Codehighlighter1_197_280_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_197_280_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_197_280_Closed_Text.style.display='none'; Codehighlighter1_197_280_Open_Image.style.display='inline'; Codehighlighter1_197_280_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(dto.getType()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;Constants.CANDIDATE_OBJ_TYPE_SET)&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_197_280_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_197_280_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dto.setVoteType(Constants.VOTE_TYPE_ABSTAIN);</span><span style="color: #008000;">//</span><span style="color: #008000;">对该候选项投弃权票</span><span style="color: #008000;"><br />
<img id="Codehighlighter1_290_331_Open_Image" onclick="this.style.display='none'; Codehighlighter1_290_331_Open_Text.style.display='none'; Codehighlighter1_290_331_Closed_Image.style.display='inline'; Codehighlighter1_290_331_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_290_331_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_290_331_Closed_Text.style.display='none'; Codehighlighter1_290_331_Open_Image.style.display='inline'; Codehighlighter1_290_331_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" /></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;<img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_290_331_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="../../Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_290_331_Open_Text"><span style="color: #000000;">{<br />
<img src="../../Images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ite.remove();<br />
<img src="../../Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000;"><br />
<img src="../../Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp; <br />
</span></div>
<img src ="http://www.blogjava.net/sealyu/aggbug/238364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2008-11-03 16:07 <a href="http://www.blogjava.net/sealyu/archive/2008/11/03/238364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>velocity字符串相加</title><link>http://www.blogjava.net/sealyu/archive/2008/09/16/229135.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 16 Sep 2008 06:11:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2008/09/16/229135.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/229135.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2008/09/16/229135.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/229135.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/229135.html</trackback:ping><description><![CDATA[<p>&nbsp;今天写code的时候遇到&nbsp;velocity字符串如何相加的问题，后来我就想当然的用：</p>
<p>#set($temp="AA")</p>
<p>#set($temp=$temp+$temp)</p>
<p>后来发现怎么调都不通过，最后经过测试发现原来应该是这样子的：</p>
<p>#set($temp="AA")</p>
<p>#set($temp="$temp$temp")</p>
<p>今天记下来，以防日后忘记。</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/229135.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2008-09-16 14:11 <a href="http://www.blogjava.net/sealyu/archive/2008/09/16/229135.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>