﻿<?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-Sung in Blog-文章分类-Ｊａｖａ</title><link>http://www.blogjava.net/qq13367612/category/3953.html</link><description>&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font class="subhead" size=3&gt;&lt;b&gt;一些技术文章 &amp; 一些生活杂碎&lt;/b&gt;&lt;/font&gt;</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 08:39:12 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 08:39:12 GMT</pubDate><ttl>60</ttl><item><title>AJAX基础教程</title><link>http://www.blogjava.net/qq13367612/articles/18822.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Tue, 08 Nov 2005 09:49:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/18822.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18822.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/18822.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18822.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18822.html</trackback:ping><description><![CDATA[这篇文章将带您浏览整个AJAX的基本概貌,并展示两个简单的例子让您轻松上路. <BR><BR>　　什么是 AJAX? <BR>　　AJAX (异步 JavaScript 和 XML) 是个新产生的术语,专为描述JavaScript的两项强大性能.这两项性能在多年来一直被网络开发者所忽略,直到最近Gmail, Google suggest和google Maps的横空出世才使人们开始意识到其重要性. <BR><BR>　　这两项被忽视的性能是: <BR>　　无需重新装载整个页面便能向服务器发送请求. <BR>　　对XML文档的解析和处理．<BR><BR><FONT size=4><B>步骤 1 – "请!" --- 如何发送一个HTTP请求</B></FONT><BR><BR>　　为了用JavaScript向服务器发送一个HTTP请求, 需要一个具备这种功能的类实例. 这样的类首先由Internet Explorer以ActiveX对象引入, 被称为XMLHTTP. 后来Mozilla, Safari 和其他浏览器纷纷仿效, 提供了XMLHttpRequest类,它支持微软的ActiveX对象所提供的方法和属性. <BR><BR>　　因此, 为了创建一个跨浏览器的这样的类实例(对象), 可以应用如下代码: <BR>
<P class=code>if&nbsp;(window.XMLHttpRequest)&nbsp;{&nbsp;//&nbsp;Mozilla,&nbsp;Safari,&nbsp;...<BR>&nbsp;&nbsp;&nbsp;&nbsp;http_request&nbsp;=&nbsp;new&nbsp;XMLHttpRequest();<BR>}&nbsp;else&nbsp;if&nbsp;(window.ActiveXObject)&nbsp;{&nbsp;//&nbsp;IE<BR>&nbsp;&nbsp;&nbsp;&nbsp;http_request&nbsp;=&nbsp;new&nbsp;ActiveXObject("Microsoft.XMLHTTP");<BR>}</P>　　(上例对代码做了一定简化,这是为了解释如何创建XMLHTTP类实例. 实际的代码实例可参阅本篇步骤3.) <BR><BR>　　如果服务器的响应没有XML mime-type header,某些Mozilla浏览器可能无法正常工作. 为了解决这个问题, 如果服务器响应的header不是text/xml,可以调用其它方法修改该header. <BR>
<P class=code>http_request&nbsp;=&nbsp;new&nbsp;XMLHttpRequest();<BR>http_request.overrideMimeType('text/xml');</P>　　接下来要决定当收到服务器的响应后,需要做什么.这需要告诉HTTP请求对象用哪一个JavaScript函数处理这个响应.可以将对象的onreadystatechange属性设置为要使用的JavaScript的函数名,如下所示: <BR><BR>http_request.onreadystatechange = nameOfTheFunction; <BR><BR>　　注意:在函数名后没有括号,也无需传递参数.另外还有一种方法,可以在扉页(fly)中定义函数及其对响应要采取的行为,如下所示: <BR>
<P class=code>http_request.onreadystatechange&nbsp;=&nbsp;function(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;do&nbsp;the&nbsp;thing<BR>};</P>　　在定义了如何处理响应后,就要发送请求了.可以调用HTTP请求类的open()和send()方法, 如下所示: <BR>
<P class=code>http_request.open('GET',&nbsp;'http://www.example.org/some.file',&nbsp;true);<BR>http_request.send(null);</P>　　open()的第一个参数是HTTP请求方式 – GET, POST, HEAD 或任何服务器所支持的您想调用的方式. 按照HTTP规范,该参数要大写;否则,某些浏览器(如Firefox)可能无法处理请求.有关HTTP请求方法的详细信息可参考http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html W3C specs <BR>　　第二个参数是请求页面的URL.由于自身安全特性的限制,该页面不能为第三方域名的页面.同时一定要保证在所有的页面中都使用准确的域名,否则调用open()会得到"permission denied"的错误提示.一个常见的错误是访问站点时使用domain.tld,而当请求页面时,却使用www.domain.tld. <BR>　　第三个参数设置请求是否为异步模式.如果是TRUE, JavaScript函数将继续执行,而不等待服务器响应.这就是"AJAX"中的"A". <BR>　　如果第一个参数是"POST",send()方法的参数可以是任何想送给服务器的数据. 这时数据要以字符串的形式送给服务器,如下所示: <BR>
<P class=code>name=value&amp;anothername=othervalue&amp;so=on&nbsp;</P><BR><FONT size=4><B>步骤 2 – "收到!" --- 处理服务器的响应 </B></FONT><BR><BR>　　当发送请求时,要提供指定处理响应的JavaScript函数名. <BR>
<P class=code>http_request.onreadystatechange&nbsp;=&nbsp;nameOfTheFunction;&nbsp;</P>　　我们来看看这个函数的功能是什么.首先函数会检查请求的状态.如果状态值是4,就意味着一个完整的服务器响应已经收到了,您将可以处理该响应. <BR>
<P class=code>if&nbsp;(http_request.readyState&nbsp;==&nbsp;4)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;everything&nbsp;is&nbsp;good,&nbsp;the&nbsp;response&nbsp;is&nbsp;received<BR>}&nbsp;else&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;still&nbsp;not&nbsp;ready<BR>}</P>　　readyState的取值如下: <BR>　　0 (未初始化) <BR>　　1 (正在装载) <BR>　　2 (装载完毕) <BR>　　3 (交互中) <BR>　　4 (完成) <BR><BR>　　接着,函数会检查HTTP服务器响应的状态值. 完整的状态取值可参见 W3C site. 我们着重看值为200 OK的响应. <BR>
<P class=code>if&nbsp;(http_request.status&nbsp;==&nbsp;200)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;perfect!<BR>}&nbsp;else&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;there&nbsp;was&nbsp;a&nbsp;problem&nbsp;with&nbsp;the&nbsp;request,<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;for&nbsp;example&nbsp;the&nbsp;response&nbsp;may&nbsp;be&nbsp;a&nbsp;404&nbsp;(Not&nbsp;Found)<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;or&nbsp;500&nbsp;(Internal&nbsp;Server&nbsp;Error)&nbsp;response&nbsp;codes<BR>}</P>　　在检查完请求的状态值和响应的HTTP状态值后, 您就可以处理从服务器得到的数据了.有两种方式可以得到这些数据: <BR>
<P class=code>http_request.responseText&nbsp;–&nbsp;以文本字符串的方式返回服务器的响应&nbsp;<BR>http_request.responseXML&nbsp;–&nbsp;以XMLDocument对象方式返回响应.处理XMLDocument对象可以用JavaScript&nbsp;DOM函数&nbsp;</P><BR><FONT size=4><B>步骤 3 – "万事俱备!" - 简单实例</B></FONT><BR><BR>　　我们现在将整个过程完整地做一次,发送一个简单的HTTP请求. 我们用JavaScript请求一个HTML文件, test.html, 文件的文本内容为"I'm a test.".然后我们"alert()"test.html文件的内容. <BR>
<P class=code>&lt;script&nbsp;type="text/javascript"&nbsp;language="javascript"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;http_request&nbsp;=&nbsp;false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;makeRequest(url)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request&nbsp;=&nbsp;false;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(window.XMLHttpRequest)&nbsp;{&nbsp;//&nbsp;Mozilla,&nbsp;Safari,...<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request&nbsp;=&nbsp;new&nbsp;XMLHttpRequest();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(http_request.overrideMimeType)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request.overrideMimeType('text/xml');<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(window.ActiveXObject)&nbsp;{&nbsp;//&nbsp;IE<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request&nbsp;=&nbsp;new&nbsp;ActiveXObject("Msxml2.XMLHTTP");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(e)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request&nbsp;=&nbsp;new&nbsp;ActiveXObject("Microsoft.XMLHTTP");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(e)&nbsp;{}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!http_request)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alert('Giving&nbsp;up&nbsp;:(&nbsp;Cannot&nbsp;create&nbsp;an&nbsp;XMLHTTP&nbsp;instance');<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request.onreadystatechange&nbsp;=&nbsp;alertContents;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request.open('GET',&nbsp;url,&nbsp;true);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request.send(null);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;alertContents()&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(http_request.readyState&nbsp;==&nbsp;4)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(http_request.status&nbsp;==&nbsp;200)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alert(http_request.responseText);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alert('There&nbsp;was&nbsp;a&nbsp;problem&nbsp;with&nbsp;the&nbsp;request.');<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&lt;/script&gt;<BR>&lt;span<BR>&nbsp;&nbsp;&nbsp;&nbsp;style="cursor:&nbsp;pointer;&nbsp;text-decoration:&nbsp;underline"<BR>&nbsp;&nbsp;&nbsp;&nbsp;onclick="makeRequest('test.html')"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Make&nbsp;a&nbsp;request<BR>&lt;/span&gt;</P><BR>　　本例中: <BR>　　用户点击浏览器上的"请求"链接; <BR>　　接着函数makeRequest()将被调用.其参数 – HTML文件test.html在同一目录下; <BR>　　这样就发起了一个请求.onreadystatechange的执行结果会被传送给alertContents(); <BR>　　alertContents()将检查服务器的响应是否成功地收到,如果是,就会"alert()"test.html文件的内容. <BR><BR><FONT size=4><B>步骤 4 – "X-文档" --- 处理XML响应</B></FONT><BR><BR>　　在前面的例子中,当服务器对HTTP请求的响应被收到后,我们会调用请求对象的reponseText属性.该属性包含了test.html文件的内容.现在我们来试试responseXML属性. <BR><BR>　　首先,我们新建一个有效的XML文件,后面我们将使用这个文件.该文件(test.xml)源代码如下所示: <BR>
<P class=code>&lt;?xml&nbsp;version="1.0"&nbsp;?&gt;<BR>&lt;root&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;I'm&nbsp;a&nbsp;test.<BR>&lt;/root&gt;</P>　　在该脚本中,我们只需修改请求部分: <BR>
<P class=code>...<BR>onclick="makeRequest('test.xml')"&gt;<BR>...</P>　　接着,在alertContents()中,我们将alert()的代码alert(http_request.responseText);换成: <BR>
<P class=code>　　var&nbsp;xmldoc&nbsp;=&nbsp;http_request.responseXML;<BR>　　var&nbsp;root_node&nbsp;=&nbsp;xmldoc.getElementsByTagName('root').item(0);<BR>　　alert(root_node.firstChild.data);<BR></P><BR>　　这里,我们使用了responseXML提供的XMLDocument对象并用DOM方法获取存于XML文件中的内容.<img src ="http://www.blogjava.net/qq13367612/aggbug/18822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-08 17:49 <a href="http://www.blogjava.net/qq13367612/articles/18822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDK5.0的11个主要新特征</title><link>http://www.blogjava.net/qq13367612/articles/18800.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Tue, 08 Nov 2005 08:39:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/18800.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18800.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/18800.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18800.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18800.html</trackback:ping><description><![CDATA[<DIV><B>1 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>泛型(Generic)</B></DIV>
<DIV><B>1.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>说明</B></DIV>
<DIV>增强了java的类型<A href="http://www.gamvan.com/server/" target=_blank>安全</A>，可以在编译期间对容器内的对象进行类型检查，在运行期不必进行类型的转换。而在j2se5之前必须在运行期动态进行容器内对象的检查及转换</DIV>
<DIV>减少含糊的容器，可以定义什么类型的数据放入容器</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>ArrayList&lt;Integer&gt;&nbsp;listOfIntegers;&nbsp;//&nbsp;&lt;TYPE_NAME&gt;&nbsp;is&nbsp;new&nbsp;to&nbsp;the&nbsp;syntax <BR>Integer&nbsp;integerObject; <BR>listOfIntegers&nbsp;=&nbsp;new&nbsp;ArrayList&lt;Integer&gt;();&nbsp;//&nbsp;&lt;TYPE_NAME&gt;&nbsp;is&nbsp;new&nbsp;to&nbsp;the&nbsp;syntax <BR>listOfIntegers.add(new&nbsp;Integer(10));&nbsp;//&nbsp;只能是Integer类型 <BR>integerObject&nbsp;=&nbsp;listOfIntegers.get(0);&nbsp;//&nbsp;取出对象不需要转换</TD></TR></TBODY></TABLE></DIV>
<DIV><B>1.2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>用法</B></DIV>
<DIV><B>声明及实例化泛型类：</B></DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>HashMap&lt;String,Float&gt;&nbsp;hm&nbsp;=&nbsp;new&nbsp;HashMap&lt;String,Float&gt;(); <BR>//不能使用原始类型 <BR>GenList&lt;int&gt;&nbsp;nList&nbsp;=&nbsp;new&nbsp;GenList&lt;int&gt;();&nbsp;&nbsp;//编译错误 <BR>J2SE&nbsp;5.0目前不支持原始类型作为类型参数(type&nbsp;parameter)</TD></TR></TBODY></TABLE></DIV>
<DIV><B>定义泛型接口：</B></DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;interface&nbsp;GenInterface&lt;T&gt;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;func(T&nbsp;t); <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV><B>定义泛型类：</B></DIV>
<DIV>public class ArrayList&lt;ItemType&gt; { ... }</DIV>
<DIV>public class GenMap&lt;T, V&gt; { ... }</DIV>
<DIV>例1：</DIV>
<DIV>&nbsp;</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容<BR><FONT color=#000000>public class MyList&lt;Element&gt; extends LinkedList&lt;Element&gt;</FONT></FONT><BR>{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;swap(int&nbsp;i,&nbsp;int&nbsp;j) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element&nbsp;temp&nbsp;=&nbsp;this.get(i); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.set(i,&nbsp;this.get(j)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.set(j,&nbsp;temp); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyList&lt;String&gt;&nbsp;list&nbsp;=&nbsp;new&nbsp;MyList&lt;String&gt;(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.add("hi"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.add("andy"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(list.get(0)&nbsp;+&nbsp;"&nbsp;"&nbsp;+&nbsp;list.get(1)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.swap(0,1); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(list.get(0)&nbsp;+&nbsp;"&nbsp;"&nbsp;+&nbsp;list.get(1)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV>例2：</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;class&nbsp;GenList&nbsp;&lt;T&gt;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;T[]&nbsp;elements; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;size&nbsp;=&nbsp;0; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;length&nbsp;=&nbsp;0; <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;GenList(int&nbsp;size)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elements&nbsp;=&nbsp;(T[])new&nbsp;Object[size]; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.size&nbsp;=&nbsp;size; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;T&nbsp;get(int&nbsp;i)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(i&nbsp;&lt;&nbsp;length)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;elements[i]; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;add(T&nbsp;e)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(length&nbsp;&lt;&nbsp;size&nbsp;-&nbsp;1) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elements[length++]&nbsp;=&nbsp;e; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV><B>泛型方法：</B></DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;class&nbsp;TestGenerics{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;&lt;T&gt;&nbsp;String&nbsp;getString(T&nbsp;obj)&nbsp;{&nbsp;//实现了一个泛型方法 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;obj.toString(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;[]&nbsp;args){ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestGenerics&nbsp;t&nbsp;=&nbsp;new&nbsp;TestGenerics(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;s&nbsp;=&nbsp;"Hello"; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer&nbsp;i&nbsp;=&nbsp;100; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(t.getString(s)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(t.getString(i)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV><B>1.3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>受限泛型</B></DIV>
<DIV>　　受限泛型是指类型参数的取值范围是受到限制的. extends关键字不仅仅可以用来声明类的继承关系, 也可以用来声明类型参数(type parameter)的受限关系.例如, 我们只需要一个存放数字的列表, 包括整数(Long, Integer, Short), 实数(Double, Float), 不能用来存放其他类型, 例如字符串(String), 也就是说, 要把类型参数T的取值泛型限制在Number极其子类中.在这种情况下, 我们就可以使用extends关键字把类型参数(type parameter)限制为数字</DIV>
<DIV>示例</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;class&nbsp;Limited&lt;T&nbsp;extends&nbsp;Number&gt;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Limited&lt;Integer&gt;&nbsp;number;&nbsp;&nbsp;&nbsp;//正确 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Limited&lt;String&gt;&nbsp;str;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//编译错误 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV><B>1.4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>泛型与异常</B></DIV>
<DIV>类型参数在catch块中不允许出现，但是能用在方法的throws之后。例：</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>import&nbsp;java.io.*; <BR>interface&nbsp;Executor&lt;E&nbsp;extends&nbsp;Exception&gt;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;execute()&nbsp;throws&nbsp;E; <BR>} <BR><BR>public&nbsp;class&nbsp;GenericExceptionTest&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[])&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Executor&lt;IOException&gt;&nbsp;e&nbsp;=&nbsp;new&nbsp;Executor&lt;IOException&gt;()&nbsp;{ <BR>&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;&nbsp;&nbsp;public&nbsp;void&nbsp;execute()&nbsp;throws&nbsp;IOException{ <BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;code&nbsp;here&nbsp;that&nbsp;may&nbsp;throw&nbsp;an <BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;IOException&nbsp;or&nbsp;a&nbsp;subtype&nbsp;of <BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;IOException <BR>&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;&nbsp;&nbsp;} <BR>&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;&nbsp;&nbsp;}; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.execute(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch(IOException&nbsp;ioe)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("IOException:&nbsp;"&nbsp;+&nbsp;ioe); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ioe.printStackTrace(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV><B>1.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>泛型的通配符"?"</B></DIV>
<DIV>"?"可以用来代替任何类型, 例如使用通配符来实现print方法。</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;static&nbsp;void&nbsp;print(GenList&lt;?&gt;&nbsp;list)&nbsp;{})</TD></TR></TBODY></TABLE></DIV>
<DIV><B>1.6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>泛型的一些局限型</B></DIV>
<DIV>不能实例化泛型</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>T&nbsp;t&nbsp;=&nbsp;new&nbsp;T();&nbsp;//error</TD></TR></TBODY></TABLE></DIV>
<DIV>不能实例化泛型类型的数组</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>T[]&nbsp;ts=&nbsp;new&nbsp;T[10];&nbsp;&nbsp;&nbsp;//编译错误</TD></TR></TBODY></TABLE></DIV>
<DIV>不能实例化泛型参数数</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>Pair&lt;String&gt;[]&nbsp;table&nbsp;=&nbsp;new&nbsp;Pair&lt;String&gt;(10);&nbsp;//&nbsp;ERROR</TD></TR></TBODY></TABLE></DIV>
<DIV>类的静态变量不能声明为类型参数类型</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;class&nbsp;GenClass&lt;T&gt;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;static&nbsp;T&nbsp;t;&nbsp;&nbsp;&nbsp;&nbsp;//编译错误 <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV>泛型类不能继承自Throwable以及其子类</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;GenExpection&lt;T&gt;&nbsp;extends&nbsp;Exception{}&nbsp;&nbsp;&nbsp;&nbsp;//编译错误&nbsp;</TD></TR></TBODY></TABLE></DIV>
<DIV>不能用于基础类型int等</DIV>
<DIV>
<DIV><FONT face=宋体><B></B></FONT></DIV><B>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>Pair&lt;double&gt;&nbsp;//error <BR>Pair&lt;Double&gt;&nbsp;//right</TD></TR></TBODY></TABLE>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>增强循环(Enhanced for Loop)</B></DIV>
<DIV>旧的循环</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>LinkedList&nbsp;list&nbsp;=&nbsp;new&nbsp;LinkedList();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>list.add("Hi"); <BR>list.add("everyone!"); <BR>list.add("Was"); <BR>list.add("the"); <BR>list.add("pizza"); <BR>list.add("good?");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;list.size();&nbsp;i++) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println((String)&nbsp;list.get(i)); <BR>//或者用以下循环 <BR>//for(Iterator&nbsp;iter&nbsp;=&nbsp;list.iterator();&nbsp;iter.hasNext();&nbsp;)&nbsp;{ <BR>//Integer&nbsp;stringObject&nbsp;=&nbsp;(String)iter.next(); <BR>//&nbsp;...&nbsp;more&nbsp;statements&nbsp;to&nbsp;use&nbsp;stringObject...&nbsp; <BR>//} <BR>新的循环 <BR>LinkedList&lt;String&gt;&nbsp;list&nbsp;=&nbsp;new&nbsp;LinkedList&lt;String&gt;();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>list.add("Hi"); <BR>list.add("everyone!"); <BR>list.add("Was"); <BR>list.add("the"); <BR>list.add("pizza"); <BR>list.add("good?");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>for&nbsp;(String&nbsp;s&nbsp;:&nbsp;list) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(s);</TD></TR></TBODY></TABLE></DIV>
<DIV>很清晰、方便，一看便知其用法</DIV>
<DIV><B>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>可变参数(Variable Arguments)</B></DIV>
<DIV>实现了更灵活的方法参数传入方式，System.out.printf是个很好的例子</DIV>
<DIV>用法：void test(Object … args)</DIV>
<DIV>一个很容易理解的例子</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;static&nbsp;int&nbsp;add(int&nbsp;...&nbsp;args){ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;total&nbsp;=&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;args.length;&nbsp;i++) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total&nbsp;+=&nbsp;args[i];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;total; <BR>} <BR>public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args){ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;a; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;Varargs.add(1,&nbsp;2,&nbsp;3,&nbsp;4,&nbsp;5,&nbsp;6,&nbsp;7,&nbsp;8,&nbsp;9,&nbsp;10); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(a); <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV><B>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>自动实现装箱和解箱操作(Boxing/Unboxing Conversions)</B></DIV>
<DIV>说明：实现了基本类型与外覆类之间的隐式转换。基本类型至外覆类的转换称为装箱，外覆类至基本类型的转换为解箱。这些类包括</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>Primitive&nbsp;Type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reference&nbsp;Type <BR>boolean&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boolean <BR>byte&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Byte <BR>char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Character <BR>short&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Short <BR>int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer <BR>long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Long <BR>float&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Float <BR>double&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Double <BR>例如，旧的实现方式 <BR>Integer&nbsp;intObject; <BR>int&nbsp;intPrimitive; <BR>ArrayList&nbsp;arrayList&nbsp;=&nbsp;new&nbsp;ArrayList(); <BR>intPrimitive&nbsp;=&nbsp;11; <BR>intObject&nbsp;=&nbsp;new&nbsp;Integer(intPrimitive); <BR>arrayList.put(intObject);&nbsp;//&nbsp;不能放入int类型，只能使Integer <BR>新的实现方式 <BR>int&nbsp;intPrimitive; <BR>ArrayList&nbsp;arrayList&nbsp;=&nbsp;new&nbsp;ArrayList(); <BR>intPrimitive&nbsp;=&nbsp;11; <BR>//在这里intPrimitive被自动的转换为Integer类型 <BR>arrayList.put(intPrimitive);</TD></TR></TBODY></TABLE></DIV>
<DIV><B>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>静态导入(Static Imports)</B></DIV>
<DIV>很简单的东西，看一个例子：</DIV>
<DIV>没有静态导入</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>Math.sqrt(Math.pow(x,&nbsp;2)&nbsp;+&nbsp;Math.pow(y,&nbsp;2));</TD></TR></TBODY></TABLE></DIV>
<DIV>有了静态导入</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>import&nbsp;static&nbsp;java.lang.Math.*; <BR>sqrt(pow(x,&nbsp;2)&nbsp;+&nbsp;pow(y,&nbsp;2));</TD></TR></TBODY></TABLE></DIV>
<DIV>&nbsp;</DIV>
<DIV>其中import static java.lang.Math.*;就是静态导入的语法，它的意思是导入Math类中的所有static方法和属性。这样我们在使用这些方法和属性时就不必写类名。</DIV>
<DIV>需要注意的是默认包无法用静态导入，另外如果导入的类中有重复的方法和属性则需要写出类名，否则编译时无法通过。</DIV>
<DIV><B>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>枚举类(Enumeration Classes)</B></DIV>
<DIV>用法：public enum Name {types, ….}</DIV>
<DIV>简单的例子：</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;enum&nbsp;Colors&nbsp;{Red,&nbsp;Yellow,&nbsp;Blue,&nbsp;Orange,&nbsp;Green,&nbsp;Purple,&nbsp;Brown,&nbsp;Black} <BR>public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args){ <BR>&nbsp;&nbsp;&nbsp;&nbsp;Colors&nbsp;myColor&nbsp;=&nbsp;Colors.Red; <BR>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(myColor); <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV>又一个简单例子：</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>import&nbsp;java.util.*; <BR>enum&nbsp;OperatingSystems&nbsp;{windows,&nbsp;unix,&nbsp;linux,&nbsp;macintosh} <BR>public&nbsp;class&nbsp;EnumExample1&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[])&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OperatingSystems&nbsp;os; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;os&nbsp;=&nbsp;OperatingSystems.windows; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;switch(os)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;windows: <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(“You&nbsp;chose&nbsp;Windows!”); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;unix: <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(“You&nbsp;chose&nbsp;Unix!”); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;linux: <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(“You&nbsp;chose&nbsp;Linux!”); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;macintosh: <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(“You&nbsp;chose&nbsp;Macintosh!”); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;default: <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(“I&nbsp;don’t&nbsp;know&nbsp;your&nbsp;OS.”); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV>应运enum简写的例子：</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>import&nbsp;java.util.*; <BR>public&nbsp;class&nbsp;EnumTest <BR>{ <BR>&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args) <BR>&nbsp;&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Scanner&nbsp;in&nbsp;=&nbsp;new&nbsp;Scanner(System.in); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.print("Enter&nbsp;a&nbsp;size:&nbsp;(SMALL,&nbsp;MEDIUM,&nbsp;LARGE,&nbsp;EXTRA_LARGE)&nbsp;"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;input&nbsp;=&nbsp;in.next().toUpperCase(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Size&nbsp;size&nbsp;=&nbsp;Enum.valueOf(Size.class,&nbsp;input); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("size="&nbsp;+&nbsp;size); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("abbreviation="&nbsp;+&nbsp;size.getAbbreviation()); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(size&nbsp;==&nbsp;Size.EXTRA_LARGE) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Good&nbsp;job--you&nbsp;paid&nbsp;attention&nbsp;to&nbsp;the&nbsp;_."); <BR>&nbsp;&nbsp;&nbsp;} <BR>} <BR><BR>enum&nbsp;Size <BR>{ <BR>&nbsp;&nbsp;&nbsp;SMALL("S"),&nbsp;MEDIUM("M"),&nbsp;LARGE("L"),&nbsp;EXTRA_LARGE("XL"); <BR><BR>&nbsp;&nbsp;&nbsp;private&nbsp;Size(String&nbsp;abbreviation)&nbsp;{&nbsp;this.abbreviation&nbsp;=&nbsp;abbreviation;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;getAbbreviation()&nbsp;{&nbsp;return&nbsp;abbreviation;&nbsp;} <BR><BR>&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;abbreviation; <BR>} <BR>enum类中拥有方法的一个例子： <BR>enum&nbsp;ProgramFlags&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;showErrors(0x01), <BR>&nbsp;&nbsp;&nbsp;&nbsp;includeFileOutput(0x02), <BR>&nbsp;&nbsp;&nbsp;&nbsp;useAlternateProcessor(0x04); <BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;bit; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ProgramFlags(int&nbsp;bitNumber)&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bit&nbsp;=&nbsp;bitNumber; <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;getBitNumber()&nbsp;&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return(bit); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>} <BR>public&nbsp;class&nbsp;EnumBitmapExample&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[])&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ProgramFlags&nbsp;flag&nbsp;=&nbsp;ProgramFlags.showErrors; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(“Flag&nbsp;selected&nbsp;is:&nbsp;“&nbsp;+ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag.ordinal()&nbsp;+ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;“&nbsp;which&nbsp;is&nbsp;“&nbsp;+ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag.name()); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV><B>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>元数据(Meta data)</B></DIV>
<DIV>请参考</DIV>
<DIV><A href="http://www-900.ibm.com/developerWorks/cn/java/j-annotate1/">http://www-900.ibm.com/developerWorks/cn/java/j-annotate1/</A></DIV>
<DIV><A href="http://www-900.ibm.com/developerworks/cn/java/j-annotate2.shtml">http://www-900.ibm.com/developerworks/cn/java/j-annotate2.shtml</A><B></B></DIV>
<DIV><B>8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>Building Strings(StringBuilder</B><B>类)</B></DIV>
<DIV>在JDK5.0中引入了StringBuilder类，该类的方法不是同步(synchronized)的，这使得它比StringBuffer更加轻量级和有效。</DIV>
<DIV><B>9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>控制台输入(Console Input)</B></DIV>
<DIV>在JDK5.0之前我们只能通过JOptionPane.showInputDialog进行输入，但在5.0中我们可以通过类Scanner在控制台进行输入操作</DIV>
<DIV>例如在1.4中的输入</DIV>
<DIV><B>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>String&nbsp;input&nbsp;=&nbsp;JOptionPane.showInputDialog(prompt); <BR>int&nbsp;n&nbsp;=&nbsp;Integer.parseInt(input); <BR>double&nbsp;x&nbsp;=&nbsp;Double.parseDouble(input); <BR>s&nbsp;=&nbsp;input; <BR>在5.0中我们可以 <BR>Scanner&nbsp;in&nbsp;=&nbsp;new&nbsp;Scanner(System.in); <BR>System.out.print(prompt); <BR>int&nbsp;n&nbsp;=&nbsp;in.nextInt(); <BR>double&nbsp;x&nbsp;=&nbsp;in.nextDouble(); <BR>String&nbsp;s&nbsp;=&nbsp;in.nextLine();</TD></TR></TBODY></TABLE>10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>Covariant Return Types(</B><B>不晓得怎么翻译)</B></DIV>
<DIV>JDK5之前我们覆盖一个方法时我们无法改变被方法的返回类型，但在JDK5中我们可以改变它</DIV>
<DIV>例如1.4中我们只能</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;Object&nbsp;clone()&nbsp;{&nbsp;...&nbsp;} <BR>... <BR>Employee&nbsp;cloned&nbsp;=&nbsp;(Employee)&nbsp;e.clone();</TD></TR></TBODY></TABLE></DIV>
<DIV>但是在5.0中我们可以改变返回类型为Employee</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;Employee&nbsp;clone()&nbsp;{&nbsp;...&nbsp;} <BR>... 
<DIV>Employee cloned = e.clone();</DIV></TD></TR></TBODY></TABLE></DIV>
<DIV><B>11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </B><B>格式化I/O(Formatted I/O)</B></DIV>
<DIV>增加了类似C的格式化输入输出，简单的例子：</DIV>
<DIV>
<TABLE style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" borderColor=#e0e0e0 cellSpacing=1 cellPadding=4 width="95%" align=center border=1 fixed TABLE-LAYOUT:>
<TBODY>
<TR>
<TD style="HEIGHT: 25px; WORD-WRAP: break-word" vAlign=top bgColor=#f6f6f6><FONT style="COLOR: #b0b0b0">代码内容</FONT><BR>public&nbsp;class&nbsp;TestFormat{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args){ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;a&nbsp;=&nbsp;150000,&nbsp;b&nbsp;=&nbsp;10; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;c&nbsp;=&nbsp;5.0101f,&nbsp;d&nbsp;=&nbsp;3.14f; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf("%4d&nbsp;%4d%n",&nbsp;a,&nbsp;b); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf("%x&nbsp;%x%n",&nbsp;a,&nbsp;b); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf("%3.2f&nbsp;%1.1f%n",&nbsp;c,&nbsp;d); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf("%1.3e&nbsp;%1.3e%n",&nbsp;c,&nbsp;d*100); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}</TD></TR></TBODY></TABLE></DIV>
<DIV>输出结果为：</DIV>
<DIV>150000&nbsp;&nbsp; 10</DIV>
<DIV>249f0 a</DIV>
<DIV>5.01 3.1</DIV>
<DIV>5.010e+00 3.140e+02</DIV>
<DIV>下面是一些格式化参数说明(摘自Core Java 2 Volume I - Fundamentals, Seventh Edition)</DIV>
<DIV align=center><B>Table 3-5. Conversions for printf</B></DIV>
<TABLE cellSpacing=0 cellPadding=0 width=584 border=0>
<TBODY>
<TR>
<TD vAlign=bottom width=85>
<DIV align=left><B>Conversion Character</B></DIV></TD>
<TD vAlign=bottom width=324>
<DIV align=left><B>Type</B></DIV></TD>
<TD vAlign=bottom width=175>
<DIV align=left><B>Example</B></DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>d</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Decimal integer</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>159</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>x</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left><A name=RANGE!B3>Hexadecimal integer</A></DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>9f</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>o</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Octal integer</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>237</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>f</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Fixed-point floating-point</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>15.9</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>e</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Exponential floating-point</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>1.59E+01</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>g</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>General floating-point (the shorter of e and f)</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>&nbsp;</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>a</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Hexadecimal floating point</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>0x1.fccdp3</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>s</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>String</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>Hello</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>c</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Character</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>H</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>b</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Boolean</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>TRUE</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>h</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Hash code</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>42628b2</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>tx</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>Date and time</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left><U><A HREF="/gufen/admin/EditPosts.aspx#RANGE!ch03table07#RANGE!ch03table07">See Table 3-7</A></U></DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>%</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>The percent symbol</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>%</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>n</DIV></TD>
<TD vAlign=top width=324>
<DIV align=left>The platform-dependent line separator</DIV></TD>
<TD vAlign=top width=175>
<DIV align=left>&nbsp;</DIV></TD></TR></TBODY></TABLE>
<DIV>&nbsp;</DIV>
<DIV align=center><B>Table 3-7. Date and Time Conversion Characters</B></DIV>
<TABLE cellSpacing=0 cellPadding=0 width=584 border=0>
<TBODY>
<TR>
<TD vAlign=bottom width=85>
<DIV align=left><B>Conversion Character</B></DIV></TD>
<TD vAlign=bottom width=304>
<DIV align=left><B>Type</B></DIV></TD>
<TD vAlign=bottom width=195>
<DIV align=left><B>Example</B></DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>C</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Complete date and time</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>Mon Feb 09 18:05:19 PST 2004</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>F</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>ISO 8601 date</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>2004-02-09</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>D</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>U.S. formatted date (month/day/year)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>02/09/2004</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>T</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>24-hour time</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>18:05:19</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>r</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>12-hour time</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>06:05:19 pm</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>R</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left><A name=RANGE!B7>24-hour time, no seconds</A></DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>18:05</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>Y</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Four-digit year (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>2004</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>y</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Last two digits of the year (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>04</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>C</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>First two digits of the year (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>20</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>B</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Full month name</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>February</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>b or h</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left><A name=RANGE!B12>Abbreviated month name</A></DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>Feb</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>m</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit month (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>02</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>d</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left><A name=RANGE!B14>Two-digit day (with leading zeroes)</A></DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>09</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>e</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit day (without leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>9</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>A</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Full weekday name</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>Monday</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>a</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Abbreviated weekday name</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>Mon</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>j</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Three-digit day of year (with leading zeroes), between 001 and 366</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>069</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>H</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit hour (with leading zeroes), between 00 and 23</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>18</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>k</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit hour (without leading zeroes), between 0 and 23</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>18</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>I</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit hour (with leading zeroes), between 01 and 12</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>06</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>l</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit hour (without leading zeroes), between 1 and 12</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>6</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>M</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit minutes (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>05</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>S</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Two-digit seconds (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>19</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>L</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Three-digit milliseconds (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>047</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>N</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Nine-digit nanoseconds (with leading zeroes)</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>047000000</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>P</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Uppercase morning or afternoon marker</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>PM</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>p</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Lowercase morning or afternoon marker</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>pm</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>z</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>RFC 822 numeric offset from GMT</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>-0800</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>Z</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Time zone</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>PST</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>s</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left>Seconds since 1970-01-01 00:00:00 GMT</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>1078884319</DIV></TD></TR>
<TR>
<TD vAlign=top width=85>
<DIV align=left>E</DIV></TD>
<TD vAlign=top width=304>
<DIV align=left><A name=RANGE!B32>Milliseconds since 1970-01-01 00:00:00 GMT</A></DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>1078884319047</DIV></TD></TR></TBODY></TABLE>
<DIV align=center><B></B>&nbsp;</DIV>
<DIV align=center><B>Table 3-6. Flags for printf</B></DIV>
<TABLE cellSpacing=0 cellPadding=0 width=584 border=0>
<TBODY>
<TR>
<TD vAlign=top width=157>
<DIV align=left><B>Flag</B></DIV></TD>
<TD vAlign=top width=232>
<DIV align=left><B>Purpose</B></DIV></TD>
<TD vAlign=top width=195>
<DIV align=left><B>Example</B></DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>+</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Prints sign for positive and negative numbers</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>+3333.33</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>space</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Adds a space before positive numbers</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>| 3333.33|</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>0</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Adds leading zeroes</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>003333.33</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>-</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Left-justifies field</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>|3333.33 |</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>(</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Encloses negative number in parentheses</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>(3333.33)</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>,</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Adds group separators</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>3,333.33</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left># (for f format)</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Always includes a decimal point</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>3,333.</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left># (for x or o format)</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Adds 0x or 0 prefix</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>0xcafe</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>^</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Converts to upper case</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>0XCAFE</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>$</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Specifies the index of the argument to be formatted; for example, %1$d %1$x prints the first argument in decimal and hexadecimal</DIV></TD>
<TD vAlign=top width=195>
<DIV align=left>159 9F</DIV></TD></TR>
<TR>
<TD vAlign=top width=157>
<DIV align=left>&lt;&nbsp;</DIV></TD>
<TD vAlign=top width=232>
<DIV align=left>Formats the same value as the previous specification; for example, %d %&lt;x prints the same number in decimal and hexadecimal</DIV></TD>
<TD noWrap width=195>
<DIV align=left>&nbsp;</DIV></TD></TR></TBODY></TABLE>
<DIV>&nbsp;</DIV>
<DIV>这里是一些简单的介绍，更详细的说明请参考：</DIV>
<DIV>Core Java 2 Volume I - Fundamentals, Seventh Edition</DIV>
<DIV>Core Java 2 Volume II - Advanced Features, Seventh Edition</DIV>
<DIV>里面都有一些很精彩的描述，中文名称就是《Java核心技术》。只有第七版才有J2SE5.0的介绍</DIV><img src ="http://www.blogjava.net/qq13367612/aggbug/18800.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-08 16:39 <a href="http://www.blogjava.net/qq13367612/articles/18800.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>详解如何利用iText在JSP中生成PDF报表</title><link>http://www.blogjava.net/qq13367612/articles/18151.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 07:24:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/18151.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18151.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/18151.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18151.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18151.html</trackback:ping><description><![CDATA[前久做了一个通过JSP生成PDF报表的小项目，算得上开了一次眼界。企业的一些信息通过网络形成Html报表，虽然IE可以直接打印显示在其中的内容，但是从界面上来看，如果直接将Html的显示结果打印出来，显得不太美观。如果将它转成PDF文件再打印，则打印效果会好很多。<BR><BR>2、iText简介<BR><BR>iText是一个开放源码的Java类库，可以用来方便地生成PDF文件。大家通过访问http://sourceforge.net/project/showfiles.php?group_id=15255&amp;release_id=167948下载最新版本的类库，下载完成之后会得到一个.jar包，把这个包加入JDK的classpath即可使用。<BR><BR>如果生成的PDF文件中需要出现中文、日文、韩文字符，则还需要通过访问http://itext.sourceforge.net/downloads/iTextAsian.jar下载iTextAsian.jar包。<BR><BR>关于iText类库的使用，http://www.lowagie.com/iText/tutorial/index.html有比较详细的教程。该教程从入门开始，比较系统地介绍了在PDF文件中放入文字、图片、表格等的方法和技巧。<BR><BR>读完这片教程，大致就可以做一些从简单到复杂的PDF文件了。不过，试图通过教程解决在生成PDF文件过程中遇到的所有困难无疑是一种奢望。所以，阅读iText的api文档显得非常重要。读者在下载类库的同时，也可以下载类库的文档。<BR><BR>3、如何利用iText在java程序中生成PDF报表<BR><BR>以下是上述教程中一个最简单的例子，这个例子刻画了通过iText生成PDF文件的一般程序框架。读者只需要在document.open();和document.close();两条语句中间加入自己希望放在PDF文件中的内容即可。该例子只在PDF文件中加了“Hello World“一行文字。<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>Document document = new Document();
   try 
   { 
  PdfWriter.getInstance
  (document, new FileOutputStream
  ("Chap0101.pdf"));
            document.open();
 document.add(new Paragraph("Hello World"));
        }
        catch(DocumentException de) 
		{
            System.err.println(de.getMessage());
        }
        catch(IOException ioe) 
		{
            System.err.println(ioe.getMessage());
        }
        document.close();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>由以上的例子可见，程序的框架十分清楚明了。然而在PDF中指定文字、图画、表格的位置是一件非常麻烦的事情。除了不断地在程序中修改位置、然后运行程序、生成PDF文件、观察元素在PDF中的位置是否合理这样的过程以外，似乎还没有其它更好的方法。 <BR><BR>4、如何通过JSP生成PDF报表 <BR><BR>这一部分是在iText的教程中所没有的，网上的相关资料也比较少。我经过一段时间研究发现：先在服务器上生成PDF文件，然后用户通过点击指向PDF文件的超链接选择下载或打开。这是一个思路，或者说是思路之一。本文实现了这个思路，又给出另外一个思路并通过两种途径实现之。 <BR><BR>1）直接在服务器上生成PDF文件。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page import ="com.lowagie.text.*
,com.lowagie.text.pdf.*, java.io.*"%&gt;
&lt;%
   String filename = 
   "PDF"+(new Random()).nextInt()+".pdf" ;
Document document = 
new Document(PageSize.A4);
 ServletOutputStream out1
 = response.getOutputStream();
 try{
 PdfWriter writer = 
 PdfWriter.getInstance(document, 
 new FileOutputStream(filename) );
            document.open();
  document.add(new Paragraph("Hello World"));
            document.close();
    }
            catch(Exception e){}
%&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>上面的程序在服务器上生成了一个静态的PDF文件。显然，每次运行所得的PDF文件的名称应该是独一无二不能有重的。本程序通过随机函数来命名生成的PDF文件。本程序的缺点就是，每次运行都会在服务器上产生一个PDF文件，如果不及时删除，数量会越来越大，这显然是站点维护者所不愿意看到的。 <BR><BR>2）将PDF文件通过流的形式输送到客户端的缓存。这样做的好处是不会在服务器上留下任何“遗迹”。 <BR><BR>i）直接通过JSP页面生成 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@
       page import="java.io.*,
	   java.awt.Color,com.lowagie.text.*,
	   com.lowagie.text.pdf.*"%&gt;
&lt;%
response.setContentType
( "application/pdf" );
Document document = new Document();
ByteArrayOutputStream buffer
= new ByteArrayOutputStream();
PdfWriter writer=
PdfWriter.getInstance( document, buffer );
document.open();
document.add(new Paragraph("Hello World"));
document.close();
DataOutput output = 
new DataOutputStream
( response.getOutputStream() );
byte[] bytes = buffer.toByteArray();
response.setContentLength(bytes.length);
for( int i = 0;
i &lt; bytes.length; 
i++ ) 
{ 
output.writeByte( bytes[i] ); 
}
%&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>ii）通过Servlet生成 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
public void doGet
(HttpServletRequest request,
HttpServletResponse response)
 throws IOException,ServletException
{
  Document document = 
  new Document(PageSize.A4, 36,36,36,36);
  ByteArrayOutputStream ba
  = new ByteArrayOutputStream();
  try
  {
     PdfWriter writer = 
	 PdfWriter.getInstance(document, ba);
     document.open();
     document.add(new 
	 Paragraph("Hello World"));
  }
  catch(DocumentException de)
  {
    de.printStackTrace();
    System.err.println
	("A Document error:" +de.getMessage());
   }
  document.close();
  response.setContentType
  ("application/pdf");
  response.setContentLength(ba.size());
  ServletOutputStream out 
  = response.getOutputStream();
  ba.writeTo(out);
  out.flush();
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>5、结束 <BR><BR>我在项目中采用的是第二种方法。本文的源码在我的tomcat4上面都是调试通过的。希望可以给大家带来方便。 <BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 15:24 <a href="http://www.blogjava.net/qq13367612/articles/18151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用HttpSessionListener统计在线人数</title><link>http://www.blogjava.net/qq13367612/articles/18144.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/18144.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18144.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/18144.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18144.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18144.html</trackback:ping><description><![CDATA[JSP显示在线人数代码<BR><BR>编写以下SessionCounter.java 并编译为SessiionCounter.class 然后放到你的网站的classpath的 SessionCount(自己建立此目录)下面<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package SessionCount;  
import javax.servlet.*;  
import javax.servlet.http.*;  

public class SessionCounter implements HttpSessionListener {  

private static int activeSessions = 0;  

public void sessionCreated(HttpSessionEvent se) {  
activeSessions++;  
}  

public void sessionDestroyed(HttpSessionEvent se) {  
if(activeSessions &gt; 0)  
activeSessions--;  
}  

public static int getActiveSessions() {  
return activeSessions;  
}  
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>接着建立online.jsp文件用于显示在线人数 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page import="SessionCount.SessionCounter" %&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>在线： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%= SessionCounter.getActiveSessions() %&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>然后需要在你的网站的WEB-INF中建立web.xml 文件内容如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;!-- Web.xml --&gt;  
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;  
&lt;!DOCTYPE web-app  
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"  
"http://java.sun.com/j2ee/dtds/web-app_2.3.dtd"&gt;  

&lt;web-app&gt;  

&lt;!-- Listeners --&gt;  
&lt;listener&gt;  
&lt;listener-class&gt;  
SessionCount.SessionCounter  
&lt;/listener-class&gt;  
&lt;/listener&gt;  

&lt;/web-app&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>然后重新启动你的应用服务器，访问online.jsp检查是否显示正确。 <BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18144.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 15:03 <a href="http://www.blogjava.net/qq13367612/articles/18144.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java应用开发技巧：尽可能使用堆栈变量</title><link>http://www.blogjava.net/qq13367612/articles/18145.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/18145.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18145.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/18145.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18145.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18145.html</trackback:ping><description><![CDATA[如果您频繁存取变量，就需要考虑从何处存取这些变量。变量是static变量，还是堆栈变量，或者是类的实例变量？变量的存储位置对存取它的代码的性能有明显的影响？例如，请考虑下面这段代码：<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>　　class StackVars
　　{
　　private int instVar;
　　private static int staticVar;
　　
　　//存取堆栈变量
　　void stackAccess(int val)
　　{
　　int j=0;
　　for (int i=0; i&lt;val; i++)
　　j += 1;
　　}
　　
　　//存取类的实例变量
　　void instanceAccess(int val)
　　{
　　for (int i=0; i&lt;val; i++)
　　instVar += 1;
　　}   
　　
　　//存取类的 static 变量
　　void staticAccess(int val)
　　{
　　for (int i=0; i&lt;val; i++)
　　staticVar += 1;
　　}
　　}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>这段代码中的每个方法都执行相同的循环，并反复相同的次数。唯一的不同是每个循环使一个不同类型的变量递增。方法stackAccess使一个局部堆栈变量递增，instanceAccess使类的一个实例变量递增，而 staticAccess 使类的一个 static 变量递增。 <BR><BR>instanceAccess和staticAccess的执行时间基本相同。但是，stackAccess要快两到三倍。存取堆栈变量如此快是因为，JVM存取堆栈变量比它存取static变量或类的实例变量执行的操作少。请看一下为这三个方法生成的字节码： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>Method void stackAccess(int)
　　0 iconst_0         //将 0 压入堆栈。
　　1 istore_2         //弹出 0 并将它存储在局部分变量表中索引为 2 的位置 (j)。
　　2 iconst_0         //压入 0。
　　3 istore_3         //弹出 0 并将它存储在局部变量表中索引为 3 的位置 (i)。
　　4 goto 13          //跳至位置 13。
　　7 iinc 2 1         //将存储在索引 2 处的 j 加 1。
　　10 iinc 3 1         //将存储在索引 3 处的 i 加 1。
　　13 iload_3          //压入索引 3 处的值 (i)。
 
　　14 iload_1          //压入索引 1 处的值 (val)。
　　15 if_icmplt 7      //弹出 i 和 val。如果 i 小于 val，则跳至位置 7。
　　18 return           //返回调用方法。
　　
　　Method void instanceAccess(int)
　　0 iconst_0         //将 0 压入堆栈。
　　1 istore_2         //弹出 0 并将它存储在局部变量表中索引为 2 的位置 (i)。
　　2 goto 18          //跳至位置 18。
　　5 aload_0          //压入索引 0 (this)。
　　6 dup              //复制堆栈顶的值并将它压入。
　　7 getfield #19 &lt;Field int instVar&gt;
　　//弹出 this 对象引用并压入 instVar 的值。
　　10 iconst_1         //压入 1。
　　11 iadd             //弹出栈顶的两个值，并压入它们的和。
　　12 putfield #19 &lt;Field int instVar&gt;
　　//弹出栈顶的两个值并将和存储在 instVar 中。
　　15 iinc 2 1         //将存储在索引 2 处的 i 加 1。
　　18 iload_2          //压入索引 2 处的值 (i)。
　　19 iload_1          //压入索引 1 处的值 (val)。
　　20 if_icmplt 5      //弹出 i 和 val。如果 i 小于 val，则跳至位置 5。
　　23 return           //返回调用方法。
 
　　
 　　Method void staticAccess(int)
　　0 iconst_0         //将 0 压入堆栈。
　　1 istore_2         //弹出 0 并将它存储在局部变量表中索引为 2 的位置 (i)。
　　2 goto 16          //跳至位置 16。
　　5 getstatic #25 &lt;Field int staticVar&gt;
　　//将常数存储池中 staticVar 的值压入堆栈。
　　8 iconst_1         //压入 1。
　　9 iadd             //弹出栈顶的两个值，并压入它们的和。
　　10 putstatic #25 &lt;Field int staticVar&gt;
　　//弹出和的值并将它存储在 staticVar 中。
　　13 iinc 2 1         //将存储在索引 2 处的 i 加 1。
　　16 iload_2          //压入索引 2 处的值 (i)。
　　17 iload_1          //压入索引 1 处的值 (val)。
　　18 if_icmplt 5      //弹出 i 和 val。如果 i 小于 val，则跳至位置 5。
　　21 return           //返回调用方法。</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>查看字节码揭示了堆栈变量效率更高的原因。JVM是一种基于堆栈的虚拟机，因此优化了对堆栈数据的存取和处理。所有局部变量都存储在一个局部变量表中，在Java操作数堆栈中进行处理，并可被高效地存取。存取static变量和实例变量成本更高，因为JVM必须使用代价更高的操作码，并从常数存储池中存取它们。（常数存储池保存一个类型所使用的所有类型、字段和方法的符号引用。） <BR><BR>通常，在第一次从常数存储池中访问static变量或实例变量以后，JVM将动态更改字节码以使用效率更高的操作码。尽管有这种优化，堆栈变量的存取仍然更快。考虑到这些事实，就可以重新构建前面的代码，以便通过存取堆栈变量而不是实例变量或 static 变量使操作更高效。请考虑修改后的代码： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>　　class StackVars
　　{
　　//与前面相同...
　　void instanceAccess(int val)
　　{
　　int j = instVar;
　　for (int i=0; i&lt;val; i++)
　　j += 1;
　　instVar = j;
　　}  
　　
　　void staticAccess(int val)
　　{
　　int j = staticVar;
　　for (int i=0; i&lt;val; i++)
　　j += 1;
　　staticVar = j;
　　}
　　}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>方法 instanceAccess 和 staticAccess 被修改为将它们的实例变量或 static 变量复制到局部堆栈变量中。当变量的处理完成以后，其值又被复制回实例变量或 static 变量中。这种简单的更改明显提高了 instanceAccess 和 staticAccess 的性能。这三个方法的执行时间现在基本相同，instanceAccess 和 staticAccess 的执行速度只比 stackAccess 的执行速度慢大约 4%。 <BR><BR>这并不表示您应该避免使用 static 变量或实例变量。您应该使用对您的设计有意义的存储机制。例如，如果您在一个循环中存取 static 变量或实例变量，则您可以临时将它们存储在一个局部堆栈变量中，这样就可以明显地提高代码的性能。这将提供最高效的字节码指令序列供 JVM 执行。 <BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18145.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 15:03 <a href="http://www.blogjava.net/qq13367612/articles/18145.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常见的十四种Java开发工具及其特点</title><link>http://www.blogjava.net/qq13367612/articles/18132.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 06:03:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/18132.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18132.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/18132.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18132.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18132.html</trackback:ping><description><![CDATA[1、JDK （Java Development Kit）Java开发工具集<BR><BR>从初学者角度来看，采用JDK开发Java程序能够很快理解程序中各部分代码之间的关系，有利于理解Java面向对象的设计思想。JDK的另一个显著特点是随着Java （J2EE、J2SE以及J2ME）版本的升级而升级。但它的缺点也是非常明显的就是从事大规模企业级Java应用开发非常困难，不能进行复杂的Java软件开发，也不利于团体协同开发。<BR><BR>2、Java Workshop <BR><BR>3、NetBeans 与Sun Java Studio 5<BR><BR>NetBeans是开放源码的Java集成开发环境(IDE)，适用于各种客户机和Web应用。Sun Java Studio是Sun公司最新发布的商用全功能Java IDE，支持Solaris、Linux和Windows平台，适于创建和部署2层Java Web应用和n层J2EE应用的企业开发人员使用。<BR><BR>NetBeans是业界第一款支持创新型Java开发的开放源码IDE。开发人员可以利用业界强大的开发工具来构建桌面、Web或移动应用。同时，通过NetBeans和开放的API的模块化结构，第三方能够非常轻松地扩展或集成NetBeans平台。<BR><BR>NetBeans3.5.1主要针对一般Java软件的开发者，而Java One Studio5则主要针对企业做网络服务等应用的开发者。Sun不久还将推出Project Rave，其目标是帮助企业的开发者进行软件开发。<BR><BR>NetBeans 3.5.1版本与其他开发工具相比，最大区别在于不仅能够开发各种台式机上的应用，而且可以用来开发网络服务方面的应用，可以开发基于J2ME的移动设备上的应用等。在NetBeans 3.5.1基础上，Sun开发出了Java One Studio5，为用户提供了一个更加先进的企业编程环境。<BR><BR>在新的Java One Studio5里有一个应用框架，开发者可以利用这些模块快速开发自己在网络服务方面的各种应用程序。<BR><BR>4、Borland 的JBuilder<BR><BR>Jbuilder进入了Java集成开发环境的王国，它满足很多方面的应用，尤其是对于服务器方以及EJB开发者们来说。下面简单介绍一下Jbuilder的特点: <BR><BR>1）Jbuilder支持最新的Java技术，包括Applets、JSP/Servlets、JavaBean以及EJB(Enterprise JavaBeans)的应用。<BR><BR>2）用户可以自动地生成基于后端数据库表的EJB Java类，Jbuilder同时还简化了EJB的自动部署功能.此外它还支持CORBA，相应的向导程序有助于用户全面地管理IDL(分布应用程序所必需的接口定义语言Interface Definition Language)和控制远程对象。<BR><BR>3）Jbuilder支持各种应用服务器。Jbuilder与Inprise Application Server紧密集成，同时支持WebLogic Server，支持EJB 1.1和EJB 2.0，可以快速开发J2EE的电子商务应用。<BR><BR>4）Jbuilder能用Servlet和JSP开发和调试动态Web 应用。<BR><BR>5）利用Jbuilder可创建(没有专有代码和标记)纯Java2应用。由于Jbuilder是用纯Java语言编写的，其代码不含任何专属代码和标记，它支持最新的Java标准。<BR><BR>6）Jbuilder拥有专业化的图形调试介面，支持远程调试和多线程调试，调试器支持各种JDK版本,包括J2ME/J2SE/J2EE。<BR><BR>JBuilder环境开发程序方便，它是纯的Java 开发环境，适合企业的J2EE开发；缺点是往往一开始人们难于把握整个程序各部分之间的关系，对机器的硬件要求较高，比较吃内存，这时运行速度显得较慢。<BR>5、Oracle 的JDeveloper<BR><BR>Oracle9i JDeveloper（定为9.0版，最新为10g）为构建具有J2EE功能，XML和Web services的复杂的，多层的Java应用程序提供了一个完全集成的开发环境。它为运用Oracle9i数据库和应用服务器的开发人员提供特殊的功能和增强性能，除此以外，它也有资格成为用于多种用途Java开发的一个强大的工具。<BR><BR>Oracle9i JDeveloper的主要特点如下：<BR><BR>①具有UML（Unified Modeling Language，一体化建模语言）建模功能。可以将业务对象及e-business应用模型化。<BR><BR>②配备有高速Java调试器（Debuger）、内置Profiling工具、提高代码质量的工具“CodeCoach”等。<BR><BR>③支持SOAP（Simple Object Access Protocol）“简单对象访问协议”、UDDI（Universal Description, Discovery and Integration）“统一描述、发现和集成协议”、WSDL（Web Services Description Language）“WEB服务描述语言”等Web服务标准。<BR><BR>JDeveloper 不仅仅是很好的 Java 编程工具，而且是 Oracle Web 服务的延伸，支 Apache SOAP，以及 9iAS ，可扩充的环境和 XML 和 WSDL 语言紧密相关。Oracle9i Jdeveloper完全利用Java编写，能够与以前的Oracle服务器软件以及其他厂商支持J2EE的应用服务器产品相兼容，而且在设计时着重针对Oracle9i，能够无缝化跨平台之间的应用开发，提供了业界第一个完整的、集成了J2EE和XML的开发环境。<BR><BR>允许开发者快速开发可以通过Web、无线设备及语音界面访问的Web服务和交易应用，以往只能通过将传统Java编程技巧与最新模块化方式结合到一个单一集成的开发环境中之后才能完成J2EE应用开发生命周期管理的事实，从根本上得到改变。缺点就是对于初学者来说，较复杂，也比较难。<BR><BR>6、IBM的Visual Age for Java<BR><BR>Visual Age for Java是一个非常成熟的开发工具，它的特性以于IT开发者和业余的Java编程人员来说都是非常用有用的。它提供对可视化编程的广泛支持，支持利用CICS连接遗传大型机应用，支持EJB的开发应用，支持与Websphere的集成开发，方便的bean创建和良好的快速应用开发(RAD)支持和无文件式的文件处理。<BR><BR>IBM为建设Web站点所推出的WebSphere Studio Advanced Edition及其包含的Visua Age for Java Professional Edition软件已全面转向以Java为中心，这样，Java开发人员对WebSphere全套工具的感觉或许会好了许多。<BR><BR>Studio所提供的工具有：Web站点管理、快速开发 JDBC页向导程序、HTML编辑器和HTML语法检查等。这确实是个不错的HTML站点页面编辑环境。Studio和VisualAge集成度很高，菜单中提供了在两种软件包之间快速移动代码的选项。这就让使用Studio的Web页面设计人员和使用VisualAge的Java程序员可以相互交换文件、协同工作。<BR><BR>Visual Age for Java支持团队开发，内置的代码库可以自动地根据用户做出改动而修改程序代码，这样就可以很方便地将目前代码和早期版本做出比较。<BR><BR>与Visual Age紧密结合的Websphere Studio本身并不提供源代码和版本管理的支持，它只是包含了一个内置文件锁定系统,当编辑项目的时候可以防止其他人对这些文件的错误修改，软件还支持诸如Microsoft Visual SourceSafe这样的第三方源代码控制系统。Visual Age for Java完全面向对象的程序设计思想使得开发程序非常快速、高效。你可以不编写任何代码就可以设计出一个典型的应用程序框架。<BR><BR>Visual Age for Java作为IBM电子商务解决方案其中产品之一，可以无缝地与其他IBM产品，如WebSphere、DB2融合, 迅速完成从设计、开发到部署应用的整个过程。<BR><BR>Visual Age for Java独特的管理文件方式使其集成外部工具非常困难,你无法让Visual Age for Java与其他工具一起联合开发应用。<BR>7、BEA 的 WebLogic Workshop<BR><BR>BEA WebLogic Workshop是一个统一、简化、可扩展的开发环境，使所有的开发人员都能在 BEA WebLogic Enterprise Platform之上构建基于标准的企业级应用，从而提高了开发部门的生产力水平，加快了价值的实现。<BR><BR>WebLogic Workshop除了提供便捷的Web服务之外，它能够用于创建更多种类的应用。作为整个BEA WebLogic Platform的开发环境。不管是创建门户应用、编写工作流、还是创建Web应用，Workshop 8.1都可以帮助开发人员更快更好地完成。<BR><BR>WebLogic Workshop的主要特点如下：<BR><BR>① 使 J2EE 开发切实可行，提高开发效率<BR><BR>BEA WebLogic Workshop 使开发人员远离 J2EE 内在的复杂性，集中精力专注业务逻辑，无须操心单调乏味的基础结构代码。这种创新意味着，已被企业验证的 J2EE 的强大功能，最终被大多数不熟悉 Java 和 J2EE 的应用开发人员所掌握，从而使 IT 部门的工作效率提高一个数量级。<BR><BR>可视化设计器以及直观的概念，如事件、属性和控件等，实现了基于事件的开发。Workshop 简化的程序设计模型，使开发人员不必掌握复杂的 J2EE API 和面向对象的程序设计原理。<BR><BR>所有开发人员，包括 J2EE 专家和具有可视化和过程化语言技能的应用开发人员在内，都可以共同工作在 BEA WebLogic Enterprise Platform 之上。Workshop 的可视化开发环境，创建带有代码注释的标准 Java 文件，用来说明由运行时框架实施的企业级需求。<BR><BR>J2EE 和其他高级开发人员，借助功能强大的代码编辑功能，可以访问 Java 源代码，从而弥补了可视化设计器的不足。<BR><BR>② 构建企业级应用<BR><BR>通过在可伸缩、安全可靠的企业级架构上实施各种应用，BEA WebLogic Workshop 大大降低了开发风险。<BR><BR>而且，所有应用的创建都使用标准的 J2EE 组件，既保护了您的技术投资，又保持了最大的灵活性。 BEA WebLogic Workshop 运行框架，是统一整个架构的汇聚层，使单一、简化的程序设。<BR><BR>计模型扩展到所有的 BEA WebLogic Enterprise Platform 应用类型。通过解释设计时创建的注释代码，运行时框架可以实现必要的 J2EE 组件，并且提取出与 J2EE 应用开发有关的所有底层细节。<BR><BR>③ 降低 IT 复杂性<BR><BR>BEA WebLogic Workshop 提供各种 Java 控件，使得与 IT 资源的连接更轻而易举。另外，在构建任何 BEA WebLogic Platform 的应用中，Java 控件不仅可扩展而且完全相同。<BR><BR>这种强大、有效的方法能够：降低 IT 技术的复杂性，优化信息的可用性，推动包含"最佳业务方案"的可重用服务的开发，使开发人员能以更低的成本、更短的时间实现更大的产出。<BR><BR>利用 BEA WebLogic Workshop，任何开发人员都能以最大的生产效率，构建各种Web 服务、Web 应用、门户和集成项目。BEA WebLogic Workshop是BEA的产品战略核心，它帮助客户接触和利用面向服务架构(SOA)的强大功能。<BR><BR>BEA Weblogic Workshop 8.1极大简化了当前实际企业集成环境中企业级应用和服务的构建，并成为全面支持关键企业级应用(如异步、真正松耦合和粗粒度消息传送等)的自然选择。它的缺点就是过于复杂，对于初学者来说，理解起来较为困难。<BR>8、WebGain 的Visual Cafe for Java<BR><BR>Visual Cafe 是只能在Symantec公司的Java虚拟机、Netscape公司的Java虚拟机和Microsoft虚拟机上工作的调试器。这对于开发者来讲是一个重要的特性,因为用户开发的Java代码中的许多软件bug就可能中会在某种特定的虚拟机上起作用。<BR><BR>在修改后进行编译基继续进行调试时,Visual Cafe会自动将文件存盘，使用Visual Cafe创建的原生应用具有许多特点。除了明显的速度提高之外,Symantec使类库的二进制方式比正常的JDK小Visual Cafe为所指定的关系自动生成或更新必要的Java代码。利用Visual Cafe，用户可以从一个标准对象数据库中集合完整的Java应用程序和Applet，而不必再编写源代码。Visual Cafe还提供了一个扩充的源代码开发工具集。<BR><BR>Visual Cafe综合了Java软件的可视化源程序开发工具，它允许开发人员在可视化视图和源视图之间进行有效地转换。在可视化视图中进行的修改立即反映在源代码中。对源代码的改变自动更新可视化视图。<BR><BR>Visual Cafe具有许多源文件方面的特性，如全局检索和替换。绝大多数Java开发工具的文献的问题在于简单地挨个介绍开发工具的每部分组件，但用户在开应用时还需要一个面向任务的手册，利用这个手册你可以不必知道工具每一部分的特定功能就可以开始创建自己的应用。<BR><BR>Visual Cafe提供了非常全面的用户指南,它对最开始的安装到创建第一个Java应用和Applet都提供了全面的帮助，Visual Cafe将自动生成所指明关系的必要Java代码。Visual Cafe可以在Windows 95和Windows NT平台下运行，Symantec公司为Java开发工作提供一个在Macintosh操作系统下可以运行的RAD工具。Visual Cafe编译器速度很快，在国际化支持方面比较突出；缺点就是对于初学者来说，较复杂，也比较难。<BR><BR>9、Macromedia的JRUN<BR><BR>Macromedia公司的JRun是一个具有最广阔适用性的Java引擎，用于开发及实施由Java Servlets和JavaServer Pages编写的服务器端Java应用。<BR><BR>JRun是第一个完全支持JSP 1.0 规格书的商业化产品，全球有超过80,000名开发人员使用JRun在他们已有的Web服务器上添加服务器端Java的功能。其中Web服务器包括了Microsoft IIS，Netscape Enterprise Server，Apache等。<BR><BR>JRun是开发实施服务器端Java的先进引擎。如果我们希望在我们的Web应用中添加服务器端Java功能，那么JRun将成为我们的正确选择。<BR><BR>JRun目前有3个版本，它是第一个支持Java Server Pages(JSP)规格书1.0的商业化产品。JSP是一种强大的服务器端技术，它是用于创建复杂Web应用的一整套快速应用开发系统。<BR><BR>JRun可以使我们开始开发并测试Java应用。它最多接受5个并发的连接并且包括全部Java Servlet API，支持JavaServer Pages(JSP)，支持所有主要的Web servers和计算机平台。 JRun Pro能够在生产环境下承受大访问量的负载，帮助我们实施应用、服务或Web站点（包括内联网）。<BR><BR>JRun Pro 支持无限量并发式连接运行多个Java虚拟机，包括多个并发的Java虚拟机（JVM）。提供一个远程管理applet以及一个远程可再分布式的管理applet。JRun Pro Unlimited包括了所有JRun Pro的功能，除次以外，还可以运行无限量的，并发的JVM。<BR><BR>JRun依靠其内置的JRun Web Server可以单独运行。使用服务器端Java，用户可以开发出复杂的商业应用系统。最重要的一点是，由于servlets的平台独立性，以及更加简单的开发、更快速的实施、更经济的维护成本，它是CGI(Common Gateway Interface)或Perl scripts的极佳的替代产品。缺点就是对于初学者来说，较复杂，也比较难。<BR><BR>10、JCreator<BR><BR>JCreator 是一个Java程序开发工具，也是一个Java集成开发环境（IDE）。无论你是要开发Java应用程序或者网页上的Applet元件都难不倒它。在功能上与Sun公司所公布的JDK等文字模式开发工具相较之下来得容易，还允许使用者自订义操作窗口界面及无限Undo/Redo等功能。<BR><BR>JCreator为用户提供了相当强大的功能，例如项目管理功能，项目模板功能，可个性化设置语法高亮属性、行数、类浏览器、标签文档、多功能编绎器，向导功能以及完全可自定义的用户界面。通过JCreator，我们不用激活主文档而直接编绎或运行我们的JAVA程序。<BR><BR>JCreator能自动找到包含主函数的文件或包含Applet的Html文件，然后它会运行适当的工具。在JCreator中，我们可以通过一个批处理同时编绎多个项目。JCreator的设计接近Windows界面风格，用户对它的界面比较熟悉。其最大特点是与我们机器中所装的JDK完美结合，是其它任何一款IDE所不能比拟的。它是一种初学者很容易上手的java开发工具，缺点是只能进行简单的程序开发，不能进行企业J2EE的开发应用。<BR><BR>11、Microsoft VJ++ <BR><BR>Visual J++ 是Microsoft 公司推出的可视化的Java 语言集成开发环境(IDE)，为Java 编程人员提供了一个新的开发环境，是一个相当出色的开发工具。无论集成性、编译速度、调试功能、还是易学易用性，都体现了Microsoft 的一惯风格。Visual J++ 具有下面的特点：<BR><BR>1）Visual J++ 把Java 虚拟机(JVM)作为独立的操作系统组件放入Windows，使之从浏览器中独立出来。<BR><BR>2）Microsoft 的应用基本类库(AFC,Application Foundation Class Library)对SUN 公司的JDK 作了扩展，使应用基本类库更加适合在Windows 下使用。<BR><BR>3） Visual J++ 的调试器支持动态调试，包括单步执行、设置断点、观察变量数值等。<BR><BR>4） Visual J++ 提供了一些程序向导(Wizards)和生成器(Builders)，它们可以方便地帮助用户快速地生成Java 程序，帮助你在自己的工程中创建和修改文件。<BR><BR>5） Visual J++ 界面友好，其代码编辑器具有智能感知、联机编译等功能，使程序编写十分方便。Visual J++ 中建立了Java 的WFC，这一新的应用程序框架能够直接访问Windows 应用程序接口(API)，使你能够用Java 语言编写完全意义上的Windows 应用程序。<BR><BR>6）Visual J++ 中表单设计器的快速应用开发特性使用WFC 创建基于表单的应用程序变得轻松、简单。<BR><BR>通过WFC 可以方便地使用ActiveX 数据对象(ADO,ActiveX Data Objects)来检索数据和执行简单数据的绑定。通过在表单设计器中使用ActiveX 数据对象，可以快速地在表单中访问和显示数据。<BR><BR>Visual J++能结合微软的一贯的编程风格，很方便进行Java 的应用开发，但它的移植性较差，不是纯的Java 开发环境。<BR>12、Eclipse <BR><BR>Eclipse是一种可扩展的开放源代码IDE。2001年11月，IBM公司捐出价值4,000万美元的源代码组建了Eclipse联盟，并由该联盟负责这种工具的后续开发。集成开发环境(IDE)经常将其应用范围限定在“开发、构建和调试”的周期之中。<BR><BR>为了帮助集成开发环境(IDE)克服目前的局限性，业界厂商合作创建了Eclipse平台。Eclipse允许在同一IDE中集成来自不同供应商的工具，并实现了工具之间的互操作性，从而显著改变了项目工作流程，使开发者可以专注在实际的嵌入式目标上。<BR><BR>Eclipse框架的这种灵活性来源于其扩展点。它们是在XML中定义的已知接口，并充当插件的耦合点。扩展点的范围包括从用在常规表述过滤器中的简单字符串，到一个Java类的描述。<BR><BR>任何Eclipse插件定义的扩展点都能够被其它插件使用，反之，任何Eclipse插件也可以遵从其它插件定义的扩展点。除了解由扩展点定义的接口外，插件不知道它们通过扩展点提供的服务将如何被使用。<BR><BR>利用Eclipse，我们可以将高级设计(也许是采用UML)与低级开发工具(如应用调试器等)结合在一起。如果这些互相补充的独立工具采用Eclipse扩展点彼此连接，那么当我们用调试器逐一检查应用时，UML对话框可以突出显示我们正在关注的器件。<BR><BR>事实上，由于Eclipse并不了解开发语言，所以无论Java语言调试器、C/C++调试器还是汇编调试器都是有效的，并可以在相同的框架内同时瞄准不同的进程或节点。<BR><BR>Eclipse的最大特点是它能接受由Java开发者自己编写的开放源代码插件，这类似于微软公司的Visual Studio和Sun微系统公司的NetBeans平台。<BR><BR>Eclipse为工具开发商提供了更好的灵活性，使他们能更好地控制自己的软件技术。Eclipse联盟已经宣布将在2004年中期发布其3.0版软件。这是一款非常受欢迎的java开发工具，这国内的用户越来越多，实际上实用它java开发人员是最多的。缺点就是较复杂，对初学者来说，理解起来比较困难。<BR><BR>13、Ant <BR><BR>Another Neat Tool(Ant)是一种基于Java的build工具。理论上来说，它有些类似于（Unix）C中的make ，但没有make的缺陷。因为Ant的原作者在多种(硬件)平台上开发软件时，无法忍受这些工具的限制和不便。<BR><BR>类似于make的工具本质上是基于shell（语言）的：他们计算依赖关系，然后执行命令（这些命令与你在命令行敲的命令没太大区别）。这就意味着你可以很容易地通过使用OS特有的或编写新的（命令）程序扩展该工具；然而，这也意味着你将自己限制在了特定的OS，或特定的OS类型上，如Unix。Ant就不同了。<BR><BR>与基于shell命令的扩展模式不同，Ant用Java的类来扩展。（用户）不必编写shell命令，配置文件是基于XML的，通过调用target树，就可执行各种task。每个task由实现了一个实现了特定Task接口的对象来运行。<BR><BR>Ant支持一些可选task，一个可选task一般需要额外的库才能工作。可选task与Ant的内置task分开，单独打包。这个可选包可以从你下载Ant的同一个地方下载。ANT本身就是这样一个流程脚本引擎，用于自动化调用程序完成项目的编译，打包，测试等。<BR><BR>除了基于JAVA是平台无关的外，脚本的格式是基于XML的，比make脚本来说还要好维护一些。Ant是Apache提供给Java开发人员的构建工具，它可以在Windows OS和Unix OS下运行，它不仅开放源码并且还是一个非常好用的工具。<BR><BR>Ant是Apache Jakarta中一个很好用的Java开发工具，Ant配置文件采用XML文档编写，所以Java程序员对其语法相当熟悉，Ant是专用于Java项目平台，能够用纯Java来开发，它能够运行于Java安装的平台，即体现了它的跨平台功能。它的缺点显示执行结果只能是DOS字符界面，不能进行复杂的java程序开发。<BR>14、IntelliJ<BR><BR>Intellij IDEA是一款综合的Java 编程环境，被许多开发人员和行业专家誉为市场上最好的IDE。它提供了一系列最实用的的工具组合：智能编码辅助和自动控制，支持J2EE，Ant，JUnit和CVS集成，非平行的编码检查和创新的GUI设计器。<BR><BR>IDEA把Java开发人员从一些耗时的常规工作中解放出来，显著地提高了开发效率。具有运行更快速，生成更好的代码；持续的重新设计和日常编码变得更加简易，与其它工具的完美集成；很高的性价比等特点。在4.0版本中支持Generics，BEA WebLogic集成，改良的CVS集成以及GUI设计器。<BR><BR>IntelliJ IDEA能尽可能地促进程序员的编程速度。它包括了很多辅助的功能，并且与Java结合得相当好。不同的工具窗口围绕在主编程窗口周围，当鼠标点到时即可打开，无用时也可轻松关闭，使用户得到了最大化的有效屏幕范围。<BR><BR>以技术为导向的IDEA集成了调试器，支持本地和远程的调试，即使我们需要修改一些设置上的东西使我们的工作顺利进展。另外，它还提供了通常的监视，分步调试以及手动设置断点功能，在这种断点模式下，我们可以自动地在断点之外设置现场访问，甚至可以浏览不同的变量的值。<BR><BR>IDE支持多重的JVM设置，几个编译程序和Ant建造系统，并且，它使得设置多重的自定义的类途径变得简单。<BR><BR>IntelliJ Idea是一个相对较新的Java IDE。它是Java开发环境中最为有用的一个。高度优化的IntelleJ Idea使普通任务变得相当容易，Idea支持很多整合功能，更重要的使它们设计的好容易使用。Idea支持XML中的代码实现，Idea同时还会校正XML，Idea支持JSP的结构。<BR><BR>作用于普通Java代码的众多功能同样适用于JSP（比如整合功能），同时支持JSP调试；支持EJB，尽管它不包括对个别应用服务器的特殊支持。Idea支持Ant建立工具，不仅是运行目标它还支持编译与运行程序前后运行目标，另外也支持绑定键盘快捷键。<BR><BR>在编辑一个Ant建立XML文件时，Idea还对组成Ant工程的XML部分提供支持。IntelliJ IDEA 被称为是最好的JAVA IDE开发平台，这套软件就是以其聪明的即时分析和方便的 refactoring 功能深获大家所喜爱。缺点是较复杂，对初学者来说，理解起来比较困难。<BR><BR>小结<BR><BR>现在常用的Java项目开发环境有：JBuilder、VisualAge for Java、Forte for Java, Visual Cafe、Eclipse、NetBeans IDE、JCreator +J2SDK、jdk+记事本、EditPlus+ J2SDK等等。一般开发J2EE项目时都需要安装各公司的应用服务器（中间件）和相应的开发工具，在使用这些开发工具之前，我们最好能熟知这些软件的优点和缺点，以便根据实际情况选择应用。<BR><BR>编程工具只是工具，为了方便人们工作而开发的，各有特点，因此，选工具主要的依据自己将要从事的领域是什么，而不是盲目的认为那种工具好，那种工具不好。最后希望大家都能找到自己合适的java 开发工具。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 14:03 <a href="http://www.blogjava.net/qq13367612/articles/18132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDO技术分析及如何进入企业应用的研究</title><link>http://www.blogjava.net/qq13367612/articles/18130.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 05:53:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/18130.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18130.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/18130.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18130.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18130.html</trackback:ping><description><![CDATA[JDO（Java Data Object）是JCP中较早开发出来并形成规范的JSR12，该规范对数据的持久化存储进行了一系列规范，并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术，研究并探讨其企业应用可行性是十分重要的。 <BR><BR>前言 <BR><BR>在企业级的应用开发中，常需要有良好的持久化技术来支持数据存储。通过良好的规范或API，将企业的领域业务对象进行持久化存储，大多采用O/R映射技术来进行模式化的数据转换及自动映射工作。 <BR><BR>JDO（Java Data Object）是JCP中较早开发出来并形成规范的JSR12，该规范对数据的持久化存储进行了一系列规范，并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术，研究并探讨其企业应用可行性是十分重要的。 <BR><BR>以下主要对JDO（JDO 1.0规范）的应用开发技术作扼要介绍，通过该文，可以由浅入深、并较为全面地了解JDO，掌握主要的技术细节及过程，理解其运行机制，并对企业级应用有个总体的把握，这将有助于企业应用软件的技术选型、体系架构及分析设计活动。 <BR><BR>该文适合企业应用架构师、及关心数据持久层设计开发人员。 <BR><BR>JDO基本思想及特点 <BR><BR>企业信息系统的一个重要问题是解决数据的存储，即持久化。在软件开发过程中，分析员分析领域业务，提取出领域业务模型，并对应设计出数据库中需要进行存储业务数据的数据库表及相应字段。 <BR><BR>并根据业务流程，设计业务处理逻辑单元，进行数据的加工、处理及存储、查询等业务。其中一个较为繁烦、枯燥的工作，就是处理大量的数据持久化代码。 <BR><BR>为了解决数据从业务对象层向数据存储层之间的转换工作，JDO提供了相应的开发规范及API，解决了由Java对象直接存储为数据库相应表的底层处理过程，有助于设计人员更加专注于面向业务流程、面向业务对象等较高层次的应用。 <BR><BR>由于采用JDO的映射机制，能降低了业务系统与数据存储系统的耦合，使得业务系统相对于关系数据库或对象型数据库，具有可移植性，同时，由于采用面向对象（而非传统的面向记录）的持久化技术，系统更为轻便、简洁，增强了可维护性。 <BR><BR>JDO应用示例及分析 <BR><BR>以下将通过一些示例，由浅及深地讲解JDO技术。 <BR><BR>临时对象与持久对象 <BR><BR>这是一个普通的业务对象的代码。<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package business.model; 
public class Book 
{ 
  private String isbn; 
  private String name; 
  private Date publishDate; 
  public void setISBN(String isbn)
  { 
    this.isbn = isbn; 
  } 
  public String getISBN()
  { 
    return this.isbn; 
  } 
  public void setName(String name)
  { 
    this.name = name; 
  } 
  public String getName()
  { 
    return this.name; 
  } 
  public void 
  setPublishDate(Date pubDate)
  { 
    this.publishDate = pubDate; 
  } 
  public Date getPublishDate()
  { 
    return this.publishDate; 
  } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>现在将它作为一个JDO中对象保存到数据库中。代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>Book book = new Book(); 
book.setISBN(“isbn-1234567”); 
book.setName(“Java设计模式”); 

PersistenceManager 
manager = persistenceManagerFactory.
getPersistenceManager(); 
manager.currentTransaction().begin(); 
manager.makePersistence(book); 
manager.currentTransaction().commit();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>Book类的实例book对JDO的API而言，就是一个持久对象。类Book是可持久类。那任何一个普通java类都是JDO的可持久类吗？不是的。只有具备以下的条件，一个对象才可以被JDO持久到数据库中。 <BR><BR>它所属类应标记为可持久的类，有以下两种方法： <BR><BR>显式：实现接口，javax.jdo.PersistenceCapable即可； <BR><BR>隐式：以Sun的JDO参考实现为例，Book.java类的相同路径下还须有Book.jdo文件。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version=“1.0” 
encoding = “UTF-8”?&gt; 
&lt;!DOCTYPE jdo SYSTEM “jdo.dtd”&gt; 
&lt;jdo&gt; 
  &lt;package name = “business.model”&gt; 
     &lt;class name = “Book”/&gt; 
  &lt;/package&gt; 
&lt;/jdo&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>并通过字节码增强工具（本例采用Sun的字节码增强工具）处理: <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>javac Book.java 
java com.sun.jdori.enhancer.Main 
Book.class Book.jdo</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>通过上述两种方法，获得的Book.class才是一个可持久的类。 <BR><BR>字节码增强的有如下功能：当应用程序通过set方法修改某个字段1时，由于通过增强过程，在其内部插入了某些代码，JDO会获得数据状态变化的信息，从而在持久过程中，进行有选择性的处理。 <BR><BR>按照JDO规范，增强后的类可以在不同的JDO实现上使用，而无需重新编译或增强。 <BR><BR>并不是所有Book对象都是持久对象，只有当makePersistence后，该对象才是持久对象，并会通过JDO实现存储到数据库中。通过JDO的供应商扩展标记符（vendor-extension），可详细描述Book类的存储特性，如为该可持久类指定数据库表和对应字段。 <BR><BR>持久对象查询 <BR><BR>JDO查询主要有以下两种方式。 <BR><BR>使用Extend查询 <BR><BR>Extend可以查询指定类及子类的持久对象。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>PersistenceManager
manager = persistenceManagerFactory.
getPersistenceManager(); 
manager.currentTransaction().begin(); 
Extend extend =
manager.getExtend(Book.class,true);
//true表明同时查询子类 
Iterator it = extend.iterator(); 
while(it.hasNext())
{ 
  Book book = (Book)it.next(); 
  System.out.println(book.getISBN()); 
} 
extend.closeAll(); 
manager.currentTransaction().commit();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>Extend查询方法，提供了一种基于类的查询途径，它可以与下面的Query构成更为强大的查询。 <BR><BR>使用Query查询 <BR><BR>Query可以指定过滤条件，是一种常用的查询方式。 <BR><BR>下例是查找条件为“书名以‘Java设计模式’开头且出版日期小于今天”的书籍。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>String filter = 
“((String)name).startsWith(\”Java设计模式\”)
&amp;&amp; publishDate &lt; today”; 
Query query = 
pm.getQuery(Book.class,filter); 
query.declareImports(“import java.util.Date”); 
query.declareParameters(“Date today); 

Date today = new Date(); 
results = (Collection)
query.execute(today);
//传入参数值today 
if (results.isEmpty())
{ 
  System.out.println(“No data!”); 
}else{ 
  Iterator it = results.iterator(); 
  while(it.hasNext())
  { 
    Book book = (Book)it.next(); 
    System.out.println
	(“Book Name:” + book.getName()
	+ “, ISBN:” + book.getISBN()); 
  } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>注：该条件使用了一个变元‘today’，通过“declareParameters”来声明该变量，并在“execute”方法中传入该变量的实例。这种带参数的查询，很类似于我们以前采用JDBC的带?的查询方式。 <BR><BR>其中startsWith(String s)是JDO提供的标准字符方法，类似的方法还有endsWith(String s)。 <BR><BR>JDOQL：上述使用的就是一个JDOQL样例，JDOQL是JDO规范一个组成部分。使用JDOQL可以使用应用在不同的JDO实现上运行。为了解决JDOQL的某些不足，JDO规范提供了支持特定JDO供应商查询语句接口。 <BR><BR>查询排序 <BR><BR>下例是将查询结果按“出版日期降序、书名升序”进行排序。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>Query query = 
pm.newQuery(Book.class, filter); 

String orderStr = 
“publishDate decending, name ascending”; 
query.setOrdering(orderStr); 

results = query.execute(today);</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER>对象更新 <BR><BR>当客户端对业务数据进行了更新后，需要通过业务过程将其更新到持久层中。这有两个过程，首先根据主键找到该实例，接着更新字段及提交。如下例，将指定书目编号的书本的出版日期进行更改。<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public void updateBookPublishDate
(String isbn, Date newDate)
{ 
  PersistenceManager pm = null; 
  try{ 
pm = pmf.getPersistenceManager(); 
Object obj = 
pm.newObjectIdInstance(Book.class,isbn); 
Book book = 
(Book)pm.getObjectById(obj,true); 
book.setPublishDate(newDate); 
  }catch(Exception e)
  { 
xxxContext.setRollbackOnly(); 
throw new Exception(e); 
  }finally{ 
    try{ 
if (pm != null &amp;&amp; !pm.isClosed())
{ 
    pm.close(); 
} 
}catch(Exception ex)
{ 
  System.out.println(ex); 
  } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>注，在PersistenceManager使用newObjectIdInstance（）方法时，JDO是如何知道通过书目编号ISBN来找到该对象呢？其实在本可持久类Book的jdo描述文件中，还需提供如下信息： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version=“1.0” 
encoding = “UTF-8”?&gt; 
&lt;!DOCTYPE jdo SYSTEM “jdo.dtd”&gt; 
&lt;jdo&gt; 
  &lt;package name = “business.model”&gt; 
     &lt;class name = “Book”
	 identity-type=“application”
	 objectid-class=“BookKey” &gt; 
          &lt;field name=“isbn”
		  primary-key=“true”/&gt; 
     &lt;/class&gt; 
  &lt;/package&gt; 
&lt;/jdo&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>其中“identity-type=“application””声明可持久类Book采用程序标识方式，即应用程序传入ID（字段isbn为“primary-key”）信息，JDO实现构造出指定的“objectid-class”的实例（即newObjectIdInstance过程），并由JDO来检索出指定的持久化对象（即getObjectById）。 <BR><BR>BookKey类源码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package businesss.model; 
public class BookKey implements
java.io.Serializable
{ 
      public String isbn; 
      public BookKey()
	  { 
      } 
      public BookKey(String oid)
	  { 
    isbn = oid; 
    } 
    public String toString()
	{ 
          return isbn; 
    } 
    public Boolean equals(Object obj)
	{ 
      return isbn.equals
	  ((BookKey)obj).isbn); 
    } 
    public int hashCode()
	{ 
          return isbn.hashCode(); 
    } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>符合 JDO 的“objectid-class”类，如“BookKey”，须具备以下条件： <BR><BR>类声明为 public，并实现 java.io.Serializable； <BR><BR>带有一个公有且不带参数的构造方法； <BR><BR>当字段作为主键时，须有公有的，且名称和类型与持久类的字段一致，如：public String isbn； <BR><BR>equals 和 hashCode 须使用全部（特指多字段的联合主键）的主键字段值； <BR><BR>类必须有一个构造方法，与 toString 方法的处理过程是逆向过程；即将 toString 的输出值，作为该构造方法的输入值，又可以重新生成该实例（如构造方法“public BookKey(String oid)”）。 <BR><BR>综上所述，如果Book由两个字段作为主键，如isbn和name，则可能的代码是pm.newObjectIdInstance(Book.class,isbn+“#”+name)，且BookKey的构造方法作相应更改，并有两个公有字段“isbn”和“name”。 <BR><BR>对象删除 <BR><BR>对象删除采用方法deletePersistence。示例如下：<BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>pm.currentTransaction().begin(); 
Object obj = 
pm.newObjectIdInstance
(Book.class,isbn); 
Book book = 
(Book)pm.getObjectById(obj,true); 
pm.deletePersistence(book); 
pm.currentTransaction().commit();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>获得PersistenceManager实例 <BR><BR>上述的所有操作与需要PersistenceManager实例，它可以在两种环境方法下获得：非受管环境和受管环境。 <BR><BR>非受管环境 <BR><BR>非受管环境是多指两层开发模式，应用程序直接获得资源对象，进行业务操作。一般事务管理、安全管理或资源管理都需要应用程序自行维护。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>Properties properties =
new Properties(); 
properties.put(“javax.jdo.
PersistenceManagerFactoryClass”,
“com.xxx.jdo.xxxPMFClass”); 
properties.put(“javax.jdo.
option.ConnectionURL”, “xxx”); 
properties.put(“javax.jdo.
option.ConnectionUserName”, “xxx”); 
properties.put(“javax.jdo.
option.ConnectionPassword”, “xxx”); 
PersistenceManagerFactory pmf = 
JDOHelper.getPersistence
ManagerFactory(properties); 
PersistenceManager pm = 
pmf.getPersistenceManager();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>与JDBC获取类似。 <BR><BR>受管环境 <BR><BR>受管环境一般是多层开发模式，尤其是处于J2EE应用环境中，程序通过容器获得资源对象，进行业务操作，由于在容器环境下，事务、安全及资源管理都由容器进行统一集中管理，从而简化了代码结构。 <BR><BR>以下是EJB（EntityBean、SessionBean、MessageDrivenBean）中的setXXXContext中的代码示例。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>private PersistenceManagerFactory pmf; 
public void setXXXContext
(XXXContext context)
{ 
  try{ 
    InitialContext ctx =
	new InitialContext(); 
    Object obj = ctx.lookup
	(“java:compenvjdofactory”); 
    pmf = (PersistenceManagerFactory)
	PortableRemoteObject.narrow
	(o,PersistenceManagerFactory.class); 
  }catch(NamingException e)
  { 
    throw new Exception(e); 
  } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>PMF是如何绑定到J2EE环境下的JNDI上，有兴趣可参考JCA相关的技术文档。 <BR><BR>事务管理 <BR><BR>事务管理及使用，主要有以下三种情形。 <BR><BR>使用JDO事务的Bean管理情形 <BR><BR>一般在非J2EE环境下，使用该事务管理模式。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>PersistenceManager pm = 
pmf.getPersistenceManager(); 
pm.currentTransaction().begin(); 
//do some business with jdo 
pm.currentTransaction().commit(); 
pm.close();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>使用JTA事务的Bean管理情形 <BR><BR>一般在J2EE环境下，采用Bean管理的事务情形下，使用以下方式。 <BR><BR>该方式可用在EJB的事务环境下。 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>xxxContext.getUser
Transaction().begin(); 
PersistenceManager pm =
pmf.getPersistenceManager(); 
//do some business with jdo 
xxxContext.getUserTransaction().commit(); 
pm.close();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER>使用JTA事务的容器管理情形 <BR><BR>一般在J2EE环境下，采用容器管理的事务情形下，使用如下方式。 <BR><BR>如下是某个会话Bean的业务方法。<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public void doBusiness(){ 
  PersistenceManager pm ; 
  try{ 
    pm = pmf.getPersistenceManager(); 
    //do some business with jdo 
  }catch(Exception e){ 
    xxxContext.setRollbackOnly(); 
    System.out.println(e); 
  }finally{ 
    try{ 
      if (pm != null &amp;&amp; !pm.isClosed()) 
        pm.close(); 
    }catch(Exception ex){ 
      System.out.println(ex); 
    } 
  } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>综上，可以得出结论，JDO的操作完全与JDBC的操作相差无几。 <BR><BR>JDO高级应用 <BR><BR>复杂对象的持久化 <BR><BR>在实际的应用中，一个可持久化类要远比Book类复杂很多。它可能会引用其它的Java类型、类、集合或数组，及可能复杂的继承关系。当这些对象的状态发生变化时，JDO是如何感知及跟踪状态变化？ <BR><BR>JDO提供了相应的API及规范来实现该功能。 <BR><BR>基本类型及引用 <BR><BR>可持久化类的字段能被JDO实现进行持久化的原则。原始类型、java.util.Date等被支持（其它较为复杂或可选特性，详见JDO规范）；如果引用是一个可持久类，则JDO进行持久化处理；通过元数据(如jdo文件)声明的字段，一般是非可持久化类的引用，JDO进行持久化； <BR><BR>前面两种情形，当状态发生变化时，JDO会自动感知，但如果引用是非可持久化类，则需要代码进行显式通知，否则JDO不会将变化进行存储。如下例： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public class Book 
{ 
  …… 
    private Object picture; 
    public void setPicture(Object pic)
	{ 
          picture = pic; 
    } 
    public Object getPicture()
	{ 
          Return picture; 
    } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>该类字段picture需要持久化，但java.lang.Object不是一个可持久类，故声明如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version=“1.0” encoding = “UTF-8”?&gt; 
&lt;!DOCTYPE jdo SYSTEM “jdo.dtd”&gt; 
&lt;jdo&gt; 
  &lt;package name = “business.model”&gt; 
     &lt;class name = “Book” &gt; 
          &lt;field name=“picture” 
		  persistence-modifier=“persistent”/&gt; 
     &lt;/class&gt; 
  &lt;/package&gt; 
&lt;/jdo&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>如果其它模块通过getPicture获得对象，并在JDO不可感知的外部，修改对象，则变化不会存储，较好的办法是修改setPicture方法，如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public void setPicture(Object pic)
{ 
  JDOHelper.makeDirty(this, “picture”); 
  picture = pic; 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>并通过setPicture方法来更新数据。JDO的“makeDirty”方法，主要负责通知JDO实现，可持久化类Book某个实例（this）的“picture”字段发生了变化。 <BR><BR>集合 <BR><BR>可持久类的字段引用为集合时，按照JDO规范，强制支持java.util.HashSet，对HashMap、HashTable、TreeMap、TreeSet、LinkedList、ArrayList及Vector的支持对JDO实现而言是可选的，通过PersistenceManagerFactory的supportedOptions方法获得实现特性。 <BR><BR>数组 <BR><BR>如果可持久类的引用是数组类型，当数组单元发生变化时，需要调用“makeDirty”来通知JDO实现，该实例的数组引用内容发生了变化。与引用是非可持久类实例不同，不需要进行JDO的元数据声明。 <BR><BR>继承 <BR><BR>如果使用可持久性，一般继承的子类与父类都为可持久类，以减少系统复杂性，这时需要在子类的元数据中指出其可持久超类，如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;
class name=“TechBook” 
persistence-capable-superclass=“Book”/&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>可为非持久类扩展持久类，或可为持久类扩展非可持久类；这两种情形JDO实现都将忽略非 <BR><BR>可持久类的字段部分，而不保存到数据库。 <BR><BR>JDO的一些不足之处 <BR><BR>JDO对数据的持久化技术相比于成熟的SQL，还有不足之处，这些不足在某些情况下将可能会影响数据处理部分的设计实现。以下列举了常用数据访问的必要功能 <BR><BR>查询增强 <BR><BR>如字符串不支持通配符、大小写比较； <BR><BR>不支持聚合操作，无法实现MIN、MAX、SUM和AVG； <BR><BR>不支持投影操作，JDO查询返回为对象，而不像SQL那样返回字段； <BR><BR>不支持联合、交叉（UNION/INTERSECT）； <BR><BR>不支持JOIN、IN和EXISTS； <BR><BR>企业应用探究 <BR><BR>由于JDO采用面向对象的持久化处理技术，从而为解决企业业务系统的持久化问题提供了一个新技术解决方案。但是先进的未必就最适用。在某些应用场景下，需要结合各种因素，采取灵活的策略。 <BR><BR>面向对象与面向记录 <BR><BR>现在大多业务系统都采用面向对象分析设计，这就需要每个应用系统都自行实现将对象映射成记录，并存储到数据库中，而有JDO这样的面向对象的持久化技术，在某种程度上解放了这种转化设计的不规范性，进而获得相对更优的系统结构。 <BR><BR>另一方面，一个业务系统的数据持久化，一般都有这样的过程：对象层－＞记录层－＞物理层，JDO无疑使分析设计人员从记录层的苦海中解脱出来，从而更加专注于对象层，这对开发人员无疑是一个令人欢欣鼓舞的技术。 <BR><BR>JDO并不能完全代替JDBC。 <BR><BR>根据经典的“8-2原理”，如果用JDO来解决80%的问题，余下的20%由JDBC来实现，这种相互补充，各取所长的策略，是一个很有效的办法。 <BR><BR>这样一方面可以获得较好的结构及提升开发质量，另一方面解决了JDO的某些技术不足，并可根据以后的技术变化，再做适当转化。 <BR><BR>性能问题 <BR><BR>JDO与JDBC究竟谁的性能更优，目前还没有一个权威、且令人满意的答案。但对于一些JDO实现而言，通过采用缓存机制，使得性能有了较大提高。 <BR><BR>跨数据库 <BR><BR>使用JDO的系统能更好地进行数据库移植，甚至可以在对象数据库上运行；当然JDBC处理层如果完全遵循SQL-92标准，也同样具有很好的跨数据库能力。 <BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 13:53 <a href="http://www.blogjava.net/qq13367612/articles/18130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2ME 走向成熟</title><link>http://www.blogjava.net/qq13367612/articles/17256.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 11:58:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/17256.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17256.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/17256.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17256.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17256.html</trackback:ping><description><![CDATA[<BLOCKQUOTE>&nbsp;</BLOCKQUOTE>
<P>J2ME (Java 2, Micro Edition) 於 1999 年 6 月由 Sun Microsystems 第一次推向 Java 團體，它是一項能滿足 Java 開發人員的不同需求的廣泛倡議的一部分。 在 Java 2 平台下，Sun 重新定義了 Java 技術的架構，將其分為三個版本。標準版 (J2SE) 為桌面開發和低階商務應用提供了可行的解決方案。企業版 (J2EE) 是為導向以企業為環境而開發應用程式的專門開發人員而準備的。而小型版是致力於消費產品和嵌入式設備開發人員的最佳選擇。儘管早期人們對它看好而且 Java 開發人員團體中的熱衷人士也不少，然而，J2ME 最近才開始從其影響更大的同屬產品 J2EE 和 J2SE 的陰影中走出其不成熟期。</P>
<P>J2ME 的嶄露頭角對 Sun，對跨通訊產業、資訊產業和消費類電子產品業的公司，還有對 Java 開發人員來說的確是個好訊息。Java 技術將一大批設備（從伺服器到主機式和移動設備）集中到一種語言和一種技術之下。雖然這些設備的應用不同，但 Java 技術為這些不同點建起了一座橋樑，使原本致力於單一領域的開發人員能將其技能發揮到跨越不同設備和應用的領域。</P>
<P>如果您是初次接觸 J2ME，您會驚奇地發現 J2ME 沒有技術規範。這是因為 J2ME 不是一個單獨的技術規範，而是相關技術規範的一個家族，這些規範定義了 Java 技術在資源限制的設備（即能源消耗少於普通主機式的設備）中的形態。</P>
<P>在本文中，我們將討論今日的 J2ME。我會講述定義 J2ME 目前結構的各個元件，並為那些對是否使用 J2ME 持觀望態度的使用者提供該技術近期發展的概覽。我還將為您提供針對嵌入式設備的 Java 平台的二種早期實現方法：KJava 和 PersonalJava 的最新狀況。我們首先講述 J2ME 目前的二項順序原則：設定和簡檔。</P>
<P><A name=1><SPAN class=atitle><FONT face=Arial size=4>了解 J2ME</FONT></SPAN></A></P>
<P>首先考慮一下可能用到 J2ME 的各類設備。這類設備包括 PDA、蜂巢式電話和 B.B Call 、電視機機上盒、遠端遙控裝置和許多其他嵌入式設備。很明顯，要為所有這些設備定義一種最最佳化，或者接近最最佳化的單一技術是不可能的。處理器能源、記憶體、固定記憶體和使用者介面的差異非常之大。</P>
<P>為解決這一問題，Sun 將適合 J2ME 的設備的定義劃分成各個部分，然後再進一步細分。在第一步的劃分中，Sun 將各種設備按照處理效能、記憶體和儲存能力劃分成兩大類，此時並不考慮使用目的。公司然後定義 Java 語言的一個剝離版本，它能夠在每一類設備的限制下工作，而同時提供最低限度的 Java 語言功能性。</P>
<P>然後，Sun 在這二個種類別中找出功能類似的設備類 -- 比如說所有的蜂巢電話無論哪個生產廠商都歸為一類。透過 Java Community Process 中的合作夥伴的協助，Sun 然後再針對每個縱向分類定義了附加的功能性。</P>
<P>第一部分建立 J2ME 目前的二種設定：連接設備設定 (CDC) 和連接限制設備設定 (CLDC)。 <I>設定</I>是 Java 的虛擬機 (JVM) 和為選擇的一組設備提供執行環境的最小類別庫集和 API。設定指定了 Java 語言的最小公分母子集，它符合為其而開發的設備家族所強制的資源限制。 </P>
<P>由於使用者介面、功能和用途中的這種極大差異，甚至在同一設定中，一個典型的設定不能像使用者介面工具套件和固定儲存 API 那樣定義這樣的重要片斷。而該功能性的定義被稱為簡檔。</P>
<P>J2ME <I>簡檔</I>是由從事例如 B.B Call 或蜂巢電話的特定種類設備的某業界領先集團指定的一系列 Java API。每個簡檔建立在由其設定提供的 Java 語言的最小共分母子集的頂部，並補充該設定。目前有二個簡檔：補充 CDC 的基礎簡檔和補充 CLDC 的移動資訊設備簡檔 (MIDP)。更多的簡檔正處於開發階段，規範和參考實現方法即將出台。 </P>
<P>圖 1 說明了 J2ME 及其設定和簡檔與 J2SE 和 J2EE 之間的關係。</P><BR><A name=N1006B><B>圖 1. J2EE、J2SE 和 J2ME 之間的關係 </B></A><BR><IMG height=288 alt="J2EE、J2SE 和 J2ME 之間的關係" src="http://www-128.ibm.com/developerworks/tw/library/j-j2me/fig1.gif" width=512> <BR>
<P>如上所述，J2ME 不是單一的規範而是一系列規範，每一項適用於特定的一系列要求。在下文中，我將講述在 Java 2 平台下每一項規範和它與其他規範之間的關係。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR>
<TR>
<TD><IMG height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/tw/library/j-j2me/#main"><B><FONT color=#996699>回到頂端</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=2><SPAN class=atitle><FONT face=Arial size=4>CLDC: 512 KB 以下的應用程式</FONT></SPAN></A></P>
<P>讓我們首先來分析一下較小的二個設定。根據其規範，CLDC 服務於具有 512 KB 以下記憶體、有限能源供應（通常使用電池）、有限或非持續網路連接和簡單（或無）使用者介面的設備。這是最適合用來建立蜂巢電話、 B.B Call 、PDA 和類似設備的設定。</P>
<P>為了使 CLDC 適應如此嚴格的限制，開發人員不得不放棄 J2SE 中的許多功能。實際上，當完成設計後，CLDC 只包括四個套件：其中三個來自標準 Java 規範（java.lang、java.util 和 java.io），另一個專門針對 CLDC (javax.microedition)。</P>
<P>甚至這三個標準套件的內容也被縮減了。在 J2SE 中包含 47 個類別和介面的 java.util 包在 CLDC 中縮減至 10 個類。那些被保留的類的功能性足以建構應用程式 -- 省略的功能性由 MIDP 提供，我將在下文中講述。</P>
<P>表 1 羅列了類的數量，以及每個 CLDC 包的介面，讓您清楚地了解 CLDC 有多小。</P>
<P><A name=table1><SPAN class=smalltitle><STRONG><FONT face=Arial>表 1. 每個 CLDC 套件中類別和介面的數量</FONT></STRONG></SPAN></A></P>
<P>
<TABLE>
<TBODY>
<TR>
<TD><STRONG>套件</STRONG> </TD>
<TD><B>描述</B> </TD>
<TD><B>類別和介面</B> </TD></TR>
<TR>
<TD>java.io</TD>
<TD>系統輸入輸出</TD>
<TD>18</TD></TR>
<TR>
<TD>java.lang</TD>
<TD>Java 程式設計語言的基本類</TD>
<TD>38</TD></TR>
<TR>
<TD>java.util</TD>
<TD>集合、日期和時間支援、各式實用工具類別</TD>
<TD>10</TD></TR>
<TR>
<TD>javax.microedition</TD>
<TD>類屬連接</TD>
<TD>10</TD></TR></TBODY></TABLE></P>
<P>CLDC 不需要一致的實現方法來支援例如反映、結束、使用者定義的類裝載器或浮點演算法等功能，這毫無價值。然而，CLDC 並不能提供建構有用應用程式的所有功能性。很明顯，它缺少使用者介面這一大多數應用程式必需的功能。CLDC 從來就不是一個完整的解決方案。它的設計只是個開頭，是可建立提供附加功能性和定位特定產品類的簡檔的通用基礎。MIDP 就是這樣一個簡檔。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR>
<TR>
<TD><IMG height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/tw/library/j-j2me/#main"><B><FONT color=#996699>回到頂端</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=3><SPAN class=atitle><FONT face=Arial size=4>已定義的 MIDP -- 那麼 MIDlet 又是什麼呢？</FONT></SPAN></A></P>
<P>MIDP 為 CLDC 加入建立諸如蜂巢電話、 B.B Call 和簡單 PDA 應用程式必需的功能性。MIDP 的功能性包括支援計時器、簡單固定記憶體、透過 HTTP 的連網和使用者介面。</P>
<P>CLDC 中 3 項丟失的類別已加入到 java.lang 和 java.util 包中，使 MIDP 支援計時器。這些類別是：</P>
<UL>
<LI>java.util.Timer 
<LI>java.util.TimerTask 
<LI>java.lang.IllegalStateException </LI></UL>
<P>所有 MIDP 剩餘的功能包含在 J2ME 中唯一的四個套件中。表 2 羅列了這四個剩餘的套件，以及包的描述和包中含有的類別和介面。</P>
<P><A name=N100FC><SPAN class=smalltitle><STRONG><FONT face=Arial>表 2. 組成 MIDP 的 4 個 javax.microedition 包</FONT></STRONG></SPAN></A></P>
<P>
<TABLE border=0>
<TBODY>
<TR>
<TD><STRONG>包</STRONG> </TD>
<TD><B>描述</B> </TD>
<TD><B>類別和介面</B> </TD></TR>
<TR>
<TD>javax.microedition.rms</TD>
<TD>固定記錄儲存</TD>
<TD>10</TD></TR>
<TR>
<TD>javax.microedition.midlet</TD>
<TD>MIDlet 和其環境之間的介面</TD>
<TD>2</TD></TR>
<TR>
<TD>javax.microedition.io</TD>
<TD>支援 HTTP 協定</TD>
<TD>1</TD></TR>
<TR>
<TD>javax.microedition.lcdui</TD>
<TD>使用者介面工具套件</TD>
<TD>24</TD></TR></TBODY></TABLE></P>
<P>javax.microedition.midlet 包保證了進一步的觀察，它為 Java 開發引入了一個新特性。 <I>MIDlet</I> 這一名稱提供給寫入 MIDP 規範的應用程式。所有這些應用必須適合 javax.microedition.midlet 包中定義的框架。 </P>
<P>MIDlet 框架類似 J2SE 提供的小應用程式框架。MIDlet 是相關類別的集合，其中一類別擴充了 <CODE>javax.microedition.midlet.MIDlet</CODE> 類。由 MIDlet 所在設備提供的 MIDlet 環境透過傳遞使其開始、暫停和自毀的事件來控制 MIDlet。圖 2 展示了 MIDlet 的生命週期。 </P><BR><A name=figure><B>圖 2. MIDlet 的生命週期 </B></A><BR><IMG height=281 alt="MIDlet 的生命週期" src="http://www-128.ibm.com/developerworks/tw/library/j-j2me/fig2.gif" width=349> <BR>
<P>請參閱 <A href="http://www-128.ibm.com/developerworks/tw/library/j-j2me/#resources"><FONT color=#996699>參考資料</FONT></A>了解更多 MIDlet 的相關內容。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR>
<TR>
<TD><IMG height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/tw/library/j-j2me/#main"><B><FONT color=#996699>回到頂端</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=4><SPAN class=atitle><FONT face=Arial size=4>CDC 補充 CLDC 留下的問題</FONT></SPAN></A></P>
<P>目前 2 個已定義的 J2ME 的設定的大部分是 CDC，它最適合用來建立電視機機上盒、娛樂系統、汽車導航系統和其他這類規模的應用程式。</P>
<P>在資源限制的條件下，CDC 補充了 CLCD 的不足，並針對大於 2 MB 記憶體的設備，它能支援標準 Java 虛擬機和 Java 程式設計語言的完整實現。簡而言之，CDC 非常接近您熟悉的 Java 規範。</P>
<P>當只需要相容 CLDC 的虛擬機來支援標準 Java 虛擬機功能性的一個子集時，那麼 CDC 指定的虛擬機必須和標準 JVM 特性相容。這意味著如果包括對本地方法呼叫的支援，CDC JVM （或 CVM）必須符合 JNI （Java 本地介面） 1.1。如果包括對調試的支援，那就必須符合 JVMDI （Java 虛擬機調試介面）。如果需要包括簡檔支援，那就必須遵從 JVMPI （Java 虛擬機簡檔介面）。</P>
<P>在類別庫中，CDC 提供支援全相容 Java 2 虛擬機所必需的最小 API 集。這一 API 集包括所有為 CLCD 定義的 API 和針對檔 I/O、連網、進階安全性、物件序列等 API。表 3 羅列了在 CDC 規範下提供的包、每個套件中的類別和介面的數量和每個包的描述。</P>
<P><A name=N10183><SPAN class=smalltitle><STRONG><FONT face=Arial>表 3. 在 CDC 規範下的所有套件</FONT></STRONG></SPAN></A></P>
<P>
<TABLE border=0>
<TBODY>
<TR>
<TD><STRONG>套件</STRONG> </TD>
<TD><B>描述</B> </TD>
<TD><B>類別和介面</B> </TD></TR>
<TR>
<TD>java.io</TD>
<TD>系統輸入與輸出</TD>
<TD>62</TD></TR>
<TR>
<TD>java.lang</TD>
<TD>Java 程式設計語言基本類</TD>
<TD>77</TD></TR>
<TR>
<TD>java.lang.ref</TD>
<TD>特別參考類別</TD>
<TD>5</TD></TR>
<TR>
<TD>java.lang.reflect</TD>
<TD>反映支援</TD>
<TD>12</TD></TR>
<TR>
<TD>java.math</TD>
<TD>Math 支援</TD>
<TD>1</TD></TR>
<TR>
<TD>java.net</TD>
<TD>網路類別和工具</TD>
<TD>23</TD></TR>
<TR>
<TD>java.security</TD>
<TD>安全支援</TD>
<TD>36</TD></TR>
<TR>
<TD>java.security.cert</TD>
<TD>憑證支援</TD>
<TD>4</TD></TR>
<TR>
<TD>java.text</TD>
<TD>文件處理類別</TD>
<TD>13</TD></TR>
<TR>
<TD>java.util</TD>
<TD>集合、日期和時間支援，各種實用工具類別</TD>
<TD>47</TD></TR>
<TR>
<TD>java.util.jar</TD>
<TD>Jar 檔支援</TD>
<TD>7</TD></TR>
<TR>
<TD>java.util.zip</TD>
<TD>Zip 檔支援</TD>
<TD>9</TD></TR>
<TR>
<TD>javax.microedition</TD>
<TD>類屬連接</TD>
<TD>10</TD></TR></TBODY></TABLE></P>
<P>很明顯，表 3 中缺少屬於 java.awt 套件中的類別和介面。與 CLDC 的情形相同，CDC 不支援任何使用者介面。這還是由於設備與設備之間的使用者介面差異很大所造成的。必須為 CDC 加入合適的簡檔以獲得使用者介面支援。</P>
<P>CVM 是指定和 CDC 一起使用的 Java 虛擬機，它具有許多超出支援 CDC 所必需的有趣功能。它的移植性很強、支援可 ROM 的類別、允許快速執行緒同步操作，並提供對本地執行緒的支援。簡而言之，它是為支援經常出現在嵌入式應用中的作業系統而設計的。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR>
<TR>
<TD><IMG height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/tw/library/j-j2me/#main"><B><FONT color=#996699>回到頂端</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=5><SPAN class=atitle><FONT face=Arial size=4>基礎簡檔</FONT></SPAN></A></P>
<P>基礎簡檔是唯一的為 CDC 而定義的簡檔。它大大地擴充了 CDC 提供的 API。然而，它並不提供使用者介面 API。正如其名“基礎”所示，該簡檔必須透過一個或多個提供使用者介面支援的附加簡檔來擴充 -- 例如個人簡檔 (JSR 62)，它正處於開發階段並有可能最終替代 PersonalJava 的簡檔。</P>
<P>表 4 羅列了基礎簡檔中的，也包括 CDC 中的所有類別和介面的數量。這一設定及其主要簡檔提供的所有類別和介面與我們習慣使用的 Java 平台類似。</P>
<P><A name=N10253><SPAN class=smalltitle><STRONG><FONT face=Arial>表 4. 基礎類別和介面（包括 CDC 套件）</FONT></STRONG></SPAN></A></P>
<P>
<TABLE>
<TBODY>
<TR>
<TD><STRONG>包</STRONG> </TD>
<TD><B>描述</B> </TD>
<TD><B>類別和介面</B> </TD></TR>
<TR>
<TD>java.io</TD>
<TD>系統輸入與輸出</TD>
<TD>74</TD></TR>
<TR>
<TD>java.lang</TD>
<TD>Java 程式設計語言基本類</TD>
<TD>79</TD></TR>
<TR>
<TD>java.lang.ref</TD>
<TD>特別參考類別</TD>
<TD>5</TD></TR>
<TR>
<TD>java.lang.reflect</TD>
<TD>反映支援</TD>
<TD>12</TD></TR>
<TR>
<TD>java.math</TD>
<TD>Math 支援</TD>
<TD>1</TD></TR>
<TR>
<TD>java.net</TD>
<TD>網路類別和工具</TD>
<TD>35</TD></TR>
<TR>
<TD>java.security</TD>
<TD>安全支援</TD>
<TD>63</TD></TR>
<TR>
<TD>java.security.acl</TD>
<TD>存取控制表支援</TD>
<TD>8</TD></TR>
<TR>
<TD>java.security.cert</TD>
<TD>憑證支援</TD>
<TD>15</TD></TR>
<TR>
<TD>java.security.interface</TD>
<TD>安全介面類</TD>
<TD>9</TD></TR>
<TR>
<TD>java.security.spec</TD>
<TD>關鍵規範和運算法則參數規範</TD>
<TD>14</TD></TR>
<TR>
<TD>java.text</TD>
<TD>文件處理類別</TD>
<TD>23</TD></TR>
<TR>
<TD>java.util</TD>
<TD>收集、日期和時間支援，各種實用工具類別</TD>
<TD>54</TD></TR>
<TR>
<TD>java.util.jar</TD>
<TD>Jar 檔支援</TD>
<TD>8</TD></TR>
<TR>
<TD>java.util.zip</TD>
<TD>Zip 檔支援</TD>
<TD>17</TD></TR>
<TR>
<TD>javax.microedition</TD>
<TD>類屬連接</TD>
<TD>11</TD></TR></TBODY></TABLE></P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR>
<TR>
<TD><IMG height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/tw/library/j-j2me/#main"><B><FONT color=#996699>回到頂端</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=6><SPAN class=atitle><FONT face=Arial size=4>舊的規範：KJava 和 PersonalJava 重新露面</FONT></SPAN></A></P>
<P>如果近兩年一直關注著 Sun 對資源限制設備的支援，那麼您一定對 KJava 和 PersonalJava 很了解。PersonalJava 是 Sun 首次為資源限制設備建立 Java 平台版本的嘗試。Sun 的 Spotless 研究計畫產生的 KJava 是 Sun 在 1999 年 JavaOne 中為 Palm 而開發的 Java 平台版本。</P>
<P>由於為 PersonalJava 和 KJava 撰寫的程式很多，許多開發人員非常關切這兩種平台未來的發展如何。因為它們各自都獨立地發展，所以關於這兩種平台的資訊並不多，但我會告訴您我所知道的資訊。</P>
<P>PersonalJava 正逐漸轉變成 CDC 的個人簡檔。尚處於開發階段的這一簡檔將處於基礎簡檔的頂部，並且將與 PersonalJava 版本 1.1 和 1.2 向後相容。</P>
<P>KJava 的情形就不十分明朗。Sun 非常關注 KJava 的實驗狀態，並提醒開發人員注意它對 API 的不斷變化，甚至重大變化。Palm Inc. 目前正為 CLDC 定義 PDA 簡檔，它將取代 KJava。不幸的是，現在尚無法明確地知道要對現有的 KJava 應用程式做出多少修改才能使其與新簡檔協同工作。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR>
<TR>
<TD><IMG height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/tw/library/j-j2me/#main"><B><FONT color=#996699>回到頂端</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=7><SPAN class=atitle><FONT face=Arial size=4>總結</FONT></SPAN></A></P>
<P>J2ME 的前景很明顯要比 J2SE 甚至 J2EE 來的複雜，但是複雜性也預告著 J2ME 能夠支援的設備的多樣性。總之，以我的方法論習一種特定的設定和簡檔並不很困難。許多情況下，J2ME 讓我想起 1995 年的 Java 語言。</P>
<P>如果試圖決定是否在嵌入式應用程式中使用 J2ME，您會發現有許多無法回答的問題。例如，我仍然不清楚何時 J2ME 的優勢能超越它的成本，特別是鑒於虛擬機和 API 在已經資源限制的環境下使用的資源。</P>
<P>然而，隨著規範的成型和更多簡檔的加入，我確信答案會出現，而且 J2ME 將鞏固其在嵌入式設備領域的地位。透過 CDC 和 CLDC，Sun 已採取重要的步驟為該領域的開發人員的各種需求提供各種重要的服務。基礎簡檔剛開始鞏固，個人簡檔將在幾個月內出台。作為開發人員，我們將與 J2ME 一同發展，如果僅是實驗性的，並且使 Sun 以及其他在無線和嵌入設備市場的商家明確我們的需求。</P><img src ="http://www.blogjava.net/qq13367612/aggbug/17256.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 19:58 <a href="http://www.blogjava.net/qq13367612/articles/17256.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是jvm？</title><link>http://www.blogjava.net/qq13367612/articles/17255.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 11:56:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/17255.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17255.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/17255.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17255.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17255.html</trackback:ping><description><![CDATA[在Java中引入了虚拟机的概念，即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只需要面向虚拟机，生成虚拟机能够理解的代码，然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中，这种供虚拟机理解的代码叫做字节码（ByteCode），它不面向任何特定的处理器，只面向虚拟机。每一种平台的解释器是不同的，但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码，字节码由虚拟机解释执行，虚拟机将每一条要执行的字节码送给解释器，解释器将其翻译成特定机器上的机器码，然后在特定的机器上运行。<BR>&nbsp;&nbsp;&nbsp;&nbsp;可以说，Java虚拟机是Java语言的基础。它是Java技术的重要组成部分。Java虚拟机是一个抽象的计算机，和实际的计算机一样，它具有一个指令集并使用不同的存储区域。它负责执行指令，还要管理数据、内存和寄存器。Java解释器负责将字节代码翻译成特定机器的机器代码。Java是一种简单的语言。它用到的概念不多，而且多为程序员所熟悉。如果你是一名程序员，掌握Java对你来说是易如反掌的事。即使你没有学过任何编程语言，学习Java也要比学习C++要容易的多。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;由于Java最初是为控制电子产品设计的，因此它必须简单明了。为了保证这种简单性，Java去掉了C++中许多复杂的、冗余的、有二义性的概念，例如操作符重载、多继承、数据类型自动转换等。为了将程序员从复杂的内存管理的负担中解脱出来，同时也是为了减少错误，Java使用了自动内存垃圾收集机制，程序员只要在需要的时候申请即可，不需要释放,而由Java自己来收集、释放内存中的无用的块。<BR>&nbsp;&nbsp;&nbsp;&nbsp;与C++相比，Java有着更强的面向对象特性，是一种比较纯粹的面向对象语言。一般我们使用的一些所谓的面向对象的编程语言，如C++，Object Pascal等，实际上都是一种混合型的语言，即在过程式的语言中加上面向对象的扩展。在Java中，几乎万物皆对象，就连一些基本数据类型，如整型、字符型、浮点型等，在Java中都可以作为对象处理。Java的面向对象特性几乎可以与Smalltalk媲美，但是其适用于分布式计算环境的特性却远远超过了Smalltalk。<BR>&nbsp;&nbsp; Java是一种支持分布式操作的程序设计语言。使用Java提供的URL类，用户可以象访问本地文件一样访问网络上的对象，使用非常方便。在客户机/服务器的模式下，Java还可以将运算从服务器端分散到客户端，提高系统的效率，避免了服务器的瓶颈制约。Java的网络类库支持分布式的编程。Socket类提供可靠的流式网络的连接，支持TCP/IP协议。通过编写协议句柄，程序员还可以扩充Java支持的协议集合。<BR><BR>Java提供非常有效的安全控制。由于Java应用于网络程序的开发，因而安全性变的至关重要。因为Java小程序需要下载到客户端解释执行，所以，如果没有安全控制，就会给一些网络黑客以可乘之机，这对用户来说是非常危险的。所幸的是，Java的安全机制可以有效的防止病毒程序的产生、下载程序对本地文件系统的破坏，以及网络黑客窃取密码和入侵。<BR><BR>Java是一种非常健壮的语言。因为在Java中使用了以下手段：<BR><BR>不支持指针。在C++程序中，指针的错误使用通常的程序中BUG的元凶。在Java中彻底去掉了指针，杜绝了内存的非法访问，从而保证了程序的可靠性。<BR>强类型语言。<BR><BR>自动内存垃圾收集机制。Java自动收集无用的内存单元，进而防止了由于内存泄漏导致的动态内存分配问题。<BR><BR>完善的异常处理机制，既简化了错误处理任务和恢复，也增加了程序的可读性。<BR><BR>Java具有非常好的平台无关性和可移植性。因为Java最初是为对电子产品编程而设计的，所以它具有完美的平台无关性。它使用一种与平台无关的代码──字节码，而不是通常的特定机器上的机器码，由平台上的Java虚拟机中的Java解释器解释执行。Java虚拟机是免费的，在许多平台上都有。<BR><BR>Java提供了良好的可移植性。使用Java作为编程语言，只要进行一次程序开发工作，所开发的程序不需要经过任何改动，便能在各种平台上运行。Java使用两种方法使Java的应用程序不依赖与具体的系统：<BR><BR>采用基于国际标准的数据类型。Java的原始数据类型在任何机器上都是一样的，例如整型总是32位，长整型总是64位等。<BR><BR>提供了一个用于访问底层操作系统功能的可扩展类库。<BR><BR>Java是一种高性能的语言。“鱼与熊掌不可兼得”，通常，健壮性、安全性、平台无关性、可移植性等方面的提高总是要以牺牲性能为代价的。Java也不例外，Java的内存管理增加了运行时系统的复杂性，因为Java运行时系统必须内嵌一个内存管理模块；同样，Java程序的解释执行的效率也要低于直接执行编译后的源码的效率。但是Java采用了一些很好的措施来弥补这些性能上的差距：<BR><BR>生成高效的字节码。Java字节码的设计充分考虑了性能的因素，字节码的格式简单，解释器可以生成高效的机器码。<BR><BR>提供了即时编译和嵌入C代码的可选措施。即时编译是指在运行时把字节码编译成机器码。支持多线程。Java提供了对多线程的语言级的接口，而且Java环境本身就是多线程的。<BR><BR><BR>Java对多线程有良好的支持。多线程技术可以提高程序执行的并发度，提高图形用户界面的交互性能。Java提供了语言内置的多线程控制，简化了多线程应用程序的开发，还支持线程的同步控制。<BR><BR>Java是一种动态的语言。动态特性是面向对象特性的一个延伸，它使得程序能够适应不断变化的执行环境。Java的动态性主要表现在以下几个方面：<BR><BR>Java的类有运行时的表示，这样，即使在运行时刻，程序也能辨别类之间的关系和类型信息，可以动态的从本地或网上把一个类链接到运行系统中去。<BR><BR>后期联编。Java的类在运行过程中动态的装载，因此，Java可以在分布式的环境中动态的维护应用程序和Java类库之间的一致性。当类库升级后，应用程序无需重新编译，也一样可以利用新类库中新增的功能。<BR><BR>支持动态数据类型和动态协议。通过编写协议句柄，Java可以支持新的、自定义的传输协议，编写内容句柄，可以支持新的数据类型。<BR><BR>至于应用，就不必说了！<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/17255.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 19:56 <a href="http://www.blogjava.net/qq13367612/articles/17255.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机类装载：原理、实现与应用</title><link>http://www.blogjava.net/qq13367612/articles/17253.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 11:55:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/17253.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17253.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/17253.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17253.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17253.html</trackback:ping><description><![CDATA[<STRONG>一、引言</STRONG> 
<P>　　Java虚拟机(JVM)的类装载就是指将包含在类文件中的字节码装载到JVM中, 并使其成为JVM一部分的过程。JVM的类动态装载技术能够在运行时刻动态地加载或者替换系统的某些功能模块, 而不影响系统其他功能模块的正常运行。本文将分析JVM中的类装载系统，探讨JVM中类装载的原理、实现以及应用。</P>
<P>　　<STRONG>二、Java虚拟机的类装载实现与应用</STRONG></P>
<P>　　2.1&nbsp; 装载过程简介</P>
<P>　　所谓装载就是寻找一个类或是一个接口的二进制形式并用该二进制形式来构造代表这个类或是这个接口的class对象的过程，其中类或接口的名称是给定了的。当然名称也可以通过计算得到，但是更常见的是通过搜索源代码经过编译器编译后所得到的二进制形式来构造。</P>
<P>　　在Java中，类装载器把一个类装入Java虚拟机中，要经过三个步骤来完成：装载、链接和初始化，其中链接又可以分成校验、准备和解析三步，除了解析外，其它步骤是严格按照顺序完成的，各个步骤的主要工作如下：</P>
<P>　　装载：查找和导入类或接口的二进制数据； <BR>　　链接：执行下面的校验、准备和解析步骤，其中解析步骤是可以选择的； <BR>　　校验：检查导入类或接口的二进制数据的正确性； <BR>　　准备：给类的静态变量分配并初始化存储空间； <BR>　　解析：将符号引用转成直接引用； <BR>　　初始化：激活类的静态变量的初始化Java代码和静态Java代码块。 </P>
<P>　　至于在类装载和虚拟机启动的过程中的具体细节和可能会抛出的错误，请参看《Java虚拟机规范》以及《深入Java虚拟机》，它们在网络上面的资源地址是：<BR>　　<A href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html" target=_blank>http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html</A><BR>　　<A href="http://www.artima.com/insidejvm/ed2/index.html" target=_blank>http://www.artima.com/insidejvm/ed2/index.html</A><BR>　　由于本文的讨论重点不在此就不再多叙述。</P>
<P>　　2.2&nbsp; 装载的实现</P>
<P>　　JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。</P>
<P>　　在Java中，ClassLoader是一个抽象类，它在包java.lang中,可以这样说，只要了解了在ClassLoader中的一些重要的方法，再结合上面所介绍的JVM中类装载的具体的过程，对动态装载类这项技术就有了一个比较大概的掌握，这些重要的方法包括以下几个:</P>
<P>　　①loadCass方法&nbsp; loadClass(String name ,boolean resolve)其中name参数指定了JVM需要的类的名称,该名称以包表示法表示,如Java.lang.Object；resolve参数告诉方法是否需要解析类，在初始化类之前,应考虑类解析，并不是所有的类都需要解析，如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要解析。这个方法是ClassLoader 的入口点。</P>
<P>　　②defineClass方法&nbsp; 这个方法接受类文件的字节数组并把它转换成Class对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。</P>
<P>　　③findSystemClass方法&nbsp; findSystemClass方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass将字节数组转换成Class对象,以将该文件转换成类。当运行Java应用程序时,这是JVM 正常装入类的缺省机制。</P>
<P>　　④resolveClass方法&nbsp; resolveClass(Class c)方法解析装入的类,如果该类已经被解析过那么将不做处理。当调用loadClass方法时,通过它的resolve 参数决定是否要进行解析。</P>
<P>　　⑤findLoadedClass方法&nbsp; 当调用loadClass方法装入类时,调用findLoadedClass 方法来查看ClassLoader是否已装入这个类,如果已装入,那么返回Class对象,否则返回NULL。如果强行装载已存在的类,将会抛出链接错误。</P>
<P>　　2.3&nbsp; 装载的应用</P>
<P>　　一般来说，我们使用虚拟机的类装载时需要继承抽象类java.lang.ClassLoader,其中必须实现的方法是loadClass()，对于这个方法需要实现如下操作:(1) 确认类的名称;(2) 检查请求要装载的类是否已经被装载;(3) 检查请求加载的类是否是系统类;(4) 尝试从类装载器的存储区获取所请求的类;(5) 在虚拟机中定义所请求的类;(6) 解析所请求的类;(7) 返回所请求的类。</P>
<P>　　所有的Java 虚拟机都包括一个内置的类装载器，这个内置的类库装载器被称为根装载器(bootstrap ClassLoader)。根装载器的特殊之处是它只能够装载在设计时刻已知的类,因此虚拟机假定由根装载器所装载的类都是安全的、可信任的,可以不经过安全认证而直接运行。当应用程序需要加载并不是设计时就知道的类时,必须使用用户自定义的装载器(user-defined ClassLoader)。下面我们举例说明它的应用。</P>
<P>public abstract class MultiClassLoader extends ClassLoader{<BR>&nbsp;&nbsp;&nbsp; ...<BR>&nbsp;&nbsp;&nbsp; public synchronized Class loadClass(String s, boolean flag)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws ClassNotFoundException<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 检查类s是否已经在本地内存*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class class1 = (Class)classes.get(s);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 类s已经在本地内存*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(class1 != null)&nbsp; return class1; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try/*用默认的ClassLoader 装入类*/&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class1 = super.findSystemClass(s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return class1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(ClassNotFoundException _ex)&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("&gt;&gt; Not a system class.");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 取得类s的字节数组*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte abyte0[] = loadClassBytes(s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(abyte0 == null)&nbsp;&nbsp; throw new ClassNotFoundException();</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 将类字节数组转换为类*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class1 = defineClass(null, abyte0, 0, abyte0.length);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(class1 == null) throw new ClassFormatError();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(flag)&nbsp;&nbsp; resolveClass(class1); /*解析类*/</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 将新加载的类放入本地内存*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; classes.put(s, class1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("&gt;&gt; Returning newly loaded class.");</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 返回已装载、解析的类*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return class1;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; ...<BR>}</P>
<P>　　<STRONG>三、Java虚拟机的类装载原理</STRONG></P>
<P>　　前面我们已经知道，一个Java应用程序使用两种类型的类装载器：根装载器(bootstrap)和用户定义的装载器(user-defined)。根装载器是Java虚拟机实现的一部分，举个例子来说，如果一个Java虚拟机是在现在已经存在并且正在被使用的操作系统的顶部用C程序来实现的，那么根装载器将是那些C程序的一部分。根装载器以某种默认的方式将类装入，包括那些Java API的类。在运行期间一个Java程序能安装用户自己定义的类装载器。根装载器是虚拟机固有的一部分，而用户定义的类装载器则不是，它是用Java语言写的，被编译成class文件之后然后再被装入到虚拟机，并像其它的任何对象一样可以被实例化。 Java类装载器的体系结构如下所示：</P>
<P align=center><A href="http://www.54bk.com/uploadfile/20051011113621858.gif" target=_blank><IMG title=点击在新窗口查看原始图片 style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="http://www.54bk.com/uploadfile/20051011113621858.gif" onload="java_script_:if(this.width>500)this.width=500" border=0></A></P>
<P align=center>图1&nbsp; Java的类装载的体系结构</P>
<P>　　Java的类装载模型是一种代理(delegation)模型。当JVM 要求类装载器CL(ClassLoader)装载一个类时,CL首先将这个类装载请求转发给他的父装载器。只有当父装载器没有装载并无法装载这个类时,CL才获得装载这个类的机会。这样, 所有类装载器的代理关系构成了一种树状的关系。树的根是类的根装载器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根装载器以外的类装载器有且仅有一个父装载器。在创建一个装载器时, 如果没有显式地给出父装载器, 那么JVM将默认系统装载器为其父装载器。Java的基本类装载器代理结构如图2所示：</P>
<P align=center><A href="http://www.54bk.com/uploadfile/20051011113639543.gif" target=_blank><IMG title=点击在新窗口查看原始图片 style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="http://www.54bk.com/uploadfile/20051011113639543.gif" onload="java_script_:if(this.width>500)this.width=500" border=0></A></P>
<P align=center>图2&nbsp; Java类装载的代理结构</P>
<P>　　下面针对各种类装载器分别进行详细的说明。 </P>
<P>　　根(Bootstrap) 装载器:该装载器没有父装载器，它是JVM实现的一部分，从sun.boot.class.path装载运行时库的核心代码。 </P>
<P>　　扩展(Extension) 装载器:继承的父装载器为根装载器，不像根装载器可能与运行时的操作系统有关，这个类装载器是用纯Java代码实现的，它从java.ext.dirs (扩展目录)中装载代码。 </P>
<P>　　系统(System or Application) 装载器:装载器为扩展装载器，我们都知道在安装JDK的时候要设置环境变量(CLASSPATH )，这个类装载器就是从java.class.path(CLASSPATH 环境变量)中装载代码的，它也是用纯Java代码实现的，同时还是用户自定义类装载器的缺省父装载器。 </P>
<P>　　小应用程序(Applet) 装载器: 装载器为系统装载器，它从用户指定的网络上的特定目录装载小应用程序代码。 </P>
<P>　　在设计一个类装载器的时候，应该满足以下两个条件：</P>
<P>　　对于相同的类名，类装载器所返回的对象应该是同一个类对象 </P>
<P>　　如果类装载器CL1将装载类C的请求转给类装载器CL2，那么对于以下的类或接口,CL1和CL2应该返回同一个类对象:a)S为C的直接超类;b)S为C的直接超接口;c)S为C的成员变量的类型;d)S为C的成员方法或构建器的参数类型;e)S为C的成员方法的返回类型。 <BR>每个已经装载到JVM中的类都隐式含有装载它的类装载器的信息。类方法getClassLoader 可以得到装载这个类的类装载器。一个类装载器认识的类包括它的父装载器认识的类和它自己装载的类，可见类装载器认识的类是它自己装载的类的超集。注意我们可以得到类装载器的有关的信息，但是已经装载到JVM中的类是不能更改它的类装载器的。 </P>
<P>　　Java中的类的装载过程也就是代理装载的过程。比如:Web浏览器中的JVM需要装载一个小应用程序TestApplet。JVM调用小应用程序装载器ACL(Applet ClassLoader)来完成装载。ACL首先请求它的父装载器, 即系统装载器装载TestApplet是否装载了这个类, 由于TestApplet不在系统装载器的装载路径中, 所以系统装载器没有找到这个类, 也就没有装载成功。接着ACL自己装载TestApplet。ACL通过网络成功地找到了TestApplet.class 文件并将它导入到了JVM中。在装载过程中, JVM发现TestAppet是从超类java.applet.Applet继承的。所以JVM再次调用ACL来装载java.applet.Applet类。ACL又再次按上面的顺序装载Applet类, 结果ACL发现他的父装载器已经装载了这个类, 所以ACL就直接将这个已经装载的类返回给了JVM , 完成了Applet类的装载。接下来,Applet类的超类也一样处理。最后, TestApplet及所有有关的类都装载到了JVM中。</P>
<P>　　<STRONG>四、结论</STRONG></P>
<P>　　类的动态装载机制是JVM的一项核心技术, 也是容易被忽视而引起很多误解的地方。本文介绍了JVM中类装载的原理、实现以及应用，尤其分析了ClassLoader的结构、用途以及如何利用自定义的ClassLoader装载并执行Java类，希望能使读者对JVM中的类装载有一个比较深入的理解。</P><img src ="http://www.blogjava.net/qq13367612/aggbug/17253.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 19:55 <a href="http://www.blogjava.net/qq13367612/articles/17253.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机的性能</title><link>http://www.blogjava.net/qq13367612/articles/17250.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 11:52:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/17250.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17250.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/17250.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17250.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17250.html</trackback:ping><description><![CDATA[由于在当前主机操作系统上加上了Java虚拟机层，Java字节码执行速度目前要比本地机器慢10到20倍。速度问题可能是影响Java推广的唯一原因。影响Java字节码执行速度的原因很多： <BR><BR>（1）首先，验证过程要花费时间，读<SPAN id=comm_art_center><!-- Copyright 1999-2000 ThruPort Technologies http://www.thruport.com --><!-- end dynamic banner insert --></SPAN>入的类要在运行时验证，而传统程序在程序编译时即完成验证工作。 <BR><BR>（2）其次，Java指令都是字节码。由于大多数操作对象超过一个字节长，因此必须读多个字节码来取得操作符和不同操作数。 <BR><BR>（3）再次，由于Java完全采用堆栈机理，运算操作都在堆栈上执行。而传统编译器在编译时进行多种优化工作，很多计算操作可直接在寄存器中完成，大大提高程序执行速度。 <BR><BR>（4）最后，在程序执行期间，系统要进行无用内存单元回收工作，在回收过程中，程序将停止执行，这无疑也会影响性能。以上只列出了四条主要的原因，还有其他一些因素综合作用影响着Java虚拟机的性能。 <BR><BR>目前，针对Java虚拟机程序速度慢的问题，几家主要的公司（如Microsoft,Borland等）提出了及时编译技术（Just-In-Time）JIT。具体说，就是JIT编译器在程序开始执行前把所有字节码翻译成本地机器码，然后再将翻译后的机器码放在CPU上运行。这样，我们就用字节码编译器代替了原来的解释器，它对用户透明而又提高了执行速度。据称，Borland公司工发的JITJavaAppAccelerator能使应用程序运行速度比解释执行快5到10倍。也许将来JIT编译器会加到Java虚拟机中，从而解决所有性能问题。 <img src ="http://www.blogjava.net/qq13367612/aggbug/17250.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 19:52 <a href="http://www.blogjava.net/qq13367612/articles/17250.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java本纪之Java虚拟机的10年</title><link>http://www.blogjava.net/qq13367612/articles/17249.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 11:51:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/17249.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17249.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/17249.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17249.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17249.html</trackback:ping><description><![CDATA[<P>Java虚拟机的起源与构造</P>
<P>　　当我们说到“Java”这个词的时候，指的是四个相互关联的概念：Java语言、Java API、Java Class文件格式、Java虚拟机。整个Java体系是基于Java 虚拟机构造的，正因为如此，才能实现Java的安全性和网络移动性。Java并非是第一个采用“虚拟机”概念的体系，但却是第一个得到广泛运用的虚拟机平台。 “虚拟”，是一种隔离物理资源与逻辑资源的手段。Java虚拟机的“虚拟”，则是用来隔离物理机器、底层操作系统与Java语言规范实现的手段。</P>
<P>　　虽然Java是一种面向对象的语言，我们平时大量使用的，是对象间的多态、组合(Composition)、委派（Delegation）,但当我们讨论虚拟机的时候，我们看见的基本概念却是“栈(Stack)”和“堆(Heap)”。根据冯诺依曼的“存储计算”模型，所有的代码都保存在代码空间中，随着程序计数器指针的变化进行程序的执行、跳转。Java虚拟机中没有寄存器的概念，方法调用是采用“栈”进行的，这是一种安全、简洁的方法。</P>
<P>　　Java虚拟机通过类装载器支持对类的隔离，这也是Java实现安全性的基础。每个类都具有自己的命名空间，在具有不同安全级别的沙箱中运行，因此不会产生低安全级别的代码来越权访问高级别代码的机会。类装载器的出现是Java虚拟机与大部分用C实现的虚拟机的显著不同之处。</P>
<P>　　Java虚拟机的另外一个显著特点就是实现了自动的垃圾收集。在往常，写程序的时候要牢记对象之间的关联，在每个程序块中假若申请了对象空间，就必须在出口释放掉，方法调用往往同时也就是对象的边界。而自动垃圾收集带给开发者的最大好处，就是可以非常方便地从整体上把系统的对象组织成一张对象图，只需往这张图中添加对象，维护对象之间的关联，却不需要自己做复杂的清扫工作。正是有了这种思维单纯的对象图的支持，OR Mapping(关系数据库与对象映射）技术在最近得以大行其道，设计模式也更容易被Java群体所接受。<BR>　　虚拟机的优化</P>
<P>　　1995年第一代的Java出台之时，其虚拟机执行是依靠“字节码解释器（Byte Code Interceptor）”的，也就是说每条指令都由虚拟机来当场解释执行，这造成速度令人抓狂地缓慢。更有甚者有人开始总结许多的“速度优化经验”，比如说：“尽量把所有的代码都放在较大的方法中执行”与“少用接口”等等，这完全与Java语言的设计目的背道而驰，现在看起来是多么可笑的奇谈怪论，当时却是很多程序员津津乐道的经验之谈。无他，Java本身执行太慢了。Java生命的前十分之三就是如此缓慢地渡过的。</P>
<P>　　于是，Sun的工程师开始拼命想着提高执行速度。JIT静态编译器的出现是在1996年十月，Sun放出了第一个编译器。JIT编译器在每段代码执行前进行编译，编译的结果为本地静态机器码，执行速度有了质的提高。Symantec公司当时凭借其傲人的JIT编译器，在整个Java界受到热烈的追捧。在其后的1998年，Java 1.2发布的时候，附带了JIT编译器，从此Java的使用者终于可以抛开上面说的那些奇怪的“速度优化经验”了。</P>
<P>　　JIT静态编译器虽然可以解决一些问题，但是性能仍然和C/C++有很大的差距。对一段程序而言，一名优秀的程序员是如何来改进运行速度的呢？首先，他不会傻到把所有的代码都来优化，他会观察、思考到底哪段代码对整体性能影响最大？然后集中精力来优化这一段代码。按照经验，整个程序 10%-20%的代码，会占据 80%-90%的运行时间。用这种方法，在同样的时间、付出同样程度的努力后，这名优秀的程序员使整个程序的性能得到了很大程度的优化。HotSpot引擎，就是模仿人工的这种方法进行优化的。在程序运行的开始，Java代码仍然解释执行，但HotSpot引擎开始进行采样(Profiling)。</P>
<P>　　根据采样的结果，决定某段程序是占用较多运行时间的，就认为它是“HotSpot”，它也就是目前程序的瓶颈， 引擎开始启动一个单独的线程进行优化。因为不象原始的 JIT编译器那样无差别的编译所有代码，HotSpot引擎可以集中精力来对HotSpot代码进行深度优化，这样这部分代码执行起来更加迅捷。之前的静态编译器只能按照预定的策略进行编译优化，而HotSpot引擎的优化是基于采样的结果的，因此这种方法对所有的应用程序都有效。1999年3月27日，Sun放出了第一个HotSpot引擎。在随后的2000年5月的JDK 1.3中，包含了HotSopt引擎，这也使1.3成了一个具有里程碑意义的发行版本。到这里，Java的十年生命，已经过去了一半。</P>
<P>　　HotSpot代表的是一种动态编译的技术。对Java这种大量使用委派、组合等面向对象特性的程序来说，动态编译比起静态编译来有显著的优势。比如Method Inlining。方法的调用是一个很耗时的操作，假若可以把方法调用直接内嵌到调用者的代码中，就可以节省大量的时间， 这被称为“Method Inlining”。因为涉及到类的重载，静态优化很难确切知道哪些属性、方法被重载，因此很难对method进行合并，只好在方法内部进行静态编译，假若每个方法都很小，静态优化能起到的作用也就比较小。而动态编译因为可以完全随时掌握类的重载情况，就可以把相关的方法合并进行深度优化。现代的Java程序，特别是在设计模式教育得到普及之后，大量使用类的继承、委派，形成了很多短小的方法，动态编译的优势就更加明显。</P>
<P>　　自从出现了HotSpot之后，整个Java界为之一振。</P>
<P>　　最近的五年，就是继续优化的五年。继续进行优化的方法有几条路，一是研究新的采样算法。因为采样关系到不同的优化策略，会对整体性能有比较大的影响。二是研究深度优化的方法。三是研究垃圾收集的算法。垃圾收集会带来程序短暂的停顿，这会带来负面的用户体验。于是，如何提高垃圾收集的效率，减少延迟，出现了五花八门的算法，比如渐进式收集、火车算法等。在多处理器的时候，如何利用多处理器进行并行收集也是研究的一个热点。这方面，BEA的JRocket走在了前面。</P>
<P>　　现实生活中的虚拟机</P>
<P>　　最后，让我们来盘点一下目前市面上可见的各个虚拟机。</P>
<P>　　首先要提到的，毫无疑问是Sun的虚拟机。作为大众心目中的“官方实现”，Sun拥有最大的用户群，并且拥有“兼容基准”的地位，其他虚拟机都必须要考虑和Sun虚拟机的兼容性问题。比如 JRocket就会在某些特殊情况下表现出和Sun不同的特性，可能对程序运行有影响。不过Sun也的确没有让广大用户失望，虽然在早期性能比不上Symantec,后来在1.2 的时候性能又被IBM超越，但Sun一直在努力革新，特别是 1.4.2之后，性能有了长足的进步。虽然JDK 1.5的虚拟机在性能上没有什么提高，但是增强了稳定性，据说修改了8000处bug，真是让人汗流不止。原来我们在1.4.2下面一直在享受这么多bug啊。</P>
<P>　　其次是老牌劲旅IBM。IBM的JDK在1.3的时代创下了最好的性能记录，从此树立了高端形象。特别是在其WebSphere产品中得到了很好的评价。其JDK也是最早支持64bit的JDK之一。到了现在，IBM JDK在高端仍然是和BEA可以一拼的。</P>
<P>　　然后是后起之秀，BEA的JRocket。说到BEA突然在JVM领域一夜之间异军突起，多少让人有些瞠目，不过它采取的战略特别简单：自己没有，索性花钱买了在此领域深有研究的JRocket，在前面加上BEA的标志就可以了。JRocket瞄准高端服务器市场，在多处理器环境下有不俗的表现。</P>
<P>　　除此之外，还有几个开放源代码的JVM值得一提。首先就是大名鼎鼎的JikesRVM。说起其大名，大多数人都知道Jikes编译器是 IBM开发的，效率比同等的javac编译器高得多，很多开发者都使用Jikes编译器来取代javac。而JikesRVM则是IBM开源出来的一整套虚拟机技术，包含了JIT，GC的完整实现，在其网站上也有众多的论文，实在是想要深入研究JVM者的绝佳资源(<A href="http://jikesrvm.sourceforge.net">http://jikesrvm.sourceforge.net</A>)。</P>
<P>　　Kaffe是一个老牌的JVM,不过现在已经很少听到了。作者撰写此文时，<A href="http://www.kaffe.org">www.kaffe.org</A>网站已经没有响应，也不知道现在的情况如何了。</P>
<P>　　GNU则有两个计划：GCJ和GNU classpath。GNU classpath是一个底层实现，而GCJ是支持java的预编译器。</P>
<P>　　结束语</P>
<P>　　时光流转，轰轰烈烈的Java虚拟机性能争论仿佛还在耳边回响，现在新的争论却已经是“Java的性能是否已经超越C/C++”。</P>
<P>　　Joakim Dahlstedt 是 JRockit 的主要架构设计师之一，他坚持认为，Java绝不是一种速度慢，效率低的语言，JVM 是一个关键的组件，确保了系统的部署与运行和开发一样快速、轻松。特别是在目前开发趋势是采用大量预制的框架时，动态编译有可能比C/C++这样的静态优化获得更好的性能。</P>
<P>&nbsp;</P><img src ="http://www.blogjava.net/qq13367612/aggbug/17249.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 19:51 <a href="http://www.blogjava.net/qq13367612/articles/17249.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP/Servlet的重定向技术综述</title><link>http://www.blogjava.net/qq13367612/articles/17057.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 27 Oct 2005 07:41:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/17057.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17057.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/17057.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17057.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17057.html</trackback:ping><description><![CDATA[由于response是<A class=bluekey href="http://www.yesky.com/key/2911/2911.html" target=_blank>jsp</A>页面中的隐含对象，故在jsp页面中可以用response.sendRedirect()直接实现重定位。<BR><BR>　　注意：<BR><BR>　　(1).使用response.sendRedirect时，前面不能有HTML输出。<BR><BR>
<DIV class=guanggao><SPAN id=contentAdv></SPAN></DIV>　　这并不是绝对的，不能有HTML输出其实是指不能有HTML被送到了浏览器。事实上现在的<A class=bluekey href="http://www.yesky.com/key/4216/19216.html" target=_blank>server</A>都有cache机制，一般在8K（我是说JSP　SERVER），这就意味着，除非你关闭了cache，或者你使用了out.flush()强制刷新，那么在使用sendRedirect之前，有少量的HTML输出也是允许的。<BR><BR>　　(2).response.sendRedirect之后，应该紧跟一句return;<BR><BR>　　我们已经知道response.sendRedirect是通过浏览器来做转向的，所以只有在页面处理完成后，才会有实际的动作。既然你已经要做转向了，那么后的输出还有什么意义呢？而且有可能会因为后面的输出导致转向失败。 <BR><BR>　　补充<BR><BR>　　1.RequestDispatcher.forward()<BR><BR>　　是在服务器端起作用,当使用forward()时,Servlet engine传递HTTP请求从当前的Servlet or JSP到另外一个Servlet,JSP 或普通HTML文件,也即你的form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的所有信息在b.jsp都可以获得,参数自动传递.<BR><BR>　　但forward()无法重定向至有frame的jsp文件,可以重定向至有frame的html文件,同时forward()无法在后面带参数传递,比如servlet?name=frank,这样不行,可以程序内通过response.setAttribute("name",name)来传至下一个页面.<BR><BR>　　重定向后浏览器<A class=bluekey href="http://www.yesky.com/key/3035/18035.html" target=_blank>地址栏</A>URL不变.<BR><BR>　　例:在servlet中进行重定向<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#edeceb border=1>
<TBODY>
<TR>
<TD><A class=bluekey href="http://www.yesky.com/key/438/35438.html" target=_blank>public</A> void doPost(HttpServletRequest <A class=bluekey href="http://www.yesky.com/key/4313/19313.html" target=_blank>request</A>,HttpServletResponse response)<BR>throws ServletException,IOException<BR>{<BR>　response.setContentType("text/html; charset=gb2312");<BR>　ServletContext sc = getServletContext();<BR>　RequestDispatcher rd = null;<BR>　rd = sc.getRequestDispatcher("/index.jsp"); //定向的页面<BR>　rd.forward(request, response);<BR>}</TD></TR></TBODY></TABLE><BR>　　通常在servlet中使用，不在jsp中使用。<BR><BR>　　2.response.sendRedirect()<BR><BR>　　是在用户的浏览器端工作,sendRedirect()可以带参数传递,比如servlet?name=frank传至下个页面,同时它可以重定向至不同的主机上,sendRedirect()可以重定向有frame.的jsp文件.<BR><BR>　　重定向后在浏览器地址栏上会出现重定向页面的URL<BR><BR>　　例:在servlet中重定向<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#edeceb border=1>
<TBODY>
<TR>
<TD>public void doPost(HttpServletRequest request,HttpServletResponse response)<BR>throws ServletException,IOException<BR>{<BR>　response.setContentType("text/html; charset=gb2312");<BR>　response.sendRedirect("/index.jsp");<BR>}</TD></TR></TBODY></TABLE><BR>　　由于response是jsp页面中的隐含对象，故在jsp页面中可以用response.sendRedirect()直接实现重定位。<BR><BR>　　注意：<BR><BR>　　(1).使用response.sendRedirect时，前面不能有HTML输出<BR><BR>　　这并不是绝对的，不能有HTML输出其实是指不能有HTML被送到了浏览器。事实上现在的server都有cache机制，一般在8K（我是说JSP　SERVER），这就意味着，除非你关闭了cache，或者你使用了out.flush()强制刷新，那么在使用sendRedirect之前，有少量的HTML输出也是允许的。<BR><BR>　　(2).response.sendRedirect之后，应该紧跟一句return;<BR><BR>　　我们已经知道response.sendRedirect是通过浏览器来做转向的，所以只有在页面处理完成后，才会有实际的动作。既然你已经要做转向了，那么后的输出还有什么意义呢？而且有可能会因为后面的输出导致转向失败。<BR><BR>　　比较：<BR><BR>　　(1).Request Dispatcher.forward()是容器中控制权的转向，在客户端浏览器地址栏中不会显示出转向后的地址；<BR><BR>　　(2).response.sendRedirect()则是完全的跳转，浏览器将会得到跳转的地址，并重新发送请求链接。这样，从浏览器的地址栏中可以看到跳转后的链接地址。<BR><BR>　　前者更加高效，在前者可以满足需要时，尽量使用RequestDispatcher.forward()方法.<BR><BR>　　注：在有些情况下，比如，需要跳转到一个其它服务器上的资源，则必须使用HttpServletResponse.sendRequest()方法。<BR><BR>　　3.<?xml:namespace prefix = jsp /><jsp:forward page=""></jsp:forward> <BR><BR>　　它的底层部分是由RequestDispatcher来实现的，因此它带有RequestDispatcher.forward()方法的印记。<BR><BR>　　如果在<jsp:forward>之前有很多输出,前面的输出已使缓冲区满,将自动输出到客户端,那么该语句将不起作用,这一点应该特别注意。<BR><BR>　　另外要注意：它不能改变浏览器地址，刷新的话会导致重复提交<BR><BR>　　4.修改HTTP header的Location属性来重定向<BR><BR>　　通过设置直接修改地址栏来实现页面的重定向。<BR><BR>　　jsp文件代码如下：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#edeceb border=1>
<TBODY>
<TR>
<TD>＜%<BR>response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);<BR>String newLocn = "/newpath/jsa.jsp";<BR>response.setHeader("Location",newLocn);<BR>%＞</TD></TR></TBODY></TABLE><BR>　　5.JSP中实现在某页面停留若干秒后,自动重定向到另一页面<BR><BR>　　在html文件中，下面的代码：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#edeceb border=1>
<TBODY>
<TR>
<TD>
<META http-equiv=refresh content="300; url=target.jsp"></TD></TR></TBODY></TABLE><BR>　　它的含义：在5分钟之后正在浏览的页面将会自动变为target.html这一页。代码中300为刷新的延迟时间，以秒为单位。targer.html为你想转向的目标页,若为本页则为自动刷新本页。<BR><BR>　　由上可知，可以通过setHeader来实现某页面停留若干秒后,自动重定向到另一页面。<BR><BR>　　关键代码：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#edeceb border=1>
<TBODY>
<TR>
<TD>String content=stayTime+";URL="+URL;<BR>response.setHeader("REFRESH",content);</TD></TR></TBODY></TABLE><BR>　　如果总结得不够全面，请各位发表自己的意见或经验。<BR></jsp:forward><img src ="http://www.blogjava.net/qq13367612/aggbug/17057.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-27 15:41 <a href="http://www.blogjava.net/qq13367612/articles/17057.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Introducing to Spring Framework（中文修订版）之六</title><link>http://www.blogjava.net/qq13367612/articles/16888.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 08:08:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16888.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16888.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16888.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16888.html</trackback:ping><description><![CDATA[<SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">实现EJB</SPAN> <BR><BR>如果你选择使用EJB，Spring能在EJB实现和客户端访问EJB两方面都提供很大的好处。 <BR><BR>对业务逻辑进行重构，把它从EJB facades中取出到POJO已经得到了广泛的认同。（不讲别的，这使得业务逻辑更容易单元测试，因为EJB严重依赖于容器而难于分离测试。）Spring为session bean和message driver bean提供了方便的超类，使得通过自动载入基于包含在EJB Jar文件中的XML文档BeanFactory让这变得很容易。 <BR><BR>这意味着stateless session EJB可以这么获得和使用所需对象： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>import org.springframework.ejb.support.AbstractStatelessSessionBean; <BR><BR>public class MyEJB extends AbstractStatelessSessionBean <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;implements MyBusinessInterface { <BR>&nbsp; &nbsp;private MyPOJO myPOJO; <BR><BR>&nbsp; &nbsp;protected void onEjbCreate() { <BR>&nbsp; &nbsp;&nbsp; &nbsp;this.myPOJO = getBeanFactory().getBean("myPOJO"); <BR>&nbsp; &nbsp;} <BR><BR>&nbsp; &nbsp;public void myBusinessMethod() { <BR>&nbsp; &nbsp;&nbsp; &nbsp;this.myPOJO.invokeMethod(); <BR>&nbsp; &nbsp;} <BR>} <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>假定MyPOJO是一个接口，它的实现类——以及任何它需要的配置，注入基本的属性和更多的合作者——在XML bean factory 定义中隐藏。 <BR><BR>我们通过在ejb-jar.xmldeployment descriptor中名为ejb/BeanFactoryPath的环境变量定义告诉Spring去哪儿装载XML文档。如下： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>&lt;session&gt; <BR>&nbsp; &nbsp;&lt;ejb-name&gt;myComponent&lt;/ejb-name&gt; <BR>&nbsp; &nbsp;&lt;local-home&gt;com.test.ejb.myEjbBeanLocalHome&lt;/local-home&gt; <BR>&nbsp; &nbsp;&lt;local&gt;com.mycom.MyComponentLocal&lt;/local&gt; <BR>&nbsp; &nbsp;&lt;ejb-class&gt;com.mycom.MyComponentEJB&lt;/ejb-class&gt; <BR>&nbsp; &nbsp;&lt;session-type&gt;Stateless&lt;/session-type&gt; <BR>&nbsp; &nbsp;&lt;transaction-type&gt;Container&lt;/transaction-type&gt; <BR><BR>&nbsp; &nbsp;&lt;env-entry&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&lt;env-entry-name&gt;ejb/BeanFactoryPath&lt;/env-entry-name&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&lt;env-entry-value&gt;/myComponent-ejb-beans.xml&lt;/env-entry-value&gt;&lt;/env-entry&gt; <BR>&nbsp; &nbsp;&lt;/env-entry&gt; <BR>&lt;/session&gt; <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>myComponent-ejb-beans.xml 文件将会从classpath装载：在本例中，是EJB Jar文件的根目录。每个EJB都能指定自己的XML文档，因而这个机制能在每个EJB Jar文件中使用多次。 <BR><BR>Spring 的超类实现了EJB中诸如setSessionContext()和ejbCreate()的生命周期管理的方法，让应用程序开发者只需选择是否实现Spring的onEjbCreate()方法。</SPAN> 
<DIV>&nbsp;</DIV>
<DIV>&nbsp;</DIV>
<DIV><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">使用EJB</SPAN> <BR><BR>Spring还让实现EJB变得更加容易。许多EJB程序使用Service Locator和Business Delegate模式。这些比在客户代码中遍布JNDI查找强多了，但是它们常见的实现方式有显著的缺点，例如： <BR><BR>
<UL><BR>使用EJB的典型代码依赖Service Locator或者Business Delegate singletons，使得测试难于进行。 <BR><BR>在Service Locator模式没有使用Business Delegate的情况下，程序代码还要在EJB home中调用create()方法，并且处理可能导致的异常。因而仍然绑定在EJB API身上，忍受着EJB 编程模型的复杂度。 <BR><BR>实现Business Delegate模式通常导致显著的代码重复，其中我们必须编写大量仅仅是调用EJB同等方法的方法。 <BR></UL><BR>基于这些和其他原因，传统的EJB访问，如在Sun Adventure Builder和OTN J2EE Virtual Shopping Mall中展示的那样，会降低生产率并且带来显著的复杂度。 <BR><BR>Spring通过引入codeless business delegate前进了一步。有了Spring，你不再需要再编写另一个Service Locator，另一个JNDI查找，或者在硬编码的Business Delegate中重复代码，除非你肯定这增加了价值。 <BR><BR>例如，假定我们有使用local EJB的web controller。我们将遵循最佳实践，使用EJB Business Methods Interface模式，EJB的local interface extend非EJB专有的业务方法接口。（这么做的主要的一个原因是确保在本地接口和bean实现类中方法签名的自动同步。）让我们调用这个业务方法接口MyComponent。当然我们还需要实现local home接口并且提供实现SessionBean和MyComponent业务方法的bean的实现类。 <BR><BR>用了Spring EJB 访问，我们把我们的web层controller和EJB实现挂接上所需要进行的Java编码仅仅是在我们的controller中暴露一个类型MyComponent的setter方法。这将如下保存作为实例变量的引用： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>private MyComponent myComponent; <BR><BR>public void setMyComponent(MyComponent myComponent) { <BR>&nbsp; &nbsp;this.myComponent = myComponent; <BR>} <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>我们随后在任何业务方法中使用这个实例变量。 <BR><BR>Spring自动完称剩下的工作，通过像这样的XML bean定义。LocalStatelessSessionProxyFactoryBean是一个可以用于任何EJB的通用factory bean。它创建的对象能够自动被Spring转型为MyComponent类型。 <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>&lt;bean id="myComponent" <BR>class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean"&gt; <BR><BR>&nbsp; &nbsp;&lt;property name="jndiName"&gt;&lt;value&gt;myComponent&lt;/value&gt;&lt;/property&gt; <BR>&nbsp; &nbsp;&lt;property name="businessInterface"&gt;&lt;value&gt;com.mycom.MyComponent&lt;/value&gt;&lt;/property&gt; <BR>&lt;/bean&gt; <BR><BR>&lt;bean id="myController" <BR>&nbsp; &nbsp;class = "com.mycom.myController" <BR>&gt; <BR>&nbsp; &nbsp;&lt;property name="myComponent"&gt;&lt;ref bean="myComponent"/&gt;&lt;/property&gt; <BR>&lt;/bean&gt; <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>在幕后有许多魔法般的事情发生，Spring AOP framework的殷勤，虽然不强迫你使用AOP的概念享受这些结果。“myComponent”bean定义为EJB创建一个代理，它实现了业务方法的接口。EJB local home在启动的时候被缓存，因而只需要一次JNDI查找。每次EJB被调用的时候，代理调用local EJB中的create()方法并且调用EJB中对应的业务方法。 <BR><BR>myController bean定义为这个代理设置controller类的myController属性。 <BR><BR>这个EJB访问机制极大简化了应用程序的代码： <BR><BR>
<UL><BR>Web层的代码不依赖于EJB的使用。如果你想要使用POJO，mock object或者其他test stub替代EJB引用，我们可以简单地改动一下myComponent bean定义而不影响一行Java代码 <BR><BR>我们还不需要写一行JNDI查找或者其他EJB plumbing code。 <BR></UL><BR>在实际程序中的性能测试和经验标明这种方法（包括对目标EJB的反射调用）的性能影响是很小的，在典型的应用中检测不出。记住无论如何我们都不希望使用fine-grained的EJB调用，因为会有有关应用服务器上的EJB的底层架构方面的代价。 <BR><BR>我们可以把相同方法应用于远程EJB，通过类似org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean factory bean的方法。然而我们无法隐藏远程EJB的业务方法接口中的RemoteException。</SPAN></DIV><img src ="http://www.blogjava.net/qq13367612/aggbug/16888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 16:08 <a href="http://www.blogjava.net/qq13367612/articles/16888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Introducing to Spring Framework（中文修订版）之七</title><link>http://www.blogjava.net/qq13367612/articles/16889.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 08:08:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16889.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16889.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16889.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16889.html</trackback:ping><description><![CDATA[<P><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">测试</SPAN> <BR><BR>如你可能已经注意到的，我和其他Spring开发这是全面单元测试重要性的坚定支持者。我们相信框架被彻底单元测试过的是非常重要的，而且我们框架设计的主要目标是让建立在框架之上的程序易于单元测试。 <BR><BR>Spring自身有一个极好的单元测试包。我们的1.0 M1的单元测试覆盖率是75%，而且我们希望在1.0 RC1的时候能够达到80%的单元测试覆盖率。我们发现在这个项目中测试优先的开发带来的好处是实实在在的。例如，它使得作为国际化分布式开发团队的工作极端有效率，而且用户评论CVS snapshots趋向于稳定和使用安全。 <BR><BR>因为以下理由，我们相信用Spring构建的应用程序是非常易于测试的： <BR><BR>&nbsp;<BR>&nbsp;&nbsp; IoC推动了单元测试 <BR><BR>&nbsp;&nbsp; 应用程序不包括直接使用注入JNDI的J2EE服务的plumbing code，这些代码一般让测试难于进行 <BR><BR>&nbsp;&nbsp; Spring bean factories和contexts能够在容器外设置 <BR><BR>在容器外可以设置Spring bean factory的能力提供了对开发过程有趣的可选项。在几个使用Spring的web应用中，工作是从定义业务接口和在web容器外集成测试开始的。在业务功能已经足够完整之后，web接口不过是添加在其上的薄薄一层。</P>
<P><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">谁在使用Spring</SPAN> <BR><BR>虽然相对来说Spring还是一个新的项目，但是我们已经有了一个令人印象深刻并且不断增长的用户群。它们已经有许多产品使用着Spring。用户包括一个主要的全球投资银行（做大型项目的），一些知名的网络公司，几个web开发顾问机构，卫生保健公司，以及学院机构。 <BR><BR>许多用户完整地使用Spring，但是一些只单独使用一些组件。例如，大量用户使用我们地JDBC或者其他数据访问功能。</SPAN></P>
<P><SPAN class=postbody><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">Roadmap</SPAN> <BR><BR>在今年晚些时候我们主要要做的是让Spring发布release 1.0。然而，我们还有一些更长远的目标。 <BR><BR>为1.0 final规划地主要改进式源代码级地元数据支持，它主要用于（但不局限于）AOP框架。这将使得C#风格的attribute驱动的事务管理，并且让声明式企业服务在典型应用情况下非常容易配置。Attribute支持将会在Spring的1.0 final release支持中加入，并且设计的是在发布的那个时候能与JSR-175集成。 <BR><BR>1.0之后，一些可能的改进地方包括： <BR><BR></P>
<UL><BR>通过对我们的JDBC和事务支持的一个相当抽象来支持JMS <BR><BR>支持bean factories的动态重配置 <BR><BR>提供web services的能力 <BR><BR>IDE和其他工具支持 <BR></UL>
<P><BR>作为一个敏捷项目，我们主要是受到用户需求的驱动。因而我们不会开发没有一个用户需要的特性，并且我们会仔细倾听来自用户群的声音。</P>
<P><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">总结</SPAN> <BR><BR>Spring是一个解决了许多在J2EE开发中常见的问题的强大框架。 <BR><BR>Spring提供了管理业务对象的一致方法并且鼓励了注入对接口编程而不是对类编程的良好习惯。Spring的架构基础是基于使用JavaBean属性的Inversion of Control容器。然而，这仅仅是完整图景中的一部分：Spring在使用IoC容器作为构建完关注所有架构层的完整解决方案方面是独一无二的。 <BR><BR>Spring提供了唯一的数据访问抽象，包括简单和有效率的JDBC框架，极大的改进了效率并且减少了可能的错误。Spring的数据访问架构还集成了Hibernate和其他O/R mapping解决方案。 <BR><BR>Spring还提供了唯一的事务管理抽象，它能够在各种底层事务管理技术，例如JTA或者JDBC纸上提供一个一致的编程模型。 <BR><BR>Spring提供了一个用标准Java语言编写的AOP框架，它给POJOs提供了声明式的事务管理和其他企业事务——如果你需要——还能实现你自己的aspects。这个框架足够强大，使得应用程序能够抛开EJB的复杂性，同时享受着和传统EJB相关的关键服务。 <BR><BR>Spring还提供了可以和总体的IoC容器集成的强大而灵活的MVC web框架。</SPAN></P>
<P><SPAN class=postbody><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">更多信息</SPAN> <BR><BR>参见以下资源获得关于Spring的更多信息： <BR><BR></P>
<UL><BR>Expert One-on-One J2EE Design and Development（Rod Johnson，Wrox，2002）。虽然Spring在书出版之后已经极大地进步和改进了，它仍然是理解Spring动机的极佳途径。 <BR><BR>Spring的主页：http://www.springframework.org。这里包括Javadoc和几个教程。 <BR><BR>在Sourceforge上的论坛和下载 <BR><BR>Spring用户和Spring开发者的邮件列表 <BR></UL>
<P><BR>我们正在尽我们可能去改进Spring的文档和示例。我们还为在信件和邮件列表中极好的回复率自豪。我们希望你能快速融入我们的社区！</P>
<P><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">关于作者</SPAN> <BR><BR>Rod Johnson 作为Java开发者和架构师已经有了7年的经验了并且在J2EE平台出现之初就在其上进行开发了。他是《Expert One-on-One J2EE Design and Development》(Wrox，2002)的作者并且贡献了其他好几本关于J2EE的书。他当前正在为Wiley撰写另外一本有关J2EE架构的书。Rod在两个Java标准委员会服务并且经常师大会发言人。现在他在UK做一个咨询顾问。</SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></P><img src ="http://www.blogjava.net/qq13367612/aggbug/16889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 16:08 <a href="http://www.blogjava.net/qq13367612/articles/16889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Introducing to Spring Framework（中文修订版）之五</title><link>http://www.blogjava.net/qq13367612/articles/16887.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 08:07:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16887.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16887.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16887.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16887.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16887.html</trackback:ping><description><![CDATA[<SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">MVC web 框架</SPAN> <BR><BR>Spring包括一个强大而且高度可配置的MVC web 框架。 <BR><BR>Spring的MVC model类似于Struts。在多线程服务对象这点上，Spring的Controller类似于Struts Action，只有一个实例处理所有客户的请求。然而，我们相信Spring的MVC比起Struts有很多优点，例如： <BR><BR>
<UL><BR>Spring在controllers，JavaBean，models和views提供了一个非常清晰的划分。 <BR><BR>Spring的MVC是非常灵活的。不像Struts，它强制你的Action和Form对象进入固化的层次之中（因而你迫使你使用Java的实体继承），Spring MVC完全是基于接口的。而且，通过插入你自己的接口几乎Spring MVC 框架的所有部分都是可配置的。当然我们也提供了方便的类作为实现选择。 <BR><BR>Spring MVC是真正的view无关的。你不会被强制使用JSP，如果你不想那么做的话。你可以使用Velocity，XSLT或其他view技术。如果你想要使用自定义的view机制——例如，你自己的模板语言——你可以简单实现Spring的View接口并且把它集成进来。 <BR><BR>和其他对象一样，Spring的Controllers是通过IoC配置的。着使得它们易于测试，并且完美地和其他由Spring管理的对象集成。 <BR><BR>Web层变成了业务对象层之上的薄薄一层。这鼓励了好的习惯。Struts和其他专门的web框架让你去实现你自己的业务对象；Spring提供了你应用程序所有层的集成。 <BR></UL><BR>如在Struts 1.1中所见的，你可以有和你在Spring MVC 应用程序中所需要的一样多的dispatcher servlets。 <BR><BR>下面的例子展示了一个简单的Spring Controller如何能够访问定义在应用程序context中的业务对象。这个controller在它的handleRequest()方法中执行了Google搜索： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>public class GoogleSearchController <BR>&nbsp; &nbsp;&nbsp; &nbsp;implements Controller { <BR><BR>&nbsp; &nbsp;private IGoogleSearchPort google; <BR><BR>&nbsp; &nbsp;private String googleKey; <BR><BR>&nbsp; &nbsp;public void setGoogle(IGoogleSearchPort google) { <BR>&nbsp; &nbsp;&nbsp; &nbsp;this.google = google; <BR>&nbsp; &nbsp;} <BR><BR>&nbsp; &nbsp;public void setGoogleKey(String googleKey) { <BR>&nbsp; &nbsp;&nbsp; &nbsp;this.googleKey = googleKey; <BR>&nbsp; &nbsp;} <BR><BR>&nbsp; &nbsp;public ModelAndView handleRequest( <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;HttpServletRequest request, HttpServletResponse response) <BR>&nbsp; &nbsp;&nbsp; &nbsp;throws ServletException, IOException { <BR>&nbsp; &nbsp;&nbsp; &nbsp;String query = request.getParameter("query"); <BR>&nbsp; &nbsp;&nbsp; &nbsp;GoogleSearchResult result = <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;// Google property definitions omitted... <BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;// Use google business object <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;google.doGoogleSearch(this.googleKey, query, <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;start, maxResults, filter, restrict, <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;safeSearch, lr, ie, oe); <BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;return new ModelAndView("googleResults", "result", result); <BR>&nbsp; &nbsp;} <BR>} <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>这段代码使用的prototype中，IGoogleSearchPort是一个GLUE web services代理，由Spring FActoryBean返回。然而，Spring把controller从底层web service库中分离出来。接口可以使用普通的Java对象，test stub，mock对象或者如下面要讨论的EJB代理实现。这个contorller不包括资源查找；除了支持它的web交互的必要代码之外没有别的什么了。 <BR><BR>Spring还提供了数据绑定，forms，wizards和更复杂的工作流的支持。 <BR><BR>对Spring MVC 框架的优秀简介是Thomas Risberg的Spring MVC 教程（http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html）。还可以参见“Web MVC with the Spring Framework”（http://www.springframework.org/docs/web_mvc.html）。 <BR><BR>如果你乐于使用你钟情的MVC框架，Spring的分层架构使得你能够使用Spring的其他部分而不用MVC层。我们有使用Spring做中间层管理和数据访问，但是在web层使用Struts，WebWork或者Tapestry的用户。</SPAN> <img src ="http://www.blogjava.net/qq13367612/aggbug/16887.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 16:07 <a href="http://www.blogjava.net/qq13367612/articles/16887.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Introducing to Spring Framework（中文修订版）之四</title><link>http://www.blogjava.net/qq13367612/articles/16883.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 07:56:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16883.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16883.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16883.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16883.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16883.html</trackback:ping><description><![CDATA[<SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">事务管理</SPAN> <BR>抽象出一个数据访问的API是不够的；我们还需要考虑事务管理。JTA是显而易见的选择，但是它是一个直接用起来很笨重的API，因而许多J2EE开发者感到EJB CMT是对于事务管理唯一合理的选择。 <BR><BR>Spring提供了它自己对事务管理的抽象。Spring提供了这些： <BR><BR>
<UL><BR>通过类似于JdbcTemplate的回调模板编程管理事务，比起直接使用JTA要容易多了 <BR><BR>类似于EJB CMT的声明式事务管理，但是不需要EJB容器 <BR></UL><BR>Spring的事务抽象式唯一的，它不绑定到JTA或者任何其他事务管理技术。Spring使用事务策略的概念把程序代码和底层的事务架构（例如JDBC）解藕。 <BR><BR>为什么你要关心这些？JTA不是所有事务管理的最好答案吗？如果你正在编写仅仅使用一个数据库的程序，你不需要JTA的复杂度。你不关心XA事务或者两阶段提交。你甚至不需要提供这些东西的高端应用服务器。但是另一方面，你不会希望在需要和多个数据源打交道的时候重写你的代码。 <BR><BR>假定你决定通过直接使用JDBC或者Hibernate的事务以避免JTA带来的额外负担。一旦你需要处理多个数据源，你必须剥开所有的事务管理代码并且使用JTA事务来替代。这不是非常有吸引力的并且导致大部分J2EE程序员，包括我自己，推荐只使用全局JTA事务。然而使用Spring事务抽象，你只需要重新配置Spring让它使用JTA，而不是JDBC或者Hibernate的事务策略，就一切OK了。这是一个配置上的改变，而不是代码的改动。因而，Spring使得你能够自由缩放应用。</SPAN> <BR><BR><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">AOP</SPAN> <BR><BR>最近在应用AOP来解决企业关注点方面大家有了很大的兴趣，例如事务管理，这些都是EJB所要解决的。 <BR><BR>Spring的AOP支持的首要目标是要给POJOs提供J2EE服务。这类似于JBoss 4的目标，Spring AOP由它能够在应用服务器之间移植的优势，因而没有绑死在厂商身上的风险。它既可以在web或者EJB容器中使用，也能够在WebLogic，Tomcat，JBoss，Resin，Jetty，Orion和许多其他应用服务器和web容器上使用。 <BR><BR>Spring AOP支持method interception。所支持关键的AOP概念包括： <BR><BR>
<UL><BR>Interception：自定义行为能够在对接口和类的调用之前和之后插入。这类似于AspectJ术语中类似的“around advice”。 <BR><BR>Introduction：指定advice会导致对象实现额外的接口。这混乱了继承。 <BR><BR>静态和动态的pointcuts：在interception发生的程序执行处指定points。静态pointcuts concern函数签名；动态pointcuts也可以在point被求值的地方考虑函数的参数。Pointcuts独立interceptors单独定义，使得标准interceptor可以应用于不同应用程序和代码上下文。 <BR></UL><BR>Spring既支持有状态（一个advised对象一个实例）也支持无状态的interceptors（所有advice使用一个实例）。 <BR><BR>Spring不支持field interception。这是一个经过深思熟虑的设计决定。我总是感觉field interception违反了封装。我比较倾向于把AOP作为补全物，而不是与OOP冲突的东西。如果在5年或者10年后，我们在AOP学习曲线上走得更远了并且觉得应该在程序设计的桌面上给AOP一个位置，我不会惊讶的。（然而在那个时候基于语言的解决方案例如AspectJ可能比它们今天看来更加具有吸引力。） <BR><BR>Spring使用动态代理实现AOP（其中存在一个接口）或者在运行时使用CGLIB生成字节码（这使得能够代理类）。两种方法都能够在任何应用服务器中使用。 <BR><BR>Spring是第一个实现AOP Alliance interfaces的AOP 框架（www.sourceforge.net/projects/aopalliance）。这些是定义在不同AOP框架中能够互操作interceptors的尝试。 <BR><BR>在TheServerSide和其他地方有一个正在进行但是不是那么引人注目的争论，就是这种interception是不是“true AOP”。我倒不怎么在意它叫什么；仅仅需要知道它是否在实践中有用就好了。我也乐于称它为“declarative middleware”（声明式中间件）。把Spring AOP认做简单，轻量级的无状态beans的替代物，这样就不需要monolithic EJB容器了，而这些仅仅是让你能够构建有你需要的服务的容器。我不推荐advising任何一个POJO，对local SLSBs的类比有助于你理解推荐的粒度。（然而，与EJB不同的是，在恰当但是少见的情况下，你可以自由地把Spring的AOP应用到粒度更好的对象上。） <BR><BR>因为Spring在实例上advises 对象，而不是在class loader层面上，使用有不同advice的同一个类的多个实例是可能的，或者与advised实例一道使用unadvised 实例。 <BR><BR>可能Spring AOP最常见的应用是声明式事务管理。这是基于前面描述的TansactionTemplate抽象上的，并且可以给任何POJO提供声明式事务管理。取决于事务策略，底层的机制可以是JTA，JDBC，Hibernate或者任何其他提供事务管理的API。 <BR><BR>Spring的声明式事务管理类似于EJB CMT，在以下方面有些不同： <BR><BR>
<UL><BR>事务管理能够应用于任何POJO。我们推荐业务对象实现接口，但是这只是一个好的编程习惯的问题，而不是由框架强制的。 <BR><BR>通过使用Spring的事务API能够在事务性POJO中实现编程回调。我们为此提供静态的方法，使用ThreadLoacal变量，因而你不需要传播诸如EJBContext这样的context对象来确保回滚。 <BR><BR>你可以声明式地定义“回滚规则”。EJB不会在未捕捉程序异常的时候自动回滚（仅仅在unchecked exceptions和其他Throwables的时候），应用程序开发者经常需要在任何异常发生时回滚。Spring事务管理让你能够声明式地指定什么异常什么子类能够导致自动回滚。缺省的行为和EJB是一致的，但是你能够在checked和unchecked异常时自动回滚。这个在最少化自编程回调代码方面有很大好处，而回调依赖于Spring的事务API（因为EJB的编程回调时在EJBContext中完成的）。 <BR><BR>事务管理不绑定于JTA。如前面解释过的，Spring的事务管理能够在不同事务策略中使用。 <BR></UL><BR>当然还可以使用Spring AOP实现程序特有的aspects。取决于你对AOP概念的接受程度，决定你是否选择这么做，而不是Spring的能力，但是它确实非常有用。我们所见过的成功例子包括： <BR><BR>
<UL><BR>自定义的security interception，当安全检查的复杂度超出了J2EE安全架构的能力的时候 <BR><BR>在开发中使用的调试和profiling aspects <BR><BR>发送email通知管理员用户不寻常的举动的Interceptors <BR></UL><BR>程序自定的aspects能够成为消除需要许多函数的样板代码的有利武器。 <BR><BR>Spring AOP透明地与Spring BeanFactory概念集成。包含一个来自Spring BeanFactory对象地代码不需要知道它是还是不是advised。和任何对象一样，契约实在接口和对象实现中定义的。 <BR><BR>下面的XML片断展示了如何定义一个AOP代理： <BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>&lt;bean id="myTest" <BR>&nbsp; &nbsp;class="org.springframework.aop.framework.ProxyFactoryBean"&gt; <BR>&nbsp; &nbsp;&lt;property name="proxyInterfaces"&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&lt;value&gt;org.springframework.beans.ITestBean&lt;/value&gt; <BR>&nbsp; &nbsp;&lt;/property&gt; <BR>&nbsp; &nbsp;&lt;property name="interceptorNames"&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&lt;list&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;value&gt;txInterceptor&lt;/value&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;value&gt;target&lt;/value&gt; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&lt;/list&gt; <BR>&nbsp; &nbsp;&lt;/property&gt; <BR>&lt;/bean&gt; <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>注意bean类的定义总是AOP框架的ProxyFactoryBean，虽然bean的类型在引用中使用或者由BeanFactory的getBean()方法返回时依赖的是代理接口。（多个代理方法是被支持的。）ProxyFactoryBean的“interceptorNames”属性需要一个字符串列表。（因为如果代理是一个“prototype”而不是singleton，有状态interceptors可能需要创建新的实例，所以必须使用Bean的名字而不是bean的引用。）列表中的名字可以是interceptor或者pointcuts（interceptors和有关它们合适被使用的信息）。列表中的“target”值自动创建一个“invoker interceptor”封装target对象。实现代理接口的是在factory中的bean的名字。这个例子中的myTest可以和其他bean factory中的bean一样使用。例如，其他对象可以使用&lt;ref&gt;元素引用它而且这些引用是由Spring IoC设置的。 <BR><BR>还可以不用BeanFactory，编程构建AOP代理，虽然这个很少用得上： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>TestBean target = new TestBean(); <BR>DebugInterceptor di = new DebugInterceptor(); <BR>MyInterceptor mi = new MyInterceptor(); <BR>ProxyFactory factory = new ProxyFactory(target); <BR>factory.addInterceptor(0, di); <BR>factory.addInterceptor(1, mi); <BR>// An "invoker interceptor" is automatically added to wrap the target <BR>ITestBean tb = (ITestBean) factory.getProxy(); <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>我们相信最好把程序装配从Java代码中移出来，而AOP也不例外。 <BR><BR>Spring在它的AOP能力方面的直接竞争者是Jon Tirsen的Nanning Aspects（http://nanning.codehaus.org）。 <BR><BR>我觉得AOP作为EJB的替代无提供企业服务这个用法方面的进步是重要的。随着时间，这将成为Spring很重要的关注点。</SPAN> <img src ="http://www.blogjava.net/qq13367612/aggbug/16883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 15:56 <a href="http://www.blogjava.net/qq13367612/articles/16883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Introducing to Spring Framework（中文修订版）之三</title><link>http://www.blogjava.net/qq13367612/articles/16882.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 07:55:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16882.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16882.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16882.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16882.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16882.html</trackback:ping><description><![CDATA[<DIV><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">O/R mapping 集成</SPAN> <BR><BR>当然你经常需要使用O/R mapping，而不是使用关系数据访问。你总体的应用程序框架也必须支持它。因而提供了对Hibernate 2.x和JDO的集成支持。它的数据访问架构使得它能和任何底层的数据访问技术集成。Spring和Hibernate集成得尤其好。 <BR><BR>为什么你要使用Hibernate加Spring，而不是直接使用Hibernate？</SPAN></DIV>
<DIV><SPAN class=postbody></SPAN>&nbsp;</DIV>
<DIV><SPAN class=postbody>Session 管理 Spring提供有效率的，简单的以并且是安全的处理Hibernate Session。使用Hibernate的相关代码为了效率和恰当的事务处理一般需要使用相同的Hibernate “Session”对象。Spring让它容易透明地创建和绑定Session到当前的线程，要么使用声明式，AOP的method interceptor方法，要么在Java代码层面使用显式的，“template”包装类。因而Spring解决了在Hibernate论坛上经常出现的用法问题。 <BR><BR>资源管理 Spring的应用程序context能够处理Hiberante SessionFactories的位置和配置，JDBC数据源和其他相关资源。这使得这些值易于管理和改变。 <BR><BR>集成的事务管理 Spring让你能够把你的Hibernate代码包装起来，要么使用声明式，AOP风格的method interceptor，要么在Java代码层面显式使用“template”包装类。在两种做法中，事务语义都为你处理了，并且在异常时也做好了恰当的事务处理（回滚，等）。如下面讨论的，你还获得了能够使用和替换不同transaction manager，而不会让你相关Hibernate代码受到影响的能力。额外的，JDBC相关的代码能够完全事务性的和Hibernate代码集成。这对于处理没有在Hibernate实现的功能很有用。 <BR><BR>如上描述的异常包装 Spring能够包装Hibernate异常，把它们从私有的，checked异常转换为一套抽象的运行时异常。这使得你能够仅仅在恰当的层面处理大部分不可恢复的持久化异常，而不影响样板catch/throw，和异常声明。你仍然能够在任何你需要的地方捕捉和处理异常。记住JDBC异常（包括DB特有的方言）也被转换到相同的层次中，意味着你能在一致的编程模型中对JDBC执行相同的操作。 <BR><BR>为了避免和厂商绑定 Hibernate是强大的，灵活的，开放源代码并且免费，但是它仍然使用私有的API。给出了一些选择，使用标准或者抽象API实现主要的程序功能通常是你想要的，当你需要因为功能，性能，或者其他考虑要转换到使用其他实现时。 <BR><BR>让测试变简单 Spring的Inversion of Control方法使得改变Hibernate的session factories，数据源，transaction manager的实现和位置很容易，如果需要的话还能改变mapper object的实现。这使得更加容易分离和测试持久化相关的代码。</SPAN></DIV><img src ="http://www.blogjava.net/qq13367612/aggbug/16882.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 15:55 <a href="http://www.blogjava.net/qq13367612/articles/16882.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Introducing to Spring Framework（中文修订版）之二</title><link>http://www.blogjava.net/qq13367612/articles/16881.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 07:53:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16881.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16881.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16881.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16881.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16881.html</trackback:ping><description><![CDATA[<DIV><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">JDBC 抽象和数据存储异常层次</SPAN> <BR><BR>数据访问是Spring 的另一个闪光点。 <BR><BR>JDBC 提供了还算不错的数据库抽象，但是需要用痛苦的API。这些问题包括： <BR><BR>
<UL>需要冗长的错误处理代码来确保ResultSets，Statements以及（最重要的）Connections在使用后关闭。这意味着对JDBC的正确使用可以快速地导致大量的代码量。它还是一个常见的错误来源。Connection leak可以在有负载的情况下快速宕掉应用程序。 <BR><BR>SQLException相对来说不能说明任何问题。JDBC不提供异常的层次，而是用抛出SQLException来响应所有的错误。找出到底哪里出错了——例如，问题是死锁还是无效的SQL？——要去检查SQLState或错误代码。这意味着这些值在数据库之间是变化的。</UL><BR>Spring用两种方法解决这些问题： <BR><BR>
<UL>提供API，把冗长乏味和容易出错的异常处理从程序代码移到框架之中。框架处理所有的异常处理；程序代码能够集中精力于编写恰当的SQL和提取结果上。 <BR><BR>为你本要处理SQLException程序代码提供有意义的异常层次。当Spring第一次从数据源取得一个连接时，它检查元数据以确定数据库。它使用这些信息把SQLException映射为自己从org.springframework.dao.DataAccessException派生下来的类层次中正确的异常。因而你的代码可以与有意义的异常打交道，并且不需要为私有的SQLState或者错误码担心。Spring的数据访问异常不是JDBC特有的，因而你的DAO并不一定会因为它们可能抛出的异常而绑死在JDBC上。</UL><BR>Spring提供两层JDBC API。第一个时，在org.springframework.jdbc.core包中，使用回调机制移动控制权——并且因而把错误处理和连接获取和释放——从程序的代码移到了框架之中。这是一种不同的Inversion of Control，但是和用于配置管理的几乎有同等重要的意义。 <BR><BR>Spring使用类似的回调机制关注其他包含特殊获取和清理资源步骤的API，例如JDO（获取和释放是由PersistenceManager完成的），事务管理（使用JTA）和JNDI。Spring中完成这些回调的类被称作template。 <BR><BR>例如，Spring的JdbcTemplate对象能够用于执行SQL查询并且在如下的列表中保存结果： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>JdbcTemplate template = new JdbcTemplate(dataSource); <BR>final List names = new LinkedList(); <BR>template.query("SELECT USER.NAME FROM USER", <BR>&nbsp; &nbsp;new RowCallbackHandler() { <BR>&nbsp; &nbsp;&nbsp; &nbsp;public void processRow(ResultSet rs) throws SQLException { <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;names.add(rs.getString(1)); <BR>&nbsp; &nbsp;&nbsp; &nbsp;} <BR>&nbsp; &nbsp;});</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>注意回调中的程序代码是能够自由抛出SQLException的：Spring将会捕捉到这些异常并且用自己的类层次重新抛出。程序的开发者可以选择哪个异常，如果有的话，被捕捉然后处理。 <BR><BR>JdbcTemplate提供许多支持不同情景包括prepared statements和批量更新的方法。Spring的JDBC抽象有比起标准JDBC来说性能损失非常小，甚至在当应用中需要的结果集数量很大的时候。 <BR><BR>在org.springframework.jdbc.object包中是对JDBC的更高层次的抽象。这是建立在核心的JDBC回调功能基础纸上的，但是提供了一个能够对RDBMS操作——无论是查询，更新或者是存储过程——使用Java对象来建模的API。这个API部分是受到JDO查询API的影响，我发现它直观而且非常有用。 <BR><BR>一个用于返回User对象的查询对象可能是这样的： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>class UserQuery extends MappingSqlQuery { <BR><BR>&nbsp; &nbsp;public UserQuery(DataSource datasource) { <BR>&nbsp; &nbsp;&nbsp; &nbsp;super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?"); <BR>&nbsp; &nbsp;&nbsp; &nbsp;declareParameter(new SqlParameter(Types.NUMERIC)); <BR>&nbsp; &nbsp;&nbsp; &nbsp;compile(); <BR>&nbsp; &nbsp;} <BR><BR>&nbsp; &nbsp;// Map a result set row to a Java object <BR>&nbsp; &nbsp;protected Object mapRow(ResultSet rs, int rownum) throws SQLException { <BR>&nbsp; &nbsp;&nbsp; &nbsp;User user = new User(); <BR>&nbsp; &nbsp;&nbsp; &nbsp;user.setId(rs.getLong("USER_ID")); <BR>&nbsp; &nbsp;&nbsp; &nbsp;user.setForename(rs.getString("FORENAME")); <BR>&nbsp; &nbsp;&nbsp; &nbsp;return user; <BR>&nbsp; &nbsp;} <BR><BR>&nbsp; &nbsp;public User findUser(long id) { <BR>&nbsp; &nbsp;&nbsp; &nbsp;// Use superclass convenience method to provide strong typing <BR>&nbsp; &nbsp;&nbsp; &nbsp;return (User) findObject(id); <BR>&nbsp; &nbsp;} <BR>} <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>这个类可以在下面用上： <BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>User user = userQuery.findUser(25); <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>这样的对象经常可以用作DAO的inner class。它们是线程安全的，除非子类作了一些超出常规的事情。 <BR><BR>在org.springframework.jdbc.object包中另一个重要的类是StoredProcedure类。Spring让存储过程通过带有一个业务方法的Java类进行代理。如果你喜欢的话，你可以定义一个存储过程实现的接口，意味着你能够把你的程序代码从对存储过程的依赖中完全解脱出来。 <BR><BR>Spring数据访问异常层次是基于unchecked（运行时）exception的。在几个工程中使用了Spring之后，我越来越确信这个决定是正确的。 <BR><BR>数据访问异常一般是不可恢复的。例如，如果我们不能链接到数据库，某个业务对象很有可能就不能完成要解决的问题了。一个可能的异常是optimistic locking violation，但是不是所有的程序使用optimistic locking。强制编写捕捉其无法有效处理的致命的异常通常是不好的。让它们传播到上层的handler，比如servlet或者EJB 容器通常更加合适。所有的Spring对象访问异常都是DataAccessException的子类，因而如果我们确实选择了捕捉所有的Spring数据访问异常，我们可以很容易做到这点。 <BR><BR>注意如果我们确实需要从unchecked数据访问异常中恢复，我们仍然可以这么做。我们可以编写代码仅仅处理可恢复的情况。例如，如果我们认为只有optimistic locking violation是可恢复的，我们可以在Spring的DAO中如下这么写： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>try { <BR>&nbsp; &nbsp;// do work <BR>} <BR>catch (OptimisticLockingFailureException ex) { <BR>&nbsp; &nbsp;// I'm interested in this <BR>} <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>如果Spring的数据访问异常是checked的，我们需要编写如下的代码。注意我们还是可以选择这么写： <BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>try { <BR>&nbsp; &nbsp;// do work <BR>} <BR>catch (OptimisticLockingFailureException ex) { <BR>&nbsp; &nbsp;// I'm interested in this <BR>} <BR>catch (DataAccessException ex) { <BR>&nbsp; &nbsp;// Fatal; just rethrow it <BR>} <BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR>第一个例子的潜在缺陷是——编译器不能强制处理可能的可恢复的异常——这对于第二个也是如此。因为我们被强制捕捉base exception（DataAccessException），编译器不会强制对子类（OptimisticLockingFailureException）的检查。因而编译器可能强制我们编写处理不可恢复问题的代码，但是对于强制我们处理可恢复的问题并未有任何帮助。 <BR><BR>Spring对于数据访问异常的unchecked使用和许多——可能是大多数——成功的持久化框架是一致的。（确实，它部分是受到JDO的影响。）JDBC是少数几个使用checked exception的数据访问API之一。例如TopLink和JDO大量使用unchecked exception。Gavin King现在相信Hibernate也应该选择使用unchecked exception。 <BR><BR>Spring的JDBC能够用以下办法帮助你： <BR><BR>
<UL><BR>你决不需要在使用JDBC时再编写finally block。 <BR><BR>总的来说你需要编写的代码更少了 <BR><BR>你再也不需要挖掘你的RDBMS的文档以找出它为错误的列名称返回的某个罕见的错误代码。你的程序不再依赖于RDBMS特有的错误处理代码。 <BR><BR>无论使用的是什么持久化技术，你都会发现容易实现DAO模式，让业务代码无需依赖于任何特定的数据访问API。 <BR></UL><BR>在实践中，我们发现所有这些都确实有助于生产力的提高和更少的bug。我过去常常厌恶编写JDBC代码；现在我发现我能够集中精力于我要执行的SQL，而不是烦杂的JDBC资源管理。 <BR><BR>如果需要的话Spring的JDBC抽象可以独立使用——不强迫你把它们用作Spring的一部分。</SPAN></DIV><img src ="http://www.blogjava.net/qq13367612/aggbug/16881.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 15:53 <a href="http://www.blogjava.net/qq13367612/articles/16881.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Introducing to Spring Framework（中文修订版）之一</title><link>http://www.blogjava.net/qq13367612/articles/16880.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 07:52:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16880.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16880.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16880.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16880.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16880.html</trackback:ping><description><![CDATA[<DIV><SPAN style="FONT-WEIGHT: bold"><SPAN style="FONT-SIZE: 18px; LINE-HEIGHT: normal">Introducing to Spring Framework</SPAN></SPAN> <BR><BR>作者：Rod Johnson <BR>译者：yanger，taowen <BR>校对：taowen <BR><BR>关于Spring Framework，今年夏天你可能已经听见很多的议论。在本文中,我将试图解释Spring能完成什么，和我怎么会认为它能帮助你开发J2EE应用程序。 <BR><BR><SPAN style="FONT-WEIGHT: bold">又来一个framework？</SPAN> <BR><BR>你可能正在想“不过是另外一个的framework”。当已经有许多开放源代码(和专有) J2EE framework时，为什么你还要耐下心子读这篇文章或去下载Spring Framework？ <BR><BR>我相信Spring是独特的，有几个原因： <BR><BR>
<UL><BR>它关注的领域是其他许多流行的Framework未曾关注的。Spring要提供的是一种管理你的业务对象的方法。 <BR><BR>Spring既是全面的又是模块化的。Spring有分层的体系结构，这意味着你能选择仅仅使用它任何一个独立的部分，而它的架构又是内部一致。因此你能从你的学习中，得到最大的价值。例如，你可能选择仅仅使用Spring来简单化JDBC的使用，或用来管理所有的业务对象。 <BR><BR>它的设计从一开始就是要帮助你编写易于测试的代码。Spring是使用测试驱动开发的工程的理想框架。 <BR></UL><BR>Spring不会给你的工程添加对其他的框架依赖。Spring也许称得上是个一站式解决方案，提供了一个典型应用所需要的大部分基础架构。它还涉及到了其他framework没有考虑到的内容。 <BR><BR>尽管它仅仅是一个从2003年2月才开始的开源项目，但Spring有深厚的历史根基。这个开源工程是起源自我在2002年晚些时候出版的《Expert One-on-One J2EE设计与开发》书中的基础性代码。这本书展示了Spring背后的基础性架构思想。然而，对这个基础架构的概念可以追溯到2000年的早些时候,并且反映了我为一系列商业工程开发基础结构的成功经验。 <BR><BR>2003年1月，Spring已经落户于SourceForge上了。现在有10个开发人员，其中6个是高度投入的积极分子。 <BR><BR><SPAN style="FONT-WEIGHT: bold">Spring架构上的好处</SPAN> <BR><BR>在我们进入细节之前，让我们来看看Spring能够给工程带来的种种好处： <BR><BR>
<UL><BR>Spring能有效地组织你的中间层对象，不管你是否选择使用了EJB。如果你仅仅使用了Struts或其他为J2EE的 API特制的framework，Spring致力于解决剩下的问题。 <BR><BR>Spring能消除在许多工程中常见的对Singleton的过多使用。根据我的经验，这是一个很大的问题，它降低了系统的可测试性和面向对象的程度。 <BR><BR>通过一种在不同应用程序和项目间一致的方法来处理配置文件，Spring能消除各种各样自定义格式的属性文件的需要。曾经对某个类要寻找的是哪个魔法般的属性项或系统属性感到不解，为此不得不去读Javadoc甚至源编码？有了Spring，你仅仅需要看看类的JavaBean属性。Inversion of Control的使用（在下面讨论）帮助完成了这种简化。 <BR><BR>通过把对接口编程而不是对类编程的代价几乎减少到没有，Spring能够促进养成好的编程习惯。 <BR><BR>Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。 <BR><BR>使用Spring构建的应用程序易于单元测试。 <BR><BR>Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口，却不会影响调用代码。 <BR><BR>Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物，它们适用于许多web应用。例如，Spring能使用AOP提供声明性事务管理而不通过EJB容器，如果你仅仅需要与单个数据库打交道，甚至不需要一个JTA实现。 <BR><BR>Spring为数据存取提供了一个一致的框架,不论是使用的是JDBC还是O/R mapping产品（如Hibernate）。 <BR></UL><BR>Spring确实使你能通过最简单可行的解决办法来解决你的问题。而这是有有很大价值的。 <BR><BR><SPAN style="FONT-WEIGHT: bold">Spring做了些什么？</SPAN> <BR><BR>Spring提供许多功能，在此我将依次快速地展示其各个主要方面。 <BR><BR><SPAN style="FONT-WEIGHT: bold">任务描述</SPAN> <BR><BR>首先,让我们明确Spring范围。尽管Spring覆盖了许多方面，但我们对它应该涉什么，什么不应该涉及有清楚的认识。 <BR><BR>Spring的主要目的是使J2EE易用和促进好编程习惯。 <BR><BR>Spring不重新轮子。因此，你发现在Spring中没有logging，没有连接池，没有分布式事务调度。所有这些东西均有开源项目提供(例如我们用于处理所有日志输出的Commons Logging以及Commons DBCP)，或由你的应用程序服务器提供了。出于同样的的原因，我们没有提供O/R mapping层。对于这个问题已经有了像Hibernate和JDO这样的优秀解决方案。 <BR><BR>Spring的目标就是让已有的技术更加易用。例如，尽管我们没有底层事务协调处理，但我们提供了一个抽象层覆盖了JTA或任何其他的事务策略。 <BR><BR>Spring没有直接和其他的开源项目竞争，除非我们感到我们能提供新的一些东西。例如，象许多开发人员一样，我们从来没有对Struts感到高兴过，并且觉得到在MVC web framework中还有改进的余地。在某些领域，例如轻量级的IoC容器和AOP框架，Spring确实有直接的竞争，但是在这些领域还没有已经较为流行的解决方案。(Spring在这些领域是开路先锋。) <BR><BR>Spring也得益于内在的一致性。所有的开发者都在唱同样的的赞歌，基础想法依然与Expert One-on-One J2EE设计与开发中提出的差不多。 并且我们已经能够在多个领域中使用一些中心的概念，例如Inversion of Control。 <BR><BR>Spring在应用服务器之间是可移植的。当然保证可移植性总是一种挑战，但是我们避免使用任何平台特有或非标准的东西，并且支持在WebLogic，Tomcat，Resin，JBoss，WebSphere和其他的应用服务器上的用户。 <BR><BR><SPAN style="FONT-WEIGHT: bold">Inversion of Control 容器</SPAN> <BR><BR>Spring设计的核心是 org.springframework.beans 包, 它是为与JavaBeans一起工作而设计的。 这个包一般不直接被用户使用，而是作为许多其他功能的基础。 <BR><BR>下一个层面高一些的抽象是"Bean Factory"。一个Spring bean factory 是一个通用的Factory，它使对象能够按名称获取，并且能管理对象之间的关系。 <BR><BR>Bean factories 支持两种模式的对象： <BR><BR>
<UL><BR>Singleton：在此模式中，有一个具有特定名称的共享对象实例，它在查找时被获取。这是默认的，而且是最为经常使用的。它对于无状态对象是一种理想的模式。 <BR><BR>Prototype：在此模式中，每次获取将创建一个独立的对象。例如，这可以被用于让用户拥有他们自己的对象。 <BR></UL><BR><BR>由于 org.springframwork.beans.factory.BeanFactory是一个简单的接口，它能被大量底层存储方法实现。你能够方便地实现你自己的BeanFactory，尽管很少用户需要这么做。最为常用的BeanFactory定义是： <BR><BR>
<UL><BR>XmlBeanFactory： 可解析简单直观的定义类和命名对象属性的XML结构。 我们提供了一个DTD来使编写更容易。 <BR><BR>ListableBeanFactoryImpl：提供了解析存放在属性文件中的bean定义的能力，并且可通过编程创建BeanFactories。 <BR></UL><BR>每个bean定义可能是一个POJO（通过类名和JavaBean初始属性定义），或是一个FactoryBean。FactoryBean接口添加了一个间接层。通常，这用于创建使用AOP或其他方法的代理对象：例如，添加声明性事务管理的代理。（这在概念上和EJB的interception相似，但实现得更简单。） <BR><BR>BeanFactories能在一个层次结构中选择性地参与，继承ancestor（祖先）的定义。这使得在整个应用中公共配置的共享成为可能，虽然个别资源，如controller servlets，还拥有他们自己的独立的对象集合。 <BR><BR>这种使用JavaBeans的动机在《Expert One-on-One J2EE Design and Development》的第四章中有描述，在TheServerSide网站上的有免费的PDF版本(http://www.theserverside.com/resources/article.jsp?l=RodJohnsonInterview)。 <BR><BR>通过BeanFactory概念，Spring成为一个Inversion of Control的容器。(我不怎么喜欢container这个词，因为它使人联想到重量级容器，如EJB容器。Spring的BeanFactory是一个可通过一行代码创建的容器，并且不需要特殊的部署步骤。) <BR><BR>Inversion of Control背后的概念经常表述为Hollywood原则的：“Don’t call me， I’ll call you。” IoC将控制创建的职责搬进了框架中，并把它从应用代码脱离开来。涉及到配置的地方，意思是说在传统的容器体系结构中，如EJB，一个组件可以调用容器并问“我需要它给我做工作的对象X在哪里?”；使用IoC容器则只需指出组件需要X对象，在运行时容器会提供给它。容器是通过查看方法的参数表（例如JavaBean的属性）做到的，也可能根据配置数据如XML。 <BR><BR>IoC有几个重要的好处，例如： <BR><BR>
<UL><BR>因为组件不需要在运行时间寻找合作者，所以他们可以更简单的编写和维护。在Spring版的IoC里，组件通过暴露JavaBean的setter方法表达他们依赖的其他组件。这相当于EJB通过JNDI来查找，EJB查找需要开发人员编写代码。 <BR><BR>同样原因，应用代码更容易测试。JavaBean属性是简单的，属于Java核心的，并且是容易测试的：仅编写一个自包含的Junit测试方法用来创建对象和设置相关属性即可。 <BR><BR>一个好的IoC实现保留了强类型。如果你需要使用一个通用的factory来寻找合作者，你必须通过类型转换将返回结果转变为想要的类型。这不是一个大不了的问题，但是不雅观。使用IoC，你在你的代码中表达了强类型依赖，框架将负责类型转换。这意味着在框架配置应用时，类型不匹配将导致错误；在你的代码中，你无需担心类型转换异常。 <BR><BR>大部分业务对象不依赖于IoC容器的APIs。这使得很容易使用遗留下来的代码，且很容易的使用对象无论在容器内或不在容器内。例如，Spring用户经常配置Jakarta Commons DBCP数据源为一个Spring bean：不需要些任何定制代码去做这件事。我们说一个IoC容器不是侵入性的：使用它并不会使你的代码依赖于它的APIs。任何JavaBean在Spring bean factory中都能成为一个组件。 <BR></UL><BR>最后应该强调的是，IoC 不同于传统的容器的体系结构，如EJB，应用代码最小程度地依靠于容器。这意味着你的业务对象可以潜在的被运行在不同的IoC 框架上——或者在任何框架之外——不需要任何代码的改动。 <BR><BR>以我和其他Spring用户的经验来说，再怎么强调IoC给应用程序代码带来的好处也不为过。 <BR><BR>IoC不是一个新概念，但是它在J2EE团体里面刚刚到达黄金时间。 有一些可供选择的IoC 容器: 例如 Apache Avalon, PicoContainer 和 HiveMind。Avalon 从没怎么流行，尽管它很强大而且有很长的历史。Avalon相当的重和复杂，并且看起来比新的IoC解决方案更具侵入性。 PicoContainer是一个轻量级而且更强调通过构造函数表达依赖性而不是JavaBean 属性。 与Spring不同，它的设计允许每个类型一个对象的定义(可能是因为它拒绝任何Java代码外的元数据导致的局限性)。在Spring， PicoContainer 和其他 IoC frameworks之间做比较，可参看文章Spring网站上的"The Spring Framework - A Lightweight Container"位于http://www.springframework.org/docs/lightweight_container.html。这个页面里面包含了PicoContainer站点的链接 。 <BR><BR>Spring BeanFactories 是非常轻量级的。用户已经成功地将他们应用在applets和单独的Swing应用中。(它们也很好地工作在EJB容器中。) 没有特殊的部署步骤和察觉得到的启动时间。这个能力表明一个容器在应用的任何层面几乎立即可以发挥非常大的价值。 <BR><BR>Spring BeanFactory 概念贯穿于Spring始终， 而且是Spring如此内在一致的关键原因。在IoC容器中，Spring也是唯一的，它使用IoC作为基础概念贯穿于整个功能丰富的框架。 <BR><BR>对应用开发人员，最重要的是，一个或多个BeanFactory提供了一个定义明确的业务对象层。这类似于local session bean层，但比它更简单。与EJBs不同，在这个层中的对象可能是相关的，并且他们的关系被拥有它们的factory管理。有一个定义明确的业务对象层对于成功的体系结构是非常重要的。 <BR><BR>Spring ApplicationContext 是BeanFactory的子接口，为下列东西提供支持： <BR><BR>
<UL><BR>信息查找，支持着国际化 <BR><BR>事件机制，允许发布应用对象以及可选的注册以接收到事件 <BR><BR>可移植的文件和资源访问 <BR></UL><BR><SPAN style="FONT-WEIGHT: bold">XmlBeanFactory 例子</SPAN> <BR><BR>Spring用户通常在XML的“bean定义”文件中配置他们的应用。Spring的XML bean定义文档的根是&lt;beans&gt; 元素。该元素包含一个或多个 &lt;bean&gt;定义。我们一般给每个bean定义的指定类和属性。我们还必须指定ID作为标识，这将成为在代码中使用该bean的名字。 <BR><BR>让我们来看一个简单的例子，它配置了三个应用程序对象，之间的关系在J2EE应用中常常能够看到： <BR><BR>
<UL><BR>J2EE DataSource <BR><BR>使用DataSource的DAO <BR><BR>在处理过程中使用DAO的业务对象 <BR></UL><BR>在下面的例子中，我们使用一个来自Jakarta Commons DBCP项目的BasicDataSource。这个class（和其他许多已有的class一样）可以简单地被应用在Spring bean factory中，只要它提供了JavaBean格式的配置。需要在shutdown时被调用的Close方法可通过Spring的"destroy-method"属性被注册，以避免BasicDataSource需要实现任何Spring 的接口。 <BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>&lt;beans&gt; <BR><BR>&nbsp; &lt;bean id="myDataSource" <BR>class="org.apache.commons.dbcp.BasicDataSource" <BR>destroy-method="close"&gt; <BR>&nbsp; &nbsp; &lt;property name="driverClassName"&gt;&lt;value&gt;com.mysql.jdbc.Driver&lt;/value&gt;&lt;/property&gt; <BR>&nbsp; &nbsp; &lt;property name="url"&gt;&lt;value&gt;jdbc:mysql://localhost:3306/mydb&lt;/value&gt;&lt;/property&gt; <BR>&nbsp; &nbsp; &lt;property name="username"&gt;&lt;value&gt;root&lt;/value&gt;&lt;/property&gt; <BR>&nbsp; &lt;/bean&gt;</TD></TR></TBODY></TABLE><SPAN class=postbody><BR>BasicDataSource中我们感兴趣的所有属性都是String类型的，因此我们用&lt;value&gt;元素来指定他们的值。如果必要的话，Spring使用标准的 JavaBean属性编辑器机制来把String转换为其他的类型。 <BR><BR>现在，我们定义DAO，它有一个对DataSource的bean引用。Bean间关系通过&lt;ref&gt;元素来指定： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>&lt;bean id="exampleDataAccessObject" <BR>&nbsp; &nbsp;&nbsp; &nbsp;class="example.ExampleDataAccessObject"&gt; <BR>&nbsp; &nbsp; &lt;property name="dataSource"&gt;&lt;ref bean="myDataSource"/&gt;&lt;/property&gt; <BR>&nbsp; &lt;/bean&gt; <BR><BR>The business object has a reference to the DAO, and an int property (exampleParam): <BR>&lt;bean id="exampleBusinessObject" <BR>&nbsp; &nbsp;&nbsp; &nbsp;class="example.ExampleBusinessObject"&gt; <BR>&nbsp; &nbsp; &lt;property name="dataAccessObject"&gt;&lt;ref bean="exampleDataAccessObject"/&gt;&lt;/property&gt; <BR>&nbsp; &nbsp; &lt;property name="exampleParam"&gt;&lt;value&gt;10&lt;/value&gt;&lt;/property&gt; <BR>&nbsp; &lt;/bean&gt; <BR><BR>&lt;/beans&gt;</TD></TR></TBODY></TABLE><SPAN class=postbody><BR>对象间的关系一般在配置中明确地设置，象这个例子一样。我们认为这样做是件好事情。然而Spring还提供了我们称做"autowire"的支持， 一个la PicoContainer，其中它指出了bean间的依赖关系。这样做的局限性——PicoContainer也是如此——是如果有一个特殊类型的多个Bean，要确定那个类型所依赖的是哪个实例是不可能。好的方面是，不满足的依赖可以在factory初始化后被捕获到。（Spring 也为显式的配置提供了一种可选的依赖检查，它可以完成这个目的） <BR><BR>在上面的例子中，如果我们不想显式的编写他们的关系，可使用如下的autowire特性： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>&lt;bean id="exampleBusinessObject" <BR>&nbsp; &nbsp;class="example.ExampleBusinessObject" <BR>&nbsp; &nbsp;autowire="byType"&gt; <BR><BR>&nbsp; &nbsp; &lt;property name="exampleParam"&gt;&lt;value&gt;10&lt;/value&gt;&lt;/property&gt; <BR>&nbsp;&lt;/bean&gt;</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>使用这个特性，Spring会找出exampleBusinessObject的dataSource属性应该被设置为在当前BeanFactory中找到的DataSource实现。在当前的BeanFactory中，如果所需要类型的bean不存在或多于一个，将产生一个错误。我们依然要设置exampleParam属性，因为它不是一个引用。 <BR><BR>Autowire支持和依赖检查刚刚加入CVS并将在Spring 1.0 M2(到10/20,2003)中提供。本文中所讨论的所有其他特性都包含在当前1.0 M1版本中。 <BR><BR>把管理从Java代码中移出来比硬编码有很大的好处，因为这样可以只改变XML文件而无需改变一行Java代码。例如，我们可以简单地改变myDataSource的bean定义引用不同的bean class以使用别的连接池，或者一个用于测试的数据源。 XML节变成另一种，我们可以用Spring的JNDI location FactoryBean从应用服务器获取一个数据源。 <BR><BR>现在让我们来看看例子中业务对象的java 代码。注意下面列出的代码中没有对Spring的依赖。不像EJB容器，Spring BeanFactory不具有侵入性：在应用对象里面你通常不需要对Spring的存在硬编码。 <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>public class ExampleBusinessObject implements MyBusinessObject { <BR><BR>&nbsp; &nbsp;private ExampleDataAccessObject dao; <BR>&nbsp; &nbsp;private int exampleParam; <BR><BR>&nbsp; &nbsp;public void setDataAccessObject(ExampleDataAccessObject dao) { <BR>&nbsp; &nbsp;&nbsp; &nbsp;this.dao = dao; <BR>&nbsp; &nbsp;} <BR><BR>&nbsp; &nbsp;public void setExampleParam(int exampleParam) { <BR>&nbsp; &nbsp;&nbsp; &nbsp;this.exampleParam = exampleParam; <BR>&nbsp; &nbsp;} <BR><BR>&nbsp; &nbsp;public void myBusinessMethod() { <BR>&nbsp; &nbsp;&nbsp; &nbsp;// do stuff using dao <BR>&nbsp; &nbsp;} <BR>}</TD></TR></TBODY></TABLE><SPAN class=postbody><BR>注意那些property setter，它们对应于bean定义文档中的XML引用。这些将在对象被使用之前由Spring调用。 <BR><BR>这些应用程序的bean不需要依赖于Spring：他们不需要实现任何Spring的接口或者继承Spring的类。他们只需要遵守JavaBeans的命名习惯。在Spring 应用环境之外重用它们是非常简单的，例如，在一个测试环境中。只需要用它们的缺省构造函数实例化它们，并且通过调用setDataSource()和setExampleParam()手工设置它的属性。如果你想以一行代码支持程序化的创建，只要你有一个无参数的构造器，你就可以自由定义其他需要多个属性的构造函数。 <BR><BR>注意在业务接口中没有声明将会一起使用的JavaBean属性。 他们是一个实现细节。我们可以“插入”带有不同bean属性的不同的实现类而不影响连接着的对象或者调用的代码。 <BR><BR>当然，Spring XML bean factories 有更多的功能没有在这里描述，但是，应当让你对基本使用有了一些感觉。以及，简单的属性，有JavaBean属性编辑器的属性，Spring可以自动处理lists，maps和java.util.Properties。 <BR><BR>Bean factories 和application contexts 通常和J2EE server定义的一个范围相关联，例如： <BR><BR>
<UL><BR>Servlet context.：在spring 的MVC 框架里, 每一个包含common objects的web 应用都定义有一个应用程序的context。Spring提供了通过listener或者servlet实例化这样的context的能力而不需要依赖于Spring 的MVC 框架，因而它也可以用于Struts，WebWork 或者其他的web框架之中。 <BR><BR>A Servlet：在Spring MVC 框架里每一个servlet控制器都有它自己的应用程序context，派生于根（全应用程序范围的）应用程序context。在Struts或者其他MVC框架中实现这些也很容意。 <BR><BR>EJB：Spring 为EJB提供方便的超类，它们简化了EJB的创建并且提供了一个从EJB Jar 文件中的XML文档载入的BeanFactory。 <BR></UL><BR>这些J2EE规范提供的hook通常避免了使用Singleton来创造一个bean factory。 <BR><BR>然而，如果我们愿意的话可以用代码创建一个BeanFactory，虽然是没有什么意义的。例如，我们在以下三行代码中可以创建bean factory并且得到一个业务对象的引用： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>InputStream is = getClass().getResourceAsStream("myFile.xml"); <BR>XmlBeanFactory bf = new XmlBeanFactory(is); <BR>MyBusinessObject mbo = (MyBusinessObject) bf.getBean("exampleBusinessObject");</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>这段代码将能工作在一个应用服务器之外：甚至不依赖J2EE，因为Spring 的IoC容器是纯java的。</SPAN><SPAN class=gensmall><BR></SPAN></DIV><img src ="http://www.blogjava.net/qq13367612/aggbug/16880.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 15:52 <a href="http://www.blogjava.net/qq13367612/articles/16880.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JasperReport使用小结</title><link>http://www.blogjava.net/qq13367612/articles/16877.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 07:47:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16877.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16877.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16877.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16877.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16877.html</trackback:ping><description><![CDATA[JasperReport是一个比较好的基于java的报表工具，支持多种输出格式 <BR>
<P><FONT size=3>1:对于A4的报表,可以这样指定页面:pageWidth="595" pageHeight="842" orientation="Portrait" ,orientation指的是打印的方向,可有2中选择<BR>Portrait(纵向),LandScape(横向)<BR>2:报表xml中是区分大小写的<BR>3:表达式:jasper中共有&lt;variableExpression&gt;, &lt;initialValueExpression&gt;, &lt;groupExpression&gt;,<BR>&lt;printWhenExpression&gt;, &lt;imageExpression&gt;, &lt;textFieldExpression&gt;这几种表达式,在表达式中可以引用3中类型的变量,分别是$P{},$V{},$F{}.<BR>P=Parameter,V=Variable,F=Field,P通常是通过程序传给报表中的,V是为了简化操作定义的一种表达式,F通常是要填充的字段.<BR>&lt;textFieldExpression&gt;<BR>$P{ReportTitle}<BR>&lt;/textFieldExpression&gt;<BR>ReportTitle是在应用程序中通过一个Map指定的:<BR>Map parameters = new HashMap();<BR>parameters.put("ReportTitle", "Address Report");<BR>JasperPrint jasperPrint = <BR>JasperFillManager.fillReport(<BR>jasperReport, <BR>parameters, <BR>new WebappDataSource()<BR>);<BR>参数也可以 在xml中指定:<BR>&lt;parameter name="MyDate" class="java.util.Date"&gt;<BR>&lt;defaultValueExpression&gt;<BR>new java.util.Date()<BR>&lt;/defaultValueExpression&gt;<BR>&lt;/parameter&gt; <BR>$F{}例子:<BR>&lt;textFieldExpression&gt;<BR>$F{FirstName} + " " + $F{LastName} + " was hired on " +<BR>(new SimpleDateFormat("MM/dd/yyyy")).format($F{HireDate}) + "."<BR>&lt;/textFieldExpression&gt;</FONT></P>
<P><FONT size=3>4:数据源:<BR>如要为报表指定一个数据源,则需要实现dori.jasper.engine.JRDataSource接口,也就是实现下面2个方法:<BR>public boolean next() throws JRException;<BR>public Object getFieldValue(JRField jrField) throws JRException;<BR>如果报表指定的是Connection,则可以在xml中指定查询语句:<BR>&lt;queryString&gt;&lt;![CDATA[SELECT * FROM Orders]]&gt;&lt;/queryString&gt;<BR>5:计算:<BR>&lt;variable name="QuantitySum" class="java.lang.Double" calculation="Sum"&gt;<BR>&lt;variableExpression&gt;$F{Quantity}&lt;/variableExpression&gt;<BR>&lt;/variable&gt;</FONT></P><img src ="http://www.blogjava.net/qq13367612/aggbug/16877.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 15:47 <a href="http://www.blogjava.net/qq13367612/articles/16877.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Class StringBuffer</title><link>http://www.blogjava.net/qq13367612/articles/16863.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 06:26:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16863.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16863.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16863.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16863.html</trackback:ping><description><![CDATA[<H2><FONT size=-1>java.lang</FONT> <BR>Class StringBuffer</H2><PRE><A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/lang/Object.html">java.lang.Object</A>
  |
  +--<B>java.lang.StringBuffer</B>
</PRE>
<DL>
<DT><B>All Implemented Interfaces:</B> 
<DD><A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/lang/CharSequence.html">CharSequence</A>, <A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/io/Serializable.html">Serializable</A> </DD></DL>
<HR>

<DL>
<DT>public final class <B>StringBuffer</B> 
<DT>extends <A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/lang/Object.html">Object</A> 
<DT>implements <A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/io/Serializable.html">Serializable</A>, <A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/lang/CharSequence.html">CharSequence</A></DT></DL>
<P>A string buffer implements a mutable sequence of characters. A string buffer is like a <A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/lang/String.html"><CODE>String</CODE></A>, but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls. 
<P>String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved. 
<P>String buffers are used by the compiler to implement the binary string concatenation operator <CODE>+</CODE>. For example, the code: 
<P>
<BLOCKQUOTE><PRE>     x = "a" + 4 + "c"
 </PRE></BLOCKQUOTE>
<P>is compiled to the equivalent of: 
<P>
<BLOCKQUOTE><PRE>     x = new StringBuffer().append("a").append(4).append("c")
                           .toString()
 </PRE></BLOCKQUOTE>which creates a new string buffer (initially empty), appends the string representation of each operand to the string buffer in turn, and then converts the contents of the string buffer to a string. Overall, this avoids creating many temporary strings. 
<P>The principal operations on a <CODE>StringBuffer</CODE> are the <CODE>append</CODE> and <CODE>insert</CODE> methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string buffer. The <CODE>append</CODE> method always adds these characters at the end of the buffer; the <CODE>insert</CODE> method adds the characters at a specified point. 
<P>For example, if <CODE>z</CODE> refers to a string buffer object whose current contents are "<CODE>start</CODE>", then the method call <CODE>z.append("le")</CODE> would cause the string buffer to contain "<CODE>startle</CODE>", whereas <CODE>z.insert(4, "le")</CODE> would alter the string buffer to contain "<CODE>starlet</CODE>". 
<P>In general, if sb refers to an instance of a <CODE>StringBuffer</CODE>, then <CODE>sb.append(x)</CODE> has the same effect as <CODE>sb.insert(sb.length(),&nbsp;x)</CODE>. 
<P>Every string buffer has a capacity. As long as the length of the character sequence contained in the string buffer does not exceed the capacity, it is not necessary to allocate a new internal buffer array. If the internal buffer overflows, it is automatically made larger. 
<P>
<P>
<DL>
<DT><B>Since:</B> 
<DD>JDK1.0 
<DT><B>See Also:</B> 
<DD><A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/io/ByteArrayOutputStream.html"><CODE>ByteArrayOutputStream</CODE></A>, <A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/java/lang/String.html"><CODE>String</CODE></A>, <A href="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.GEVER-07F7F7A8C\桌面\jdkapi140.chm::/jdk140/api/serialized-form.html" target=java.lang.StringBuffer>Serialized Form</A></DD></DL><img src ="http://www.blogjava.net/qq13367612/aggbug/16863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 14:26 <a href="http://www.blogjava.net/qq13367612/articles/16863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ant使用指南（一）</title><link>http://www.blogjava.net/qq13367612/articles/16662.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 14:19:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16662.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16662.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16662.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16662.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16662.html</trackback:ping><description><![CDATA[管理你的代码，doc文件,编译,配置等开发工作你是怎末做的：自己管理源目录，编译文件，copy,实施... <BR>在window写bat,在unix写shell？ <BR>&nbsp;&nbsp;&nbsp; &nbsp;可惜我不怎末会写。所以我制作一个ejb的过程是这样的： <BR>写代码-&gt;写个bat编译代码-&gt;组织文件(ejb-jar.xml,jboss.xml...)-&gt;压缩文件生成jar文档-&gt; <BR>实施......(还有同样的测试过程)(为甚末不用jbuild?) 
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;这中间每次修改都要经过重复的无数次手工copy,paste...过程，好累，好花时间。可我确实不想去 <BR>学脚本编写，unix,linux下我还得重学，太累。</FONT> </P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 如果你和我一样希望自动执行预定义的动作却又不想太累，那末，我觉得ANT工具不错： <BR>它可以以相同的用法用在不同平台(跨平台)。它是JAVA做的，免费的，开源的，据说网上已经成了事实的JAVA <BR>构建标准，还听说高手都用它...... ^_^</FONT> </P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 我花了2天时间才掌握了它的基本用法，我希望你能通过我的文章和试验花半天时间就掌握它。</FONT> </P>
<P><B><FONT color=#ff0000 size=2>内容：</FONT></B> </P>
<UL>
<LI><B><FONT color=#ff0000 size=2>下载，安装</FONT></B> 
<LI><B><FONT color=#ff0000 size=2>hello ant</FONT></B> 
<LI><B><FONT color=#ff0000 size=2>hello ant 进级</FONT></B> 
<LI><B><FONT color=#ff0000 size=2>参考及下载</FONT></B> </LI></UL>
<P><FONT size=2><FONT color=#ff0000><B>下载，安装 <BR>&nbsp;&nbsp; </B></FONT>又下载?又是一大堆参数变量?&nbsp; :( <BR>&nbsp;&nbsp;&nbsp; <A href="http://jakarta.apache.org/ant/index.html">http://jakarta.apache.org/ant/index.html</A> 它是apache的java子项目"jakarta"的子项目.你可以选择 <BR>当前的版本，目前我是1.5版,window版，以下就用它讲。</FONT> </P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 解压后ant_home用来方便访问。并确保你也设置了java_home 。 <BR>&nbsp;&nbsp;&nbsp; set ant_home=D:\java\kit\ant\jakarta-ant-1.5.1&nbsp;&nbsp;&nbsp; 这是我的目录&nbsp;&nbsp;&nbsp;&nbsp; <BR><BR>&nbsp;&nbsp;&nbsp; 就这些，简单吧。 <BR><BR><FONT color=#ff0000><B>hello ant</B></FONT></FONT> </P>
<P><FONT size=2>我们要开发一个java类：其内容只有一句，输出"hello ant"字符串。并使用ant完成编译和运行工作，这个例子只是为了跑通ant，不附加多余的东西。</FONT> </P>
<P><FONT size=2>下图为文件组织，请建立相应的目录，并编写HelloAnt.java <BR><IMG height=198 src="http://www.huihoo.com/java/hello-ant/images/hello-ant-dir.gif" width=425 border=1></FONT> </P>
<P><FONT size=2>按照人家老外的文件组织规则咱也照搬。</FONT> </P>
<TABLE style="BORDER-COLLAPSE: collapse" borderColor=#000000 height=69 cellSpacing=0 cellPadding=0 width="86%" border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0 height=13><FONT size=2>&nbsp;hello.ant.HelloAnt.java</FONT> </TD></TR>
<TR>
<TD width="100%" height=52><FONT size=2><TEXTAREA style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体" name=S1 rows=7 cols=75>package hello.ant;

public class HelloAnt{
    public static void main(String[] args){
        System.out.println("hello ant,ant 的第一次接触，好棒！");
    } 
}</TEXTAREA> </FONT></TD></TR></TBODY></TABLE>
<P><FONT size=2>在项目根目录(hello-ant\)写1个文件：ant执行配置文件build.xml</FONT> </P>
<TABLE style="BORDER-COLLAPSE: collapse" borderColor=#000000 height=69 cellSpacing=0 cellPadding=0 width="86%" border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0 height=13><FONT size=2>&nbsp;build.xml</FONT> </TD></TR>
<TR>
<TD width="100%" height=52><FONT size=2><TEXTAREA style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体" name=S1 rows=13 cols=75>&lt;?xml version="1.0"  encoding="GB2312" ?&gt;

&lt;!-- 一个项目,可包含很多任务组(target) --&gt;
&lt;project default="main" basedir="."&gt;

    &lt;!-- 项目中的一个任务组,可包含很多任务(task:javac,java...) --&gt;
    &lt;target name="main"&gt;

        &lt;!--编译--&gt;
        &lt;javac srcdir="src\main\hello\ant" destdir="build\classes"/&gt;

        &lt;!--运行--&gt;
        &lt;java classname="hello.ant.HelloAnt"&gt;
            &lt;classpath&gt;
                &lt;pathelement path="build\classes"/&gt;
            &lt;/classpath&gt;
        &lt;/java&gt;

    &lt;/target&gt;
&lt;/project&gt;</TEXTAREA> </FONT></TD></TR></TBODY></TABLE>
<P><FONT size=2>ok,一切大功告成，哦，不，还没有运行它。</FONT> </P>
<P><FONT size=2>dos下进入hello-ant的目录，即build.xml所在的目录，我们要用ant工具执行它 ，&nbsp;</FONT> </P>
<P><FONT size=2>执行: %ant_home%/bin/ant -file build.xml&nbsp;&nbsp;&nbsp;&nbsp; 用ant工具执行当前目录下的配置文件build.xml&nbsp;</FONT> </P>
<P><FONT size=2>或&nbsp; ：ant -file build.xml&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你如果设置%ant_home%/bin到path中</FONT> </P>
<P><FONT size=2>这次ok了，这是答案：</FONT> </P>
<TABLE style="BORDER-COLLAPSE: collapse" borderColor=#000000 height=69 cellSpacing=0 cellPadding=0 width="86%" border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0 height=13><FONT size=2>命令提示符窗口</FONT> </TD></TR>
<TR>
<TD width="100%" bgColor=#000000 height=52><FONT color=#ffffff size=2>D:\temp\hello-ant&gt;ant -file build.xml <BR>Buildfile: build.xml <BR><BR>main: <BR>[javac] Compiling 1 source file to D:\temp\hello-ant\build\classes <BR>[java] hello ant,ant 的第一次接触，好棒！ <BR><BR>BUILD SUCCESSFUL <BR>Total time: 2 seconds <BR>D:\temp\hello-ant&gt;</FONT> </TD></TR></TBODY></TABLE>
<P><FONT size=2>检查一下build/classes目录，哦，看到编译过的文件就在这里: <BR>build/classes/hello/ant/HelloAnt.class.</FONT> </P>
<P><B><FONT color=#ff0000 size=2>hello ant 进级</FONT></B> </P>
<P><FONT size=2>(此段比较废话，可以略过) <BR><FONT face=仿宋_GB2312>你也许会说：这末简单的工作写个批处理不就得了，又xml又ant的，把我的时间都浪费完了，我用jbuild或 <BR>webShpere不就得了，怎末说你才明白呢？反正网上开源项目大多数都用ant,你总不能给人家个*.jpx吧， <BR>而且这样的工具太贵，受不了(当然用D的兄弟不怕^_^ )，而且ant可以让你明确的管理和自动化所有的东西: <BR>编译-实施-测试...,哎，稍微麻烦一点点，但节约你以前花在零碎的copy,paste上的时间.而且我发现管理 <BR>代码的质量有所提高.</FONT></FONT> </P>
<P><FONT size=2>我们要改进build.xml，让它做更多的事情：</FONT> </P>
<UL>
<LI><FONT size=2>定义全局变量</FONT> 
<LI><FONT size=2>初始化,主要是建立目录</FONT> 
<LI><FONT size=2>编译&nbsp; (已有)</FONT> 
<LI><FONT size=2>打包为jar</FONT> 
<LI><FONT size=2>建立API documentation</FONT> 
<LI><FONT size=2>生成distribution产品</FONT> </LI></UL>
<P class=MsoNormal><FONT face=仿宋_GB2312 size=2>凡事都讲究平衡，你要ant给你做更多事，当然要累一点点，不过只用累一次，以后的代码修改后的构建都是"一键式"完成,我们制作一个hello的简单例子，你可以自己做j2ee的练习。</FONT> </P>
<P class=MsoNormal><FONT face=宋体 size=2>我们要扩充目录结构，使它更像回事：</FONT> </P>
<P class=MsoNormal><FONT face=宋体 size=2>ant处理编译之前的目录：</FONT> </P>
<P class=MsoNormal><FONT face=宋体 size=2><IMG height=170 src="http://www.huihoo.com/java/hello-ant/images/hello-ant-dir-advance-2.gif" width=425 border=1></FONT> </P>
<P class=MsoNormal><FONT face=宋体 size=2>ant处理之后的目录：</FONT> </P>
<P class=MsoNormal><FONT size=2><IMG height=283 src="http://www.huihoo.com/java/hello-ant/images/hello-ant-dir-advance-1.gif" width=454 border=1></FONT> </P>
<P class=MsoNormal><FONT size=2>图中：\src,\docs,\lib是自己组织的文件结构，\build,\dist是ant动态生成的成品。</FONT> </P>
<P class=MsoNormal><FONT size=2>\src&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 源文件：java源，script源，jsp源，xml配置..... <BR>\src\main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java源 <BR>\src\script&nbsp;&nbsp;&nbsp;&nbsp; window,unix,liunx的执行script，我们的简单只有一个： <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; run.bat:&nbsp; java hello.ant.HelloAnt</FONT> </P>
<P class=MsoNormal><FONT size=2>\docs&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; 手写说明文档 <BR>\lib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 程序所需类库的jar,比如j2ee.jar,mail,jar...</FONT> </P>
<P class=MsoNormal><FONT size=2>\build&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用ant动态生成的构建目录 <BR>\build\classes&nbsp; 编译的类文件 <BR>\build\docs&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; copy "\docs"的手写说明文档，和ant生成的api文档 <BR>\build\lib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 放置我们自己的HelloAnt.class打包成品hello-ant.jar</FONT> </P>
<P class=MsoNormal><FONT size=2>\dist\bin&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; copy "\src\script" 得执行文件 <BR>\dist\docs&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; copy "\build\docs" 的文档 <BR>\dist\lib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 除了copy "\build\lib"下的hello-ant.jar外， <BR>&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;&nbsp;&nbsp;&nbsp; 还应copy "\lib"的程序所需jar，这里我们没有。</FONT> </P>
<P class=MsoNormal><FONT size=2>以上是我学老外的文件组织，大家可以按照自己的爱好组织</FONT> </P>
<P class=MsoNormal><FONT size=2>我们编写必要的文件： </FONT></P>
<TABLE style="BORDER-COLLAPSE: collapse" borderColor=#000000 height=29 cellSpacing=0 cellPadding=0 width="86%" border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0 height=13><FONT size=2>hello.ant.&nbsp;HelloAnt.java</FONT> </TD></TR>
<TR>
<TD width="100%" height=12><FONT size=2>已有</FONT> </TD></TR></TBODY></TABLE>
<TABLE style="BORDER-COLLAPSE: collapse" borderColor=#000000 height=69 cellSpacing=0 cellPadding=0 width="86%" border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0 height=13><FONT size=2>\src\script.bat</FONT> </TD></TR>
<TR>
<TD width="100%" height=52><FONT size=2><TEXTAREA style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体" name=S1 rows=7 cols=75>@echo off
echo ========================================================
echo   请先设置 Environment
echo .
echo   JAVA_HOME: %JAVA_HOME%
echo ======================================================

%java_home%\bin\java -classpath ..\lib\hello-ant.jar  hello.ant.HelloAnt

pause </TEXTAREA> </FONT></TD></TR></TBODY></TABLE>
<TABLE style="BORDER-COLLAPSE: collapse" borderColor=#000000 height=69 cellSpacing=0 cellPadding=0 width="86%" border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0 height=13><FONT size=2>\docs\index.html 随便写一个手写的文档</FONT> </TD></TR>
<TR>
<TD width="100%" height=52>
<P align=center><B><FONT color=#ff0000 size=2>hello ant 软件项目手册docs</FONT></B> </P>
<HR color=#008000 SIZE=1>

<P align=center><B><A href="http://www.huihoo.com/java/hello-ant/api/index.html"><FONT color=#ff0000 size=2>访问api文档</FONT></A></B> </P>　 </TD></TR></TBODY></TABLE>
<TABLE style="BORDER-COLLAPSE: collapse" borderColor=#000000 height=69 cellSpacing=0 cellPadding=0 width="86%" border=1>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0 height=13><FONT size=2>\build.xml 配置文件&nbsp;</FONT> </TD></TR>
<TR>
<TD width="100%" height=52><FONT size=2><TEXTAREA style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体" name=S1 rows=24 cols=83>&lt;?xml version="1.0"  encoding="GB2312" ?&gt;
&lt;!--
    =======================================================================
      hello-ant 项目 ,学习ant工具的第2个build file.

      参照ant的jakarta-ant-1.6alpha的build.xml

      Copyright (c) 2002 The Neusoft Software Foundation.  All rights
      reserved.

    =======================================================================
--&gt;

&lt;!--
    文档结构为:
    &lt;project&gt;
        &lt;property/&gt;               全局变量的定义
        &lt;property/&gt;...

        &lt;target name="1"&gt;         任务组(tasks)
            &lt;javac&gt;&lt;/javac&gt;       一项javac任务
            ...
            &lt;oneTask&gt;&lt;/ontTask&gt;   一项其它任务
        &lt;/target&gt;

        &lt;target name="2"&gt;
            &lt;javac&gt;&lt;/javac&gt;
            ...
            &lt;oneTask&gt;&lt;/ontTask&gt;
        &lt;/target&gt;
    &lt;/project&gt;

    project代表一个项目，
    default:运行到名称为"dist"的target(任务组)
    basedir:基准路径。
--&gt;
&lt;project default="dist" basedir="."&gt;

&lt;!--
    ===================================================================
      定义属性（property tasks）
      最好把用到的路径呀，名称呀都在这里定义成全局变量
      例：定义
          &lt;property name="a" value="hello"/&gt;
      以后就可以这样用它：
          &lt;property name="b" value="${a}/b"/&gt;
      现在:b=="hello/b"
    ===================================================================
--&gt;

    &lt;!--主要的系统环境属性--&gt;
    &lt;property environment="env"/&gt;&lt;!--取window,unix...的环境变量--&gt;
    &lt;property name="java.home" value="${env.JAVA_HOME}"/&gt;
    &lt;property name="ant.home"  value="${env.ANT_HOME}"/&gt;

    &lt;!--主要的app环境属性--&gt;
    &lt;property name="app.name"      value="hello-ant"/&gt;
    &lt;property name="app.jar"       value="${app.name}.jar"/&gt;
    &lt;property name="app.copyright" value=" Copyright (c) 2002 The Neusoft Software Foundation.  All rights reserved."/&gt;


    &lt;!--app中src的属性--&gt;
    &lt;property name="src.dir"    value="src" /&gt;
    &lt;property name="src.main"   value="${src.dir}/main"/&gt;
    &lt;property name="src.script" value="${src.dir}/script"/&gt;

    &lt;!--app用到的lib--&gt;
    &lt;property name="lib.dir" value="lib"/&gt;

    &lt;!--app的build目录中--&gt;
    &lt;property name="build.dir"      value="build" /&gt;
    &lt;property name="build.classes"  value="${build.dir}/classes"/&gt;
    &lt;property name="build.docs"     value="${build.dir}/docs"/&gt;
    &lt;property name="build.docs.api" value="${build.docs}/api"/&gt;
    &lt;property name="build.lib"      value="${build.dir}/lib"/&gt;

    &lt;!--app的dist (distribution) 目录中--&gt;
    &lt;property name="dist.dir"      value="dist"/&gt;
    &lt;property name="dist.bin"      value="${dist.dir}/bin"/&gt;
    &lt;property name="dist.docs"     value="${dist.dir}/docs"/&gt;
    &lt;property name="dist.lib"      value="${dist.dir}/lib"/&gt;

    &lt;!--app的docs目录中--&gt;
    &lt;property name="docs.dir"      value="docs"/&gt;

    &lt;!--
    定义一组路径以后可以通过id重用这组路径 ，例：
    &lt;javac srcdir="src/main" destdir="build/classes"&gt;
            &lt;classpath refid="classpath"/&gt;
    &lt;/javac&gt;
    --&gt;
    &lt;path id="classpath"&gt;
        &lt;!--本项目只有一个java，用不上classpath，这里只是做个例子--&gt;
        &lt;pathelement location="${build.classes}"/&gt;
        &lt;pathelement path="${java.home}/lib/tools.jar"/&gt;
    &lt;/path&gt;

&lt;!--
    ===================================================================
      init 准备目录(File Tasks)
      主要的目录结构通常是不会变的，一起生成他们
    ===================================================================
--&gt;
    &lt;target name="init"&gt;
        &lt;!--清除以前目录--&gt;
        &lt;delete dir="${build.dir}" failonerror="false" /&gt;
        &lt;delete dir="${dist.dir}"  failonerror="false"/&gt;

        &lt;!--准备目录--&gt;
        &lt;mkdir dir="${build.dir}"/&gt;
        &lt;mkdir dir="${build.classes}"/&gt;
        &lt;mkdir dir="${build.docs}"/&gt;
        &lt;mkdir dir="${build.docs.api}"/&gt;
        &lt;mkdir dir="${build.lib}"/&gt;

        &lt;mkdir dir="${dist.dir}"/&gt;
        &lt;mkdir dir="${dist.bin}"/&gt;
        &lt;mkdir dir="${dist.lib}"/&gt;

    &lt;/target&gt;

&lt;!--
    ===================================================================
      Build the code (Compile Tasks,File Tasks)
    ===================================================================
--&gt;
    &lt;target name="build" depends="init"&gt;
        &lt;!--编译--&gt;
        &lt;javac srcdir="${src.main}" destdir="${build.classes}"&gt;
            &lt;classpath refid="classpath"/&gt;
        &lt;/javac&gt;
    &lt;/target&gt;

&lt;!--
    ===================================================================
      打包文档(Archive Tasks)
      Create the project jars: xxx1.jar and xxx2.jar
    ===================================================================
--&gt;
   &lt;target name="jars" depends="build"&gt;
        &lt;jar basedir="${build.classes}" jarfile="${build.lib}/${app.jar}"/&gt;
    &lt;/target&gt;

&lt;!--
     ===================================================================
       Creates the API documentation
     ===================================================================
--&gt;
    &lt;target name="javadocs"
            depends="jars"
            description="--&gt; creates the API documentation"&gt;
        &lt;!--copy docs 手册... --&gt;
        &lt;copy todir="${build.docs}"&gt;
            &lt;fileset dir="${docs.dir}"/&gt;
        &lt;/copy&gt;

        &lt;javadoc packagenames="hello.ant.*"
                 sourcepath="${src.main}"
                 defaultexcludes="yes"
                 destdir="${build.docs.api}"
                 author="true"
                 version="true"
                 use="true"
                 windowtitle="Docs API"&gt;
             &lt;doctitle&gt;&lt;![CDATA[&lt;h1&gt;hello ant Docs API&lt;/h1&gt;]]&gt;&lt;/doctitle&gt;
             &lt;bottom&gt;&lt;![CDATA[&lt;i&gt;${app.copyright}&lt;/i&gt;]]&gt;&lt;/bottom&gt;
             &lt;tag name="todo" scope="all" description="To do:" /&gt;
         &lt;/javadoc&gt;
    &lt;/target&gt;

&lt;!--
     ===================================================================
       Create the distribution that can run (Archive Tasks)
       主要是从各目录中把该copy的copy上
     ===================================================================
--&gt;
   &lt;target name="dist" depends="javadocs"&gt;
        &lt;!--copy bin 执行文件 --&gt;
        &lt;copy todir="${dist.bin}"&gt;
            &lt;fileset dir="${src.script}/"/&gt;
        &lt;/copy&gt;
        &lt;copy todir="${dist.docs}"&gt;
            &lt;fileset dir="${build.docs}/"/&gt;
        &lt;/copy&gt;
        &lt;!-- copy lib 文件 --&gt;
        &lt;copy todir="${dist.lib}"&gt;
            &lt;fileset dir="${build.lib}/"/&gt;
        &lt;/copy&gt;

    &lt;/target&gt;
&lt;!--
     ===================================================================
      Cleans everything(File Tasks)
      例如可以删除build中的文件，留给你发挥吧
     ===================================================================
--&gt;

&lt;/project&gt;</TEXTAREA> </FONT></TD></TR></TBODY></TABLE>
<P class=MsoNormal><FONT size=2><BR>build.xml多了些，但其实很简单：(注释比较详细可以参照，这里再简单说一下)</FONT> </P>
<P class=MsoNormal><FONT size=2>一个build.xml包含一个工程的自动化处理的完整xml说明，并且基本由3种东东组成：</FONT> </P>
<P class=MsoNormal><FONT size=2>&lt;project &gt; <BR><BR>&nbsp;&nbsp;&nbsp; 1.全局变量的定义 <BR>&nbsp;&nbsp;&nbsp; &lt;property/&gt;</FONT> </P>
<P class=MsoNormal><FONT size=2>&nbsp;&nbsp;&nbsp; 2.任务组 <BR>&nbsp;&nbsp;&nbsp; &lt;target&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.许多单项任务... 像copy,delete,javac,jar... <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;task1/&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;task2/&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;task3/&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;/target&gt;</FONT> </P>
<P class=MsoNormal><FONT size=2>&lt;/project&gt;</FONT> </P>
<P class=MsoNormal><FONT color=#ff0000 size=2><B>参考及下载： <BR><BR></B></FONT><FONT size=2><A href="http://www.huihoo.com/java/hello-ant/src/hello-ant-src-first.zip">本文程序:第1个hello-ant <BR></A><BR><A href="http://www.huihoo.com/java/hello-ant/src/hello-ant-src-advance.zip">本文程序:第2个进阶的hello-ant</A></FONT> </P>
<P class=MsoNormal><FONT size=2>ant最新下载：<A href="http://jakarta.apache.org/ant/index.html"> <BR>http://jakarta.apache.org/ant/index.html</A>&nbsp;</FONT> </P>
<P class=MsoNormal><FONT size=2>ant具体的编写方法参考ant手册以下2部分就形， <BR><A href="http://jakarta.apache.org/ant/manual/using">http://jakarta.apache.org/ant/manual/using</A> 使用说明 <BR><A href="http://jakarta.apache.org/ant/manual/coretasklist.html">http://jakarta.apache.org/ant/manual/coretasklist.html</A> 核心tasks <BR>其他一大堆东西你要看也行。不过我觉得比较浪费时间。<A href="http://jakarta.apache.org/ant/manual/index.html"> <BR>http://jakarta.apache.org/ant/manual/index.html</A>&nbsp; 手册index <BR><BR>huihoo.com翻译改编的<A href="http://jakarta.apache.org/ant/manual/using">ant/manual/using</A> <BR><A href="http://www.huihoo.com/java/ant.html">http://www.huihoo.com/java/ant.html</A> <BR><BR></FONT><FONT size=2><SPAN class=p16><B>用ANT构造Application</B></SPAN>作者：余斌斌 &nbsp;&nbsp;&nbsp; <BR><A href="http://developer.ccidnet.com/pub/disp/Article?columnID=295&amp;articleID=27619&amp;pageNO=1">http://developer.ccidnet.com/pub/disp/Article?columnID=295&amp;articleID=27619&amp;pageNO=1 <BR><BR></A>ibm 利用 Ant 和 JUnit 进行增量开发——使用单元测试来逐步改进代码</FONT><A href="http://www-900.ibm.com/developerWorks/cn/java/j-ant/index.shtml"><FONT size=2> <BR>http://www-900.ibm.com/developerWorks/cn/java/j-ant/index.shtml</FONT></A> <BR><BR></P><img src ="http://www.blogjava.net/qq13367612/aggbug/16662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 22:19 <a href="http://www.blogjava.net/qq13367612/articles/16662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ant使用指南（二） </title><link>http://www.blogjava.net/qq13367612/articles/16663.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 14:19:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16663.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16663.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16663.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16663.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16663.html</trackback:ping><description><![CDATA[<P><SPAN class=myp111><FONT id=zoom>Ant 是著名Java开源组织Apache的一个项目，是一个基于java的build工具。它可以使你通过ant脚本语言，自动你的项目拷贝到某个目录，发布项目，或者生成一些代码，执行SQL语言。总之它可以帮助你完成项目开发中除了开发代码以外的大部分辅助性工作。为什么需要手工去编写ant脚本呢，很多IDE工具会为我们生成ant脚本阿？有人会这么问。要知道靠IDE自动生成的东西，总是有这样那样的缺点，以及不足，他不能完全满足你的很多需要。如果出现了问题，你不得不手动修改ant脚本，这时候如果你不了解ant那么你将会一筹莫展。所以为了能使自己的项目变得更专业化，或者想在领导面前show一下的话(有时候很有用)。还是了解一下ant吧。 <BR><BR><B>Ant 的安装</B> <BR><BR>Ant安装很简单，只要你从apache网站上下载他的zip包，然后解压到任何本地磁盘上，在环境变量中添加 Path 的值加上 %ANT_HOME%/bin 就可以了，这样在Dos提示符中就可以使用 ant 这个命令了。其他的你需要设置一下 ANT_HOME 这个环境变量为你解压后的目录。还有就是你要检查一下你的JDK安装目录JAVA_HOME是不是加到你的环境变量里面去了。 <BR><BR><B>使用概述</B> <BR><BR>使用ant 也同样是非常简单的事。通常的做法就是在你的项目里面建立一个XML文件，名字通常可以取 build.xml 。当然你可以取任何你喜欢的名字。这个文件就是你需要ant为你项目做的一些辅助性工作的批处理文件。他的格式可能是这样的。我们先不具体说ant 标记，先有一个感性的认识会比较快的上手。 <BR><BR>例子： <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><?xml version="1.0" encoding="UTF-8"?>
<project default="all" name="pluto-project" basedir=".">
 <target name="all" depends="api, container, portal">
       </target>
       <target name="clean" depends="api-clean, container-clean, portal-clean">
       </target>
       <target name="api">
              <property name="final.name" value="portlet-api-@@DATE@@"/>
              <ant antfile="build.xml" dir="api" target="jar" inheritAll="false" />
       </target>
       <target name="api-clean">
              <ant antfile="build.xml" dir="api" 
                    target="clean" inheritAll="false" />
       </target>
       <target name="container" depends="api">
              <ant antfile="build.xml" dir="container" 
                    target="jar" inheritAll="false" />
       </target>
       <target name="container-clean">
              <ant antfile="build.xml" dir="container" 
                    target="clean" inheritAll="false" />
       </target>
       <target name="portal" depends="container">
              <ant antfile="build.xml" dir="portal" 
                    target="jar" inheritAll="false" />
       </target>
       <target name="portal-clean">
              <ant antfile="build.xml" dir="portal" 
                    target="clean" inheritAll="false" />
       </target>
</project></PRE></TD></TR></TBODY></TABLE><BR><BR>这个build.xml 文件引自apache的一个项目。里面可以看到有 project 以及target 标签。有些项目的build.xml可能不是以“?xml version="1.0" encoding="UTF-8"?”开头的，不过没关系，以后再说明。每一个build.xml 只能有一个project 标签， 每个project 标签里面包含了几个tartget标签。每一个target可以独立执行或者依赖于其他target执行完毕才能执行。</FONT></SPAN> </P>
<P></P>
<P></P>
<P><SPAN class=myp111><FONT id=zoom>以下是一个典型的例子。 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><target name="A"/>
<target name="B" depends="A"/>
<target name="C" depends="B"/>
<target name="D" depends="C,B,A"/></PRE></TD></TR></TBODY></TABLE><BR><BR>当中target A 可以独立执行，但是B、C、D 则依赖于其他target才可以执行，也就是说，执行D, ant 就会按照 A- B- C 这样的顺序先执行其他target。 <BR><BR>Project 标签里面有一个 default="all" ，这就是说明了在默认情况下 all 这个target 会被执行。 <BR><BR><B>定义变量</B> <BR><BR>在这个文件中没有看到属性定义，其实如果你想使用定义变量的话，可以在project 标签下 定义如这样的标签 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><project ……>
<property name="src" location="src"/>
  <property name="build" location="build"/>
<target ……></PRE></TD></TR></TBODY></TABLE><BR><BR>这样你在引用的时候就可以使用形如${build} <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><target name="init">
……
<mkdir dir="${build}"/>
……
  </target></PRE></TD></TR></TBODY></TABLE><BR><BR>这样的变量了，省得自己去修改每一处需要用到变量的地方 <BR><BR><B>添加目录操作</B> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><target name="init">
              <mkdir dir="${dist.dir}" />
              <mkdir dir="${dist.classes.dir}" />
              <mkdir dir="${dist.webapps.dir}" />
       </target></PRE></TD></TR></TBODY></TABLE><BR><BR>这里的dist.dir 等用${}括起来的，是我们定义的变量。</FONT></SPAN> </P>
<P></P>
<P><SPAN class=myp111><FONT id=zoom><B>编译java文件的操作</B> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><target name="compile">
              <javac srcdir="${src.dir}" destdir="${dist.classes.dir}" 
                    debug="true" encoding="GBK">
                     <classpath refid="classpath" />
              </javac>
 <jar destfile="${dist.classes.dir}/lib/app.jar"
                   basedir="${dist.classes.dir}"/>
       </target></PRE></TD></TR></TBODY></TABLE><BR><BR>这里说一下 javac 这个任务标签，其中看到 有 srcdir 、destdir、debug 、encoding 等属性，还有一个 classpath 的子标签。Srcdir就是目标source,需要编译的源文件，destdir就是目的地，编译出来的class的存放地。Debug参数是指明source是不是需要把debug信息编译进去，如果不加这个参数等于在命令行后面加上 -g:none 这个参数。Encoding 这个参数指明以何种编码方式编码你的source文件，对于有中文文字的代码来说这项比较重要。 <BR><BR>Classpath 指明了你需要应用的jar包，或者其它class文件的所在地，这也是非常重要的一项选项。使用方式有以下几种。 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><property name="lib.dir" value="${basedir}/lib" />
…….
<path id="classpath">
            <fileset dir="${lib.dir}">
                  <include name="*.jar"/>
            </fileset>
        </path>
……
<classpath refid="classpath" /></PRE></TD></TR></TBODY></TABLE><BR><BR>作为classpath 引用预先定义的jar包位置,refid 指明了一个引用变量。一般在real-world情况下会这样使用。简单的使用是这样的： <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><javac srcdir="${src}"
         destdir="${build}"
         classpath="xyz.jar"
         debug="on"
  /></PRE></TD></TR></TBODY></TABLE><BR><BR>这就简单了点。 <BR><BR><I>打jar包操作</I> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><jar destfile="${dist}/lib/app.jar" basedir="${dist.classes.dir}"/></PRE></TD></TR></TBODY></TABLE><BR><BR>这个就是把编译好的文件打成jar包的ant 脚本，和上面javac一样，可以放在任意位置。很明显destfile 就是 你想要打成的包，basedir就是你的目标class文件,其它的复杂参数手册上都有，可以对照参考。 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><jar destfile="${dist}/lib/app.jar">
    <fileset dir="${build}/classes"
             excludes="**/Test.class"
    />
    <fileset dir="${src}/resources"/>
  </jar></PRE></TD></TR></TBODY></TABLE><BR><BR>上面这段脚本也很容易理解，就是除了Test.class文件以外，把一个source的resource目录，连同编译后的class脚本一起打进app.jar包内。 <BR><BR><I>复制文件操作</I> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><copy todir="${dist.webapps.dir}/WEB-INF/lib" overwrite="true" flatten="true">
                     <fileset dir="${lib.dir}">
                            <include name="*.jar" />
                            <exclude name="j2ee.jar" />
                     </fileset>
              </copy></PRE></TD></TR></TBODY></TABLE><BR><BR>上面脚本很容易理解。 Todir 指定了需要拷贝的地点，overwrite 是否需要覆盖，flatten是否忽略目的目录结构，只得是不管什么目录，直接拷贝文件到目的地，丢弃其所在结构。 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><fileset dir="${lib.dir}">
                            <include name="*.jar" />
                            <exclude name="j2ee.jar" />
                     </fileset></PRE></TD></TR></TBODY></TABLE><BR><BR>选定除了j2ee.jar包以外的所有在lib.dir变量所定义的包下面的jar包 <BR><BR><I>其它拷贝样式</I> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><copy file="myfile.txt" todir="../some/other/dir"/></PRE></TD></TR></TBODY></TABLE><BR><BR><I>单文件拷贝</I> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><copy file="myfile.txt" todir="../some/other/dir"/></PRE></TD></TR></TBODY></TABLE><BR><BR><I>文件到目录拷贝</I> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><copy todir="../new/dir">
    <fileset dir="src_dir"/>
  </copy></PRE></TD></TR></TBODY></TABLE><BR><BR><I>目录对拷贝</I> <BR>比较有用的命令 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><copy todir="../backup/dir">
    <fileset dir="src_dir"/>
    <filterset>
      <filter token="TITLE" value="Foo Bar"/>
    </filterset>
  </copy></PRE></TD></TR></TBODY></TABLE><BR><BR>拷贝 sr_dir 目录到 backup/dir目录，并且把所有文件中的 @TITLE@ 替换成 Foo Bar 。还有很多用法，等到要使用的时候可以去查手册。 <BR><BR><I>删除操作</I> <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE><target name="clean">
              <delete dir="${dest.dir}"/>
              <delete file="${dest2.dir}"/>
       </target></PRE></TD></TR></TBODY></TABLE><BR><BR>以上操作也是很容易理解的。 <BR><BR><B>执行ant 脚本</B> <BR><BR>在有build.xml 的目录提示符下使用 ant 就可以了。如果是其他的名字，那就使用ant -buildfile 文件名 ，就可以了。 <BR><BR>其它的就是在具体开发过程中real-world式的比较经典的一些操作。比方说利用XDoclet自动生成hibernate配置文件，执行SQL脚本，这些会在后续介绍中详细描述用法。掌握了它们，你就是会在生产过程中大大提高工作效率</FONT></SPAN> </P><img src ="http://www.blogjava.net/qq13367612/aggbug/16663.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 22:19 <a href="http://www.blogjava.net/qq13367612/articles/16663.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探讨Spring框架使用真相</title><link>http://www.blogjava.net/qq13367612/articles/16145.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 20 Oct 2005 07:29:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16145.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16145.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16145.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16145.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16145.html</trackback:ping><description><![CDATA[<P>最近，Spring很热闹，因为实现IoC模式和AOP（见本站专栏），然后又成立公司，吸取上次JBoss的教训，文档不敢收费，结果迎来了一片祝贺声。</P>
<P>　　Spring真正的精华是它的Ioc模式实现的BeanFactory和AOP，它自己在这个基础上延伸的功能有些画蛇添足。</P>
<P>　　其实说白了，大家"惊奇"的是它的IoC模式(使用AOP功能需要了解AOP，比较难)，那么，Spring之类的Ioc模式是什么？ 就是：你在编制程序时，只要写被调用者的接口代码，具体子类实例可通过配置实现。</P>
<P>　　Ioc模式是什么知道的人不多，但是，当他知道生成对象不用再使用new了，只要在配置文件里配置一下，他感到新鲜，其实这就是Ioc模式的实现，PicoContainer是另外一种真正轻量的Ioc模式实现，PicoContainer还是采取代码将对象注射一个小容器中，而Spring采取配置文件。</P>
<P>　　配置式编码其实有利有弊，编码本来可通过开发工具或编译器检查错误，但是过分依赖配置时，就会经常出现因为粗心导致的小错误，如果调试程序出错经常是因为配置文件中小写字母写成大写字母，不知道你是怎么心情？</P>
<P>　　Spring最近还发表了Spring without EJB的书，这倒是说了实话，Spring和EJB其实是相竞争，如同黑与白，如果硬是将两者搭配使用，显得不协调，更不是一些人所谓优化EJB调用的谣言，原因下面将会分析。既然Spring+EJB有缺陷，那么就直接使用Spring+Hibernate架构，但是又带来了新问题：无集群分布式计算性能，只能在一台机器上运行啊，具体分析见：可伸缩性和重/轻量，谁是实用系统的架构主选？</P>
<P>　　下面来分析所谓Spring+EJB的不协调性，正如水和油搅拌在一起使用一样。<BR>　　目前，Spring+EJB有两种应用方式：</P>
<P>　　1. Spring不介入EJB容器，只做Web与EJB之间的接口，这个位置比较尴尬，Web层直接调用EJB的方法比较直接快捷，为什么要中间加个Spring？可实现Web缓存？使用性能更好的AOP框架aspectwerkz啊；实现Web和EJB解耦？这样的工具更多，自己都可以做个小框架实现，就不必打扰背着AOP和IOC双重重担的Spring了吧。</P>
<P>　　2. Spring介入EJB容器，这时，需要在你的ejb-jar.xml中配置beanFactoryPath值指向你为EJB配置的applicationContext.xml，那么你的EJB还需要继承Spring的SimpleRemoteStatelessSessionProxyFactoryBean。</P>
<P>　　好了，现在你的SLSB（无状态Session Bean）成为下面这个样子：</P>
<P>　　void updateUser(){<BR>　　　　<BR>　　　　　　myService.updateUser(); //委托给一个POJO的方法，真正业务逻辑封装在这个POJO中</P>
<P>　　} </P>
<P>　　这样做有很多“优点”，当然最大“优点”是：</P>
<P>　　由于真正业务核心在POJO中实现，因此，只要改一下applicationContext.xml配置，这样，调试时，前台就可以直接调用POJO，不必通过EJB，调试起来方便了，这有一个前提：他们认为调试EJB复杂，其实不然，在JBuilder中，结合Junit，测试EJB如同测试POJO一样方便，这是其他分支，不在此讨论。当部署使用时，再改一下applicationContext.xml配置，指引前台调用到EJB。</P>
<P>　　似乎很巧妙，这里有两个疑问，首先，指引到EJB的改变是什么时候做？持续集成前还是后，在前在后都有问题，这里不仔细分析。</P>
<P>　　这种表面巧妙的优点带来最大的问题是：粗粒度事务机制。所谓粗粒度事务机制，最早见于Petstore的WEB调用EJB Command模式，在这个帖子中有讨论。 </P>
<P>　　下面以代码描述什么是粗粒度事务机制：</P>
<P>　　ejb方法：<BR>　　public void updateUser(){<BR>　　　　service.updateUser();<BR>　　}</P>
<P>　　service是一个POJO，具体方法可能是更新两个表：<BR>　　public void updateUser(){<BR>　　　　updateTabel1();//更新table1<BR>　　　　updateTable2(); //更新table2<BR>　　}</P>
<P>　　当updateTable2()抛出异常，updateTable1()是不回滚的。这样，table1中就有一条错误的多余的记录，而table2则没有这条记录。</P>
<P>　　那么，怎么做才能使两个表记录一致，采取事务机制，只有下面这样书写才能实现真正事务：<BR>在EJB方法中写两个方法，因为EJB方法体缺省是一个事务。<BR>　　public void updateUser(){<BR>　　　　updateTabel1();//更新table1<BR>　　　　updateTable2(); //更新table2<BR>　　}</P>
<P>　　关于EJB自动的事务机制，最近也有一个道友做了测试，对于JBoss中容器管理的事务的疑惑。</P>
<P>　　如果你从事关键事务，就是带money相关操作的事务，这种粗粒度机制可能害苦你，那么，似乎有一种办法可弥补事务，不使用EJB容器事务(CMT)，在service中使用Spring的事务机制。</P>
<P>　　如果使用Spring事务机制，业务核心又在POJO中实现，那么我有一个疑问：还要套上EJB干什么？至此，你终于明白，Spring本质是和EJB竞争的，如果硬套上EJB使用，只是相借助其集群分布式功能，而这个正是Spring目前所缺少的。</P>
<P>　　我太惊异Spring精巧的诡异了，他和EJB关系，正如异型和人的关系一样。</P>
<P>　　为了避免你每次使用Spring时想到粘糊糊的异型，不如Spring without EJB，这也正是Spring的初衷，也是它的一个畅销书名。</P>
<P> </P><img src ="http://www.blogjava.net/qq13367612/aggbug/16145.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-20 15:29 <a href="http://www.blogjava.net/qq13367612/articles/16145.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>爱上Spring的5个理由</title><link>http://www.blogjava.net/qq13367612/articles/15974.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 16 Oct 2005 05:16:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/15974.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/15974.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/15974.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/15974.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/15974.html</trackback:ping><description><![CDATA[约摸15年前的6月的一个酷热的早上，我爬入一艘旧玻璃钢制小艇。这小艇十分老，船身碎片刺入我的手指，它的桨几乎是传统whitewate桨的两倍长。我似乎在游泳而不是在划船，但是无所谓。15年后，我依然为此着迷。<BR><BR>约两年前，我试了试Spring Project,这个被Hibernate站点显著提到的东西。感觉就像那旧艇，十分适合我。为企业应用核心部分的发展，Spring深深地融入了我的编程当中，所以我将其作为我的第4本java书 Spring:A Developer’s  Notebook的主题。在这篇文章中我会告诉你原因。<BR><BR><B><SPAN style="FONT-SIZE: 16px">1．Spring提供更好的平衡</SPAN></B><BR>在河中，我学会更多地利用我的腰部和背部的肌肉来划船，因为我的手臂肌肉无法坚持整天划船。我变得更有效率，更平衡地利用自己的肌肉。通过spring,我可以在每行代码中做更多的事。通过spring你会发现更多其优势，其中最重要的是在对象持久化上。这是一个来自hibernate访问数据对象的函数。<BR><PRE class=overflow title="pre code">public List getReservations( ) {<BR>  return getHibernateTemplate( ).find("from Reservation");<BR>}</PRE><BR>注意那些你没看到的东西。这里没有任何事务处理。Spring允许你建立配置代码去处理它。你不一定要通过关闭session来管理资源。你不一定写你自己的配置。你不一定在这个层次上管理异常，因为这些异常是未经检查的。你可以自由地在最适当的位置去管理他们。没用spring的hibernate方法的代码会是这样的：<BR><PRE class=overflow title="pre code">public List getBikesOldWay( ) throws Exception {<BR>  List bikes = null;<BR>  Session s = null;<BR>  try {<BR>    s = mySessionFactory.openSession( );<BR>    bikes = s.find("from Bike");<BR>  }catch (Exception ex) {<BR>    //handle exception gracefully<BR>  }finally {<BR>    s.close( );<BR>  }<BR>  return bikes;<BR>}</PRE><BR>Spring给我更多优势，让我编程更快，更易维护程序。<BR><BR><B><SPAN style="FONT-SIZE: 16px">2．Spring支持POJO编程</SPAN></B><BR>在EJB 2.x彻底失败之后,我们都在寻找更多方式避免在每个bean中加入笨重的模型去表达企业服务。当然。我们需要事务，安全，持久化，有时还需要远程调用。用EJB时，我不得不去学庞大的API以及通过新的工具和部署过程来工作。结果我变成容器(container)提供的服务的奴隶。而在用Spring时，我可以选择我自己的服务和持久化框架。我在POJOs上编程并通过配置文件添加企业服务。<BR>在Sping:A Developer’s notebook这本书中，我建立了一个RentaBike的程序。我用我的POJOhibRentaBike取代了session bean 或者entity bean,它充当了我的数据访问对象。我还在别处添加了服务。Spring配置文件是一个XML文件，被称为上下文。它含有在容器中的所有bean以及这些bean的属性，还有这些bean需要的服务。让我们来看看下面的例子。<BR><BR>Target:<BR><PRE class=overflow title="pre code"><bean id="rentaBikeTarget" class="com.springbook.HibRentABike"><BR>  <property name="storeName"><BR>    <value>Bruce's Bikes</value><BR>  </property><BR>  <property name="sessionFactory"><BR>    <ref local="sessionFactory"/><BR>  </property><BR>  <property name="transactionManager"><BR>    <ref local="transactionManager"/><BR>  </property><BR></bean></PRE><BR><BR>Interceptor:<BR><PRE class=overflow title="pre code"><bean name="transactionInterceptor" <BR>    class="org.springframework.transaction.interceptor.TransactionInterceptor"><BR>  <property name="transactionManager"><BR>    <ref local="transactionManager"/><BR>  </property><BR>  <property name="transactionAttributeSource"><BR>    <value><BR>      com.springbook.RentABike.transferReservation=<BR>      PROPAGATION_REQUIRED,-ReservationTransferException<BR>      com.springbook.RentABike.save*=PROPAGATION_REQUIRED<BR>      com.springbook.RentABike.*=PROPAGATION_REQUIRED,readOnly<BR>    </value><BR>  </property><BR></bean></PRE><BR><BR>proxy：<BR><PRE class=overflow title="pre code"><bean id="rentaBike" class="org.springframework.aop.framework.ProxyFactoryBean"><BR>  <property name="proxyInterfaces"><BR>    <value>com.springbook.RentABike</value><BR>  </property><BR>  <property name="interceptorNames"><BR>    <value>transactionInterceptor,rentaBikeTarget</value><BR>  </property><BR></bean></PRE><BR><BR>注意这3个不同的bean: The Proxy , The target, and The interceptors. The proxy将调用POJO，以及POJO需要的任何服务。Interceptors包含粘合各调用服务的代码，他们也说明了如何去对待The target中的每个方法。所有需要访问RantaBike的人调用The proxy，这个开始事务访问The target(The POJO)的事务拦截器。Thet target做自己的事返回给事务拦截器(提交事务的对象)，返回到proxy和proxy的调用者。<BR><BR><IMG onmouseover=javascript:ImgShowTip(this); style="DISPLAY: inline" onclick=javascript:ImgClick(this); alt=image src="http://www.matrix.org.cn/resource/upload/content/2005_08_27_230350_eXWOAQWHzc.gif" width=400 onload=javascript:ImgLoad(this); border=0 resized="1"><BR>Figure 1. POJO programming in action<BR><BR>你在POJO外建立了你的程序，配置了它，Spring会隐藏其他的东西。我是一个POJO编程者。<BR><BR><B><SPAN style="FONT-SIZE: 16px">3．依赖注入有助易测性</SPAN></B><BR>Spring通过叫依赖注入(Dependency Injection)的设计模式来提高你的易测性。当一个消费者（consumer）依赖一个从属物（我们会叫它一个服务），你会为consumer建立一个属性。Spring将会建立这个consumer和服务，以及设置这个consumer的属性为服务的值。换种说法，Spring在上下文中管理beans的生命周期，解决依赖性。这是个不通过spring的依赖注入的例子。首先是消费者(consumer),被作为程序的基本模样。<BR><PRE class=overflow title="pre code">public class CommandLineView {<BR><BR>  private RentABike rentaBike;<BR><BR>  public CommandLineView( ) {rentaBike = new ArrayListRentABike("Bruce's Bikes"); }<BR><BR>public void setRentABike(RentABike rentABike){<BR><BR>  this.rentABike = rentABike;<BR><BR>}<BR><BR>  public void printAllBikes( ) {<BR>    System.out.println(rentaBike.toString( ));<BR>    Iterator iter = rentaBike.getBikes().iterator( );<BR>    while(iter.hasNext( )) {<BR>      Bike bike = (Bike)iter.next( );<BR>      System.out.println(bike.toString( ));<BR>    }<BR>  }<BR><BR>  public static final void main(String[] args) {<BR>    CommandLineView clv = new CommandLineView( );<BR>    clv.printAllBikes( );<BR>  }</PRE><BR><BR>接着是service这个模型。这是个简单的通过数组表的实现，其依赖于在这个模型(RentaBike)。<BR><PRE class=overflow title="pre code">interface RentABike {<BR>List getBikes( );<BR>Bike getBike(String serialNo);<BR>}<BR><BR>public class ArrayListRentABike implements RentABike {<BR>   private String storeName;<BR>   final List bikes = new ArrayList();<BR>   public ArrayListRentABike(String storeName) {<BR>      this.storeName = storeName;<BR>      bikes.add(new Bike("Shimano", "Roadmaster", 20, "11111", 15, "Fair"));<BR>      bikes.add(new Bike("Cannondale", "F2000 XTR", 18, "22222",12,"Excellent"));<BR>      bikes.add(new Bike("Trek","6000", 19, "33333", 12.4, "Fair"));<BR>   }<BR><BR>   public String toString() { return "RentABike: " + storeName; }<BR>   public List getBikes() { return bikes; }<BR>   public Bike getBike(String serialNo) {<BR>      Iterator iter = bikes.iterator();<BR>      while(iter.hasNext()) {<BR>         Bike bike = (Bike)iter.next();<BR>         if(serialNo.equals(bike.getSerialNo())) return bike;<BR>      }<BR>      return null;<BR>   }<BR>}</PRE><BR><BR>这是装配程序。粗体的代码就是依赖注入。装配程序演示了服务和消费者，通过设置rentaBike的属性解决了依赖性。<BR><PRE class=overflow title="pre code">public class RentABikeAssembler {<BR>  public static final void main(String[] args) {<BR>    CommandLineView clv = new CommandLineView( );<BR>    RentABike rentaBike = new ArrayListRentABike("Bruce's Bikes");<BR>    clv.setRentaBike(rentaBike);<BR>    clv.printAllBikes( );<BR>  }<BR>}</PRE><BR><BR>当然，Spring将最终符合装配的法则。如果你将服务隐藏到接口程序中，那样你将可以向容器注入接口程序(interface)的任何实现(implementation)。<BR>依赖注入(Dependency injection)让你编写一个生产依赖和一个测试依赖。例如这个例子建立一个stub 对象，它让测试这个程序更轻松。（要更多地了解stubs和mocks，请看“Mocks Aren’t Stubs.”）.<BR>你已经看到RentaBike的Hibernate实现，以及其数组表版本。我也许不想在完全Hibernates实现的代码运行我的所有用户界面测试。而我更愿意简单地通过数组表实现接口。<BR>依赖注入让我联合成品版（通过HibRentaBike），开发版（通过ArrayListRentaBike列表）和测试版(通过mock对象)。当我用java编程的时候，我必须有依赖注入去取得这些mocks进入那些难以进入的位置。<BR><BR><B><SPAN style="FONT-SIZE: 16px">4．控制转入简化JDBC</SPAN></B><BR>JDBC程序是丑陋的，冗长的和乏味的。一个好的抽象层可以改进它，Spring让你通过查询语句和匿名的inner class来定制默认JDBC方法来去除那大部分苦力工作。这是一个简单的JDBC的例子。<BR><PRE class=overflow title="pre code">JdbcTemplate template = new JdbcTemplate(dataSource); <BR>final List names = new LinkedList();<BR><BR>template.query("SELECT USER.NAME FROM USER", <BR>  new RowCallbackHandler() { <BR>      public void processRow(ResultSet rs) throws SQLException {<BR>        names.add(rs.getString(1));<BR>      }<BR>  }<BR>);</PRE><BR><BR>想想这个例子，查询如一个默认JDBC方法的方法。Spring会为结果集中的每一行执行在匿名inner class里的processRow方法的。你在上下文中设置了数据源。而不需要担心开或者关的状态，或者连接，配置数据源，或者管理事务。你不需要说明一个外部的结果集，或者在更低的层次上管理异常，因为spring将你的SQLException折叠放入一个共同的未检查的异常集。其他语言例如Ruby和smalltalk经常通过代码块使用控制转入，但是在java中不经常使用。Spring简化了艰巨的任务。<BR><BR><B><SPAN style="FONT-SIZE: 16px">5．Spring的社区兴旺</SPAN></B><BR>虽然一些开源项目不需要变得相当活跃而使其变得有用。例如Juit做已定目标的工作。如果你喜欢编程模型的话，它有你需要的所有基本东西。而轻量级容器如Spring需要一个充满活力的社区。Spring是你可以找到的最活跃的社区之一，你可以拥有许多好处。<BR><BR>服务(Service)：通过Spring你可以找到数百种不同的服务，从安全到系统管理，到工作流。在持久化上，你可以插入JDO,Hibernate,Top Link,JDBC或者OJB.<BR>支持和教育(Support and education)：:许多独立顾问提供Spring服务，你可以在全世界得到出色的培训。<BR><BR>增强（Enhancements）:Spring一年放出数个主要的发行版。在框架内的出色的测试和清晰的（Factored 扩展）意味着每个发行版都是质量优良的。Spring以及取得正在进行中的Hbernate 3的支持，提供了一个全新的web流程框架。这所有都在最新的发行版中。<BR>商业上支持（Commercial support）:像我这样的作者写Spring的书。现在，你可以找到5本关于Spring的书，以及许多含有部分Spring内容的书。许多产品商也支持Spring。许多开源框架例如Geronimo和Hibernate具有对Spring的特殊支持。<BR>Spring社区让人们用这个框架变得更加容易。我可以雇用Spring开发人员，培训他们。我可以阅读一些书籍来补充我的知识，取得一些帮助我做那些我需要做的所有事情。我没有找到为另一个容器的社区来得如此亲近。<BR><BR>资料：<BR>如果你想读得更多，则有许多你可以取得地方：<BR>Martinfowler.com 有一些关于stubs and mocks 及 dependency injection.的文章。<BR>这里有Spring的框架。<BR>我的第一本书，Better, Faster, Lighter Java, 总结了轻量(lightweight)开发方法。<BR>本文源于Spring: A Developer's Notebook 及其代码。 这是一本程序员注解的书，因此请注意勘误表，当然，可以使用范例代码简化工作。<BR>还有这本O'Reilly出版的书：Hibernate: A Developer's Notebook.<BR><BR>作者简介：<BR>Bruce A. Tate是划艇和山地自行车爱好者，是两个孩子的父亲。在他的业余时间，他在得克萨斯州的奥斯丁当独立顾问。他是4本书的作者,其中包括畅销的Bitter Java以及O’Reilly最近发行的Better,Faster,Lighter Java。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/15974.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-16 13:16 <a href="http://www.blogjava.net/qq13367612/articles/15974.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Learn Spring in spring(转载)</title><link>http://www.blogjava.net/qq13367612/articles/15975.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 16 Oct 2005 05:09:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/15975.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/15975.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/15975.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/15975.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/15975.html</trackback:ping><description><![CDATA[Bromon原创 请尊重版权<BR><BR>　　当前的形势是，非常多的Java程序员言必称Spring，如此大面积的程序员集体叫”春”，体现了Spring框架的威力。春天正是叫春的好时候，你我一起来叫春吧。^_^<BR><BR>　　Spring的设计目的是简化J2EE开发，所以如果我们学习、使用它的时候还需要抓破头皮口吐白沫的话，岂不是个笑话？就我的经验来说，Spring在这方面做得很好，的确是一个很牛叉易用的框架。<BR><BR>　　之前我曾经设计过一个J2EE的考试系统，大量使用了EJB（详见http://blog.csdn.net/bromon/archive/2004/08/27/86291.aspx），我打算同样使用一个考试系统做例子，便于比较。两个系统的大致结构都差不多，不过新的版本采用了轻量级的方案，使用Hibernate作为ORM框架，所有的对象都交给spring来管理。<BR><BR><B>一、IoC与DI</B><BR><BR>　　首先想说说IoC（Inversion of Control，控制倒转）。这是spring的核心，贯穿始终。所谓IoC，对于spring框架来说，就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢，举个简单的例子，我们是如何找女朋友的？常见的情况是，我们到处去看哪里有长得漂亮身材又好的mm，然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………，想办法认识她们，投其所好送其所要，然后嘿嘿……这个过程是复杂深奥的，我们必须自己设计和面对每个环节。传统的程序开发也是如此，在一个对象中，如果要使用另外的对象，就必须得到它（自己new一个，或者从JNDI中查询一个），使用完之后还要将对象销毁（比如Connection等），对象始终会和其他的接口或类藕合起来。<BR><BR>　　那么IoC是如何做的呢？有点像通过婚介找女朋友，在我和女朋友之间引入了一个第三者：婚姻介绍所。婚介管理了很多男男女女的资料，我可以向婚介提出一个列表，告诉它我想找个什么样的女朋友，比如长得像李嘉欣，身材像林熙雷，唱歌像周杰伦，速度像卡洛斯，技术像齐达内之类的，然后婚介就会按照我们的要求，提供一个mm，我们只需要去和她谈恋爱、结婚就行了。简单明了，如果婚介给我们的人选不符合要求，我们就会抛出异常。整个过程不再由我自己控制，而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此，所有的类都会在spring容器中登记，告诉spring你是个什么东西，你需要什么东西，然后spring会在系统运行到适当的时候，把你要的东西主动给你，同时也把你交给其他需要你的东西。所有的类的创建、销毁都由spring来控制，也就是说控制对象生存周期的不再是引用它的对象，而是spring。对于某个具体的对象而言，以前是它控制其他对象，现在是所有对象都被spring控制，所以这叫控制反转。如果你还不明白的话，我决定放弃。<BR><BR>　　IoC的一个重点是在系统运行中，动态的向某个对象提供它所需要的其他对象。这一点是通过DI（Dependency Injection，依赖注入）来实现的。比如对象A需要操作数据库，以前我们总是要在A中自己编写代码来获得一个Connection对象，有了spring我们就只需要告诉spring，A中需要一个Connection，至于这个Connection怎么构造，何时构造，A不需要知道。在系统运行时，spring会在适当的时候制造一个Connection，然后像打针一样，注射到A当中，这样就完成了对各个对象之间关系的控制。A需要依赖Connection才能正常运行，而这个Connection是由spring注入到A中的，依赖注入的名字就这么来的。那么DI是如何实现的呢？Java 1.3之后一个重要特征是反射（reflection），它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性，spring就是通过反射来实现注入的。关于反射的相关资料请查阅java doc。<BR><BR>　　理解了IoC和DI的概念后，一切都将变得简单明了，剩下的工作只是在spring的框架中堆积木而已。<BR>二、spring管理对象的简单例子<BR><BR>Bromon原创 请尊重版权<BR><BR>　　任何需要交给spring管理的对象，都必须在配置文件中注册，这个过程被称为wiring，下面做一个最简单的Hello world演示，我们将要注册的类如下：<BR><BR><PRE class=overflow title="pre code">/*<BR> * 创建日期 2005-3-22<BR> */<BR>package org.bromon.spring.test;<BR><BR>/**<BR> * @author Bromon<BR> */<BR>public class HelloTalker <BR>{<BR>    public String greeting()<BR>    {<BR>        return "hello world";<BR>    }<BR>}</PRE><BR>　　然后我们来编写一个spring配置文件，文件名任意，在我这里它是springConfig.xml，需要注意的是这个文件应该存放在classpath所包含的路径中：<BR><BR><PRE class=overflow title="pre code"><?xml version="1.0" encoding="UTF-8"?><BR><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><BR><beans><BR>        <bean id=”helloTalker” class=” org.bromon.spring.test.HelloTalker”><BR>        </bean><BR></beans></PRE><BR><BR>　　通过使用bean标签，注册了一个HelloTalker对象，它的名字叫做helloTalker。然后我们编写一个测试类，它的工作是利用spring框架提供的接口，加载配置文件，通过指定对象的id，获得一个对象。它的代码如下：<BR><PRE class=overflow title="pre code">/*<BR> * 创建日期 2005-3-17<BR> */<BR>package org.bromon.spring.test.junit;<BR><BR>import java.io.FileInputStream;<BR><BR>import org.springframework.beans.factory.xml.XmlBeanFactory;<BR>import org.springframework.context.ApplicationContext;<BR>import org.springframework.context.support.ClassPathXmlApplicationContext;<BR><BR>import org.bromon.spring.test;<BR><BR>/**<BR> * @author Bromon<BR> */<BR>public class TestStudentManager extends TestCase {<BR><BR>    public void testHelloTalker() <BR>    {<BR>        try<BR>        {<BR>            ApplicationContext context =new ClassPathXmlApplicationContext("springConfig.xml");<BR>            <BR>HelloTalker ht=(HelloTalker)context.getBean(“helloTalker”);<BR>System.out.println(ht.greeting());<BR>        }catch(Exception e)<BR>        {<BR>            e.printStackTrace();<BR>        }<BR>    }<BR><BR>}</PRE><BR><BR>　　这个程序就完成了，因为只有一个对象HelloTalker被注册到了spring中，所以不存在对象间的依赖，当然也就不涉及依赖注入。下面演示一个简单的依赖注入：<BR><BR>　　第一步是修改HelloTalker，增加一个String name属性：<BR><PRE class=overflow title="pre code">public String name;</PRE><BR>　　为该属性编写set方法，该方法必须严格遵守javabean的命名规则：<BR><PRE class=overflow title="pre code">public void setName(String name)<BR>{<BR>        this.name=name;<BR>}</PRE><BR>　　修改greeting方法：<BR><BR><PRE class=overflow title="pre code">public String greeting()<BR>{<BR>　　return "hello "+name;<BR>}</PRE><BR><BR>　　如你所见，name属性没有初试化，因为它的值将在运行过程中被spring动态注射入。<BR><BR>　　第二步，修改springConfig.xml中唯一的这个bean配置：<BR><BR><PRE class=overflow title="pre code"><bean id=”helloTalker” class=” org.bromon.spring.test.HelloTalker”><BR>        <property name=”name”><BR>                <value>bromon</value><BR>        </property><BR></bean></PRE><BR><BR>　　修改完成。我们将一个名字”bromon”写死在springConfig.xml中，它会被动态的注入到HelloTalker的name属性中，greeting方法将会把它打印出来。重新运行刚才的junit类，可以看到结果。<BR><BR>　　我们只演示了如何注入一个最简单的String，实际上我们可以注入任何值类型，也可以注入任何类的实例，也可以注入List、Map、Properties。配置文件管理了所有的对象和对象间的关系，而对象则只负责执行自己的功能，他们的职责越少，藕合度越低，系统就越容易测试，管理维护也更容易。<BR><BR>　　<bean>标签还有很多属性，用于指定对象如何被实例化，它也有很多子标签用于配置对象的属性，请大家参考相关的DTD和文档，能够很快的掌握。本系列文章不是spring手册，spring的基础知识请参考spring in action，足够详细准确。后面的章节更多的讨论系统设计、开发的一些细节和高级特性。<BR>三、spring中的hibernate开发<BR><BR>Bromon原创　请尊重版权<BR><BR>　　spring中对hibernate的支持是非常强大的，从一个简单的例子就看得出来，从这个例子中我们还将对所谓的轻量级容器做一些讨论。<BR><BR>　　首先需要配置数据源，通常我们有两种方式获得Connection，一是自己编写代码获得连接，二是从JNDI环境中得到DataSource，然后产生一个Connection。无论怎样，既然是spring下面的对象，就应该注册到配置文件中。假设我们需要一个连接mysql下面一个叫做examer的数据库，手动方式的配置是：<BR><BR><PRE class=overflow title="pre code"><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><BR>                <property name="driverClassName"><BR>                        <value>com.mysql.jdbc.Driver</value><BR>                </property><BR>                <property name="url"><BR>                        <value>jdbc:mysql://localhost/examer</value><BR>                </property><BR>                <property name="username"><BR>                        <value>root</value><BR>                </property><BR>                <property name="password"><BR>                        <value></value><BR>                </property><BR>        </bean></PRE><BR><BR>　　很好读是不是？假如我们使用JNDI数据源，那么dataSource的声明就应该是：<BR><BR><PRE class=overflow title="pre code"><bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"><BR>                <property name="jndiName"><BR>                        <value>java:comp/env/jdbc/springExamer</value><BR>                </property><BR>        </bean></PRE><BR><BR>　　你需要在JNDI环境中绑定一个名为jdbc/springExamer的东西，这段代码才有实际意义。另外需要提醒的是，所有的bean声明，它的id必须是唯一的。<BR><BR>　　在本系统中，数据库操作是被hibernate封装起来的，所以dataSource是不需要注入到具体的逻辑类中，它只会被注给hibernate的sessionFactory。<BR><BR>　　按照常规思路，我们需要在spring中注册hibernate的sessionFactory，它应该是我们自己编写的一个类，获得dataSource，返回sessionFactory，其他的逻辑类通过这个sessionFactory获得session进行数据库操作。<BR><BR>　　但是我们有另外一种选择，spring直接提供了对sessionFactory的封装，你只需要注册一个spring自己的类，给它提供必须的属性，它会返回一个org.springframework.orm.hibernate.HibernateTemplate，这个类封装了add、del等操作，它的封装程度相当高，通过它来编写hibernate应用非常简单。但是问题出来了，我们该如何选择？<BR><BR>　　表面上看，使用spring自己的库无疑更加简单，但是请注意，spring是一个轻量级的框架，所谓轻量级，一个重要特征就是无侵入性，也就是你使用这套框架，不会被它绑定，被spring管理的类，应该不需要使用它的接口和抽象类，这样你的系统不会对spring产生依赖。但是如果你使用了spring封装的方式去操作hibernate，就必须继承org.springframework.orm.hibernate.support.HibernateDaoSupport类，这导致了绑定。所以做这样的选择是有点痛苦的，如果有一天spring框架不存在了，你的代码怎么升级维护？具体问题只能具体分析，在我们的应用中，完全使用了spring封装的HibernateTemplate，它太好用了，所以容易上瘾。<BR><BR>        假设我们有一张student表，结构很简单：<BR><BR>id                                自动增长<BR>name                        varchar(40)<BR>password                        varchar(32)<BR>grade                        int(4)                        年级<BR>sex                                Boolean                        性别(true为男，false为女)<BR><BR>        设计一个Student类来映射这张表：<BR><BR><PRE class=overflow title="pre code">        /*<BR> * 创建日期 2005-3-17<BR> */<BR>package net.bromon.spring.examer.pojo;<BR><BR>/**<BR> * @author Bromon<BR> */<BR>public class Student <BR>{<BR>    private int id;<BR>    private String name;<BR>    private String password;<BR>    private int grade;//年级<BR>    private boolean sex;<BR>    <BR>    get/set方法……….<BR>}</PRE><BR><BR>编写Student.hbm.xml，让hibernate知道如何去关联student表和Student类，该文件和Student.java在同一目录：<BR><BR><PRE class=overflow title="pre code"><hibernate-mapping><BR>        <class name="net.bromon.spring.examer.pojo.Student" table="student"><BR>                <id name="id" column="id"><BR>                        <generator class="identity"/><BR>                </id><BR>                <BR>                <property name="name" column="name" /><BR>                <property name="password" column="password" /><BR>                <property name="grade" column="grade" /><BR>                <property name="sex" column="sex" /><BR>        </class><BR></hibernate-mapping></PRE><BR><BR>　　然后我们可以在spring中配置sessionFactory：<BR><BR><PRE class=overflow title="pre code"><bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"><BR>                <property name="dataSource"><BR>                        <ref bean="dataSource"/><BR>                </property><BR>                <BR>                <property name="hibernateProperties"><BR>                        <props><BR>                                <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop><BR>                        </props><BR>                </property><BR>                <BR>                <property name="mappingDirectoryLocations"><BR>                        <list><BR>                                <value>classpath:/net/bromon/spring/examer/pojo</value><BR>                        </list><BR>                </property><BR>        </bean></PRE><BR><BR>　　其中引用了我们之前注册过的dataSource，mappingDirectoryLocations属性指明了.hbm.xml文件在哪里路径，该文件夹下面的.hbm.xml文件会被全部加载。<BR><BR>　　一切都准备就绪，现在我们要加入一个StudentManager类，来进行增删查改的操作：<BR><PRE class=overflow title="pre code">/*<BR> * 创建日期 2005-3-17<BR> */<BR>package net.bromon.spring.examer.student;<BR><BR>import net.bromon.spring.examer.pojo.Student;<BR><BR>import org.springframework.orm.hibernate.HibernateTemplate;<BR>import org.springframework.orm.hibernate.LocalSessionFactoryBean;<BR>import org.springframework.orm.hibernate.support.HibernateDaoSupport;<BR><BR>/**<BR> * @author Bromon<BR> */<BR>public class StudentManager extends HibernateDaoSupport<BR>{<BR>    private LocalSessionFactoryBean sessionFactory;<BR>    private HibernateTemplate ht;<BR>    public StudentManager()<BR>    {<BR>        this.ht=super.getHibernateTemplate();<BR>    }<BR>    <BR>    public void add(Student s)<BR>    {   <BR>        ht.save(s);//插入一条数据只需要这一行代码<BR>    }<BR>}</PRE><BR><BR>　　该类只演示了如何增加一个Student，HibernateTemplate还封装了很多有用的方法，请查阅spring文档。StudentManager中的sessionFactory是由spring注入的，但是StudentManager并没有对sessionFactory做任何的处理，这是因为所有的处理都被HibernateDaoSupport.getHibernateTemplate()封装。整个StudentManager中也看不到任何的异常处理，他们也都被基类封装了。<BR><BR>　　最后一个步骤就是在spring中注册StudentManger，然后向它注入sessionFactory：<BR><BR><PRE class=overflow title="pre code"><bean id="studentManager" class="net.bromon.spring.examer.student.StudentManager"><BR>                <property name="sessionFactory"><BR>                        <ref bean="sessionFactory"/><BR>                </property><BR>        </bean></PRE><BR><BR>        所有的配置都完成了，下面做单元测试：<BR><BR><PRE class=overflow title="pre code">        /*<BR> * 创建日期 2005-3-17<BR> */<BR>package net.bromon.spring.examer.student.test;<BR><BR>import java.io.FileInputStream;<BR><BR>import org.springframework.beans.factory.xml.XmlBeanFactory;<BR>import org.springframework.context.ApplicationContext;<BR>import org.springframework.context.support.ClassPathXmlApplicationContext;<BR><BR>import net.bromon.spring.examer.pojo.Student;<BR>import net.bromon.spring.examer.student.StudentManager;<BR>import junit.framework.TestCase;<BR><BR>/**<BR> * @author Bromon<BR> */<BR>public class TestStudentManager extends TestCase {<BR><BR>    public void testAdd() <BR>    {<BR>        try<BR>        {<BR>            ApplicationContext context =new ClassPathXmlApplicationContext("springConfig.xml");<BR>            <BR>            Student s=new Student();<BR>            s.setName("bromon");<BR>            s.setPassword("123");<BR>            s.setGrade(3);<BR>            s.setSex(true);<BR>            <BR>            ((StudentManager)context.getBean("studentManager")).add(s);<BR>        }catch(Exception e)<BR>        {<BR>            e.printStackTrace();<BR>        }<BR>    }<BR><BR>}</PRE><BR><BR>　　Spring已经将hibernate的操作简化到了非常高的程度，最关键的是整个开发可以由设计来驱动，如果一个团队对spring有足够的熟悉，那么完全可以由设计师规划所有的类，整理清楚类之间的关系，写成配置文件，然后编写hibernate映射文件，将数据表与pojo关联，成员就可以完全在设计方案内工作，利用spring封装好的hibernate模版，开发起来速度非常快，调试也很容易。它能够解决如何在团队内贯彻设计方案的问题。<BR><BR>　　由于本文不讲解hibernate的使用，所以相关内容请查阅hibernate文档。<BR>四、Spring中的事务控制<BR><BR>        Spring和EJB一样，提供了两种事务管理方式：编程式和声明式。在考试系统中我们将使用声明式的事务管理，这是spring推荐的做法。使用这种方式可以体验到spring的强大便捷，而且我们无须在Dao类中编写任何特殊的代码，只需要通过配置文件就可以让普通的java类加载到事务管理中，这个意义是很重大的。<BR><BR>        Spring中进行事务管理的通常方式是利用AOP（面向切片编程）的方式，为普通java类封装事务控制，它是通过动态代理实现的，由于接口是延迟实例化的，spring在这段时间内通过拦截器，加载事务切片。原理就是这样，具体细节请参考jdk中有关动态代理的文档。本文主要讲解如何在spring中进行事务控制。<BR><BR>        动态代理的一个重要特征是，它是针对接口的，所以我们的dao要通过动态代理来让spring接管事务，就必须在dao前面抽象出一个接口，当然如果没有这样的接口，那么spring会使用CGLIB来解决问题，但这不是spring推荐的方式，我们也不做讨论。<BR><BR>        参照前面的例子，我们为StudentManager.java定义一个接口，它的内容如下：<BR><BR><PRE class=overflow title="pre code">/*<BR> * 创建日期 2005-3-25<BR> */<BR>package org.bromon.spring.examer.student;<BR><BR>import java.util.List;<BR><BR>import org.bromon.spring.examer.pojo.Student;<BR><BR>/**<BR> * @author Bromon<BR> */<BR>public interface StudentManagerInterface<BR>{<BR>    public void add(Student s);<BR>    public void del(Student s);<BR>    public void update(Student s);<BR>    <BR>    public List loadAll();<BR>    public Student loadById(int id);<BR>}</PRE><BR>StudentManager也应该做出修改，实现该接口：<BR><BR><PRE class=overflow title="pre code">public class StudentManager extends HibernateDaoSupport implements StudentManagerInterface</PRE>        <BR>现在需要修改配置文件，用于定义Hibrenate适用的事务管理器，并且把sessionFactory注入进去，同时还需要通过注册一个DefaultTransactionAttribute对象，来指出事务策略。其中sessionFactory的定义已经在本文的第三章中说明。<BR><BR>        首先定义一个Hibernate的事务管理器，让它来管理sessionFactory：<BR><PRE class=overflow title="pre code"><bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"><BR>                <property name="sessionFactory"><BR>                        <ref bean="sessionFactory"/><BR>                </property><BR></bean></PRE><BR>下面定义事务管理策略，我们希望把策略定义在方法这个级别上，提供最大的灵活性，本例中将add方法定义为：PROPAGATION_REQUIRES_NEW，这可以保证它将始终运行在一个事务中。<BR><BR><PRE class=overflow title="pre code"><bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource"><BR>                <property name="properties"><BR>                        <props><BR>                                <prop key="add"><BR>                                        PROPAGATION_REQUIRES_NEW<BR>                                </prop><BR>                        </props><BR>                </property><BR>        </bean></PRE><BR>我们不仅可以为add方法定义事务策略,还可以定义事务隔离程度和回滚策略,他们以逗号隔开,比如我们的add事务可以定义为:<BR><BR><PRE class=overflow title="pre code"><prop key="add"><BR>        PROPAGATION_REQUIRES_NEW,-ExamerException<BR></prop</PRE>><BR><BR>        这个事务策略表示add方法将会独占一个事务，当事务过程中产生ExamerException异常，事务会回滚。<BR><BR>        Add/update/del都是写入方法，对于select（读取）方法，我们可以指定较为复杂的事务策略，比如对于loadAll()方法：<BR><BR>        <PRE class=overflow title="pre code"><prop key=”loadAll”><BR>                PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITED,readOnly<BR>        </prop></PRE><BR><BR>        该事务的含义为，loadAll方法支持事务，不会读去位提交的数据，它的数据为只读（可提高执行速度）。<BR><BR>        如你所见，我们的StudentManagerInterface接口中还有一个loadById(int id)方法，也许我们将来还会有很多的loadByXXXX的方法，难道要意义为他们指定事务策略？太烦人了，他们应该和loadAll()一样，所以我们可以使用通配符，定义所有的loadXXXX方法：<BR><BR>        <PRE class=overflow title="pre code"><prop key=”load*”><BR>                PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITED,readOnly<BR>        </prop></PRE><BR><BR>        现在可以定义事务管理器：<BR><PRE class=overflow title="pre code"><bean id="studentManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><BR>                <property name="target"><BR>                        <ref bean="studentManager"/><BR>                </property><BR>                <property name="transactionManager"><BR>                        <ref bean="transactionManager"/><BR>                </property><BR>                <property name="transactionAttributeSource"><BR>                        <ref bean="transactionAttributeSource"/><BR>                </property><BR></bean></PRE><BR><BR>        这个bean的外观是一个接口(StudentManagerInterface)，我们指出了它的具体实现(studentManager)，而且为它绑定了事务策略。在客户端使用的时候，获得对象是StudentManagerInterface，所有的操作都是针对这个接口的。测试代码并没有改变，我们虽然修改了很多地方，加入了事务控制，但是客户端并没有受到影响，这也体现了spring的一些优势。测试代码如下：<BR><BR>        <PRE class=overflow title="pre code">public void testAdd() <BR>    {<BR>        ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");<BR>        StudentManager sm=(StudentManager)ctx.getBean("studentManager");<BR>        <BR>        Student s=new Student();<BR>        s.setId(1);<BR>        s.setName("bromon");<BR>        s.setPassword("123");<BR>        s.setGrade(1);<BR>        s.setSex(0);<BR>        <BR>        sm.add(s);<BR>}</PRE><BR><BR>        通过以上的代码可以看出，spring可以简单的把普通的java class纳入事务管理，声明性的事务操作起来也很容易。有了spring之后，声明性事务不再是EJB独有，我们不必为了获得声明性事务的功能而去忍受EJB带来的种种不便。<BR><BR>        我所使用的mysql是不支持事务的，你可以更换使用PostgreSQL，有了spring+hibernate，更换db并不像以前那样恐怖了，步骤很简单：<BR><BR>1、        添加PostgreSQL的jdbc驱动<BR>2、        修改dataSource配置，包括驱动名称、url、帐号、密码<BR>3、        修改sessionFactory的数据库dailet为net.sf.hibernate.dialect.PostgreSQLDialect<BR>4、        修改hbm.xml中的主键生成策略为increment<BR><BR>所有的修改都在配置文件中完成，业务代码不需要任何修改，我很满意，How about u?<BR><BR>附A  pring中的所有事务策略<BR><BR>        PROPAGATION_MANDATORY<BR>        PROPAGATION_NESTED                        <BR>        PROPAGATION_NEVER                        <BR>        PROPAGATION_NOT_SUPPORTED<BR>        PROPAGATION_REQUIRED<BR>        PROPAGATION_REQUIRED_NEW<BR>        PROPAGATION_SUPPORTS<BR><BR>附B                Spring中所有的隔离策略：<BR><BR>        ISOLATION_DEFAULT<BR>        ISOLATION_READ_UNCOMMITED<BR>        ISOLATION_COMMITED<BR>        ISOLATION_REPEATABLE_READ<BR>        ISOLATION_SERIALIZABLE<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/15975.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-16 13:09 <a href="http://www.blogjava.net/qq13367612/articles/15975.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析</title><link>http://www.blogjava.net/qq13367612/articles/15976.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 16 Oct 2005 05:06:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/15976.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/15976.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/15976.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/15976.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/15976.html</trackback:ping><description><![CDATA[一、引子<BR><BR><BR><BR>JUnit源码是我仔细阅读过的第一个开源项目源码。阅读高手写的代码能学到一些好的编程风格和实现思路，这是提高自己编程水平行之有效的方法，因此早就想看看这些赫赫有名的框架是怎么回事了。今天就拿最简单的JUnit下手，也算开始自己的源码分析之路。<BR><BR>JUnit作为最著名的单元测试框架，由两位业界有名人士协力完成，已经经历了多次版本升级（了解JUnit基础、JUnit实践）。JUnit总体来说短小而精悍，有不少值得我们借鉴的经验在里面；但是也有一些不足存在，当然这对于任何程序来说都是难免的。<BR><BR><BR>下面我们将从整体（宏观）和细节（微观）两方面来分析JUnit源码，以下分析基于3.8.1版。<BR><BR><BR>二、宏观——架构与模式<BR><BR><BR><BR>打开源码文件，你会发现JUnit源码被分配到6个包中：junit.awtui、junit.swingui、junit.textui、junit.extensions、junit.framework、junit.runner。其中前三个包中包含了JUnit运行时的入口程序以及运行结果显示界面，它们对于JUnit使用者来说基本是透明的。junit.runner包中包含了支持单元测试运行的一些基础类以及自己的类加载器，它对于JUnit使用者来说是完全透明的。<BR><BR><BR><BR>剩下的两个包是和使用JUnit进行单元测试紧密联系在一起的。其中junit.framework包含有编写一般JUnit单元测试类必须是用到的JUnit类；而junit.extensions则是对framework包在功能上的一些必要扩展以及为更多的功能扩展留下的接口。<BR><BR><BR><BR>JUnit提倡单元测试的简单化和自动化。这就要求JUnit的使用要简单化，而且要很容易的实现自动化测试。整个JUnit的设计大概也是遵循这个前提吧。整个框架的骨干仅有三个类组成（下图所示）。<BR><IMG onmouseover=javascript:ImgShowTip(this); style="DISPLAY: inline" onclick=javascript:ImgClick(this); alt=image src="http://www.matrix.org.cn/resource/article/upload/article/a2005314104925.jpg" onload=javascript:ImgLoad(this); border=0><BR><BR>       如果你掌握了TestCase、TestSuite、BaseTestRunner的工作方式，那么你就可以随心所欲的编写测试代码了。<BR><BR><BR><BR>       下面我们来看看junit.framework中类之间的关系，下图是我根据源代码分析出来的，大部分关系都表示了出来。<BR><BR><IMG onmouseover=javascript:ImgShowTip(this); style="DISPLAY: inline" onclick=javascript:ImgClick(this); alt=image src="http://www.matrix.org.cn/resource/article/upload/article/2005314105118.jpg" onload=javascript:ImgLoad(this); border=0><BR><BR>先来看看各个类的职责。Assert类提供了JUnit使用的一整套的断言，这套断言都被TestCase继承下来，Assert也就变成了透明的。Test接口是为了统一TestCase和TestSuite的类型；而TestCase里面提供了运行单元测试类的方法；在TestSuite中则提供了加载单元测试类，检验测试类格式等等的方法。TestResult故名思意就是提供存放测试结果的地方，但是在JUnit中它还带有一点控制器的功能。<BR><BR><BR>在这里指出其中我认为有些不妥的地方。图上TestCase和TestResult之间是双向的依赖关系，而在UML类图的关系中指出：依赖关系总是单向的。就让我们来看看这这个可疑的地方。<BR><BR><BR>TestCase中的代码：<BR><BR><PRE class=overflow title="pre code">/**<BR><BR>* Runs the test case and collects the results in TestResult.<BR>*/<BR><BR>public void run(TestResult result) {<BR>//调用了result中的run方法，<BR>//TestResult按照名称来看应该是一个记录测试结果的类，怎么还能run？<BR>       result.run(this);<BR><BR>}<BR><BR>相应得TestResult中的代码：<BR><BR>/**<BR>* Runs a TestCase.<BR>*/<BR>protected void run(final TestCase test) {<BR><BR>       //开始测试<BR>       startTest(test);<BR><BR>       //这个匿名内类的使用一会再讲<BR><BR>       Protectable p= new Protectable() {<BR>              public void protect() throws Throwable {<BR><BR>                    //天那，这里又调用了TestCase里面的runBare方法<BR><BR>                    test.runBare();<BR><BR>             }<BR><BR>      };<BR><BR>       runProtected(test, p); //这个方法就是要执行上面制定的匿名内类<BR>       endTest(test);<BR><BR><BR><BR>}</PRE><BR><BR><BR><BR>TestResult中runProtected方法：<BR><BR><BR><BR><PRE class=overflow title="pre code">public void runProtected(final Test test, Protectable p) {<BR><BR><BR><BR>       try {<BR><BR><BR><BR>              p.protect();<BR><BR><BR><BR>       } <BR><BR><BR><BR>       catch (AssertionFailedError e) {<BR><BR><BR><BR>              addFailure(test, e);              //给TestResult添加失败记录<BR><BR><BR><BR>       }<BR><BR><BR><BR>       catch (ThreadDeath e) { // don't catch ThreadDeath by accident<BR><BR><BR><BR>              throw e;<BR><BR><BR><BR>       }<BR><BR><BR><BR>       catch (Throwable e) {<BR><BR><BR><BR>              addError(test, e);        //给TestResult添加出错记录<BR><BR><BR><BR>       }<BR><BR><BR><BR>}</PRE><BR><BR><BR><BR>为什么JUnit里面会出现这样奇怪的依赖关系，还有违反单一职责原则的TestResult？当我看到junit.extentions包中的TestSetup时，也许我猜到了作者的用意。我们来看下TestSetup中有关的代码：<BR><BR><BR><BR><PRE class=overflow title="pre code">public void run(final TestResult result) {<BR><BR><BR><BR>       //又看到了上面类似的匿名内部类<BR><BR><BR><BR>       Protectable p= new Protectable() {<BR><BR><BR><BR>              public void protect() throws Exception {<BR><BR><BR><BR>                     //不过这个内部类里面的实现有所不同<BR><BR><BR><BR>setUp();<BR><BR><BR><BR>                     basicRun(result);<BR><BR><BR><BR>                     tearDown();<BR><BR><BR><BR>              }<BR><BR><BR><BR>       };<BR><BR><BR><BR>       //调用了TestResult中的runProtected方法来执行上面的实现<BR><BR><BR><BR>       result.runProtected(this, p);<BR><BR><BR><BR>}</PRE><BR><BR><BR><BR>这个类的产生是为了弥补TestCase类的一个小小的缺陷（具体请见下部分）。注意到在这个类里面也有和TestResult类似的匿名内部类。这种匿名内部类全是Protected接口的无名实现，这里的目的我认为有两点：<BR><BR><BR><BR>1)        由于内部类可以在接下来的情景中完全不可见，而且不被任何人使用，因此也就隐藏了接口的实现细节。<BR><BR><BR><BR>2)        为了提高可重用性，而使用内部类比较快捷。这样不管你protect方法里面具体执行什么，对它错误、失败、异常捕捉的代码（TestResult中的runProtected方法）就可以重用了。<BR><BR><BR><BR>这也正是为什么会出现上面那样奇怪的依赖关系：为了复用，就要让runProtected方法放在一个TestCase和TestSetup都能调用的地方。<BR><BR><BR><BR>不过我认为为了复用而破坏了系统良好的结构和可读性，是需要仔细斟酌的。JUnit这样的设计估计是为了以后框架多次扩展后的重用考虑的。<BR><BR><BR><BR>说完了让我费解的问题。谈谈我觉得JUnit框架中最让我感叹的地方，那就是小小的框架里面使用了很多设计模式在里面。而这些模式的使用也正是为了体现出整个框架结构的简洁、可扩展。我将粗略的分析如下（模式应用的详细内容请关注我关于设计模式的文章）。先看看在junit.framework里面使用的设计模式。<BR><BR><BR><BR>       命令模式：作为辅助单元测试的框架，开发人员在使用它的时候，应该仅仅关心测试用例的编写，JUnit只是一个测试用例的执行器和结果查看器，不应该关心太多关于这个框架的细节。而对于JUnit来说，它并不需要知道请求TestCase的操作信息，仅把它当作一种命令来执行，然后把执行测试结果发给开发人员。命令模式正是为了达到这种送耦合的目的。<BR><BR><BR><BR>       组合模式：当系统的测试用例慢慢变得多起来，挨个运行测试用例就成了一个棘手的问题。作为一个方便使用的单元测试框架，这一点是必须解决的。因此JUnit里面提供了TestSuite的功能，它允许将多个测试用例放到一个TestSuite里面来一次执行；而且要进一步的支持TestSuite里面套TestSuite的功能。使用组合模式能够很好的解决这个问题。<BR>在上面我们已经提到了junit.extentions包中的内容TestSetup。来看看整个包的结构吧。<IMG onmouseover=javascript:ImgShowTip(this); style="DISPLAY: inline" onclick=javascript:ImgClick(this); alt=image src="http://www.matrix.org.cn/resource/article/upload/article/a2005314105638.jpg" onload=javascript:ImgLoad(this); border=0><BR><BR><BR>先简要的介绍下包中各个类的功能。ActiveTestSuite对TestSuite进行了改进，使得每个test运行在一个单独的线程里面，并且只到所有的线程都结束了才会结束整个测试。ExceptionTestCase是对TestCase进行的改进，可以方便的判断测试类是否抛出了期望的异常。而剩下的三个类，大概你看的出来是使用了装饰模式来设计的。其中TestDecorator为具体装饰类制定好了使用规则，RepeatedTest和TestSetup则是具体实现的装饰类。<BR><BR><BR><BR>那为什么extentions包中ActiveTestSuite和ExceptionTestCase没有使用装饰模式呢？原因在于装饰模式在结构上要求存在类似于组合模式的递归。而对于已有的TestCase和TestSuite来说，直接继承它们要比构建一个新的递归结构要来得快得多而且简单；并且这些增强功能都只是针对TestCase或者TestSuite。使用了装饰模式来扩展的类与以上不同的是，它们功能的增强是针对任何Test实现的。如果不采用装饰模式同样的功能要为TestCase、TestSuite以及以后的其他Test实现分别写出子类。因此使用装饰模式能够很巧妙的解决这个问题。<BR><BR>下面来介绍下junit.runner包。上面已经提到，对于JUnit使用者来说，它可说是完全透明的，这个包里面提供了JUnit自己的测试类加载。下面就是包中所有类的关系图。<BR><BR><BR><IMG onmouseover=javascript:ImgShowTip(this); style="DISPLAY: inline" onclick=javascript:ImgClick(this); alt=image src="http://www.matrix.org.cn/resource/article/upload/article/2005314105721.jpg" onload=javascript:ImgLoad(this); border=0><BR><BR>没有什么好讲的，都是使用反射机制来将测试类加载进来，还有读取properties文件的操作。如果想学习下反射机制的应用可以阅读这部分的源码。<BR><BR>剩下的三个包这里也不作介绍，大部分的内容都是GUI的绘制（当然junit.textui包除外）。<BR><BR>JUnit中还使用了观察者模式来完成单元测试结果的自动更新（详细内容请见我关于观察者模式的文章）。<BR><BR>这样，对JUnit的整体框架有了全面的认识。总体来说各个包分工明确，设计上采用了必要的设计模式来增强了扩展性和重用性，很值得学习和借鉴。 <BR><img src ="http://www.blogjava.net/qq13367612/aggbug/15976.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-16 13:06 <a href="http://www.blogjava.net/qq13367612/articles/15976.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Junit 学习笔记</title><link>http://www.blogjava.net/qq13367612/articles/15977.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 16 Oct 2005 05:04:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/15977.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/15977.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/15977.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/15977.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/15977.html</trackback:ping><description><![CDATA[<A title="Junit Home page" href="http://www.junit.org/index.htm">JUnit</A>是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架（regression testing framework）,供Java开发人员编写单元测试之用。<A name=more></A> 
<P><STRONG>1、概述</STRONG><BR>　　Junit测试是程序员测试，即所谓白盒测试，因为程序员知道被测试的软件如何（How）完成功能和完成什么样（What）的功能。<BR>　　Junit本质上是一套框架，即开发者制定了一套条条框框，遵循这此条条框框要求编写测试代码，如继承某个类，实现某个接口，就可以用Junit进行自动测试了。<BR>　　由于Junit相对独立于所编写的代码，可以测试代码的编写可以先于实现代码的编写，XP 中推崇的 test first design的实现有了现成的手段：用Junit写测试代码，写实现代码，运行测试，测试失败，修改实现代码，再运行测试，直到测试成功。以后对代码的修改和优化，运行测试成功，则修改成功。<BR>　　Java 下的 team 开发，采用 cvs(版本控制) + ant(项目管理) + junit(集成测试) 的模式时，通过对ant的配置，可以很简单地实现测试自动化。</P>
<P>　　对不同性质的被测对象，如Class，Jsp，Servlet，Ejb等，Junit有不同的使用技巧，以后慢慢地分别讲叙。以下以Class测试为例讲解，除非特殊说明。</P>
<P><STRONG>2、下载安装</STRONG><BR>
<UL>
<LI>去<A href="http://www.junit.org/index.htm">Junit主页</A>下载最新版本3.8.1程序包junit-3.8.1.zip<BR>
<LI>用winzip或unzip将junit-3.8.1.zip解压缩到某一目录名为$JUNITHOME<BR>
<LI>将junit.jar和$JUNITHOME/junit加入到CLASSPATH中，加入后者只因为测试例程在那个目录下。<BR>
<LI><INS>注意不要将junit.jar放在jdk的extension目录下</INS><BR>
<LI>运行命令,结果如下图。 
<DIV class=code>java junit.swingui.TestRunner junit.samples.AllTests</DIV></LI></UL>
<P></P>
<P align=center><IMG height=504 alt=junit-alltest.gif src="http://hedong.3322.org/archives/pics/junit-alltest.gif" width=474 align=center border=0></P>
<P><STRONG>3、Junit架构</STRONG><BR>　　下面以Money这个类为例进行说明。 
<DIV class=code>public class Money {<BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;fAmount;//余额<BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;fCurrency;//货币类型 
<P></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Money(int&nbsp;amount,&nbsp;String&nbsp;currency)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fAmount=&nbsp;amount;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fCurrency=&nbsp;currency;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;amount()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;fAmount;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;currency()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;fCurrency;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Money&nbsp;add(Money&nbsp;m)&nbsp;{//加钱<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;Money(amount()+m.amount(),&nbsp;currency());<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;boolean&nbsp;equals(Object&nbsp;anObject)&nbsp;{//判断钱数是否相等<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(anObject&nbsp;instanceof&nbsp;Money)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;aMoney=&nbsp;(Money)anObject;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;aMoney.currency().equals(currency())<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;&amp;&nbsp;amount()&nbsp;==&nbsp;aMoney.amount();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<BR>}</P></DIV><BR>　　Junit本身是围绕着两个<ACRONYM title="design pattern">设计模式</ACRONYM>来设计的：<ACRONYM title="command pattern">命令模式</ACRONYM>和<ACRONYM title="composite pattern">集成模式</ACRONYM>.<BR>
<UL>
<LI>命令模式<BR>　　利用TestCase定义一个子类，在这个子类中生成一个被测试的对象，编写代码检测某个<ACONYM title=method>方法</ACRONYM>被调用后对象的状态与预期的状态是否一致，进而<ACRONYM title=assert>断言</ACRONYM>程序代码有没有bug。<BR>　　当这个子类要测试不只一个<ACRONYM title=method>方法</ACRONYM>的实现代码时，可以先建立<ACRONYM title=fixture>测试基础</ACRONYM>，让这些测试在同一个基础上运行，一方面可以减少每个测试的初始化，而且可以测试这些不同方法之间的联系。<BR>　　例如，我们要测试Money的Add方法，可以如下: 
<DIV class=code>public&nbsp;class&nbsp;MoneyTest&nbsp;extends&nbsp;TestCase&nbsp;{&nbsp;//<INS>TestCase的子类</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;testAdd()&nbsp;{ //<INS>把测试代码放在testAdd中</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;m12CHF=&nbsp;new&nbsp;Money(12,&nbsp;"CHF");&nbsp;&nbsp;//<INS>本行和下一行进行一些初始化</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;m14CHF=&nbsp;new&nbsp;Money(14,&nbsp;"CHF");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;expected=&nbsp;new&nbsp;Money(26,&nbsp;"CHF");//<INS>预期的结果</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;result=&nbsp;m12CHF.add(m14CHF);&nbsp;&nbsp;&nbsp;&nbsp;//<INS>运行被测试的方法</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(expected.equals(result));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<INS>判断运行结果是否与预期的相同</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</DIV><BR>　　如果测试一下equals方法，用类似的代码，如下： 
<DIV class=code>public&nbsp;class&nbsp;MoneyTest&nbsp;extends&nbsp;TestCase&nbsp;{&nbsp;//<INS>TestCase的子类</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;testEquals()&nbsp;{&nbsp;//<INS>把测试代码放在testEquals中</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;m12CHF=&nbsp;new&nbsp;Money(12,&nbsp;"CHF");&nbsp;//<INS>本行和下一行进行一些初始化</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;m14CHF=&nbsp;new&nbsp;Money(14,&nbsp;"CHF"); 
<P></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(!m12CHF.equals(null));//<INS>进行不同情况的测试</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(m12CHF,&nbsp;m12CHF);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(m12CHF,&nbsp;new&nbsp;Money(12,&nbsp;"CHF"));&nbsp;//&nbsp;(1)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(!m12CHF.equals(m14CHF));<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</P></DIV><BR>　　当要同时进行测试Add和equals方法时，可以将它们的各自的初始化工作，合并到一起进行，形成测试基础,用setUp初始化，用tearDown清除。如下： 
<DIV class=code>public&nbsp;class&nbsp;MoneyTest&nbsp;extends&nbsp;TestCase&nbsp;{//<INS>TestCase的子类</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Money&nbsp;f12CHF;//<INS>提取公用的对象</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Money&nbsp;f14CHF;&nbsp;&nbsp;&nbsp; 
<P></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;setUp()&nbsp;{//<INS>初始化公用对象</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f12CHF=&nbsp;new&nbsp;Money(12,&nbsp;"CHF");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f14CHF=&nbsp;new&nbsp;Money(14,&nbsp;"CHF");<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;testEquals()&nbsp;{//测试equals方法的正确性<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(null));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF,&nbsp;f12CHF);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF,&nbsp;new&nbsp;Money(12,&nbsp;"CHF"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(f14CHF));<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;testSimpleAdd()&nbsp;{//测试add方法的正确性<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;expected=&nbsp;new&nbsp;Money(26,&nbsp;"CHF");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;result=&nbsp;f12CHF.add(f14CHF);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(expected.equals(result));<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</P></DIV><BR>　　将以上三个中的任一个TestCase子类代码保存到名为MoneyTest.java的文件里，并在文件首行增加 
<DIV class=code>import junit.framework.*;</DIV>，都是可以运行的。关于Junit运行的问题很有意思，下面单独说明。<BR>　　上面为解释概念“测试基础(fixture)”，引入了两个对两个方法的测试。命令模式与集成模式的本质区别是，前者一次只运行一个测试。<BR>
<LI>集成模式<BR>　　利用TestSuite可以将一个TestCase子类中所有test***()方法包含进来一起运行，还可将TestSuite子类也包含进来，从而行成了一种等级关系。可以把TestSuite视为一个容器，可以盛放TestCase中的test***()方法，它自己也可以嵌套。这种体系架构，非常类似于现实中程序一步步开发一步步集成的现况。<BR>　　对上面的例子，有代码如下： 
<DIV class=code>public&nbsp;class&nbsp;MoneyTest&nbsp;extends&nbsp;TestCase&nbsp;{//TestCase的子类<BR>&nbsp;&nbsp;&nbsp;&nbsp;....<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;Test&nbsp;suite()&nbsp;{//<INS>静态Test</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestSuite&nbsp;suite=&nbsp;new&nbsp;TestSuite();//<INS>生成一个TestSuite</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suite.addTest(new&nbsp;MoneyTest("testEquals"));&nbsp;//<INS>加入测试方法</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suite.addTest(new&nbsp;MoneyTest("testSimpleAdd"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;suite;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</DIV><BR>　　从Junit2.0开始，有列简捷的方法: 
<DIV class=code>public&nbsp;class&nbsp;MoneyTest&nbsp;extends&nbsp;TestCase&nbsp;{//TestCase的子类<BR>&nbsp;&nbsp;&nbsp;&nbsp;....<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;Test&nbsp;suite()&nbsp;{<INS>静态Test</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;TestSuite(MoneyTest.class);&nbsp;//<INS>以类为参数</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</DIV><BR>　　TestSuite见嵌套的例子，在后面应用案例中有。<BR>　　</LI></UL>
<P></P>
<P><STRONG>4、测试代码的运行</STRONG><BR>　　先说最常用的集成模式。<BR>　　测试代码写好以后，可以相应的类中写main方法，用java命令直接运行；也可以不写main方法，用Junit提供的运行器运行。Junit提供了textui,awtui和swingui三种运行器。<BR>　　以前面第2步中的AllTests运行为例，可有四种： 
<DIV class=code>java junit.textui.TestRunner junit.samples.AllTests<BR>java junit.awtui.TestRunner junit.samples.AllTests<BR>java junit.swingui.TestRunner junit.samples.AllTests<BR>java junit.samples.AllTests</DIV><BR>　　main方法中一般也都是简单地用Runner调用suite()，当没有main时，TestRunner自己以运行的类为参数生成了一个TestSuite.<BR>　　<BR>　　对于命令模式的运行，有两种方法。<BR>
<UL>
<LI>静态方法<BR>
<DIV class=code>TestCase test= new MoneyTest("simple add") {<BR>public void runTest() {<BR>testSimpleAdd();<BR>}<BR>};</DIV><BR>
<LI>动态方法<BR>
<DIV class=code>TestCase test= new MoneyTest("testSimpleAdd");</DIV></LI></UL><BR>　　我试了一下，<DEL>好象有问题，哪位朋友成功了，请指点我一下。</DEL>确实可以。<BR>
<DIV class=code>import&nbsp;junit.framework.*; 
<P></P>
<P>public&nbsp;class&nbsp;MoneyTest&nbsp;extends&nbsp;TestCase&nbsp;{//<INS>TestCase的子类</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Money&nbsp;f12CHF;//<INS>提取公用的对象</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Money&nbsp;f14CHF;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;MoneyTest(String&nbsp;name){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(name);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;setUp()&nbsp;{//<INS>初始化公用对象</INS><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f12CHF=&nbsp;new&nbsp;Money(12,&nbsp;"CHF");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f14CHF=&nbsp;new&nbsp;Money(14,&nbsp;"CHF");<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;testEquals()&nbsp;{//测试equals方法的正确性<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(null));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF,&nbsp;f12CHF);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF,&nbsp;new&nbsp;Money(12,&nbsp;"CHF"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(f14CHF));<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;testAdd()&nbsp;{//测试add方法的正确性<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;expected=&nbsp;new&nbsp;Money(26,&nbsp;"CHF");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Money&nbsp;result=&nbsp;f12CHF.add(f14CHF);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(expected.equals(result));<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>//&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{<BR>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestCase&nbsp;test=new&nbsp;MoneyTest("simple&nbsp;add")&nbsp;{<BR>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;runTest()&nbsp;{<BR>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;testAdd();<BR>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<BR>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;junit.textui.TestRunner.run(test);<BR>//&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestCase&nbsp;test=new&nbsp;MoneyTest("testAdd");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;junit.textui.TestRunner.run(test);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</P></DIV><BR>再给一个静态方法用集成测试的例子： 
<DIV class=code>public&nbsp;static&nbsp;Test&nbsp;suite()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;TestSuite&nbsp;suite=&nbsp;new&nbsp;TestSuite();<BR>&nbsp;&nbsp;&nbsp;&nbsp;suite.addTest(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;testCar("getWheels")&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;runTest()&nbsp;{&nbsp;testGetWheels();&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;); 
<P></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;suite.addTest(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;testCar("getSeats")&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;runTest()&nbsp;{&nbsp;testGetSeats();&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;suite;<BR>}</P></DIV>
<P></P>
<P><STRONG>5、应用案例</STRONG><BR>
<OL>
<LI>Junit Primer例程，运行如下： 
<DIV class=code>java com.hedong.JunitLearning.Primer.ShoppingCartTest</DIV><BR>
<LI>Ant+Junit+Mailto实现自动编译、调试并发送结果的build.xml<BR>
<LI>JUnit实施,写得很棒，理解也深刻。例程运行如下： 
<DIV class=code>java com.hedong.JunitLearning.car.testCarNoJunit<BR>java junit.swingui.TestRunner com.hedong.JunitLearning.car.testCar</DIV><BR>
<LI>Junit与log4j结合，阿菜的例程运行： 
<DIV class=code>cd acai<BR>ant junit</DIV><BR>
<LI><BR>
<LI><BR>
<LI><BR>
<LI><BR></LI></OL><BR><STRONG>6、一些问题</STRONG><BR>　　有人在实践基础上总结出一些非常有价值的使用技巧，我没有经过一一“测试”，暂列在此。<BR>
<OL>
<LI>不要用TestCase的构造函数初始化Fixture，而要用setUp()和tearDown()方法。<BR>
<LI>不要依赖或假定测试运行的顺序，因为JUnit利用Vector保存测试方法。所以不同的平台会按不同的顺序从Vector中取出测试方法。<INS>不知3.8中是不是还是如此，不过它提供的例子有一个是指定用VectorSuite的，如果不指定呢？</INS><BR>
<LI>避免编写有副作用的TestCase。例如：如果随后的测试依赖于某些特定的交易数据，就不要提交交易数据。简单的回滚就可以了。<BR>
<LI>当继承一个测试类时，记得调用父类的setUp()和tearDown()方法。<BR>
<LI>将测试代码和工作代码放在一起，一边同步编译和更新。（使用Ant中有支持junit的task.）<BR>
<LI>测试类和测试方法应该有一致的命名方案。如在工作类名前加上test从而形成测试类名。<BR>
<LI>确保测试与时间无关，不要依赖使用过期的数据进行测试。导致在随后的维护过程中很难重现测试。<BR>
<LI>如果你编写的软件面向国际市场，编写测试时要考虑国际化的因素。不要仅用母语的Locale进行测试。<BR>
<LI>尽可能地利用JUnit提供地assert/fail方法以及异常处理的方法，可以使代码更为简洁。<BR>
<LI>测试要尽可能地小，执行速度快。<BR>
<LI>把测试程序建立在与被测对象相同的包中<BR>
<LI>在你的原始代码目录中避免测试码出现，可在一个源码镜像目录中放测试码<BR>
<LI>在自己的应用程序包中包含一个TestSuite测试类<BR>
<LI><BR></LI></OL><BR><BR><STRONG>7、相关资源下载</STRONG><BR>以下jar包，我只是做了打包、编译和调试的工作，供下载学习之用，相关的权利属于原作者。<BR>
<OL>
<LI><A href="http://hedong.3322.org/archives/docs/prim.jar">可运行例程.jar</A><BR>
<LI><A href="http://hedong.3322.org/archives/docs/ant_junit_mailto.build.xml">Build.xml</A><BR>
<LI><A href="http://hedong.3322.org/archives/docs/acai.zip">阿菜的例程</A><BR>
<LI><INS><A href="http://hedong.3322.org/archives/docs/JavaX-14.zip">Junit API 汉译</A>(pdf)</INS><BR></LI></OL>
<P></P>
<P><STRONG>8、未完成的任务</STRONG><BR>
<OL>
<LI>httpunit<BR>
<LI>cactus<BR>
<LI>将Junit用链接池测试<BR></LI></OL><BR>主要参考文献：<BR>
<OL><BR>
<LI>JUnit入門<BR>http://www.dotspace.twmail.org/Test/JUnit_Primer.htm<BR>
<LI>怎样使用Junit Framework进行单元测试的编写<BR>http://www.chinaunix.net/bbsjh/14/546.html<BR>
<LI>Ant+Junit+Log4J+CVS进行XP模式开发的建立<BR>http://ejb.cn/modules/tutorials/printpage.php?tid=4<BR>
<LI>用HttpUnit测试Web应用程序<BR>http://www.zdnet.com.cn/developer/code/story/0,2000081534,39033726,00.htm<BR>
<LI>有没有用过Cactus的，Web层的测试是Cactus还是JUnit？<BR>http://www.jdon.com/jive/thread.jsp?forum=16&amp;thread=9156<BR>
<LI>Ant+junit的测试自动化 biggie（原作）<BR>http://www.csdn.net/Develop/article/19%5C19748.shtm<BR>
<LI>JUnit实施<BR>http://www.neweasier.com/article/2002-08-07/1028723459.html<BR>
<LI>JUnitTest Infected: Programmers Love Writing Tests<BR>http://junit.sourceforge.net/doc/testinfected/testing.htm<BR>
<LI>JUnit Cookbook<BR>http://junit.sourceforge.net/doc/cookbook/cookbook.htm<BR>
<LI>JUnit Primer<BR>http://www.itu.dk/~lthorup/JUnitPrimer.html<BR>
<LI>IBM DevelopWorks<BR>http://www-106.ibm.com/search/searchResults.jsp?query=junit&amp;searchScope=dW&amp;<BR>searchType=1&amp;searchSite=dWChina&amp;pageLang=zh&amp;langEncoding=gb2312&amp;Search.x=0&amp;<BR>Search.y=0&amp;Search=Search<BR></LI></OL><img src ="http://www.blogjava.net/qq13367612/aggbug/15977.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-16 13:04 <a href="http://www.blogjava.net/qq13367612/articles/15977.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>心得:Spring 3种注入的比较分析</title><link>http://www.blogjava.net/qq13367612/articles/15979.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 16 Oct 2005 04:58:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/15979.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/15979.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/15979.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/15979.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/15979.html</trackback:ping><description><![CDATA[<P>Type1 接口注入</P>
<P>我们常常借助接口来将调用者与实现者分离。如:</P>
<P> </P>
<P>public class ClassA {<BR>private InterfaceB clzB;<BR>public init() {<BR>Ojbect obj =<BR>Class.forName(Config.BImplementation).newInstance();<BR>clzB = (InterfaceB)obj;<BR>}<BR>……<BR>}</P>
<P>　　上面的代码中，ClassA依赖于InterfaceB的实现，如何获得InterfaceB实现类的实例？传统的方法是在代码中创建InterfaceB实现类的实例，并将起赋予clzB。而这样一来，ClassA在编译期即依赖于InterfaceB的实现。为了将调用者与实现者在编译期分离，于是有了上面的代码，我们根据预先在配置文件中设定的实现类的类名，动态加载实现类，并通过InterfaceB强制转型后为ClassA所用。 </P>
<P>　　这就是接口注入的一个最原始的雏形。 </P>
<P>　　而对于一个Type1型IOC容器而言，加载接口实现并创建其实例的工作由容器完成，如J2EE开发中常用的Context.lookup（ServletContext.getXXX），都是Type1型IOC的表现形式。Apache Avalon是一个典型的Type1型IOC容器。 </P>
<P>　　Type2 构造子注入 </P>
<P>　　构造子注入，即通过构造函数完成依赖关系的设定，如： <BR>public class DIByConstructor <BR>{<BR>private final DataSource dataSource;<BR>private final String message;<BR>public DIByConstructor(DataSource ds, String msg)<BR>{<BR>this.dataSource = ds;<BR>this.message = msg;<BR>}<BR>……<BR>}</P>
<P>　　可以看到，在Type2类型的依赖注入机制中，依赖关系是通过类构造函数建立，容器通过调用类的构造方法，将其所需的依赖关系注入其中。PicoContainer（另一种实现了依赖注入模式的轻量级容器）首先实现了Type2类型的依赖注入模式。 </P>
<P>　　Type3 设值注入 </P>
<P>　　在各种类型的依赖注入模式中，设值注入模式在实际开发中得到了最广泛的应用（其中很大一部分得力于Spring框架的影响）。 </P>
<P>　　在笔者看来，基于设置模式的依赖注入机制更加直观、也更加自然。Quick Start中的示例，就是典型的设置注入，即通过类的setter方法完成依赖关系的设置。 </P>
<P>　　几种依赖注入模式的对比总结 </P>
<P>　　接口注入模式因为具备侵入性，它要求组件必须与特定的接口相关联，因此并不被看好，实际使用有限。Type2和Type3的依赖注入实现模式均具备无侵入性的特点。 </P>
<P>　　Type2 构造子注入的优势： </P>
<P>　　1、“在构造期即创建一个完整、合法的对象”，对于这条Java设计原则，Type2无疑是最好的响应者。 </P>
<P>　　我的理解：就是你要通过一种方式来保证对象的引用完整性，type2选择了构造器的方式来实现。 </P>
<P>　　2、避免了繁琐的setter方法的编写，所有依赖关系均在构造函数中设定，依赖关系集中呈现，更加易读。 </P>
<P>　　我的理解：使用构造方法就不需要每个属性都写set和get方法了，这样省去了很多的代码。 </P>
<P>　　3、由于没有setter方法，依赖关系在构造时由容器一次性设定，因此组件在被创建之后即处于相对“不变”的稳定状态，无需担心上层代码在调用过程中执行setter方法对组件依赖关系产生破坏，特别是对于Singleton模式的组件而言，这可能对整个系统产生重大的影响。 </P>
<P>　　我的理解：使用构造器来实现，那么你需要一次对所有的属性都初始话，相对set方法来说，缺少了一些灵活性。 </P>
<P>　　4、同样，由于关联关系仅在构造函数中表达，只有组件创建者需要关心组件内部的依赖关系。对调用者而言，组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息，也为系统的层次清晰性提供了保证。 </P>
<P>　　我的理解： spring的这设计就是要屏蔽依赖关系，你只需要对接口编程，而不需要考虑依赖关系的实现。所以对调用者来说，依赖关系是处于黑盒当中。 </P>
<P>　　5、通过构造子注入，意味着我们可以在构造函数中决定依赖关系的注入顺序，对于一个大量依赖外部服务的组件而言，依赖关系的获得顺序可能非常重要，比如某个依赖关系注入的先决条件是组件的DataSource及相关资源已经被设定。 </P>
<P>　　我的理解：关于顺序问题，我们来看以下两段代码： <BR>public DIByConstructor(DataSource ds, String msg)<BR>{<BR>this.dataSource = ds;<BR>this.message = msg;<BR>}<BR>public DIByConstructor(DataSource ds, String msg)<BR>{<BR>this.dataSource = ds;<BR>this.message = msg;<BR>}</P>
<P>　　在本例中，顺序不太重要，但是如果message的初始化需要用到datasource的话，那么就必须要先初始化datasource,所以相对来说，顺序就是确定了。 </P>
<P>　　Type3 设值注入的优势 </P>
<P>　　1、对于习惯了传统JavaBean开发的程序员而言，通过setter方法设定依赖关系显得更加直观，更加自然。 </P>
<P>　　2、如果依赖关系（或继承关系）较为复杂，那么Type2模式的构造函数也会相当庞大（我们需要在构造函数中设定所有依赖关系），此时Type3模式往往更为简洁。 </P>
<P>　　我的理解：依赖关系（或继承关系）较为复杂，指的是，属性较多，需要写很多的set和get方法。 </P>
<P>　　3、对于某些第三方类库而言，可能要求我们的组件必须提供一个默认的构造函数（如Struts中的Action），此时Type2类型的依赖注入机制就体现出其局限性，难以完成我们期望的功能。 </P>
<P>　　可见，Type2和Type3模式各有千秋，而Spring、PicoContainer都对Type2和Type3类型的依赖注入机制提供了良好支持。这也就为我们提供了更多的选择余地。理论上，以Type2类型为主，辅之以Type3类型机制作为补充，可以达到最好的依赖注入效果，不过对于基于Spring Framework开发的应用而言，Type3使用更加广泛。 </P><img src ="http://www.blogjava.net/qq13367612/aggbug/15979.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-16 12:58 <a href="http://www.blogjava.net/qq13367612/articles/15979.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>