<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/xsl/rss.xsl" type="text/xsl" media="screen"?>
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:ppp="http://blog.sohu.com/rss/module/ppp/"
	>

	<channel>
		<title>-------------------</title>
		<link>http://rire-azure.blog.sohu.com/</link>
		<description><![CDATA[-------------------]]></description>
		<pubDate>Wed, 28 May 2008 15:49:59 +0800</pubDate>
		<generator>搜狐博客</generator>
		<ppp:ebi>c00cd0e792</ppp:ebi>
		<image>
			<title>http://blog.sohu.com</title>
			<url>http://js.pp.sohu.com/ppp/blog/images/common/logo_150_60.gif</url>
			<link>http://blog.sohu.com/</link>
			<width>100</width>
			<height>43</height>
			<description>搜狐博客</description>
		</image>
		<item>
			<title>Hibernate by 林信良(良葛格)</title>
			<link>http://rire-azure.blog.sohu.com/88603186.html</link>
			<comments>http://rire-azure.blog.sohu.com/88603186.html#comment</comments>
			<dc:creator>-------------------</dc:creator>
			<pubDate>Wed, 28 May 2008 15:49:59 +0800</pubDate>
			<category> Hibernate </category>
			<guid>http://rire-azure.blog.sohu.com/88603186.html</guid>
			<description><![CDATA[<p><a href="http://www.javaworld.com.tw/jute/ePaper/jboss/200706/200706.htm#2">http://www.javaworld.com.tw/jute/ePaper/jboss/200706/200706.htm#2</a></p>
<p><a href="http://www.javaworld.com.tw/jute/ePaper/jboss/200708/200708.htm#1">http://www.javaworld.com.tw/jute/ePaper/jboss/200708/200708.htm#1</a></p>
<p>http://www.javaworld.com.tw/jute/ePaper/jboss/200709/200709.htm#1</p>
<p><a href="http://www.javaworld.com.tw/jute/ePaper/jboss/200710/200710.htm#1">http://www.javaworld.com.tw/jute/ePaper/jboss/200710/200710.htm#1</a></p>
<p><a href="http://www.javaworld.com.tw/jute/ePaper/jboss/200711/200711.htm#1">http://www.javaworld.com.tw/jute/ePaper/jboss/200711/200711.htm#1</a></p>
<p>http://www.javaworld.com.tw/jute/ePaper/jboss/200712/200712.htm</p>
<p>&nbsp;</p>]]></description>
		</item>
		    
		
		<item>
			<title>Session详解(轉載) </title>
			<link>http://rire-azure.blog.sohu.com/88391298.html</link>
			<comments>http://rire-azure.blog.sohu.com/88391298.html#comment</comments>
			<dc:creator>-------------------</dc:creator>
			<pubDate>Sun, 25 May 2008 15:28:31 +0800</pubDate>
			<guid>http://rire-azure.blog.sohu.com/88391298.html</guid>
			<description><![CDATA[<div><a href="http://www.blogjava.net/Sunspl/archive/2006/08/28/66172.html">http://www.blogjava.net/Sunspl/archive/2006/08/28/66172.html</a></div>
<div><strong></strong>&nbsp;</div>
<div>&nbsp;</div>
<div>虽然session机制在web应用程序中被采用已经很长时间了，但是仍然有很多人不清楚session机制的本质，以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。 
<div>
<div><br />&nbsp;</div></div><!-- end of summary line -->
<div><b>一、术语session</b><br />在我的经验里，session这个词被滥用的程度大概仅次于transaction，更加有趣的是transaction与session在某些语境下的含义是相同的。<br /><br />session，中文经常翻译为会话，其本来的含义是指有始有终的一系列动作/消息，比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话&ldquo;在一个浏览器会话期间，...&rdquo;，这里的会话一词用的就是其本义，是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是&ldquo;用户（客户端）在一次会话期间&rdquo;这样一句话，它可能指用户的一系列动作（一般情况下是同某个具体目的相关的一系列动作，比如从登录到选购商品到结账登出这样一个网上购物的过程，有时候也被称为一个transaction），然而有时候也可能仅仅是指一次连接，也有可能是指含义①，其中的差别只能靠上下文来推断②。<br /><br />然而当session一词与网络协议相关联时，它又往往隐含了&ldquo;面向连接&rdquo;和/或&ldquo;保持状态&rdquo;这样两个含义， &ldquo;面向连接&rdquo;指的是在通信双方在通信之前要先建立一个通信的渠道，比如打电话，直到对方接了电话通信才能开始，与此相对的是写信，在你把信发出去的时候你并不能确认对方的地址是否正确，通信渠道不一定能建立，但对发信人来说，通信已经开始了。&ldquo;保持状态&rdquo;则是指通信的一方能够把一系列的消息关联起来，使得消息之间可以互相依赖，比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有&ldquo;一个TCP session&rdquo;或者 &ldquo;一个POP3 session&rdquo;③。<br /><br />而到了web服务器蓬勃发展的时代，session在web开发语境下的语义又有了新的扩展，它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构，如&ldquo;把xxx保存在session 里&rdquo;⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持，所以在某种特定语言的语境下，session也被用来指代该语言的解决方案，比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。<br /><br />鉴于这种混乱已不可改变，本文中session一词的运用也会根据上下文有不同的含义，请大家注意分辨。<br />在本文中，使用中文&ldquo;浏览器会话期间&rdquo;来表达含义①，使用&ldquo;session机制&rdquo;来表达含义④，使用&ldquo;session&rdquo;表达含义⑤，使用具体的&ldquo;HttpSession&rdquo;来表达含义⑥<br /><br /><b>二、HTTP协议与状态保持</b><br />HTTP 协议本身是无状态的，这与HTTP协议本来的目的是相符的，客户端只需要简单的向服务器请求下载某些文件，无论是客户端还是服务器都没有必要纪录彼此过去的行为，每一次请求之间都是独立的，好比一个顾客和一个自动售货机或者一个普通的（非会员制）大卖场之间的关系一样。<br /><br />然而聪明（或者贪心？）的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用，就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为，另一方面在服务器端则出现了CGI规范以响应客户端的动态请求，作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。<br /><br />让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠，然而一次性消费5杯咖啡的机会微乎其微，这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案：<br />1、该店的店员很厉害，能记住每位顾客的消费数量，只要顾客一走进咖啡店，店员就知道该怎么对待了。这种做法就是协议本身支持状态。<br />2、发给顾客一张卡片，上面记录着消费的数量，一般还有个有效期限。每次消费时，如果顾客出示这张卡片，则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。<br />3、发给顾客一张会员卡，除了卡号之外什么信息也不纪录，每次消费时，如果顾客出示该卡片，则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。<br /><br />由于HTTP协议是无状态的，而出于种种考虑也不希望使之成为有状态的，因此，后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案，而session机制采用的是在服务器端保持状态的方案。同时我们也看到，由于采用服务器端保持状态的方案在客户端也需要保存一个标识，所以session机制可能需要借助于cookie机制来达到保存标识的目的，但实际上它还有其他选择。<br /><br /><b>三、理解cookie机制</b><br />cookie机制的基本原理就如上面的例子一样简单，但是还有几个问题需要解决：&ldquo;会员卡&rdquo;如何分发；&ldquo;会员卡&rdquo;的内容；以及客户如何使用&ldquo;会员卡&rdquo;。<br /><br />正统的cookie分发是通过扩展HTTP协议来实现的，服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。<br /><br />而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie，如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置，则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示，如果某家分店还发行了自己的会员卡，那么进这家店的时候除了要出示麦当劳的会员卡，还要出示这家店的会员卡。<br /><br />cookie的内容主要包括：名字，值，过期时间，路径和域。<br />其中域可以指定某一个域比如.google.com，相当于总店招牌，比如宝洁公司，也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com，可以用飘柔来做比。<br />路径就是跟在域名后面的URL路径，比如/或者/foo等等，可以用某飘柔专柜做比。<br />路径与域合在一起就构成了cookie的作用范围。<br />如果不设置过期时间，则表示这个cookie的生命期为浏览器会话期间，只要关闭浏览器窗口，cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里，当然这种行为并不是规范规定的。如果设置了过期时间，浏览器就会把cookie保存到硬盘上，关闭后再次打开浏览器，这些cookie仍然有效直到超过设定的过期时间。<br /><br />存储在硬盘上的cookie 可以在不同的浏览器进程间共享，比如两个IE窗口。而对于保存在内存里的cookie，不同的浏览器有不同的处理方式。对于IE，在一个打开的窗口上按 Ctrl-N（或者从文件菜单）打开的窗口可以与原窗口共享，而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie；对于 Mozilla Firefox0.8，所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。<br /><br />下面就是一个goolge设置cookie的响应头的例子<br /><pre>HTTP/1.1 302 Found<br />Location: http://www.google.com/intl/zh-CN/<br />Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com<br />Content-Type: text/html</pre><br /><br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235649_MdbyYrtKVN.jpg" border="0" /><br />这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分<br /><br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235718_CZMAULRUCN.jpg" border="0" /><br />浏览器在再次访问goolge的资源时自动向外发送cookie<br /><br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235746_TgiVaUuZME.jpg" border="0" /><br />用Firefox可以很容易的观察现有的cookie的值<br />使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。<br /><br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235812_qtRvksXzxP.jpg" border="0" /><br />IE也可以设置在接受cookie前询问<br /><br /><b>四、理解session机制</b><br /><br />session机制是一种服务器端的机制，服务器使用一种类似于散列表的结构（也可能就是使用散列表）来保存信息。<br /><br />当程序需要为某个客户端的请求创建一个session的时候，服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id，如果已包含一个session id则说明以前已经为此客户端创建过session，服务器就按照session id把这个 session检索出来使用（如果检索不到，可能会新建一个），如果客户端请求不包含session id，则为此客户端创建一个session并且生成一个与此session相关联的session id，session id的值应该是一个既不会重复，又不容易被找到规律以仿造的字符串，这个 session id将被在本次响应中返回给客户端保存。<br /><br />保存这个session id的方式可以采用cookie，这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID，而。比如weblogic对于web应用程序生成的cookie，JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764，它的名字就是 JSESSIONID。<br /><br />由于cookie可以被人为的禁止，必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写，就是把session id直接附加在URL路径的后面，附加方式也有两种，一种是作为URL路径的附加信息，表现形式为http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764<br />另一种是作为查询字符串附加在URL后面，表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764<br />这两种方式对于用户来说是没有区别的，只是服务器在解析的时候处理的方式不同，采用第一种方式也有利于把session id的信息和正常程序参数区分开来。<br />为了在整个交互过程中始终保持状态，就必须在每个客户端可能请求的路径后面都包含这个session id。<br /><br />另一种技术叫做表单隐藏字段。就是服务器会自动修改表单，添加一个隐藏字段，以便在表单提交时能够把session id传递回服务器。比如下面的表单<br /><pre>&lt;form name=&quot;testform&quot; action=&quot;/xxx&quot;&gt;<br />&lt;input type=&quot;text&quot;&gt;<br />&lt;/form&gt;</pre><br /><br />在被传递给客户端之前将被改写成<br /><pre>&lt;form name=&quot;testform&quot; action=&quot;/xxx&quot;&gt;<br />&lt;input type=&quot;hidden&quot; name=&quot;jsessionid&quot; value=&quot;ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764&quot;&gt;<br />&lt;input type=&quot;text&quot;&gt;<br />&lt;/form&gt;</pre><br /><br />这种技术现在已较少应用，笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。<br />实际上这种技术可以简单的用对action应用URL重写来代替。<br /><br />在谈论session机制的时候，常常听到这样一种误解&ldquo;只要关闭浏览器，session就消失了&rdquo;。其实可以想象一下会员卡的例子，除非顾客主动对店家提出销卡，否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的，除非程序通知服务器删除一个session，否则服务器会一直保留，程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭，因此服务器根本不会有机会知道浏览器已经关闭，之所以会有这种错觉，是大部分session机制都使用会话cookie来保存session id，而关闭浏览器后这个 session id就消失了，再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上，或者使用某种手段改写浏览器发出的HTTP请求头，把原来的session id发送给服务器，则再次打开浏览器仍然能够找到原来的session。<br /><br />恰恰是由于关闭浏览器不会导致session被删除，迫使服务器为seesion设置了一个失效时间，当距离客户端上一次使用session的时间超过这个失效时间时，服务器就可以认为客户端已经停止了活动，才会把session删除以节省存储空间。<br /><br /><b>五、理解javax.servlet.http.HttpSession</b><br />HttpSession是Java平台对session机制的实现规范，因为它仅仅是个接口，具体到每个web应用服务器的提供商，除了对规范支持之外，仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。<br /><br />首先，Weblogic Server提供了一系列的参数来控制它的HttpSession的实现，包括使用cookie的开关选项，使用URL重写的开关选项，session持久化的设置，session失效时间的设置，以及针对cookie的各种设置，比如设置cookie的名字、路径、域， cookie的生存时间等。<br /><br />一般情况下，session都是存储在内存里，当服务器进程被停止或者重启的时候，内存里的session也会被清空，如果设置了session的持久化特性，服务器就会把session保存到硬盘上，当服务器进程重新启动或这些信息将能够被再次使用， Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。<br /><br />复制严格说来不算持久化保存，因为session实际上还是保存在内存里，不过同样的信息被复制到各个cluster内的服务器进程中，这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。<br /><br />cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。<br /><br />cookie的路径对于web应用程序来说是一个非常重要的选项，Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。<br /><br />关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869<br /><br /><b>六、HttpSession常见问题</b><br />（在本小节中session的含义为⑤和⑥的混合）<br /><br />1、session在何时被创建<br />一个常见的误解是以为session在有客户端访问时就被创建，然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建，注意如果JSP没有显示的使用 &lt;% @page session=&quot;false&quot;%&gt; 关闭session，则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。<br /><br />由于session会消耗内存资源，因此，如果不打算使用session，应该在所有的JSP中关闭它。<br /><br />2、session何时被删除<br />综合前面的讨论，session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止（非持久session）<br /><br />3、如何做到在浏览器关闭时删除session<br />严格的讲，做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作，然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。<br /><br />4、有个HttpSessionListener是怎么回事<br />你可以创建这样的listener去监控session的创建和销毁事件，使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener，而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener，HttpSessionActivationListener和 HttpSessionAttributeListener。<br /><br />5、存放在session中的对象必须是可序列化的吗<br />不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果 session中有不可序列化的对象，在session销毁时会有一个Exception，很奇怪。<br /><br />6、如何才能正确的应付客户端禁止cookie的可能性<br />对所有的URL使用URL重写，包括超链接，form的action，和重定向的URL，具体做法参见[6]<br />http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770<br /><br />7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session<br />参见第三小节对cookie的讨论，对session来说是只认id不认人，因此不同的浏览器，不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。<br /><br />8、如何防止用户打开两个浏览器窗口操作导致的session混乱<br />这个问题与防止表单多次提交是类似的，可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端，同时保存在session里，客户端提交表单时必须把这个id也返回服务器，程序首先比较返回的id与保存在session里的值是否一致，如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口，一般不设置这个id，或者使用单独的id，以防主窗口无法操作，建议不要再window.open打开的窗口里做修改操作，这样就可以不用设置。<br /><br />9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue<br />做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变，需要向其他服务器进程复制新的session值。<br /><br />10、为什么session不见了<br />排除session正常失效的因素之外，服务器本身的可能性应该是微乎其微的，虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过；浏览器插件的可能性次之，笔者也遇到过3721插件造成的问题；理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。<br />出现这一问题的大部分原因都是程序的错误，最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。<br /><br /><b>七、跨应用程序的session共享</b><br /><br />常常有这样的情况，一个大项目被分割成若干小项目开发，为了能够互不干扰，要求每个小项目作为一个单独的web应用程序开发，可是到了最后突然发现某几个小项目之间需要共享一些信息，或者想使用session来实现SSO(single sign on)，在session中保存login的用户信息，最自然的要求是应用程序间能够访问彼此的session。<br /><br />然而按照Servlet规范，session的作用范围应该仅仅限于当前应用程序下，不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范，但是实现的细节却可能各有不同，因此解决跨应用程序session共享的方法也各不相同。<br /><br />首先来看一下Tomcat是如何实现web应用程序之间session的隔离的，从 Tomcat设置的cookie路径来看，它对不同的应用程序设置的cookie路径是不同的，这样不同的应用程序所用的session id是不同的，因此即使在同一个浏览器窗口里访问不同的应用程序，发送给服务器的session id也可以是不同的。<br /><br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235848_ApHWDSAInh.jpg" border="0" /><br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235854_enWszyCQxX.jpg" border="0" /><br /><br />根据这个特性，我们可以推测Tomcat中session的内存结构大致如下。<br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235909_sNDpkQLtDg.jpg" border="0" /><br /><br />笔者以前用过的iPlanet也采用的是同样的方式，估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器，解决的思路很简单，实际实行起来也不难。要么让所有的应用程序共享一个session id，要么让应用程序能够获得其他应用程序的session id。<br /><br />iPlanet中有一种很简单的方法来实现共享一个session id，那就是把各个应用程序的cookie路径都设为/（实际上应该是/NASApp，对于应用程序来讲它的作用相当于根）。<br /><pre>&lt;session-info&gt;<br />&lt;path&gt;/NASApp&lt;/path&gt;<br />&lt;/session-info&gt;</pre><br /><br />需要注意的是，操作共享的session应该遵循一些编程约定，比如在session attribute名字的前面加上应用程序的前缀，使得 setAttribute(&quot;name&quot;, &quot;neo&quot;)变成setAttribute(&quot;app1.name&quot;, &quot;neo&quot;)，以防止命名空间冲突，导致互相覆盖。<br /><br />在Tomcat中则没有这么方便的选择。在Tomcat版本3上，我们还可以有一些手段来共享session。对于版本4以上的Tomcat，目前笔者尚未发现简单的办法。只能借助于第三方的力量，比如使用文件、数据库、JMS或者客户端cookie，URL参数或者隐藏字段等手段。<br /><br />我们再看一下Weblogic Server是如何处理session的。<br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235920_OFuZWKrUSy.jpg" border="0" /><br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235929_VqyJhESxZq.jpg" border="0" /><br /><br />从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/，这是不是意味着在Weblogic Server中默认的就可以共享session了呢？然而一个小实验即可证明即使不同的应用程序使用的是同一个session，各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下<br /><img style="DISPLAY: inline" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_12_235953_LogterrCRh.jpg" border="0" /><br /><br />对于这样一种结构，在 session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量，比如使用文件、数据库、JMS或者客户端 cookie，URL参数或者隐藏字段等手段，还有一种较为方便的做法，就是把一个应用程序的session放到ServletContext中，这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下，<br /><br />应用程序A<br /><pre>context.setAttribute(&quot;appA&quot;, session); </pre><br /><br />应用程序B<br /><pre>contextA = context.getContext(&quot;/appA&quot;);<br />HttpSession sessionA = (HttpSession)contextA.getAttribute(&quot;appA&quot;); </pre><br /><br />值得注意的是这种用法不可移植，因为根据ServletContext的JavaDoc，应用服务器可以处于安全的原因对于context.getContext(&quot;/appA&quot;);返回空值，以上做法在Weblogic Server 8.1中通过。<br /><br />那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢？原来是为了SSO，凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点，修改首先登录的那个应用程序的描述符weblogic.xml，把cookie路径修改为/appA 访问另外一个应用程序会重新要求登录，即使是反过来，先访问cookie路径为/的应用程序，再访问修改过路径的这个，虽然不再提示登录，但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM，因为浏览器和web服务器对basic认证方式有其他的处理方式，第二次请求的认证不是通过 session来实现的。具体请参看[7] secion 14.8 Authorization，你可以修改所附的示例程序来做这些试验。<br /><br /><b>八、总结</b><br />session机制本身并不复杂，然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器，服务器的经验当作普遍适用的经验，而是始终需要具体情况具体分析。<br />摘要：虽然session机制在web应用程序中被采用已经很长时间了，但是仍然有很多人不清楚session机制的本质，以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。</div></div>]]></description>
		</item>
		    
		
		<item>
			<title>SAXBuilder，Document，Element等作用</title>
			<link>http://rire-azure.blog.sohu.com/88384426.html</link>
			<comments>http://rire-azure.blog.sohu.com/88384426.html#comment</comments>
			<dc:creator>-------------------</dc:creator>
			<pubDate>Sun, 25 May 2008 13:57:50 +0800</pubDate>
			<category>java</category>
			<guid>http://rire-azure.blog.sohu.com/88384426.html</guid>
			<description><![CDATA[<p>http://blog.csdn.net/nengyu/archive/2007/12/20/1955532.aspx</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>import org.jdom.Document;<br />import org.jdom.Element;<br />import org.jdom.JDOMException;<br />import org.jdom.input.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">SAXBuilder</strong>;<br />import org.jdom.output.XMLOutputter;<br />（1）使用JDOM首先要指定使用什么解析器。如：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">SAXBuilder</strong> builder=new <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">SAXBuilder</strong>(false); 这表示使用的是默认的解析器<br />（2）得到Document，我们以后要进行的所有操作都是对这个Document操作的：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Document doc=builder.build(xmlpath);<br />（3）得到根元素：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Element books=doc.getRootElement();<br />在JDOM中所有的节点（DOM中的概念）都是一个org.jdom.Element类，当然他的子节点也是一个org.jdom.Element类。<br />（4）得到元素（节点）的集合：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List booklist=books.getChildren(&quot;book&quot;);<br />这表示得到&ldquo;books&rdquo;元素的所在名称为&ldquo;book&rdquo;的元素，并把这些元素都放到一个List集合中<br />（5）轮循List集合<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Iterator iter = booklist.iterator(); iter.hasNext();) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Element book = (Element) iter.next();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ｝<br />还有一种轮循方法是：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0;I&lt;booklist.size();I++){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Element book=(Element)booklist.get(i);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />（6）取得元素的属性：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String email=book.getAttributeValue(&quot;email&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp; 取得元素book的属性名为&ldquo;email&rdquo;的属性值。<br />（7）取得元素的子元素（为最低层元素）的值：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String name=book.getChildTextTrim(&quot;name&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意的是，必须确定book元素的名为&ldquo;name&rdquo;的子元素只有一个。<br />（8）改变元素（为最低层元素）的值：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.getChild(&quot;name&quot;).setText(&quot;alterrjzjh&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这只是对Document的修改，并没有在实际的XML文档中进行修改<br />（9）保存Document的修改到XML文件中：<br />&nbsp;&nbsp;&nbsp;&nbsp; XMLOutputter outputter=new XMLOutputter();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outputter.output(doc,new FileOutputStream(xmlpath));<br /><br />我们先要有一个XMLOutputter类，再把已经修改了的Document保存进XML文档中</p>]]></description>
		</item>
		    
		
		<item>
			<title>請問為什麼可以直接寫method的名稱??</title>
			<link>http://rire-azure.blog.sohu.com/88232804.html</link>
			<comments>http://rire-azure.blog.sohu.com/88232804.html#comment</comments>
			<dc:creator>-------------------</dc:creator>
			<pubDate>Fri, 23 May 2008 15:31:14 +0800</pubDate>
			<category>java</category>
			<guid>http://rire-azure.blog.sohu.com/88232804.html</guid>
			<description><![CDATA[<p><span>http://www.javaworld.com.tw/jute/post/print?bid=5&amp;id=4410</span></p>
<p><span></span>&nbsp;</p>
<p><span>例如這段程式: <br />try { <br />// Set the attribute and Forward to hello.jsp <br />request.setAttribute (&quot;servletName&quot;, &quot;servletToJsp&quot;); <br />getServletConfig().getServletContext().getRequestDispatcher(&quot;/jsp/jsptoserv/hello.jsp&quot;).forward(request, response); <br />} catch (Exception ex) { <br />ex.printStackTrace (); <br />} <br />為什麼可以直接打getServletConfig()...... <br />不是應該 --&gt; 物件.此類別的方法 嗎?? </span></p>
<p><span>------------------------------------------------------------------------------------------</span></p>
<p><span></span>&nbsp;</p>
<p><span>因為使用同一個 class 裡的 method <br />會自動隱含 this 這個 reference <br /><br />所以原本的 getServletConfig().~~ <br />可以被看成 this.getServletConfig().~~ </span></p><span>
<blockquote><font color="#0000a0"><b>gu wrote:</b> <br /><br />不好意思..還有一個小問題!! <br />我覺得java有一個很令人困擾的地方在: <br />它常會一串譬如說 <br />getServletConfig().getServletContext().getRequestDispatcher().forward(); <br />我怎麼會知道說forward()這個方法是如何經由getServletConfig()然後經過很多方法才可以得到的呢?? <br /></font></blockquote>
<p><br /><br />假設 <br />getServletConfig() 此 method 會 return 一個物件 a <br />getServletContext() 此 method 會 return 一個物件 b 且它為 a 中眾多 <br />的 method 之一 <br />getRequestDispatcher() 此 method 會 return 一個物件 c 它為 b 中眾多 <br />的 method 之一 <br />forward() 此 method 為 c 中眾多的 method 之一 <br /><br />那 <br />getServletContext().getRequestDispatcher().forward(); <br />--&gt; <br />a.getServletContext().getRequestDispatcher().forward(); <br />--&gt; <br />b.getRequestDispatcher().forward(); <br />--&gt; <br />c.forward(); <br /><br />這樣了嗎? <br /><br />最前面的寫法只是省略了 reference ，這樣寫的好處是可以節省 <br />name space 的浪費。 <br /><br />那要如何知道? 那就要查 API 文件了。 </p></span><p></p>]]></description>
		</item>
		    
		
		<item>
			<title>MySQL常见错误问答!</title>
			<link>http://rire-azure.blog.sohu.com/88190455.html</link>
			<comments>http://rire-azure.blog.sohu.com/88190455.html#comment</comments>
			<dc:creator>-------------------</dc:creator>
			<pubDate>Fri, 23 May 2008 01:09:46 +0800</pubDate>
			<category>sql</category>
			<guid>http://rire-azure.blog.sohu.com/88190455.html</guid>
			<description><![CDATA[<p align="center">&nbsp;</p>
<p align="center">18.2.1 MySQL server has gone away错误 <br />本小节也涉及有关Lost connection to server during query的错误。 <br />对MySQL server has gone away错误最常见的原因是服务器超时了并且关闭了连接。缺省地，如果没有事情发生，服务器在 8个小时后关闭连接。你可在启动mysqld时通过设置wait_timeout变量改变时间限制。 <br />你可以通过执行mysqladmin version并且检验正常运行的时间来检查MySQL还没死掉。 <br />如果你有一个脚本，你只须再发出查询让客护进行一次自动的重新连接。 <br />在这种请下，你通常能获得下列错误代码(你得到的是OS相关的)： <br />CR_SERVER_GONE_ERROR 客户不能发送一个问题给服务器。 <br />CR_SERVER_LOST 当写服务器时，客户没有出错，但是它没有得到对问题的一个完整的答案(或任何答案)。 </p>
<p>如果你向服务器发送不正确的或太大的查询，你也可能得到这些错误。如果mysqld得到一个太大或不正常的包，它认为客户出错了并关闭连接。如果你需要较大的查询(例如，如果你正在处理较大的BLOB列)，你可以使用-O max_allowed_packet=#选项(缺省1M)启动mysqld以增加查询限制。多余的内存按需分配，这样mysqld只有在你发出较大差询时或mysqld必须返回较大的结果行时，才使用更多的内存！ </p>
<p><br />18.2.2 Can&rsquo;t connect to [local] MySQL server错误 <br />一个MySQL客户可以两种不同的方式连接mysqld服务器：Unix套接字，它通过在文件系统中的一个文件(缺省&ldquo;/tmp/mysqld.sock&rdquo;)进行连接；或TCP/IP，它通过一个端口号连接。Unix套接字比TCP/IP更快，但是只有用在连接同一台计算机上的服务器。如果你不指定主机名或如果你指定特殊的主机名localhost，使用Unix套接字。 <br />错误(2002）Can&rsquo;t connect to ...通常意味着没有一个MySQL服务器运行在系统上或当试图连接mysqld服务器时，你正在使用一个错误的套接字文件或TCP/IP端口。 <br />由检查(使用ps)在你的服务器上有一个名为mysqld的进程启动！如果没有任何mysqld过程，你应该启动一个。见4.15.2 启动MySQL服务器的问题。 <br />如果一个mysqld过程正在运行，你可以通过尝试这些不同的连接来检查服务器(当然，端口号和套接字路径名可能在你的安装中是不同的)： <br />shell&gt; mysqladmin version <br />shell&gt; mysqladmin variables <br />shell&gt; mysqladmin -h `hostname` version variables <br />shell&gt; mysqladmin -h `hostname` --port=3306 version <br />shell&gt; mysqladmin -h &rsquo;ip for your host&rsquo; version <br />shell&gt; mysqladmin --socket=/tmp/mysql.sock version </p>
<p>注意hostname命令使用反引号&ldquo;`&rdquo;而非正引号&ldquo;&rsquo;&rdquo;；这些导致hostname输出（即，当前主机名)被代替进mysqladmin命令中。 <br />这是可能造成Can&rsquo;t connect to local MySQL server错误的一些原因： <br />mysqld不在运行。 <br />你正在使用MIT-pthreads的一个系统上运行。如果正在运行在一个没有原生线程的系统上，mysqld使用 MIT-pthreads 软件包。见4.2 由MySQL支持的操作系统。然而，MIT-pthreads不支持Unix套接字，因此当与服务器连接时，在这样一个系统上，你总是必须明确地指定主机名。试试使用这个命令检查到服务器的连接： <br />shell&gt; mysqladmin -h `hostname` version </p>
<p>某人删除了mysqld使用的Unix套接字(缺省&ldquo;/tmp/mysqld.sock&rdquo;)。你可能有一个cron任务删除了MySQL套接字(例如，一个把旧文件从&ldquo;/tmp&rdquo;目录中删除的任务)。你总是可以运行mysqladmin version并且检查mysqladmin正在试图使用的套接字确实存在。在这种情况下，修复方法是删除cron任务而不删除&ldquo;mysqld.sock 或将套接字放在其他地方。你能用这个命令在MySQL配置时指定一个不同的套接字地点： <br />shell&gt; ./configure --with-unix-socket-path=/path/to/socket </p>
<p>你也可以使用--socket=/path/to/socket选项启动safe_mysqld和在启动你的MySQL客户前设置环境变量MYSQL_UNIX_PORT为套接字路径名。你可用--socket=/path/to/socket选项启动mysqld服务器。如果你改变了服务器的套接字路径名，你也必须通知MySQL客户关于新路径的情况。你可以通过设置环境变量MYSQL_UNIX_PORT为套接字路径名或由提供套接字路径名作为客户的参数做到。你可用这个命令测试套接字： <br />shell&gt; mysqladmin --socket=/path/to/socket version </p>
<p>你正在使用 Linux和线程已经死了(核心倾倒了)。在这种情况中，你必须杀死其它mysqld线程(例如在启动一个新的MySQL服务器之前，可以用mysql_zap脚本）。见18.1 如果MySQL总是崩溃怎么办。 <br />如果你得到错误Can&rsquo;t connect to MySQL server on some_hostname，你可以尝试下列步骤找出问题是什么： <br />通过执行telnet your-host-name tcp-ip-port-number并且按几次回车来检查服务器是否正常运行。如果有一个MySQL运行在这个端口上，你应该得到一个包含正在运行的MySQL服务器的版本号的应答。如果你得到类似于telnet: Unable to connect to remote host: Connection refused的一个错误，那么没有服务器在使用的端口上运行。 <br />尝试连接本地机器上的mysqld守护进程，并用mysqladmin variables检查mysqld被配置使用的TCP/IP端口(变量port)。 <br />检查你的mysqld服务器没有用--skip-networking选项启动。 </p>
<p><br />18.2.3 Host &rsquo;...&rsquo; is blocked错误 <br />如果你得到象这样的一个错误： <br />Host &rsquo;hostname&rsquo; is blocked because of many connection errors. <br />Unblock with &rsquo;mysqladmin flush-hosts&rsquo; </p>
<p>这意味着，mysqld已经得到了大量(max_connect_errors)的主机&rsquo;hostname&rsquo;的在中途被中断了的连接请求。在max_connect_errors次失败请求后，mysqld认定出错了(象来字一个黑客的攻击)，并且阻止该站点进一步的连接，直到某人执行命令mysqladmin flush-hosts。 <br />缺省地，mysqld在10个连接错误后阻塞一台主机。你可以通过象这样启动服务器很容易地调整它： <br />shell&gt; safe_mysqld -O max_connect_errors=10000 &amp; </p>
<p>注意，对给定的主机，如果得到这条错误消息，你应该首先检查该主机的TCP/IP连接有没有问题。如果你的TCP/IP连接不在运行，增加max_connect_errors变量的值对你也不会有帮助！ </p>
<p><br />18.2.4 Too many connections错误 <br />如果在你试土连接MySQL时，你得到错误Too many connections，这意味着已经有max_connections个客户连接了mysqld服务器。 <br />如果你需要比缺省(100)更多的连接，那么你应该重启mysqld，用更大的 max_connections 变量值。 <br />注意，mysqld实际上允许(max_connections+1)个客户连接。最后一个连接是为一个用Process权限的用户保留的。通过不把这个权限给一般用户(他们不应该需要它)，有这个权限一个管理员可以登录并且使用SHOW PROCESSLIST找出什么可能出错。见7.21 SHOW句法(得到表，列的信息）。 <br />18.2.5 Out of memory错误 <br />如果你发出查询并且得到类似于下面的错误： <br />mysql: Out of memory at line 42, &rsquo;malloc.c&rsquo; <br />mysql: needed 8136 byte (8k), memory in use: 12481367 bytes (12189k) <br />ERROR 2008: MySQL client ran out of memory </p>
<p>注意，错误指向了MySQL客户mysql。这个错误的原因很简单，客户没有足够的内存存储全部结果。 <br />为了修正这个问题，首先检查你的查询是否正确。它应该返回这么多的行，这合理吗？如果是这样，你可以使用mysql --quick，它使用mysql_use_result()检索结果集合。这将较少的负担放在了客户端(只是服务器更多)。 </p>
<p><br />18.2.6 Packet too large错误 <br />当一个MySQL客户或mysqld服务器得到一个比max_allowed_packet个字节长的包，它发出一个Packet too large错误并终止连接。 <br />如果你正在使用mysql客户，你可以通过用mysql --set-variable=max_allowed_packet=8M指定一个更大的缓冲区来启动客户程序。 <br />如果你正在使用不允许你指定最大包大小的其他客户(例如 DBI)，你需要在你启动服务器时设置包大小。你可以使用mysqld的命令行选项设置max_allowed_packet为一个更大的尺寸。例如，如果你正期望将一个全长的BLOB存入一张表中，你将需要用--set-variable=max_allowed_packet=24M选项来启动服务器。 <br />　 </p>
<p>18.2.7 The table is full错误 <br />这个错误发生在内存临时表变得比tmp_table_size字节大时。为了避免这个问题，你可以使用mysqld的-O tmp_table_size=#选项来增加临时表的大小，或在你发出有疑问的查询之前使用SQL选项SQL_BIG_TABLES。见7.25 SET OPTION句法。 <br />你也可以使用--big-tables选项启动mysqld。这与为所有查询使用SQL_BIG_TABLES完全相同。<br />18.2.8 Commands out of sync in client错误 <br />如果你在你的客户代码中得到Commands out of sync; You can&rsquo;t run this command now，你正在以错误的次序调用客户函数！ <br />这可能发生，例如，如果你正在使用mysql_use_result()并且在你已经调用了mysql_free_result()之前试图执行新查询。如果你在mysql_use_result()或mysql_store_result()之间试图执行返回数据的2个查询，它也可能发生。 </p>
<p><br />18.2.9 Ignoring user错误 <br />如果你得到下列错误： <br />Found wrong password for user: &rsquo;some_user@some_host&rsquo;; Ignoring user <br />这意味着在mysqld启动时或在它再次装载权限表时，它在user表中找到了一个有一个无效口令的条目。结果，条目简单地被权限系统忽略。 <br />可能导致这个问题的原因和修正： <br />你可能正在运行一个有一个老的user表的新版本mysqld。你可以通过执行mysqlshow mysql user看看口令字段是否少于 16个字符来检查它。如果是这样，你可以通过运行scripts/add_long_password脚本改正这种情况。 <br />用户有一个老式的口令(8个字符长)并且你没使用--old-protocol选项启动mysqld。用一个新口令更新在user表中的用户或用--old-protocol重启mysqld。 <br />你没有使用PASSWORD()函数在在user表中指定了一个口令。使用mysql以一个新口令更新在user表中的用户。确保使用PASSWORD()函数： <br />mysql&gt; update user set password=PASSWORD(&rsquo;your password&rsquo;) <br />where user=&rsquo;XXX&rsquo;; </p>
<p><br />18.2.10 Table &rsquo;xxx&rsquo; doesn&rsquo;t exist错误 <br />如果你得到错误Table &rsquo;xxx&rsquo; doesn&rsquo;t exist或Can&rsquo;t find file: &rsquo;xxx&rsquo; (errno: 2)，这意味着在当前数据库中没有名为xxx的表存在。 <br />注意，因为MySQL使用目录和文件存储数据库和表，数据库和表名件是区分大小写的！（在Win32上，数据库和表名不是区分大小写的，但是在查询中对所有表的引用必须使用相同的大小写！） <br />你可以用SHOW TABLES检查你在当前数据库中有哪个表。见7.21 SHOW句法(得到表、列的信息）。 <br />18.3 MySQL怎样处理一个溢出的磁盘 <br />当出现一个磁盘溢出的情况时，MySQL做下列事情： <br />它每分钟检查一次看是否有足够空间写入当前行。如果有足够的空间，它继续好像发生什么事情。 <br />每6分钟它将有关磁盘溢出的警告写入日志文件。 <br />为了缓和这个问题，你可以采取下列行动： <br />继续，你只需释放足够的空闲磁盘空间以便插入所有记录。 <br />放弃线程，你必须发一个mysqladmin kill到线程。在下一次检查磁盘时，线程将被放弃(在1分钟内)。 <br />注意，其他线程可能正在等待引起&ldquo;磁盘溢出&rdquo;条件的表。如果你有几个&ldquo;锁定的&rdquo;的线程，杀死正在等待磁盘溢出条件的那个线程将允许其他线程继续。 </p>
<p><br />18.4 如何从一个文本文件运行SQL命令 <br />一般地，mysql客户被交互性地使用，象这样： <br />shell&gt; mysql database <br />然而，也可以把你的SQL命令放在一个文件中并且告诉mysql从该文件读取其输入。要想这样做，创造一个文本文件&ldquo;text_file&rdquo;，它包含你想要执行的命令。然后如下那样调用mysql： <br />shell&gt; mysql database &lt; text_file </p>
<p>你也能启动有一个USE db_name语句的文本文件。在这种情况下，在命令行上指定数据库名是不必要的： <br />shell&gt; mysql &lt; text_file </p>
<p>见12.1 不同的MySQL程序概述。 </p>
<p><br />18.5 MySQL在哪儿存储临时文件 <br />MySQL使用TMPDIR环境变量的值作为存储临时文件的目录的路径名。如果你没有设置TMPDIR，MySQL使用系统缺省值，它通常是&ldquo;/tmp&rdquo;或&ldquo;/usr/tmp&rdquo;。如果包含你的临时文件目录的文件系统太小，你应该编辑safe_mysqld设定TMPDIR指向你有足够空间的一个文件系统！你也可以使用mysqld的--tmpdir选项目设置临时目录。 <br />MySQL以&ldquo;隐含文件&rdquo;创建所有临时文件。这保证了如果mysqld被终止，临时文件也将被删除。使用隐含文件的缺点是你将看不到一个大的临时文件填满了临时文件目录所在的文件系统。 <br />当排序(ORDER BY或GROUP BY)时，MySQL通常使用一个或两个临时文件。最大磁盘空间需求是： <br />(存储东西的长度 + sizeof (数据库指针)) <br />* 匹配的行数 <br />* 2 </p>
<p>sizeof(数据库指针)通常是4，但是在未来对确实很大的表可能增加。 <br />对一些SELECT查询，MySQL也创建临时SQL表。这些没被隐含且有&ldquo;SQL_*&rdquo;格式的名字。 <br />ALTER TABLE和OPTIMIZE TABLE在原数据库表的同一个目录中创建一张临时表。 </p>
<p><br />18.6 怎样保护&ldquo;/tmp/mysql.sock &rdquo;不被删除 <br />如果你有这个问题，事实上任何人可以删除MySQL通讯套接字&ldquo;/tmp/mysql.sock&rdquo;，在Unix的大多数版本上，你能通过为其设置sticky（t）位来保护你的&ldquo;/tmp&rdquo;文件系统。作为root登录并且做下列事情： <br />shell&gt; chmod +t /tmp </p>
<p>这将保护你的&ldquo;/tmp&rdquo;文件系统使得文件仅能由他们的所有者或超级用户(root)删除。 <br />你能执行ls -ld /tmp检查sticky位是否被设置，如果最后一位许可位是t，该位被设置了。 </p>
<p><br />18.7 Access denied错误 <br />见6.6 权限系统如何工作。并且特别要看6.13 引起Access denied错误的原因。 </p>
<p><br />18.8 怎样作为一个一般用户运行MySQL <br />MySQL服务器mysqld能被任何用户启动并运行。为了将mysqld改由Unix用户user_name来运行，你必须做下列事情： <br />如果它正在运行，停止服务器(使用mysqladmin shutdown)。 <br />改变数据库目录和文件以便user_name有权限读和写文件(你可能需要作为Unix的root用户才能做到)： <br />shell&gt; chown -R user_name /path/to/mysql/datadir </p>
<p>如果在MySQL数据目录中的目录或文件是符号链接，你也将需要顺着那些链接并改变他们指向的目录和文件。chown -R不能跟随符号链接。 <br />以user_name用户启动服务器，或如果你正在使用MySQL 3.22或以后版本，以Unix root用户启动mysqld并使用--user=user_name选项，mysqld将在接受任何连接之前切换到以Unix user_name用户运行。 <br />如果在系统被重新启动时，你使用mysql.server脚本启动mysqld，你应该编辑mysql.server用su以用户user_name运行mysqld，或使用--user选项调用mysqld。（不改变safe_mysqld是必要的。） <br />现在，你的mysqld进程应该正在作为Unix用户user_name运行，并运行完好。尽管有一件事情没有变化：权限表的内容。缺省 地(就在运行了脚本mysql_install_db安装的权限表后)，MySQL用户root是唯一有存取mysql数据库或创建或抛弃数据库权限的用户。除非你改变了那些权限，否则他们仍然保持。当你作为一个Unix用户而不是root登录时，这不应该阻止你作为MySQL root用户来存取MySQL；只要为客户程序指定-u root的选项。 <br />注意通过在命令行上提供-u root，作为root存取MySQL，与作为Unix root用户或其他Unix用户运行MySQL没有关系。MySQL的存取权限和用户名与Unix用户名字是完全分开的。唯一与Unix用户名有关的是，如果当你调用一个客户程序时，你不提供一个-u选项，客户将试图使用你的Unix登录名作为你的MySQL用户名进行连接。 <br />如果你的Unix机器本身不安全，你可能应该至少在存取表中为MySQL root用户放上一个口令。否则，在那台机器上有一个帐号的任何用户能运行mysql -u root db_name并且做他喜欢做的任何事情。<br />18.9 怎样重新设置一个忘记的口令 <br />如果你忘记了MySQL的root用户的口令，你可以用下列过程恢复它。 <br />通过发送一个kill（不是kill -9)到mysqld服务器来关闭mysqld服务器。pid 被保存在一个.pid文件中，通常在MySQL数据库目录中： <br />kill `cat /mysql-data-directory/hostname.pid` </p>
<p>你必须是一个UNIX root用户或运行服务器的相同用户做这个。 <br />使用--skip-grant-tables选项重启mysqld。 <br />用mysql -h hostname mysql连接mysqld服务器并且用一条GRANT命令改变口令。见7.26 GRANT和REVOKE句法。你也可以用mysqladmin -h hostname -u user password &rsquo;new password&rsquo; 进行。 <br />用mysqladmin -h hostname flush-privileges或用SQL命令FLUSH PRIVILEGES来装载权限表。 </p>
<p><br />18.10 文件许可权限问题 <br />如果你有与文件许可有关的问题，例如，如果当你创建一张表时，mysql发出下列错误消息： <br />ERROR: Can&rsquo;t find file: &rsquo;path/with/filename.frm&rsquo; (Errcode: 13) </p>
<p>那么可能是在mysqld启动时，环境变量UMASK可能设置不正确。缺省的umask值是0660。你可以如下启动safe_mysqld改变其行为： <br />shell&gt; UMASK=384 # = 600 in octal <br />shell&gt; export UMASK <br />shell&gt; /path/to/safe_mysqld &amp; </p>
<p><br />18.11 文件没找到 <br />如果你从MySQL得到ERROR &rsquo;...&rsquo; not found (errno: 23), Can&rsquo;t open file: ... (errno: 24)或任何其他有errno 23或errno 24的错误，它意味着，你没有为MySQL分配足够的文件描述符。你能使用perror实用程序得到错误号含义是什么的描述： <br />shell&gt; perror 23 <br />File table overflow <br />shell&gt; perror 24 <br />Too many open files </p>
<p>这里的问题是mysqld正在试图同时保持打开太多的文件。你也可以告诉mysqld一次不打开那么多的文件，或增加mysqld可得到的文件描述符数量。为了告诉mysqld一次保持打开更少的文件，你可以通过使用safe_mysqld的-O table_cache=32选项（缺省值是64)使表缓冲更小。减小max_connections值也将减少打开文件的数量(缺省值是90)。 <br />要想改变mysqld可用的文件描述符数量，修改safe_mysqld脚本。脚本中有一条注释了的行ulimit -n 256。你可以删除&rsquo;#&rsquo;字符来去掉该行的注释，并且改变数字256改变为mysqld可用的文件描述符的数量。 <br />ulimit能增加文件描述符的数量，但是只能到操作系统强加的限制。如果你需要增加每个进程可用的文件描述符数量的OS限制，参见你的操作系统文档。注意，如果你运行tcsh外壳，ulimit将不工作！当你请求当前限制时，tcsh也将报告不正确的值！在这种情况下，你应该用sh启动safe_mysqld！ </p>
<p><br />18.12 使用DATE列的问题 <br />一个DATE值的格式是&rsquo;YYYY-MM-DD&rsquo;。根据ANSI SQL，不允许其他格式。你应该在UPDATE表达式和SELECT语句的WHERE子句中使用这个格式。例如： <br />mysql&gt; SELECT * FROM tbl_name WHERE date &gt;= &rsquo;1997-05-05&rsquo;; </p>
<p>为了方便，如果日期用在数字上下文，MySQL自动变换一个日期到一个数字(并且反过来也如此)。当更新时和将一个日期与TIMESTAMP、DATE或DATETIME列比较的一个WHERE子句中，也是足够灵活以允许一种&ldquo;宽松&rdquo;的字符串格式。（宽松格式意味着任何标点字符用作在部件之间的分割符。例如，&rsquo;1998-08-15&rsquo;和&rsquo;1998#08#15&rsquo;是等价的。）MySQL也能变换不包含分割符的一个字符串(例如 &rsquo;19980815&rsquo;)，如果它作为一个日期说得通。特殊日期&rsquo;0000-00-00&rsquo;可以作为&rsquo;0000-00-00&rsquo;被存储和检索。当通过MyODBC使用一个&rsquo;0000-00-00&rsquo;日期时，在MyODBC 2.50.12和以上版本，它将自动被转换为NULL，因为ODBC不能处理这种日期。 <br />因为MySQL实行了上述的变换，下列语句可以工作： <br />mysql&gt; INSERT INTO tbl_name (idate) VALUES (19970505); <br />mysql&gt; INSERT INTO tbl_name (idate) VALUES (&rsquo;19970505&rsquo;); <br />mysql&gt; INSERT INTO tbl_name (idate) VALUES (&rsquo;97-05-05&rsquo;); <br />mysql&gt; INSERT INTO tbl_name (idate) VALUES (&rsquo;1997.05.05&rsquo;); <br />mysql&gt; INSERT INTO tbl_name (idate) VALUES (&rsquo;1997 05 05&rsquo;); <br />mysql&gt; INSERT INTO tbl_name (idate) VALUES (&rsquo;0000-00-00&rsquo;); </p>
<p>mysql&gt; SELECT idate FROM tbl_name WHERE idate &gt;= &rsquo;1997-05-05&rsquo;; <br />mysql&gt; SELECT idate FROM tbl_name WHERE idate &gt;= 19970505; <br />mysql&gt; SELECT mod(idate,100) FROM tbl_name WHERE idate &gt;= 19970505; <br />mysql&gt; SELECT idate FROM tbl_name WHERE idate &gt;= &rsquo;19970505&rsquo;; </p>
<p>然而，下列将不工作： <br />mysql&gt; SELECT idate FROM tbl_name WHERE STRCMP(idate,&rsquo;19970505&rsquo;)=0; </p>
<p>STRCMP()是字符串函数，因此它将idate转换为一个字符串并且实施字符串比较。它不将&rsquo;19970505&rsquo;转换为一个日期并实施日期比较。 <br />注意，MySQL不检查日期是否正确。如果你存储一个不正确的日期，例如&rsquo;1998-2-31&rsquo;，错误的日期将被存储。如果日期不能被变换到任何合理的值，在DATE字段中存储一个0。这主要是一个速度问题并且我们认为检查日期是应用程序的责任，而不服务器。 </p>
<p><br />18.13 时区问题 <br />如果你有一个问题，SELECT NOW()以GMT时间返回值而不是你的本地时间，你必须设定TZ环境变量为你的当前时区。这应该在服务器运行的环境进行，例如在safe_mysqld或mysql.server中。 </p>
<p><br />18.14 在搜索中的大小写敏感性 <br />缺省地，MySQL搜索是大小写不敏感的(尽管有一些字符集从来不是忽略大小写的，例如捷克语)。这意味着，如果你用col_name LIKE &rsquo;a%&rsquo;搜寻，你将得到所有以A或a开始的列值。如果你想要使这个搜索大小写敏感，使用象INDEX(col_name, &quot;A&quot;)=0检查一个前缀。或如果列值必须确切是&quot;A&quot;，使用STRCMP(col_name, &quot;A&quot;) = 0。 <br />简单的比较操作(&gt;=、&gt;、= 、&lt; 、&lt;=、排序和聚合)是基于每个字符的&ldquo;排序值&rdquo;。有同样排序值的字符(象E，e和&rsquo;e)被视为相同的字符！ <br />LIKE比较在每个字符的大写值上进行(E==e 但是E&lt;&gt;&rsquo;e）。 <br />如果你想要一个列总是被当作大小写敏感的方式，声明它为BINARY。见7.7 CREATE TABLE句法。 <br />如果你使用以所谓的big5编码的中文数据，你要使所有的字符列是BINARY，它可行，是因为big5编码字符的排序顺序基于 ASCII代码的顺序。 </p>
<p><br />18.15 NULL值问题 <br />NULL值的概念是造成SQL的新手的混淆的普遍原因，他们经常认为NULL是和一个空字符串&rsquo;&rsquo;的一样的东西。不是这样的！例如，下列语句是完全不同的： <br />mysql&gt; INSERT INTO my_table (phone) VALUES (NULL); <br />mysql&gt; INSERT INTO my_table (phone) VALUES (&quot;&quot;); </p>
<p>两个语句把值插入到phone列，但是第一个插入一个NULL值而第二个插入一个空字符串。第一个的含义可以认为是&ldquo;电话号码不知道&rdquo;，而第二个则可意味着&ldquo;她没有电话&rdquo;。 <br />在SQL中，NULL值在于任何其他值甚至NULL值比较时总是假的（FALSE）。包含NULL的一个表达式总是产生一个NULL值，除非在包含在表达式中的运算符和函数的文档中指出。在下列例子，所有的列返回NULL： <br />mysql&gt; SELECT NULL,1+NULL,CONCAT(&rsquo;Invisible&rsquo;,NULL); </p>
<p>如果你想要寻找值是NULL的列，你不能使用=NULL测试。下列语句不返回任何行，因为对任何表达式，expr = NULL是假的： <br />mysql&gt; SELECT * FROM my_table WHERE phone = NULL; </p>
<p>要想寻找NULL值，你必须使用IS NULL测试。下例显示如何找出NULL电话号码和空的电话号码： <br />mysql&gt; SELECT * FROM my_table WHERE phone IS NULL; <br />mysql&gt; SELECT * FROM my_table WHERE phone = &quot;&quot;; </p>
<p>在MySQL中，就像很多其他的SQL服务器一样，你不能索引可以有NULL值的列。你必须声明这样的列为NOT NULL，而且，你不能插入NULL到索引的列中。当用LOAD DATA INFILE读取数据时，空列用&rsquo;&rsquo;更新。如果你想要在一个列中有NULL值，你应该在文本文件中使用\N。字面词&rsquo;NULL&rsquo;也可以在某些情形下使用。见7.16 LOAD DATA INFILE句法。当使用ORDER BY时，首先呈现NULL值。如果你用DESC以降序排序，NULL值最后显示。当使用GROUP BY时，所有的NULL值被认为是相等的。为了有助于NULL的处理，你能使用IS NULL和IS NOT NULL运算符和IFNULL()函数。 <br />对某些列类型，NULL值被特殊地处理。如果你将NULL插入表的第一个TIMESTAMP列，则插入当前的日期和时间。如果你将NULL插入一个AUTO_INCREMENT列，则插入顺序中的下一个数字。<br />18.16 alias问题 <br />你可以在GROUP BY、ORDER BY或在HAVING部分中使用别名引用列。别名也可以用来为列取一个更好点的名字： <br />SELECT SQRT(a*b) as rt FROM table_name GROUP BY rt HAVING rt &gt; 0; <br />SELECT id,COUNT(*) AS cnt FROM table_name GROUP BY id HAVING cnt &gt; 0; <br />SELECT id AS &quot;Customer identity&quot; FROM table_name; </p>
<p>注意，你的 ANSI SQL 不允许你在一个WHERE子句中引用一个别名。这是因为在WHERE代码被执行时，列值还可能没有终结。例如下列查询是不合法：SELECT id,COUNT(*) AS cnt FROM table_name WHERE cnt &gt; 0 GROUP BY id; </p>
<p>WHERE语句被执行以确定哪些行应该包括GROUP BY部分中，而HAVING用来决定应该只用结果集合中的哪些行。 </p>
<p><br />18.17 从关联的表中删除行 <br />因为MySQL不支持子选择或在DELETE语句中使用多个表，你应该使用下列方法从2个关联的表中删除行： <br />在主表中基于某个WHERE条件SELECT行。 <br />在主表中基于相同的条件DELETE行。 <br />DELETE FROM related_table WHERE related_column IN (selected_rows) <br />如果在related_column查询中的字符的全部数量超过1,048,576(缺省值max_allowed_packet），你应该分成更小的部分并且执行多个DELETE语句。如果related_column是一个索引，你每次只删除100-1000个related_column id将可能使得DELETE最快。如果related_column不是一个索引，速度与IN子句中参数的数量无关。 </p>
<p><br />18.18 解决没有匹配行的问题 <br />如果你有一个复杂的查询，涉及多个表，但没有返回任何行，你应该使用下列过程查找你的询问有什么不对： <br />EXPLAIN测试查询并且检查你是否能找出显然是错误的一些东西。见7.22 EXPLAIN句法(得到关于一个SELECT的信息)。 <br />仅选择那些在WHERE子句中使用的字段。 <br />一次从查询中删除一个表，直到它返回一些行。如果表很大，对查询使用LIMIT 10是一个好主意。 <br />对应该已经匹配一行的列做一个SELECT，针对从询问中做后被删除的表。 <br />如果你将FLOAT或DOUBLE列与有小数的数字进行比较，你不能使用=!。这个问题在大多数计算机语言是常见的，因为浮点值不是准确的值。 <br />mysql&gt; SELECT * FROM table_name WHERE float_column=3.5; <br />-&gt; <br />mysql&gt; SELECT * FROM table_name WHERE float_column between 3.45 and 3.55; </p>
<p>在大多数情况下，将FLOAT改成一个DOUBLE将修正它！ <br />如果你仍然不能发现错误是什么，创建一个最小的可运行mysql test &lt; query.sql的测试来显示你的问题。你可以用mysqldump --quick database tables &gt; query.sql创建一个测试文件，在一个编辑器编辑文件，删除一些插入行(如果有太多这些语句)并且在文件末尾加入你的选择语句。测试你仍然有问题，可以这样做： <br />shell&gt; mysqladmin create test2 <br />shell&gt; mysql test2 &lt; query.sql </p>
<p>使用mysqlbug的邮寄测试文件到mysql@lists.mysql.com。 </p>
<p><br />18.19 与ALTER TABLE有关的问题 <br />如果ALTER TABLE死于这样一个错误： <br />Error on rename of &rsquo;./database/name.frm&rsquo; to &rsquo;./database/B-a.frm&rsquo; (Errcode: 17) </p>
<p>问题可能是MySQL在前一个ALTER TABLE中已经崩溃并且留下了一个名为&ldquo;A-xxx&rdquo;或&ldquo;B-xxx&rdquo;的老的数据库表。在这种情况下，到MySQL数据目录中并删除所有名字以A-或B-开始的文件。（你可以把他们移到别的地方而不是删除他们)。 <br />ALTER TABLE工作方式是： <br />以要求的改变创建一个名为&ldquo;A-xxx&rdquo;的新表。 <br />从老表把所有行拷贝到&ldquo;A-xxx&rdquo;。 <br />老表被改名为&ldquo;B-xxx&rdquo;。 <br />&ldquo;A-xxx&rdquo;被改名为你的老表的名字。 <br />&ldquo;B-xxx&rdquo;被删除。 <br />如果某些改名操作出错，MySQL试图还原改变。如果出错严重(当然，这不应该发生。)，MySQL可能留下了老表为&ldquo;B-xxx&rdquo;但是一个简单改名就应该恢复你的数据。 </p>
<p><br />18.20 怎样改变一张表中列的顺序 <br />SQL的要点是中抽象应用程序以避免数据存储格式。你应该总是以你想要检索数据的意愿指定顺序。例如： <br />SELECT col_name1, col_name2, col_name3 FROM tbl_name; </p>
<p>将以col_name1、col_name2、col_name3的顺序返回列，而： <br />SELECT col_name1, col_name3, col_name2 FROM tbl_name; </p>
<p>将以col_name1、col_name3、col_name2的顺序返回列。 <br />在一个应用程序中，你应该决不基于他们的位置使用SELECT * 检索列，因为被返回的列的顺序永远不能保证；对你的数据库的一个简单改变可能导致你的应用程序相当有戏剧性地失败。 <br />不管怎样，如果你想要改变列的顺序，你可以这样做： <br />以正确的列顺序创建一张新表。 <br />执行INSERT INTO new_table SELECT fields-in-new_table-order FROM old_table. <br />删除或改名old_table。 <br />ALTER TABLE new_table RENAME old_table。 </p>]]></description>
		</item>
		    
		
		<item>
			<title>JDK常用命令</title>
			<link>http://rire-azure.blog.sohu.com/88136183.html</link>
			<comments>http://rire-azure.blog.sohu.com/88136183.html#comment</comments>
			<dc:creator>-------------------</dc:creator>
			<pubDate>Thu, 22 May 2008 14:50:10 +0800</pubDate>
			<category>java</category>
			<guid>http://rire-azure.blog.sohu.com/88136183.html</guid>
			<description><![CDATA[<p><a href="http://blog.csdn.net/andyelvis/archive/2007/10/22/1837252.aspx">http://blog.csdn.net/andyelvis/archive/2007/10/22/1837252.aspx</a></p>
<p>&nbsp;</p>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>1.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>appletviewer</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">sun公司的<span>jdk中包括一个名为appletviewer的工具，它能从html文件中抽取出&lt;applet&gt;标签，然后只运行这个applet而不显示周围的html文本内容。这是因为appletviewer会忽略除applet标签以外的所有信息，所以你可以把这些标签作为注释放在java源代码文件里面：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">// &lt;applet code=MyApplet width=200 height=100&gt;&lt;/applet&gt;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">这样，你就可以通过&ldquo;<span>appletviewer MyApplet.java&rdquo;这个命令来运行applet，而且也不用为了测试而专门写一个小的html文件。例如，你可以在Applet1.java里加上作为注释出现的html标签：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">// &lt;applet code=Applet1b width=100 height=50&gt;&lt;/applet&gt;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Import javax.swing.*;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Import java.awt.*;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Public class Applet1b extends JApplet{</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Public void init(){</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">getContentPane().add(new JLabel(&ldquo;Applet!&rdquo;));</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">}</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">}</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">现在可以使用下面的命令来运行<span>applet了：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Appletviewer Applet1b.java</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>2.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>htmlconverter</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">将包含<span>applets的文件转换成可以使用java插件的形式。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">更详细的资料：<span><a href="http://72.5.124.55/products/plugin/1.3/docs/htmlconv.html"><font color="#800080">http://72.5.124.55/products/plugin/1.3/docs/htmlconv.html</font></a></span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>3.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>jar</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><strong>jar</strong> 工具是个<span> java 应用程序，可将多个文件合并为单个 JAR 归档文件。<strong>jar</strong> 是个多用途的存档及压缩工具，它基于 ZIP 和 <a href="http://www.cdrom.com/pub/infozip/zlib/">ZLIB</a> 压缩格式。然而，设计 <strong>jar</strong> 的主要目的是便于将 java&nbsp; applet 或应用程序打包成单个归档文件。将 applet 或应用程序的组件（ .class 文件、图像和声音）合并成单个归档文件时，可以用 java 代理（如浏览器）在一次 HTTP 事务处理过程中 对它们进行下载，而不是对每个组件都要求一个新连接。这大大缩短了下载时间。<strong>jar</strong> 还能压缩文件，从而进一步提高了下载速度。此外，它允许 applet 的作者对文件中的各个项进行签名，因而可认证其来源。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">用法<span>: jar {ctxui}[vfm0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ...</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">选项包括：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -c&nbsp;</span>创建新的归档文件</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -t&nbsp;</span>列出归档目录</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -x&nbsp;</span>解压缩已归档的指定（或所有）文件</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -u&nbsp;</span>更新现有的归档文件</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -v&nbsp;</span>在标准输出中生成详细输出</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -f&nbsp;</span>指定归档文件名</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -m&nbsp;</span>包含指定清单文件中的清单信息</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -e&nbsp;</span>为捆绑到可执行<span> jar 文件的独立应用程序</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>指定应用程序入口点</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -0&nbsp;</span>仅存储；不使用任何<span> ZIP 压缩</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -M&nbsp;</span>不创建条目的清单文件</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -i&nbsp;</span>为指定的<span> jar 文件生成索引信息</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp; -C&nbsp;</span>更改为指定的目录并包含其中的文件</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">如果有任何目录文件，则对其进行递归处理。</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">清单文件名、归档文件名和入口点名的指定顺序</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">与<span> &quot;m&quot;、&quot;f&quot; 和 &quot;e&quot; 标志的指定顺序相同。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">示例<span> 1：将两个类文件归档到一个名为 classes.jar 的归档文件中：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jar cvf classes.jar Foo.class Bar.class</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">示例<span> 2：使用现有的清单文件 &quot;mymanifest&quot; 并</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>将<span> foo/ 目录中的所有文件归档到 &quot;classes.jar&quot; 中：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jar cvfm classes.jar mymanifest -C foo/ .</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>4.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>java</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java程序执行命令</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>5.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>javac</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java源文件编译命令</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>6.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>javadoc</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc的命令行语法如下：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc [ options ] [ packagenames ] [ sourcefiles ] [ @files ]</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">参数可以按照任意顺序排列。下面分别就这些参数和相关的一些内容进行说明：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&quot; Packagenames 包列表。这个选项可以是一系列的包名（用空格隔开），例如</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java.lang java.lang.reflect java.awt。不过，因为<span>javadoc不递归作用于子包，不</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">允许对包名使用通配符；所以你必须显示地列出希望建立文档的每一个包。</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&quot; Sourcefiles 源文件列表。这个选项可以是一系列的源文件名（用空格隔开），可以</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">使用通配符。<span>javadoc允许四种源文件：类源代码文件、包描述文件、总体概述文件、</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">其他杂文件。</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇ 类源代码文件：类或者接口的源代码文件。</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇ 包描述文件：每一个包都可以有自己的包描述文件。包描述文件的名称必须是</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">package.html，与包的<span>.java文件放置在一起。包描述文件的内容通常是使用HTML标记</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">写的文档。<span>javadoc执行时将自动寻找包描述文件。如果找到，javadoc将首先对描述文</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">件中<span>&lt;body&gt;&lt;/body&gt;之间的内容进行处理，然后把处理结果放到该包的Package Summary</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">页面中，最后把包描述文件的第一句（紧靠<span>&lt;body&gt;）放到输出的Overview summary页面中，并在语句前面加上该包的包名。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇ 总体概述文件：<span>javadoc可以创建一个总体概述文件描述整个应用或者所有包。总体</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">概述文件可以被任意命名，也可以放置到任意位置。<span>-overview选项可以指示总体概述</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">文件的路径和名称。总体概述文件的内容是使用<span>HTML标记写的文档。javadoc在执行的时候，如果发现-overview选项，那么它将首先对文件中&lt;body&gt;&lt;/body&gt;之间的内容进行处理；然后把处理后的结果放到输出的Overview summary 页面的底部；最后把总体概述文件中的第一句放到输出的Overview summary页面的顶部。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇ 其他杂文件：这些文件通常是指与<span>javadoc输出的HTML文件相关的一些图片文件、</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Java源代码文件（<span>.java）、Java程序（.class）、Java小程序（Applets）、HTML文件。这些文件必须放在doc-files目录中。每一个包都可以有自己的doc-files目录。举个例子，你希望在java.awt.Button的HTML文档中使用一幅按钮的图片（Button.gif）。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">首先，你必须把图片文件放到<span>C:usersrcjavaawtdoc-files中；然后在Button.java文件</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">中加入下面注释</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/**</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* This button looks like this:</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* &lt;img src=doc-files/Button.gif&gt;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&quot; @files 包含文件。为了简化<span>javadoc命令，你可以把需要建立文档的文件名和包名放</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">在一个或多个文本文件中。例如，为了简化下面命令：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc -d apidoc com.mypackage1 com.mypackage2 com.mypackage3</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">你可以建立一个名称为<span>mypackage.txt的文件，其内容如下：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">com.mypackage1</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">com.mypackage2</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">com.mypackage3</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">然后执行下面命令即可：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc -d apidoc @mypackage.txt</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&quot; options 命令行选项。<span>javadoc使用doclets（doclets是指用doclet API编写的程序。）来确定输出的内容和格式。命令行选项中一部分是可用于所有doclet的通用选项，一部分是由默认的标准doclet提供的专用的选项。下面对各自一些常用的选项分别进行介绍：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">通用选项：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -1.1 生成具有javadoc 1.1版本生成的文档的外观和功能的文档。不是所有的选项都可以用于-1.1选项，具体可以使用javadoc -1.1 -help察看。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -help 显示联机帮助。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -bootclasspath classpathlist 指定根类（通常是Java平台自带的一些类。例如java.awt.*等）的路径。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -sourcepath sourcepathlist 指定包的源文件搜索路径。但是必须注意，只有在javadoc命令中指定了包名的时候才可以使用-sourcepath选项。如果指定了包名，而省略了-sourcepath，那么javadoc使用类路径查找源文件。举例说明：假定你打算为com.mypackage建立文档，其源文件的位置是C:usersrc。那么你可以使用下面的命令：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc -sourcepath c:usersrc com.mypackage</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -classpath classpathlist 指定javadoc查找引用类的路径。引用类是指带文档的类加上它们引用的任何类。javadoc将搜索指定路径的所有子目录。Classpathlist可以包含多个路径（使用;隔开）。如果省略-classpath，则javadoc使用-sourcepath查找源文件和类文件。举例说明：假定你打算为com.mypackage建立文档，其源文件的位置是C:usersrc，包依赖C:userlib中的库。那么你可以使用下面的命令：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc -classpath c:userlib -sourcepath c:usersrc com.mypackage</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -overview pathfilename 告诉javadoc从pathfilename所指定的文件中获取概述文</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">档，并且把它放到输出的概述页面（<span>overview-summary.html）中。其中pathfilename是相对于-sourcepath的相对路径。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -public 只显示公共类以及成员。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -protected 只显示受保护的和公共的类以及成员。缺省选项。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -package只显示包、受保护的和公共的类以及成员。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -private 显示所有类和成员。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -doclet class 指定javadoc产生输出内容的自定义doclet类。如果忽略这个选项，javadoc将使用默认的doclet产生一系列HTML文档。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -docletpath classpathlist 与- doclet选项相关，制定自定义的doclet类文件的路径。Classpathlist可以包含多条路径（用;隔开）。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -verbose 在javadoc运行时提供更详细的信息。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">标准<span>doclet专用选项：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -author 在生成的文档中包含作者项。编译时要加上!否则不会显示! &ndash;author </span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> - d directory 指定javadoc保存生成的HTML文件的目录。省略该选项将把文件保存在当前目录。Directory可以是绝对目录，也可以是相对当前目录的相对目录。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -version 在生成的文档中包含版本项。编译时要加上!否则不会显示! -version</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -use 为类和包生成use（用法）页面。这些页面描述了该类和包在javadoc命令涉及</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">的文件中被使用的情况。例如：对于给定的类<span>C，在C的用法页面中将包含C的子类，类型为C的域，返回变量类型为C的方法以及在参数中有变量类型为C的方法和构造器。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -splitindex 把索引文件按照字母顺序分为多个文件。每一个文件对应一个字母。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -windowtitle title 指定输出的HTML文档的标题。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -header header 指定输出的HTML文档的页眉文本。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -footer footer 指定输出的HTML文档的脚注文本。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -bottom text 指定输出的HTML文档底部的文本。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> - group groupheading packagepatten;packagepatten;&hellip; 在总体概述页面中按照命令的指定方式分隔各个包。例如执行下面命令：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc -group Core Packages java.lang*:java.util</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">-group Extension Packages javax.*</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java.lang java.lang.reflect java.util javax.servlet java.new</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">在页面中将有如下结果：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Core Packages</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java.lang</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java.lang.reflect</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java.util</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Extension Packages</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javax.servlet</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Other Packages</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">java.new</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> - noindex 不输出索引文件。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> - help 在文件的导航条中忽略help链接。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> - helpfile pathfilename 指定导航条中的help链接所指向的帮助文件。忽略该选项，javadoc将生成缺省的帮助文件。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> -stylesheetfile pathfilename 指定javadoc的HTML样式表文件的路径。忽略该选项，javadoc将自动产生一个样式表文件stylesheet.css。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">JavaDoc文档标记</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc注释以<span>/**开始，以*/结束，里面可以包含普通文本、HTML标记和javadoc标记。javadoc只处理源文件中在类/接口定义、方法、域、构造器之前的注释，忽略位于其他地方的注释。举例如下：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/**</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*Demo--&lt;b&gt;Helloworld&lt;/b&gt;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*@author sunjl</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*@version 1.0 2001/10/15</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">public class myHelloworld</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">{</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/**</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*在<span>main( )方法中使用的显示用字符串</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*@see #main(java.lang.String[])</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">static String SDisplay;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/**</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*显示<span>HelloWorld</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*@param args 从命令行中带入的字符串</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*@return 无</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">public static void main(String args[])</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">{</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">SDisplay = Hello World ;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">System.out.println( SDisplay );</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">}</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">}</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">使用下面命令：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javadoc -private -d doc -author -version myHelloworld.java</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">即可以生成漂亮的关于<span>myHelloworld.java的API文档了。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">上面例子中以<span>@开头的标记就是javadoc标记。在Java程序中正确使用javadoc标记是一个良好的注释习惯，将非常有助于javadoc自动从源代码文件生成完整的格式化API文档。下面就对各种标记进行详细说明。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @author name-text 指定生成文档中的作者项，从JDK/SDK 1.0开始引入。name-text可以指定多个名字（使用,隔开）。文档注释可以包含多个类。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> {@docroot} 代表产生文档的根路径，从JDK/SDK 1.3开始引入。用法举例如下</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/**</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*see the &lt;a href={@docroot}/copyright.html&gt;copyright&lt;/a&gt;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">假定生成文档的根目录是<span>doc，上面注释所在的文件最后生成的文件是docutilityutl.html，那么copyright的链接会指向..copyright.html。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @deprecated deprecated-text 添加注释，表明不推荐使用该API。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @exception class-name description @throw的同义标记，从JDK/SDK 1.0开始引入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> {@link package.class#member label} 插入指向package.class#member的内嵌链接，从JDK/SDK 1.2开始引入。举例说明，假定注释中有如下文档：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/** Use the {@link #getComponentAt(int, int) getComponentAt} method. */</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">那么<span>javadoc最终生成的HTML页面中将有如下内容</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Use the &lt;a href = Component.html#getComponentAt(int,int) &gt; getComponentAt &lt;/a&gt; method.</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @param parameter-name description 描述参数，标记成员变量。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @return description 描述返回值，返回函数值。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @see reference 添加参见标题，其中有指向reference的链接或者文本项，从</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">JDK/SDK 1.0开始引入。<span>@see标记有三种形式，下面分别说明：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">（<span>1）、@see string 为string添加文本项，不产生链接。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">（<span>2）、@see &lt;a href=URL#Value&gt;Label&lt;/a&gt; 使用HTML标记产生链接</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">（<span>3）、@see package.class#member Label 使用Java语言的名字package.class #member产生链接。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @serial field-description 用于缺省可序列化域的注释，从JDK/SDK 1.2开始引入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @serialField field-name field-type field-description 建立Serializable类的serialPersistentFields成员的ObjectStreamField组件的文档，从JDK/SDK 1.2开始引入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @serialData data-description data-description建立数据序列和类型的文档，从JDK/SDK 1.2开始引入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @since since-text 利用since-text内容为文档增加since标题，从JDK/SDK 1.1开始引入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @throws class-name description 与@exception同义。用class-name和description为输出文档添加抛出标题，从JDK/SDK 1.2开始引入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">◇<span> @version version-text 添加版权标题，从JDK/SDK 1.0开始引入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">上面介绍了标准<span>doclet提供的所有标记。不过，需要注意这些标记的使用是有位置限制的。其中可以出现在类或者接口文档注释中的标记有：@see、{@link}、@since、@deprecated、@author、@version。可以出现在方法或者构造器文档注释中的标记有：@see、{@link}、@since、@deprecated、@param、@return、@throws、@exception、@serialData。可以出现在域文档注释中的有：@see、{@link}、@since、@desprecated、@serial、@serialField。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">除了<span>javadoc自身提供的标准标记以外，我们可以定制自己的标记吗？当然可以。只需</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">要对<span>javadoc标准的doclet程序进行扩充即可。实际上，利用javadoc提供的doclet API</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">，不仅可以扩充<span>doclet标记，甚至还可以改变javadoc的整个输出。为了满足需要，你</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">可以使<span>javadoc输出普通文本、XML文件等。由于扩充doclet涉及到Java编程，本文不再做深入介绍。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">总之，<span>javadoc提供了完整规范的API文档功能。在软件项目管理中，合理地使用javadoc不仅可以减少开发时的文档工作量，提高效率；而且还非常有利于将来软件的修改和维护。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">JavaDoc 书写规范：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">1）<span> File Header Comments : 每个文件都应该加上文件头标记，包括文件名、修改历史、版权信息和附加信息。例如：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/**</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @(#)demo.java 1.00 2002/05/27</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* Copyright (c) 2000-2002 中国平安保险股份有限公司版权所有</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* Ping An Insurance Company of China. All rights reserved.</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* This software is the confidential and proprietary</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* information of Ping An Insurance Company of China.</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* (Confidential Information). You shall not disclose</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* such Confidential Information and shall use it only</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* in accordance with the terms of the contract agreement</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* you entered into with Ping An.</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">2）<span>class description:类信息，概括的描述类的功能和实现。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/** class description</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">3）<span>Variable Description:描述变量的意义和取值含义。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/** var variable description</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">4）<span>Method Description:标明每个方法的输入、输出参数和返回值类型，说明特殊变量取值的含义。相关类文档链接。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/** method description</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @param var signification</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @exception exception class name</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @return return_value return signification</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">5）<span>Association Description:关联类文档描述，在注释当中需要参引其它文档描述的地方，可在相应的注释当中如下插入：</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">/** method description</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @param var signification</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @exception exception class name</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @return return_value return signification</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">* @see package.class#member label</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">*/</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">6）包描述文件：概括描述包的功能和设计概要。为每个包创建一个描述文件，命名为<span>package.html，与包的java文件放在一起。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">注：<span>javadoc生成文档时，会将该html文件的第一句放在package summary中，而把整个内容放在Overview summary中.</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>7.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>javap</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><strong>javap</strong> 命令用于解析类文件。其输出取决于所用的选项。若没有使用选项，<strong><span>jav</span></strong>将输出传递给它的类的 public 域及方法。<strong>javap</strong> 将其输出到标准输出设备上。例如，编译下面的类声明： </div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; import java.awt.*;</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; import java.applet.*;</span></div>
<div style="TEXT-ALIGN: left" align="left">&nbsp;</div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; public class DocFooter extends Applet {</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String date;</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String email;</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void init() {</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;resize(500,100);</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; date = getParameter(&quot;LAST_UPDATED&quot;);</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; email = getParameter(&quot;EMAIL&quot;);</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void paint(Graphics g) {</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawString(date + &quot; by &quot;,100, 15);</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawString(email,290,15);</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; }</span></div>
<div align="left"><strong>javap DocFooter </strong>的输出生成：<strong><span> </span></strong></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; Compiled from DocFooter.java</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; public class DocFooter extends java.applet.Applet {</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.lang.String date;</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.lang.String email;</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void init();</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void paint(java.awt.Graphics);</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public DocFooter();</span></div>
<div style="TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; }</span></div>
<div style="TEXT-ALIGN: left" align="left">&nbsp;</div>
<div style="TEXT-ALIGN: left" align="left"><span>选项：</span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-help </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">输出<span> javap 的帮助信息。 </span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-l </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">输出行及局部变量表。 </div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-b </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">确保与<span> JDK 1.1 javap 的向后兼容性。 </span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-public </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">只显示<span> public 类及成员。 </span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-protected </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">只显示<span> protected 和 public 类及成员。 </span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-package </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">只显示包、<span>protected 和 public 类及成员。这是缺省设置。 </span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-private </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">显示所有类和成员。 </div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-J<em>flag</em> </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">直接将<em><span> flag </span></em>传给运行时系统。举例说明： </div>
<div style="MARGIN: 0cm 0cm 0pt 72pt; TEXT-ALIGN: left" align="left"><strong>&nbsp;</strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt; TEXT-ALIGN: left" align="left">javap -J-version</div>
<div style="MARGIN: 0cm 0cm 0pt 72pt; TEXT-ALIGN: left" align="left">javap -J-Djava.security.manager -J-Djava.security.policy=MyPolicy MyClassName</div>
<div align="left"><strong>-s </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">输出内部类型签名。 </div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-c </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">输出类中各方法的未解析的代码，即构成<span> Java 字节码的指令。这些在 <a href="http://java.sun.com/docs/books/vmspec/">Java <span><span>虚拟机规范</span></span>中说明。 </a></span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-verbose </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">输出堆栈大小、各方法的<span> locals 及 args 数。 </span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-classpath </strong><strong><em>路径</em></strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">指定<span> javap 用来查找类的路径。如果设置了选项，它将覆盖缺省值或 CLASSPATH 环境变量。目录用分号分隔。因此，<em>路径</em>的一般格式是： </span></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt; TEXT-ALIGN: left" align="left"><strong>&nbsp;</strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt; TEXT-ALIGN: left" align="left"><strong><span>&nbsp;&nbsp; .;&lt;</span></strong><strong>您的路径<span>&gt;</span></strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">例如：</div>
<div style="MARGIN: 0cm 0cm 0pt 72pt; TEXT-ALIGN: left" align="left"><span>&nbsp;&nbsp; .;C:\usrs\dac\classes;C:\tools\java\classes</span></div>
<div align="left"><strong>- bootclasspath </strong><strong><em>路径</em></strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">指定加载自举类所用的路径。缺省情况下，自举类是实现核心<span> Java 平台的类，位于 jre\lib\rt.jar 和 jre\lib\i18n.jar 中。 </span></div>
<div align="left"><strong>　</strong></div>
<div align="left"><strong>-extdirs <em>dirs</em> </strong></div>
<div style="MARGIN: 0cm 0cm 0pt 72pt" align="left">覆盖搜索安装方式扩展的位置。扩展的缺省位置是<span> jre\lib\ext。 </span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>8.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>javaw</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">javaw命令和<span>java命令大体相同，但是javaw和控制台窗口无关。当你不想在出现一个命令提示符窗口时可以使用javaw命令，但是当启动失败时，javaw启动器会弹出一个错误对话框来提示错误信息。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>9.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>native2ascii</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">native2ascii-本地码至<span>ASCII码转换器</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">将含有本地编码字符（既非<span>Latin1又非Unicode字符）的文件转换为Unicode编码字符的文件。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">结构：<span>native2ascii [options] [inputfile] [outputfile]</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">说明：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">Java编译器和其他<span>java工具只能处理含有Latin-1和Unicode编码字符的文件。Native2ascii将含有其他字符编码的文件转换成Latin-1和/或Unicode编码字符的文件。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">若省略<span>outputfile，则使用标准输出设备输出。此外，如果省略inputfile，则使用标准输入设备输入。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">选项：</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">-reverse</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">执行相反的操作</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">-encoding encoding_name</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">指定转换过程使用的编码名称。缺省的编码从系统属性<span>file.encoding中得到。</span></div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">&nbsp;</div>
<div style="MARGIN: 0cm 19.2pt 0pt 18pt; TEXT-INDENT: -18pt"><span>10.&nbsp;</span>serialver</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">功能说明： <br />　　serialver 命令返回 serialVersionUID。 </div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">语法： <br />　　serialver [-classpath 类路径] [-show] [类名称...]</div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">补充说明： <br />　　serialver 以适于复制到演变类的形式返回一个或多个类的 serialVersionUID。不带参数调用时，它输出用法行。 </div>
<div style="MARGIN: 0cm 19.2pt 0pt 0cm">命令选项<span> <br />　　-show 显示一个简单的用户界面。输入完整的类名并按回车键或&ldquo;显示&rdquo;按钮可显示 serialVersionUID。</span></div>
<p>&nbsp;</p>]]></description>
		</item>
		    
		
		<item>
			<title>Java正则表达式详解</title>
			<link>http://rire-azure.blog.sohu.com/87842453.html</link>
			<comments>http://rire-azure.blog.sohu.com/87842453.html#comment</comments>
			<dc:creator>-------------------</dc:creator>
			<pubDate>Mon, 19 May 2008 18:22:44 +0800</pubDate>
			<category>java</category>
			<guid>http://rire-azure.blog.sohu.com/87842453.html</guid>
			<description><![CDATA[<table align="center">
<tbody>
<tr>
<td>
<p><font color="#66ccff"><a href="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4.asp">http://www.ccw.com.cn/htm/app/aprog/01_7_31_4.asp</a></font></p>
<p>&nbsp;</p>
<p>如果你曾经用过Perl或任何其他内建正则表达式支持的语言，你一定知道用正则表达式处理文本和匹配模式是多么简单。如果你不熟悉这个术语，那么&ldquo;正则表达式&rdquo;（Regular Expression）就是一个字符构成的串，它定义了一个用来搜索匹配字符串的模式。 </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>许多语言，包括Perl、PHP、Python、JavaScript和JScript，都支持用正则表达式处理文本，一些文本编辑器用正则表达式实现高级&ldquo;搜索-替换&rdquo;功能。那么Java又怎样呢？本文写作时，一个包含了用正则表达式进行文本处理的Java规范需求（Specification Request）已经得到认可，你可以期待在JDK的下一版本中看到它。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>然而，如果现在就需要使用正则表达式，又该怎么办呢？你可以从Apache.org下载源代码开放的Jakarta-ORO库。本文接下来的内容先简要地介绍正则表达式的入门知识，然后以Jakarta-ORO API为例介绍如何使用正则表达式。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b><font size="4">一、正则表达式基础知识</font></b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>我们先从简单的开始。假设你要搜索一个包含字符&ldquo;cat&rdquo;的字符串，搜索用的正则表达式就是&ldquo;cat&rdquo;。如果搜索对大小写不敏感，单词&ldquo;catalog&rdquo;、&ldquo;Catherine&rdquo;、&ldquo;sophisticated&rdquo;都可以匹配。也就是说： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_a.jpg" border="0" /> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>1.1 句点符号</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>假设你在玩英文拼字游戏，想要找出三个字母的单词，而且这些单词必须以&ldquo;t&rdquo;字母开头，以&ldquo;n&rdquo;字母结束。另外，假设有一本英文字典，你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式，你可以使用一个通配符&mdash;&mdash;句点符号&ldquo;.&rdquo;。这样，完整的表达式就是&ldquo;t.n&rdquo;，它匹配&ldquo;tan&rdquo;、&ldquo;ten&rdquo;、&ldquo;tin&rdquo;和&ldquo;ton&rdquo;，还匹配&ldquo;t#n&rdquo;、&ldquo;tpn&rdquo;甚至&ldquo;t n&rdquo;，还有其他许多无意义的组合。这是因为句点符号匹配所有字符，包括空格、Tab字符甚至换行符： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_b.jpg" border="0" /> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>1.2 方括号符号</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>为了解决句点符号匹配范围过于广泛这一问题，你可以在方括号（&ldquo;[]&rdquo;）里面指定看来有意义的字符。此时，只有方括号里面指定的字符才参与匹配。也就是说，正则表达式&ldquo;t[aeio]n&rdquo;只匹配&ldquo;tan&rdquo;、&ldquo;Ten&rdquo;、&ldquo;tin&rdquo;和&ldquo;ton&rdquo;。但&ldquo;Toon&rdquo;不匹配，因为在方括号之内你只能匹配单个字符： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_c.jpg" border="0" /></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>1.3 &ldquo;或&rdquo;符号</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>如果除了上面匹配的所有单词之外，你还想要匹配&ldquo;toon&rdquo;，那么，你可以使用&ldquo;|&rdquo;操作符。&ldquo;|&rdquo;操作符的基本意义就是&ldquo;或&rdquo;运算。要匹配&ldquo;toon&rdquo;，使用&ldquo;t(a|e|i|o|oo)n&rdquo;正则表达式。这里不能使用方扩号，因为方括号只允许匹配单个字符；这里必须使用圆括号&ldquo;()&rdquo;。圆括号还可以用来分组，具体请参见后面介绍。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_d.jpg" border="0" /> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>1.4 表示匹配次数的符号</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>表一显示了表示匹配次数的符号，这些符号用来确定紧靠该符号左边的符号出现的次数： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4n.jpg" border="0" /></p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中，连字符（&ldquo;-&rdquo;）有着特殊的意义，它表示一个范围，比如从0到9。因此，匹配社会安全号码中的连字符号时，它的前面要加上一个转义字符&ldquo;\&rdquo;。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4a.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图一：匹配所有123-12-1234形式的社会安全号码</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>假设进行搜索的时候，你希望连字符号可以出现，也可以不出现&mdash;&mdash;即，999-99-9999和999999999都属于正确的格式。这时，你可以在连字符号后面加上&ldquo;？&rdquo;数量限定符号，如图二所示： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4b.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图二：匹配所有123-12-1234和123121234形式的社会安全号码</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分&ldquo;[0-9]{4}&rdquo;，再加上字母部分&ldquo;[A-Z]{2}&rdquo;。图三显示了完整的正则表达式。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4c.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图三：匹配典型的美国汽车牌照号码，如8836KV</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>1.5 &ldquo;否&rdquo;符号 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>&ldquo;^&rdquo;符号称为&ldquo;否&rdquo;符号。如果用在方括号内，&ldquo;^&rdquo;表示不想要匹配的字符。例如，图四的正则表达式匹配所有单词，但以&ldquo;X&rdquo;字母开头的单词除外。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4d.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图四：匹配所有单词，但&ldquo;X&rdquo;开头的除外</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>1.6 圆括号和空白符号 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>假设要从格式为&ldquo;June 26, 1951&rdquo;的生日日期中提取出月份部分，用来匹配该日期的正则表达式可以如图五所示： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4e.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图五：匹配所有Moth DD,YYYY格式的日期</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>新出现的&ldquo;\s&rdquo;符号是空白符号，匹配所有的空白字符，包括Tab字符。如果字符串正确匹配，接下来如何提取出月份部分呢？只需在月份周围加上一个圆括号创建一个组，然后用ORO API（本文后面详细讨论）提取出它的值。修改后的正则表达式如图六所示： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4f.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图六：匹配所有Month DD,YYYY格式的日期，定义月份值为第一个组</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>1.7 其它符号</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>为简便起见，你可以使用一些为常见正则表达式创建的快捷符号。如表二所示： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>表二：常用符号 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4o.jpg" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>例如，在前面社会安全号码的例子中，所有出现&ldquo;[0-9]&rdquo;的地方我们都可以使用&ldquo;\d&rdquo;。修改后的正则表达式如图七所示： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4g.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图七：匹配所有123-12-1234格式的社会安全号码</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b><font size="4">二、Jakarta-ORO库</font></b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>有许多源代码开放的正则表达式库可供Java程序员使用，而且它们中的许多支持Perl 5兼容的正则表达式语法。我在这里选用的是Jakarta-ORO正则表达式库，它是最全面的正则表达式API之一，而且它与Perl 5正则表达式完全兼容。另外，它也是优化得最好的API之一。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>Jakarta-ORO库以前叫做OROMatcher，Daniel Savarese大方地把它赠送给了Jakarta Project。你可以按照本文最后参考资源的说明下载它。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>我首先将简要介绍使用Jakarta-ORO库时你必须创建和访问的对象，然后介绍如何使用Jakarta-ORO API。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>▲ PatternCompiler对象</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>首先，创建一个Perl5Compiler类的实例，并把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实现，允许你把正则表达式编译成用来匹配的Pattern对象。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_e.jpg" border="0" /> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>▲ Pattern对象</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>要把正则表达式编译成Pattern对象，调用compiler对象的compile()方法，并在调用参数中指定正则表达式。例如，你可以按照下面这种方式编译正则表达式&ldquo;t[aeio]n&rdquo;： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_f.jpg" border="0" /> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>默认情况下，编译器创建一个大小写敏感的模式（pattern）。因此，上面代码编译得到的模式只匹配&ldquo;tin&rdquo;、&ldquo;tan&rdquo;、 &ldquo;ten&rdquo;和&ldquo;ton&rdquo;，但不匹配&ldquo;Tin&rdquo;和&ldquo;taN&rdquo;。要创建一个大小写不敏感的模式，你应该在调用编译器的时候指定一个额外的参数： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_g.jpg" border="0" /> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>创建好Pattern对象之后，你就可以通过PatternMatcher类用该Pattern对象进行模式匹配。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>▲ PatternMatcher对象</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>PatternMatcher对象根据Pattern对象和字符串进行匹配检查。你要实例化一个Perl5Matcher类并把结果赋值给PatternMatcher接口。Perl5Matcher类是PatternMatcher接口的一个实现，它根据Perl 5正则表达式语法进行模式匹配： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_h.jpg" border="0" /> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>使用PatternMatcher对象，你可以用多个方法进行匹配操作，这些方法的第一个参数都是需要根据正则表达式进行匹配的字符串： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>&middot; boolean matches(String input, Pattern pattern)：当输入字符串和正则表达式要精确匹配时使用。换句话说，正则表达式必须完整地描述输入字符串。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>&middot; boolean matchesPrefix(String input, Pattern pattern)：当正则表达式匹配输入字符串起始部分时使用。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>&middot; boolean contains(String input, Pattern pattern)：当正则表达式要匹配输入字符串的一部分时使用（即，它必须是一个子串）。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>另外，在上面三个方法调用中，你还可以用PatternMatcherInput对象作为参数替代String对象；这时，你可以从字符串中最后一次匹配的位置开始继续进行匹配。当字符串可能有多个子串匹配给定的正则表达式时，用PatternMatcherInput对象作为参数就很有用了。用PatternMatcherInput对象作为参数替代String时，上述三个方法的语法如下： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>&middot; boolean matches(PatternMatcherInput input, Pattern pattern) </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>&middot; boolean matchesPrefix(PatternMatcherInput input, Pattern pattern) </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>&middot; boolean contains(PatternMatcherInput input, Pattern pattern) </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b><font size="4">三、应用实例</font></b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>下面我们来看看Jakarta-ORO库的一些应用实例。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><b>3.1 日志文件处理</b> </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>任务：分析一个Web服务器日志文件，确定每一个用户花在网站上的时间。在典型的BEA WebLogic日志文件中，日志记录的格式如下： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_i.jpg" border="0" /></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>分析这个日志记录，可以发现，要从这个日志文件提取的内容有两项：IP地址和页面访问时间。你可以用分组符号（圆括号）从日志记录提取出IP地址和时间标记。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>首先我们来看看IP地址。IP地址有4个字节构成，每一个字节的值在0到255之间，各个字节通过一个句点分隔。因此，IP地址中的每一个字节有至少一个、最多三个数字。图八显示了为IP地址编写的正则表达式： </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4h.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图八：匹配IP地址</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>IP地址中的句点字符必须进行转义处理（前面加上&ldquo;\&rdquo;），因为IP地址中的句点具有它本来的含义，而不是采用正则表达式语法中的特殊含义。句点在正则表达式中的特殊含义本文前面已经介绍。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>日志记录的时间部分由一对方括号包围。你可以按照如下思路提取出方括号里面的所有内容：首先搜索起始方括号字符（&ldquo;[&rdquo;），提取出所有不超过结束方括号字符（&ldquo;]&rdquo;）的内容，向前寻找直至找到结束方括号字符。图九显示了这部分的正则表达式。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4i.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图九：匹配至少一个字符，直至找到&ldquo;]&rdquo;</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>现在，把上述两个正则表达式加上分组符号（圆括号）后合并成单个表达式，这样就可以从日志记录提取出IP地址和时间。注意，为了匹配&ldquo;- -&rdquo;（但不提取它），正则表达式中间加入了&ldquo;\s-\s-\s&rdquo;。完整的正则表达式如图十所示。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4j.gif" border="0" /> </p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>
<p align="center">图十：匹配IP地址和时间标记</p></td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>现在正则表达式已经编写完毕，接下来可以编写使用正则表达式库的Java代码了。 </td></tr></tbody></table>
<table align="center">
<tbody>
<tr>
<td>为