﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-憨厚生-随笔分类-Java</title><link>http://www.blogjava.net/hulizhong/category/20312.html</link><description>----Java's Slave----&lt;br&gt;
***Java's Host***</description><language>zh-cn</language><lastBuildDate>Tue, 15 Dec 2009 05:23:38 GMT</lastBuildDate><pubDate>Tue, 15 Dec 2009 05:23:38 GMT</pubDate><ttl>60</ttl><item><title>JSR规范大全</title><link>http://www.blogjava.net/hulizhong/archive/2009/12/15/305985.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 15 Dec 2009 01:32:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/12/15/305985.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/305985.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/12/15/305985.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/305985.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/305985.html</trackback:ping><description><![CDATA[<br />
<a href="http://jcp.org/en/jsr/detail?id=168">The Java Community Process(SM) Program</a><br />
<br />
转 http://blog.csdn.net/sergeycao/archive/2009/02/04/3861560.aspx<br />
<br />
<p>J2ME 配置规范<br />
=========<br />
JSR 30 --- Connected Limited Device Configuration 1.0<br />
http://jcp.org/en/jsr/detail?id=30</p>
<p>JSR 139 --- Connected Limited Device Configuration 1.1<br />
http://jcp.org/en/jsr/detail?id=139</p>
<p>JSR 36 --- Connected Device Configuration 1.0<br />
http://jcp.org/en/jsr/detail?id=36</p>
<p>JSR 218 --- Connected Device Configuration 1.1<br />
http://jcp.org/en/jsr/detail?id=218</p>
<p>========================================<br />
1、JSR 30、JSR139 简介 及它们之间的关系<br />
CLDC全称为Connected Limited Device Configuration(有限连接设备配置)，<br />
分别对应了JSR 30和JSR 139两个JSR。</p>
<p>CLDC专门针对移动电话、阅读器和主流的PDA（个人数字助理）定义了一组基础的应用程序编程接口和虚拟机标准，<br />
和简表文件一起配合，就构成了一套实用的Java平台，可以为内存不多、处理器性能有限、图形能力一般的设备开发应用程序。</p>
<p>JSR 30 CLDC 1.0 提供了基本的语言类库,主要是定义了JAVA编程语言的一套子集，包括虚拟机的功能上，网络支持，安全安装以及其他核心API上都是子集和全集的关系，主要目标是某类嵌入式的消费类产品。由于不支持浮点运算,可以用CLDC1.1替代CLDC1.0;</p>
<p>JSR 139 CLDC 1.1是CLDC 1.0技术标准的修订版本，包含了一些新的特性比如浮点运算和弱引用等方面的支持，和CLDC-1.0是完全向后兼容的;</p>
<p>2、JSR 36、JSR218 简介 及 它们之间的关系<br />
JSR 36 CDC (Connected Device Configuration，连接设备配置)。CDC的目标设备较CLDC具有更大的内存、更快速的处理器、更稳定的电源，以及更出色的网络连接能力。<br />
CDC主要应用在工业控制器、高端PDA、电视机顶盒及车载娱乐与导航系统上。</p>
<p>JSR 218 是在JSR 36基础上进行补充，并兼容JSR 36. </p>
<p>J2ME 简表规范<br />
=========<br />
CDC 简表规范<br />
------------------<br />
JSR 46 --- Foundation Profile<br />
http://jcp.org/en/jsr/detail?id=46</p>
<p>JSR 129 --- Personal Basis Profile Specification<br />
http://jcp.org/en/jsr/detail?id=129</p>
<p>JSR 62 --- Personal Profile Specification<br />
http://jcp.org/en/jsr/detail?id=62</p>
<p>JSR 219 --- Foundation Profile 1.1<br />
http://jcp.org/en/jsr/detail?id=219</p>
<p>JSR 217 --- Personal Basis Profile 1.1<br />
http://jcp.org/en/jsr/detail?id=217</p>
<p>JSR 216 --- Personal Profile 1.1<br />
http://jcp.org/en/jsr/detail?id=216</p>
<p>CLDC 简表规范<br />
--------------------<br />
JSR 37 --- Mobile Information Device Profile 1.0<br />
http://jcp.org/en/jsr/detail?id=37</p>
<p>JSR 118 --- Mobile Information Device Profile 2.0<br />
http://jcp.org/en/jsr/detail?id=118</p>
<p>JSR 195 --- Information Module Profile <br />
http://jcp.org/en/jsr/detail?id=195</p>
<p>JSR 228 --- Information Module Profile 2.0)<br />
http://jcp.org/en/jsr/detail?id=228 </p>
<p><br />
厂商可选包(Optional Packages)<br />
-----------------------------------------</p>
<p>CDC设备厂商可选包<br />
...........................<br />
JSR 66 --- RMI Optional Package Specification Version 1.0<br />
http://jcp.org/en/jsr/detail?id=66</p>
<p>JSR 80 --- Java USB API<br />
http://jcp.org/en/jsr/detail?id=80</p>
<p>JSR 113 --- Java Speech API 2.0<br />
http://jcp.org/en/jsr/detail?id=113</p>
<p>JSR 169 --- JDBC Optional Package for CDC/Foundation Profile<br />
http://jcp.org/en/jsr/detail?id=169</p>
<p>JSR 209 --- Advanced Graphics and User Interface Optional Package for the J2ME Platform<br />
http://jcp.org/en/jsr/detail?id=209</p>
<p>CLDC设备厂商可选包<br />
...........................<br />
JSR 75 --- PDA Optional Packages for the J2ME Platform<br />
http://jcp.org/en/jsr/detail?id=75</p>
<p>JSR 82 --- Java APIs for Bluetooth<br />
http://jcp.org/en/jsr/detail?id=82</p>
<p>JSR 120 --- Wireless Messaging API<br />
http://jcp.org/en/jsr/detail?id=120</p>
<p>JSR 135 --- Mobile Media API<br />
http://jcp.org/en/jsr/detail?id=135</p>
<p>JSR 172 --- J2ME Web Services Specification<br />
http://jcp.org/en/jsr/detail?id=172</p>
<p>JSR 177 --- Security and Trust Services API for J2ME<br />
http://jcp.org/en/jsr/detail?id=177</p>
<p>JSR 179 --- Location API for J2ME<br />
http://jcp.org/en/jsr/detail?id=179</p>
<p>JSR 180 --- SIP API for J2ME<br />
http://jcp.org/en/jsr/detail?id=180</p>
<p>JSR 184 --- Mobile 3D Graphics API for J2ME<br />
http://jcp.org/en/jsr/detail?id=184</p>
<p>JSR 190 --- Event Tracking API for J2ME<br />
http://jcp.org/en/jsr/detail?id=190</p>
<p>JSR 205 --- Wireless Messaging API 2.0<br />
http://jcp.org/en/jsr/detail?id=205</p>
<p>JSR 211 --- Content Handler API<br />
http://jcp.org/en/jsr/detail?id=211</p>
<p>JSR 226 --- Scalable 2D Vector Graphics API for J2ME<br />
http://jcp.org/en/jsr/detail?id=226</p>
<p>JSR 229 --- Payment API<br />
http://jcp.org/en/jsr/detail?id=229</p>
<p>JSR 230 --- Data Sync API<br />
http://jcp.org/en/jsr/detail?id=230 </p>
<p>运行环境规范<br />
..................<br />
JSR 185 --- JavaTM Technology for the Wireless Industry<br />
http://jcp.org/en/jsr/detail?id=185 </p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/sergeycao/archive/2009/02/04/3861560.aspx</p><img src ="http://www.blogjava.net/hulizhong/aggbug/305985.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-12-15 09:32 <a href="http://www.blogjava.net/hulizhong/archive/2009/12/15/305985.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java编程常见BUG---不完善的异常处理</title><link>http://www.blogjava.net/hulizhong/archive/2009/12/09/305300.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 09 Dec 2009 08:59:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/12/09/305300.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/305300.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/12/09/305300.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/305300.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/305300.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;这样的错误以前我也犯过，也见过不少人这样的写法！下面我也举个例子：<br />
&nbsp;&nbsp;<br />
</p>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img id="Codehighlighter1_31_371_Open_Image" onclick="this.style.display='none'; Codehighlighter1_31_371_Open_Text.style.display='none'; Codehighlighter1_31_371_Closed_Image.style.display='inline'; Codehighlighter1_31_371_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_31_371_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_31_371_Closed_Text.style.display='none'; Codehighlighter1_31_371_Open_Image.style.display='inline'; Codehighlighter1_31_371_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;writeFile(File&nbsp;f)&nbsp;</span><span id="Codehighlighter1_31_371_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_31_371_Open_Text"><span style="color: #000000">{<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;String&nbsp;content&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
<img id="Codehighlighter1_64_189_Open_Image" onclick="this.style.display='none'; Codehighlighter1_64_189_Open_Text.style.display='none'; Codehighlighter1_64_189_Closed_Image.style.display='inline'; Codehighlighter1_64_189_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_64_189_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_64_189_Closed_Text.style.display='none'; Codehighlighter1_64_189_Open_Image.style.display='inline'; Codehighlighter1_64_189_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;</span><span style="color: #0000ff">try</span><span style="color: #000000">&nbsp;</span><span id="Codehighlighter1_64_189_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_64_189_Open_Text"><span style="color: #000000">{<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">byte</span><span style="color: #000000">[]&nbsp;b&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">byte</span><span style="color: #000000">[</span><span style="color: #000000">1024</span><span style="color: #000000">];<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;FileInputStream&nbsp;in&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;FileInputStream(f);<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;in.read(b);<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;content&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;String(b);<br />
<img id="Codehighlighter1_211_254_Open_Image" onclick="this.style.display='none'; Codehighlighter1_211_254_Open_Text.style.display='none'; Codehighlighter1_211_254_Closed_Image.style.display='inline'; Codehighlighter1_211_254_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_211_254_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_211_254_Closed_Text.style.display='none'; Codehighlighter1_211_254_Open_Image.style.display='inline'; Codehighlighter1_211_254_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">catch</span><span style="color: #000000">&nbsp;(Exception&nbsp;e)&nbsp;</span><span id="Codehighlighter1_211_254_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_211_254_Open_Text"><span style="color: #000000">{<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;System.out.println(e.getMessage());<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /><br />
<img id="Codehighlighter1_294_328_Open_Image" onclick="this.style.display='none'; Codehighlighter1_294_328_Open_Text.style.display='none'; Codehighlighter1_294_328_Closed_Image.style.display='inline'; Codehighlighter1_294_328_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_294_328_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_294_328_Closed_Text.style.display='none'; Codehighlighter1_294_328_Open_Image.style.display='inline'; Codehighlighter1_294_328_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(content.indexOf(</span><span style="color: #000000">"</span><span style="color: #000000">hello</span><span style="color: #000000">"</span><span style="color: #000000">)&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">)&nbsp;</span><span id="Codehighlighter1_294_328_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_294_328_Open_Text"><span style="color: #000000">{<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">yes</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
<img id="Codehighlighter1_335_368_Open_Image" onclick="this.style.display='none'; Codehighlighter1_335_368_Open_Text.style.display='none'; Codehighlighter1_335_368_Closed_Image.style.display='inline'; Codehighlighter1_335_368_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_335_368_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_335_368_Closed_Text.style.display='none'; Codehighlighter1_335_368_Open_Image.style.display='inline'; Codehighlighter1_335_368_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000">&nbsp;</span><span id="Codehighlighter1_335_368_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_335_368_Open_Text"><span style="color: #000000">{<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">no</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />&nbsp;}</span></span><span style="color: #000000"><br />
<img alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span></div>
<p><br />
&nbsp;上面是个简单的方法,代码中有个隐藏的bug。我在维护一个系统的时候就遇到类似的代码，实际中类似的BUG隐藏<br />
的更深！在对系统业务和代码不是很很熟悉的情况下，我推荐如下写法：<br />
</p>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span><img id="Codehighlighter1_31_494_Open_Image" onclick="this.style.display='none'; Codehighlighter1_31_494_Open_Text.style.display='none'; Codehighlighter1_31_494_Closed_Image.style.display='inline'; Codehighlighter1_31_494_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_31_494_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_31_494_Closed_Text.style.display='none'; Codehighlighter1_31_494_Open_Image.style.display='inline'; Codehighlighter1_31_494_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;writeFile(File&nbsp;f)&nbsp;</span><span id="Codehighlighter1_31_494_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_31_494_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">&nbsp;2</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;String&nbsp;content&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
</span><span style="color: #008080">&nbsp;3</span><span style="color: #000000"><img id="Codehighlighter1_64_189_Open_Image" onclick="this.style.display='none'; Codehighlighter1_64_189_Open_Text.style.display='none'; Codehighlighter1_64_189_Closed_Image.style.display='inline'; Codehighlighter1_64_189_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_64_189_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_64_189_Closed_Text.style.display='none'; Codehighlighter1_64_189_Open_Image.style.display='inline'; Codehighlighter1_64_189_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;</span><span style="color: #0000ff">try</span><span style="color: #000000">&nbsp;</span><span id="Codehighlighter1_64_189_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_64_189_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">&nbsp;4</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">byte</span><span style="color: #000000">[]&nbsp;b&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">byte</span><span style="color: #000000">[</span><span style="color: #000000">1024</span><span style="color: #000000">];<br />
</span><span style="color: #008080">&nbsp;5</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;FileInputStream&nbsp;in&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;FileInputStream(f);<br />
</span><span style="color: #008080">&nbsp;6</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;in.read(b);<br />
</span><span style="color: #008080">&nbsp;7</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;content&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;String(b);<br />
</span><span style="color: #008080">&nbsp;8</span><span style="color: #000000"><img id="Codehighlighter1_211_342_Open_Image" onclick="this.style.display='none'; Codehighlighter1_211_342_Open_Text.style.display='none'; Codehighlighter1_211_342_Closed_Image.style.display='inline'; Codehighlighter1_211_342_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_211_342_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_211_342_Closed_Text.style.display='none'; Codehighlighter1_211_342_Open_Image.style.display='inline'; Codehighlighter1_211_342_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">catch</span><span style="color: #000000">&nbsp;(Exception&nbsp;e)&nbsp;</span><span id="Codehighlighter1_211_342_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_211_342_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">&nbsp;9</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;content</span><span style="color: #000000">=</span><span style="color: #000000">""</span><span style="color: #000000">;<br />
</span><span style="color: #008080">10</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">如果异常发生的话,content可能为空<br />
</span><span style="color: #008080">11</span><span style="color: #008000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">下面对content的操作就有可能发生NullPointerException异常</span><span style="color: #008000"><br />
</span><span style="color: #008080">12</span><span style="color: #008000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;System.out.println(e.getMessage());<br />
</span><span style="color: #008080">13</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">14</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">下面操作有可能发生NullPointerException异常</span><span style="color: #008000"><br />
</span><span style="color: #008080">15</span><span style="color: #008000"><img id="Codehighlighter1_417_451_Open_Image" onclick="this.style.display='none'; Codehighlighter1_417_451_Open_Text.style.display='none'; Codehighlighter1_417_451_Closed_Image.style.display='inline'; Codehighlighter1_417_451_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_417_451_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_417_451_Closed_Text.style.display='none'; Codehighlighter1_417_451_Open_Image.style.display='inline'; Codehighlighter1_417_451_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" /></span><span style="color: #000000">&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(content.indexOf(</span><span style="color: #000000">"</span><span style="color: #000000">hello</span><span style="color: #000000">"</span><span style="color: #000000">)&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">)&nbsp;</span><span id="Codehighlighter1_417_451_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_417_451_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">16</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">yes</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">17</span><span style="color: #000000"><img id="Codehighlighter1_458_491_Open_Image" onclick="this.style.display='none'; Codehighlighter1_458_491_Open_Text.style.display='none'; Codehighlighter1_458_491_Closed_Image.style.display='inline'; Codehighlighter1_458_491_Closed_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_458_491_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_458_491_Closed_Text.style.display='none'; Codehighlighter1_458_491_Open_Image.style.display='inline'; Codehighlighter1_458_491_Open_Text.style.display='inline';" alt="" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000">&nbsp;</span><span id="Codehighlighter1_458_491_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img alt="" src="http://www.blogjava.net/Images/dot.gif" /></span><span id="Codehighlighter1_458_491_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">18</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">no</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">19</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">20</span><span style="color: #000000"><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />&nbsp;}</span></span></div>
<p><br />
&nbsp;一般来说异常处理不推荐直接system.out.println打印出来！<br />
&nbsp;几条建议：<br />
&nbsp;如果无法处理某个异常，那就不要捕获它。 <br />
　　☆ 如果捕获了一个异常，请不要胡乱处理它。 <br />
　　☆ 尽量在靠近异常被抛出的地方捕获异常。 <br />
　　☆ 在捕获异常的地方将它记录到日志中，除非您打算将它重新抛出。 <br />
　　☆ 按照您的异常处理必须多精细来构造您的方法。 <br />
　　☆ 需要用几种类型的异常就用几种，尤其是对于应用程序异常。<br />
　　☆ 把低层次的异常封装成层次较高程序员较容易理解的异常。<br />
　　☆ 尽量输出造成异常的完整数据<br />
　　☆ 尽量捕获具有特定含义的异常：比如SqlException，而不是简单地捕获一个Exception</p>
<p><br />
&nbsp;&nbsp;希望对大家有帮助！<br />
<br />
参考：<br />
http://www.blogjava.net/usherlight/archive/2006/10/23/76782.html</p><img src ="http://www.blogjava.net/hulizhong/aggbug/305300.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-12-09 16:59 <a href="http://www.blogjava.net/hulizhong/archive/2009/12/09/305300.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>计算机科学经典论文</title><link>http://www.blogjava.net/hulizhong/archive/2009/11/25/303616.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 25 Nov 2009 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/11/25/303616.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/303616.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/11/25/303616.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/303616.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/303616.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/g9yuayon/archive/2007/02/23/1512851.aspx<br />
<br />
<p>从Jao的Programming Musing 看到的：Babar Kazar 整理了一堆经典论文。Jao强烈建议每个严肃的程序员读每篇论文，说它们都或多或少有意思。粗粗扫了一下，很多论文都没读过。挑了些俺多少知道一点的介绍。</p>
<p><br />
&#183; An axiomatic basis for computer programming C. A. R. Hoare<br />
Tony Hoare名下的公理化语义（Axiomatic Semantics）。著名的Hoare Triples, P{C}Q, 就是从这里来的。论文不长，双列6页。前辈们就是这样的，6页纸就能开宗立派。不像俺，6页纸连介绍部分都写不周全。哪位老大想知道怎么证明程序正确。前置条件，不变条件，后置条件的妙用，可以用这篇论文开牙。<br />
&#183; Communicating Sequential Processes (CSP) C. A. R. Hoare<br />
Hoare, 又见Hoare。其实也正常。牛人之牛，就在于成就深广。链接的文档应该不算论文，而算专著。260页。从1985年推出到现在20多年过去，这本书的引用率在CS历史上排名第三，可见其影响之深。对并发编程有强烈兴趣的老大可以去钻研一把。我没读过。<br />
&#183; Call-by-name, call-by-value, and the lambda calculus Gordon Plotkin<br />
没读过。只见LtU介绍过。Gordon老大这篇论文的要点之一是要想顺利地对程序进行推导，就需要有合适的lambda理论。想深入理解call-by-name，call-by-value，和lambda算子的老大们可以上了。<br />
&#183; Towards a theory of type structure John C. Reynolds<br />
号称经典中的经典。不过也没读过。类型系统一直是编程语言研发的热点，也是非常有趣的方向――类型系统的编程好比让机器证明一系列定理。Reynolds在论文里讨论了什么才是正确的类型结构，和句法正确必须独立于任何具体的类型表达形式，并且给出了带类型的lambda算子的一种扩展，允许他描述用户自定义类型和多态函数。满篇公式，有勇气去读的老大要有心理准备。<br />
&#183; Structured Programming with go to Statements Donald E. Knuth<br />
这篇论文详细结构化编程时讨论了什么时候用goto，什么时候不用goto。高爷爷精细务实的态度非常值得学习。高老太爷用了一辈子goto(MIX和MMIX程序里没了Goto怎么玩儿得转嗫？)，岂能轻易被Dijkstra对goto的批评吓退？他仔细探讨了几种不同的程序，考察goto用在那些程序里的利弊。最后得出结论，goto在某些程序里仍然高效实用。虽然论文是30年前的，但里面的分析手法和利用goto的优化技术至今可用。<br />
&#183; Definitional interpreters for higher-order programming languages John C. Reynolds<br />
这篇文章俺喜欢。&#8221;Metacircular&#8221;这个性感的概念就是在这篇论文里首次提出的。想深入了解用一门语言写出的解释器定义这门语言自身的神奇理念，这篇论文是必读材料。有兴趣的老大可以先读SICP的第四章。 <br />
&#183; An APL Machine 1970 Philip S. Abrams<br />
只知道APL是门有历史意义的语言。顺便说一句，APL这个名字太土了。A Programming Language ＝＝APL。象什么话嘛。</p>
<p><br />
&#183; The Anatomy of a Large-Scale Hypertextual Web Search Engine Sergey Brin and Lawrence Page<br />
网络是个大的矩阵(transition probability matrix of Markov Chain)。网页的声誉(page rank)就是这个巨大矩阵的principle eigenvector的某个元素。嗯，反正我只有佩服的份儿。<br />
&#183; No Silver Bullet: Essence and Accidents of Software Engineering Frederic P. Brooks, Jr.<br />
地球银都知道。不用俺多嘴了。<br />
&#183; A Mathematical Theory of Communication Claude Shannon<br />
Bell实验室当年辉煌一时。出了名的叫人做A，结果发明了B。香农老大就是其中杰出代表。香农进了Bell实验室后，居然没人吩咐他干嘛。香农老大转念一想，自己喜欢数学，Bell的生意尽在通讯，干嘛不看看把数学应用到通讯上有什么结果呢？于是1948年这篇论文问世乐。搞通讯的人崩溃乐。现代信息理论就诞生乐。<br />
&#183; Bayesian Networks without Tears<br />
贝叶斯理论热了好几年了。估计还会继续热下去。现在信息越来越多，我们已经审美疲劳。大家渴望的不是信息，而是知识。靠个人的力量把信息提炼成知识太慢，我们需要机器的帮忙。机器学习不热都难，而贝叶斯理论在机器学习里有很好的应用。这篇文章行为浅显，可以轻松读完。对了，那个人人喝骂的微软回形针的智能引擎就是用贝叶斯网络实现的。<br />
&#183; A Universal Algorithm for Sequential Data Compression<br />
没读过。无耻地找个借口：我们系开信息理论课的时候，俺刚好毕业。<br />
&#183; A Relational Model of Data for Large Shared Data Banks 1970 Edgar F. Codd<br />
没有关系代数，人类将会怎样？Codd划时代的论文奠定了现代数据库的基础。嘿嘿，其实俺也没有读过这篇论文。顺便说一句，现在的ORM试图把data schema和对象系统映射起来。问题是，data schema只是对关系的一种表达方式而已，还和具体的系统实现有关。也许把对象间的结构和关系映射起来才是正道。<br />
&#183; Let's Build a Compiler 1988-1995<br />
教你一步一步写出一坨编译器。不算论文吧。一篇相当不错的指南。<br />
&#183; Gauging Similarity via N-Grams: Language-Independent Sorting... Marc Damashek<br />
第一次听说<br />
&#183; Worse Is Better Richard P. Gabriel<br />
网上脍炙人口的文章。很有教育意义。简单说，worse is better包括下面几点：<br />
-- 简单：设计要简单。但如果接口和实现不能两全，追求实现的简单。文章里给出的Unix vs Multics的例子非常有意思。<br />
-- 正确：程序必须在所有可见的方面正确。其它地方，如果简单和正确不能两全，追求简单。<br />
-- 一致性：程序不能太不一致。但为了简单，可以在少数地方不一致。<br />
-- 完备性：程序应该尽可能照顾到重要的地方，但是不能牺牲简洁。<br />
强烈推荐。<br />
&#183; Hints on Programming Language Design C.A.R. Hoare<br />
Hoare对设计语言的经验总结。这些经验至今有效。文章很容易读，读后绝对增长程序设计的功力。<br />
&#183; Why Functional Programming Matters John Hughes<br />
为普通程序员准备的大餐，所以写得通俗。没有公式，也没有拗口的术语。着重展示了Fold和Map的强大抽象能力。不由想到我在大学里修的一门课，编程语言。课是好课，老师是一流老师。课上我们学习了浅显的程序语言理论，重点学习了函数编程（用Common Lisp）和逻辑编程（用Prolog）。这门课彻底改变我对编程的理解，明白了imperative programming和OO programming外还有精彩世界。至今想来都觉得幸运。那门课的作业也很有意思，实现一个驻留内存的数据库，支持关系代数里的常见操作。<br />
&#183; On the Expressive Power of Programming Languages Matthias Felleisen<br />
没读过。待读。<br />
&#183; The Early History Of Smalltalk Alan Kay<br />
还有什么好说的呢？Alan Kay这个名字说明一切。30年前Alan Kay就做出来Smalltalk，现在想来仍然让人惊叹。引一段文章Alan Kay评述Smalltalk的话：In computer terms, Smalltalk is a recursion on the notion of computer itself. Instead of dividing "computer stuff" into things each less strong than the whole--like data structures, procedures, and functions which are the usual paraphernalia of programming languages--each Smalltalk object is a recursion on the entire possibilities of the computer. Thus its semantics are a bit like having thousands and thousands of computer all hooked together by a very fast network. Questions of concrete representation can thus be postponed almost indefinitely because we are mainly concerned that the computers behave appropriately, and are interested in particular strategies only if the results are off or come back too slowly.<br />
&#183; Computer Programming as an Art Donald E. Knuth<br />
高老太爷在1974年图灵奖仪式上的致词。真是顶尖geek的风范啊。高太爷在文章里解释了问什么他的书取名为《编程的艺术》。明显他对人们谈到编程时把科学置于艺术之上很不了然。高爷爷追溯&#8220;艺术&#8221;的词源，说艺术的本意就是技能，也是技术和技巧两次的起源。从这里开始，他开始讨论艺术和科学的关联，讨论艺术在编程里的表现形式和意义。用他的话说，他作为教育者和作者的毕生目标就是叫人写美妙的程序。读起来让人心潮彭湃的说。<br />
&#183; The next 700 programming languages Peter J. Landin<br />
42年前的论文，影响深远。Peter在论文里描述的函数语言ISWIM（If You See What I Mean）现在没有几个人知道了。但他对lambda算子的推崇和对函数语言的论述影响了后来的函数语言设计。<br />
&#183; Recursive Functions of Symbolic Expressions and their Computation by Machine (Part I) 1960 John McCarthy<br />
47年前提出LISP的那篇著名论文。没读过。动态类型检查，Garbage Collection, 递归函数，S-expression, 程序及数据。。。可谓贡献辉煌。</p>
<p><br />
&#183; FORTH - A Language for Interactive Computing Charles H.Moore <br />
只知道Forth是一门stack oriented的编程语言，影响了后来的一些语言，比如CAT。其它的就不知道了。<br />
&#183; Teach Yourself Programming in Ten Years 2001 Peter Norvig<br />
大牛之所以为大牛，原因之一就是目光深远。这篇文章批评那些《24秒学会C＋＋》之类教材的无稽，讨论了学习编程，从菜鸟变成鲲鹏的方法。中文版已经传得满世界都是，赶快找来看吧。Peter Norvig的网站上还有很多高质量的文章。强烈推荐一读。<br />
&#183; The Definition and Implementation of a Computer Language based on constraints Guy Lewis Steele Jr.<br />
好像是Guy Steels的硕士论文。没读过。<br />
&#183; Growing a Language Guy Lewis Steele Jr.<br />
好文！G老大在OOPSLA 98上的主题演讲。G老大主张应该采取渐进的方式设计一门可以被自由扩展的语言（LISP圈子里的牛人们多半都持这种观点吧？）。这篇演讲稿针对该观点做了精练地论述。说起进化的观点，可以参看另外一篇好文章，SICP作者之一，Jay Sussman的近作。<br />
&#183; Epigrams on Programming Alan J. Perlis<br />
A老大发表的一系列关于编程的格言。幽默而深刻。每读必笑。笑后必哭。嗯嗯嗯，夸张一下。不要当真。<br />
&#183; The Complexity of Theorem Proving Procedures Stephen A. Cook<br />
仙风道骨的库克爷爷的成名作。这篇文章一出，好比有人在加州荒漠里发现第一块狗头金，立刻掀起开发加州的狂潮。计算复杂性理论迅速遍地开花。相比这篇论文开创性的贡献，库克因此得到图灵奖不过小小点缀。NP-Complete在这篇论文里被严格定义。更重要的是，库克证明了第一个NP-Complete的问题，SAT(Boolean Satisfiability Problem)。有了SAT，再加上折磨了无数学生的Polynomial Reducibility，无数的NPC问题就出现乐。。。别看俺在这里唾沫横飞，当年做有关计算理论的证明题还是相当吃力的，没有少熬夜。奇怪的是，某一天我给同学讲解我的解法，NPC的相关定义突然变得清晰起来。当初让我绞尽脑汁的证明竟然变得相当机械。后来知道，给人讲解（包括写作）是非常有效地学习方法。怀着备课的目标读文章，假设自己给别人讲解正在读的文章，有助快速理解所读内容。SAT的证明相当复杂，我反正没有耐心读完。<br />
&#183; Steps Toward Artificial Intelligence Marvin Minsky<br />
AI的奠基论文。不过我没读过。<br />
&#183; The Original 'Lambda Papers' Guy Steele and Gerald Sussman<br />
一系列讲解lambda算子和scheme设计的经典论文。学scheme时读过，对理解scheme的设计理念很有帮助。</p>
<p><br />
&#183; The UNIX Time-Sharing System Dennis Ritchie and Ken Thompson<br />
作者不用介绍了吧？这篇文章里介绍的Unix特性早为人熟知。不过第八部分(VIII Perspective)讨论了作者的设计理念，仍然值得一读。</p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/g9yuayon/archive/2007/02/23/1512851.aspx</p><img src ="http://www.blogjava.net/hulizhong/aggbug/303616.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-11-25 14:53 <a href="http://www.blogjava.net/hulizhong/archive/2009/11/25/303616.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用Eclipse进行重构</title><link>http://www.blogjava.net/hulizhong/archive/2009/11/25/303588.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 25 Nov 2009 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/11/25/303588.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/303588.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/11/25/303588.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/303588.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/303588.html</trackback:ping><description><![CDATA[转 http://xiaofengtoo.javaeye.com/blog/149129<br />
<span style="font-size: 18pt">利用<span class="hilite1">Eclipse</span>进行重构</span> <br />
<br />
重构和单元测试是程序员的两大法宝，他们的作用就像空气和水对于人一样，平凡，不起眼，但是意义深重。预善事，必先利器，本文就介绍怎样在<span class="hilite1">Eclipse</span>中进行重构。 <br />
<br />
本文介绍了<span class="hilite1">Eclipse</span>支持的重构种类，它们的含义，以及怎样重构。本文同时也可以作为学习重构知识的快速手册。 <br />
<br />
什么是重构 <br />
重构是指在保持程序的全部功能的基础上改变程序结构的过程。重构的类型有很多，如更改类名，改变方法名，或者提取代码到方法中。每一次重构，都要执行一系列的步骤，这些步骤要保证代码和原代码相一致。 <br />
<br />
为什么重构很重要&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 手工重构时，很容易在代码中引入错误，例如拼写错误或者漏掉了重构的某一步。为了防止引入错误，在每次重构前后，都要执行充分的测试。你可能会好奇重构是否是值得的。 <br />
重构的理由很多。你可能想要更新一段代码很烂的程序。或者最初的设计队伍都不在了，现在队伍中每人了解这些程序。为了更新，你必须要重新设计构建程序来满足你的需求。另一个原因是原来的设计无法使你将新的特性添加进去。为了添加进去，你要重构这些代码。第三个原因是一个自动重构的工具可以为你自动生成代码，例如<span class="hilite1">Eclipse</span>中的重构功能。使用重构，你可以在重写尽量少的代码和仍保持软件功能的同时，使代码的逻辑性更好。 <br />
<br />
<br />
测试 <br />
在重构时，测试是十分重要的。应为重构改变了代码的结构，你要保证重构后代码的功能没有被改变。手工重构时，一个好的测试套是必须的。使用自动重构工具是，测试也是必要的，但不需要很频繁，应为自动重构工具不会产生手工重构时的那些错误，如拼写错误。 <br />
在<span class="hilite1">Eclipse</span>中可以使用JUnit方便的为程序创建测试代码，具体方法不在本文描述。 <br />
<br />
<br />
<span class="hilite1">Eclipse</span>中的重构 <br />
JDT，<span class="hilite1">Eclipse</span>中的Java插件，能够对Java项目，类，或成员进行多种类型的自动重构。可以采取多种方法快速的为Java项目中的某个元素进行重构。 <br />
为某些元素进行重构的前提是你必须选中他们。你可以在多个视图中选择这些元素，像大纲视图或包浏览视图。可以按住Ctrl或Shift键，在视图中选择多个元素。另外一种选择的方法是使该元素的编辑区高亮显示，或者把鼠标定位到源程序文件。在选中希望重构的元素后，可以从重构菜单的下拉项选择重构，也可以从右键单击后弹出菜单中选择重构子菜单。同时，<span class="hilite1">Eclipse</span>还提供了重构的快捷键操作。 <br />
某些重构可以应用在任意元素上，有些则只能用在特定类型的元素上，如类或方法。在本文的最后的表格中，列出了重构能够应用的元素类型，以及重构的快捷键。 <br />
在<span class="hilite1">Eclipse</span>中，所有的重构都能够在正式执行之前预览一下。在重构对话框中点击&#8220;预览&#8221;按钮，可以查看所有将要被改变的地方。唯一没有预览按钮的的重构是Pull Up，在它的重构向导中，到最后，预览面板总会出现。可以将其中的个别变化反选掉，这样这些改变就不会生效。 <br />
<br />
<br />
撤销和重做 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在重构菜单中有撤销和重做项。他们和编辑菜单中的撤销重做不同。即使重构改变了很多文件，编辑菜单中的撤销重做只会更改当前文件。重构菜单中的撤销和重做则会对一次重构的所有文件进行撤销和重做操作。但是在使用时，它们有一定的限制。 <br />
重构后，无论重构改变了文件与否，如果任一个文件被另外改变而且保存了，你就无法撤销或重做这个重构。假如一个文件在重构中被修改了，然后又被编辑了，但是还没有保存，这时就会有错误信息提示，如果你想要撤销或重做该重构，必须撤销未保存的文件。 <br />
只要注意到以上的限制条件，你就可以随心所欲的对重构进行撤销或重做。你甚至能够编译，运行你的程序测试一下，然后再撤销该重构，只要你没有改变并保存任何文件。 <br />
<br />
<span class="hilite1">Eclipse</span>中的重构类型 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你看一下<span class="hilite1">Eclipse</span>的重构菜单，可以看到四部分。第一部分是撤销和重做。其他的三部分包含<span class="hilite1">Eclipse</span>提供的三种类型的重构。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一种类型的重构改变代码的物理结构，像Rename和Move。第二种是在类层次上改变代码结构，例如Pull Up和Push Down。第三种是改变类内部的代码，像Extract Method和Encapsulate Field。这三部分的重构列表如下。 <br />
<br />
类型1 物理结构 <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rename <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Move <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Change Method signature <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Convert Anonymous Class to Nested <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Convert Member Type to New File <br />
<br />
类型2 类层次结构 <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Push Down <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Push Up <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Extract Interface <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Generalize Type (<span class="hilite1">Eclipse</span> 3) <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; User <span class="hilite3">Supertype</span> <span class="hilite4">Where</span> <span class="hilite5">Possible</span> <br />
类型3 类内部结构 <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Inline <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Extract Method <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Extract Local Variable <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Extract Constant <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Introduce Parameter <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Introduce Factory <br />
l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Encapsulate Field <br />
<br />
<br />
<br />
Rename: <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rename用来改变一个Java元素的名字。虽然你可以手工改变Java文件Java元素的名字，但是这样不能自动更新所有引用它们的文件或Java元素。你必须在项目中搜索文件然后手工替换这些引用。很可能你就会漏掉一个或者改错一个。Rename重构会智能的更新所有有此引用的地方。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有时候，Java元素的名字不是很明了，或者它的功能已经改变了。为了保持代码的可读性，该元素的名字也要更新。使用Rename重构，能够十分快捷的更新元素的名字和所有引用它的地方。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要为一个Java元素改名，在包浏览视图或大纲视图选中该元素，从重构菜单中选择Rename项，或者使用快捷键Alt+Shift+R。Rename对话框会出现。在这里添入新的名字，选择是否更新该元素的引用。点击预览按钮，会打开预览窗口，在这里，你可以看到那些内容会被改变。点击OK按钮，重构结束。 <br />
<br />
Move <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Move和Rename很相似。它用来把元素从一个位置移动到另一个位置。它主要用来将类从一个包移动到另一个包。选中要移动的元素，从重构菜单中选择Move,或者使用快捷键，Alt+Shift+V,在弹出窗口中选择要移动的目的地。你仍然可以用预览功能检查一下有什么改变，也可以按OK按钮直接让其生效。 <br />
<br />
<br />
Change Method Signature <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 更改方法签名能够改变参数名，参数类型，参数顺序，返回类型，以及方法的可见性。也可以添加，删除参数。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要执行此重构，选择要重构的方法，选中重构菜单的更改方法签名项，会出现更改方法签名对话框。 <br />
<br />
在此对话框中选择方法的修饰词，返回类型，参数。参数的添加，修改，移动，删除可以通过右边的按钮控制。当添加新的参数时，会自动赋予默认值。凡是调用此方法的地方都会用此默认值作为参数输入。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 改变方法签名可能在方法中导致问题，如果有问题，当你点击预览或OK时，会被标记出来。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
<br />
Move Members Type to New File <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此重构将嵌套类转为一个单独类。将会创建一个新的Java文件包含此嵌套类。选中要重构的类，在重构菜单上选择Move Member Type to New File项，在弹出的对话框中添入要创建的实例的名字。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
<br />
Push Down <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此重构将算中的方法和成员从父类中移动到它的直接子类中，所有下推的方法都可选作为一个抽象方法留在父类中。下推重构对于重新构建项目设计十分有用。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 选择若干方法或成员，从重构菜单中选择下推项，弹出下推对话框。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在此对话框中，可以分别选择方法或成员，所有选中元素都会移动到当前类的子类中。当点击Add Required按钮时，所有已选择元素所必需的元素也会自动选上，此行为并不能保证所有必须的元素都能自动选中，还是需要人工确认。当有方法被选中时，编辑按钮就会可用，点击编辑按钮，弹出编辑对话框。在其中可以选择为选中方法在当前类中遗留抽象方法，还是在当前类中删除这些方法。双击一天选中的方法，也可以打开编辑对话框。在方法的Action列点击，会出现一个下拉列表，可以在其中选择遗留抽象方法还是在当前类中删除方法。按回车键确认编辑结果。 <br />
<br />
<br />
Pull Up <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上移与下推类似，也是在类之间移动方法和成员。上移将方法或成员从一个类移动到它的一个父类中。选中若干个方法或成员，在重构菜单中选择上移项，上移向导马上会出现。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在选择目标类多选框中，列出了当前类继承的所有父类。你只能将方法或成员移动到它们其中的一个里面。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果在选中方法的Action列，被设置成在目标类中声明抽象方法，那么在目标类的非抽象子类中创建必须的方法选项变为可选。当它选中时，目标类的所有子类，如果它们中没有选中的方法，则会为它们创建选中的方法。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 和在下推中一样，选择多个方法，点击编辑按钮，或者双击一个方法，都会打开编辑成员对话框。其中有两个选项，上移和在目标类中声明抽象方法。上移只是简单的复制方法到到父类中，并提供选择是否在当前类中删除该方法。在目标类中声明抽象方法会在父类中创建一个选中方法的抽象方法，如果父类不是抽象类则置为抽象类，最后选中方法留在当前类中。和在下推中一样，也可以点击Action列，可以在出现的下拉列表中选择。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果方法的Action列选为上移，在下一步的向导中，将会要求你选择是否在当前类中删除这些方法，选中的方法会在当前类中被删除。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在向导的任意一步都可以按完成按钮，结束重构操作，此时按照默认规则进行重构。 <br />
<br />
<br />
Extract Interface <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 提炼接口可以从一个存在的类中创建一个接口。你可以选择在接口中包含着个类的那些方法。选中一个类，从重构菜单选择提炼接口项，就可以打开提炼接口对话框。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这此对话框中添入接口的名字，选择希望包含的方法，在这个列表里面只列出了公共方法。选中改变对类[当前类名]的应用为对接口的引用选择框，将把所有对当前类的引用更新为对此接口的引用。 <br />
<br />
<br />
Generalize Type <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 泛化类型重构可以将一个声明对象的类型改变为它的超类，选择变量，参数，对象成员，方法返回类型，然后选择重构菜单的泛化类型项。在打开的泛化类型对话框，选择希望的新类型，然后点击完成按钮，结束重构。 <br />
<br />
<br />
<span class="hilite2">Use</span> <span class="hilite3">Supertype</span> <span class="hilite4">Where</span> <span class="hilite5">Possible</span> <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用超类会将对一个特定类型的引用改变为对它的超类的引用。选择一个类，选中重构菜单的使用超类项，会打开使用超类对话框。选中希望的超类类型，点击完成按钮完成重构。重构后，instanceof 表达式也会做相应的替换。 <br />
<br />
<br />
<br />
Inline <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内联是用代码或值来取代调用方法的地方，静态final对象成员，或局部变量。比如说，如果你内联一个方法调用，这个调用的地方就会被替换为该方法体。要内联一个方法，静态final对象成员，局部变量，选中这些元素，在重构菜单中选择内联项，或者使用快捷键Alt + Ctrl + I。在随后打开的内联对话框，你可以选择是否要内联所有的调用，或者是选择的调用。如果选择所有调用，你还可以选择是否删除声明本身。 <br />
<br />
Extract Method <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果方法中含有过多特定的操作，方法太长，或者其中的某段代码被多次使用，这时，可以用提炼方法重构将这部分代码提取到单独的方法中。在<span class="hilite1">Eclipse</span>中应用此重构方便快捷。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 选中要提炼的代码段，从重构菜单中选择提炼方法项，或者使用快捷键Alt + Shift + M。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在提炼方法对话框中，输入新方法的名字，选择修饰词，选择是否让新方法抛出运行时异常。在底部提供了新方法的预览。 <br />
<br />
<br />
Extract Local Variable <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用一个变量来代替一个表达式有很多好处。如果表达式在多处被使用，这样能够提高性能，而且也提高了代码的可读性。要把一个表达式提炼为局部变量，选择要提炼的表达式，从重构菜单中选择提炼局部变量项，或者使用快捷键Alt + Shift + L。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在提炼局部变量对话框中输入新变量的名字，选择是否要替换所有的表达式，是否使此变量为final。在对话框的底部提供变量的预览。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
Extract Constant <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 提炼常量与提炼局部变量很相似，唯一的区别是提炼常量重构可以选择提炼出的常量的修饰词，而且此常量将作为类的成员变量。 <br />
<br />
Introduce Parameter <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 介绍参数重构在方法中创建新的参数，然后用此新参数取代局部变量或者成员变量的实例。要是用此重构，选中方法中一个成员变量或局部变量的引用，然后从重构菜单中选择介绍参数项。 <br />
<br />
<br />
Introduce Factory <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 工厂是用来创建新对象，返回新创建对象的方法。你可以选择一个类的构造方法，从重构菜单中选择介绍工厂项，应用此重构，为此类创建工厂方法。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
在介绍工厂对话框，输入工厂方法的名字和需要工厂方法创建的对象的名字。选择构造方法的修饰词是否为私有。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 点击OK按钮后，在指定的类中会出现此指定工厂方法。此方法创建一个当前类的实例，然后返回此实例。 <br />
<br />
Convert Local Variable to Field <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 转换局部变量为成员变量重构，将方法内的变量声明移动到方法所在类中，使该变量对整个类可见。选择一个局部变量，从重构菜单中选择转换局部变量为成员变量项，随后打开配置的对话框。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在此对话框中，添入成员变量的名字，选择修饰词，选择在哪里实例化此成员变量。随后的声明为静态，声明为final 选择项是否可以使用，取决于实例化位置的选择情况。 <br />
<br />
Encapsulate Field <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要正确的实践面向对象编程，应该将成员变量的修饰词置为私有，提供相应的访问器来访问这些成员变量。但是这些操作很烦琐。如果使用了封装成员变量重构，则十分方便。选择一个成员变量，从重构菜单中选择封装成员变量项。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在封装局部变量对话框中，添入Getter, Setter方法的名字，选择新方法在哪个方法后出现。选择合适的修饰词。应用了此重构会创建两个新方法，将此成员变量的修饰词置为私有，将对此成员变量的引用改变为对新方法的引用。 <br />
<br />
重构项列表： <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下表从<span class="hilite1">Eclipse</span>帮助中提取，列出了各种重构支持的Java资源类型，对应的快捷键。 <br />
<br />
名字 可应用的Java元素 快捷键 <br />
Undo 在一次重构后可执行 Alt + Shift + Z <br />
Redo 在一次撤销重构后可执行 Alt + Shift + Y <br />
Rename 对方法，成员变量，局部变量，方法参数，对象，类，包，源代码目录，工程可用。 Alt + Shift + R <br />
Move 对方法，成员变量，局部变量，方法参数，对象，类，包，源代码目录，工程可用。 Alt + Shift + V <br />
Change Method Signature 对方法可用。 Alt + Shift + C <br />
Convert Anonymous Class to Nested 对匿名内部类可用。&nbsp;&nbsp; <br />
Move Member Type to New File 对嵌套类可用。&nbsp;&nbsp; <br />
Push Down 对同一个类中成员变量和方法可用。&nbsp;&nbsp; <br />
Pull Up 对同一个类中成员变量和方法，嵌套类可用。&nbsp;&nbsp; <br />
Extract Interface 对类可用。&nbsp;&nbsp; <br />
Generalize Type 对对象的声明可用。&nbsp;&nbsp; <br />
<span class="hilite2">Use</span> <span class="hilite3">Supertype</span> <span class="hilite4">Where</span> <span class="hilite5">Possible</span> 对类可用。&nbsp;&nbsp; <br />
Inline 对方法，静态final类，局部变量可用。 Alt + Shift + I <br />
&nbsp; <br />
Extract Method 对方法中的一段代码可用。 Alt + Shift + M <br />
&nbsp; <br />
Extract Local Variable 对选中的与局部变量相关的代码可用。 Alt + Shift + L <br />
&nbsp; <br />
Extract Constant 对静态final类变量，选中的与静态final类变量相关的代码可用。&nbsp;&nbsp; <br />
Introduce Parameter 对方法中对成员变量和局部变量的引用可用。&nbsp;&nbsp; <br />
Introduce Factory 对构造方法可用。&nbsp;&nbsp; <br />
Convert Local Variable to Field 对局部变量可用。 Alt + Shift + F <br />
Encapsulate Field 对成员变量可用。&nbsp;&nbsp; <br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本文介绍了<span class="hilite1">Eclipse</span>提供的各种重构。这些重构易于使用，可以确保代码重构更加方便安全。而且可以自动生成代码以提高生产率。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 某些重构改变了某些类的结构，但没有改变项目中其他类的结构，如下推，上移重构。这时，就要确保项目中所有对改变元素的引用都要被更新。这也是为什么要有一个好的测试套。同时，你也要更新测试套中的对改变元素的引用。所以说，重构和单元测试的有机结合对于软件开发是多么的重要。 <br /><img src ="http://www.blogjava.net/hulizhong/aggbug/303588.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-11-25 10:49 <a href="http://www.blogjava.net/hulizhong/archive/2009/11/25/303588.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式图</title><link>http://www.blogjava.net/hulizhong/archive/2009/11/03/300830.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 03 Nov 2009 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/11/03/300830.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/300830.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/11/03/300830.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/300830.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/300830.html</trackback:ping><description><![CDATA[<img height="620" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/oo.jpg" width="527" border="0" /><img src ="http://www.blogjava.net/hulizhong/aggbug/300830.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-11-03 09:40 <a href="http://www.blogjava.net/hulizhong/archive/2009/11/03/300830.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于ArrayList类的疑惑!</title><link>http://www.blogjava.net/hulizhong/archive/2009/10/27/299862.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 27 Oct 2009 00:53:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/10/27/299862.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/299862.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/10/27/299862.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/299862.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/299862.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在JAVA原代码中可以看到ArrayList类的实现.其中定义ArrayList类的时候,如下<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这里我有个疑惑,ArrayList继承了AbstractList,而AbstractList实现了List接口.为什么不直接这样写呢,如下:<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;implements RandomAccess, Cloneable, java.io.Serializable<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那个能解释一下!谢谢先!<img src ="http://www.blogjava.net/hulizhong/aggbug/299862.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-10-27 08:53 <a href="http://www.blogjava.net/hulizhong/archive/2009/10/27/299862.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 基于WEB应用开发的java程序员必备工具</title><link>http://www.blogjava.net/hulizhong/archive/2009/07/31/289212.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 31 Jul 2009 01:10:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/07/31/289212.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/289212.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/07/31/289212.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/289212.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/289212.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/microrain/archive/2007/05/03/1595241.aspx<br />
<br />
一个好的程序员除了具备扎实的基本功外，还应该具有更为灵活的逻辑思维与判断能力。除此之外，撑握一些行之有效的辅助工具也很重要。工欲善其事,必先利其
器。有好的工具辅助，所做的工作将事半功倍。下面向大家推荐基于WEB应用开发的java程序员应该必备的辅助开发工具。<br />
<br />
1，Java剖析工具　--　profiler<br />
是一个全功能的Java剖析工具（profiler），专用于分析J2SE和J2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用
中。JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存漏失
(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root
analysis，可以轻易找出内存漏失；heap快照（snapshot）模式让未被参照（reference）的对象、稍微被参照的对象、或在终结
（finalization）队列的对象都会被移除；整合精灵以便剖析浏览器的Java外挂功能。<br />
<span style="font-weight: bold;">下载地址：</span><a href="http://www.ej-technologies.com/download/overview.html" target="_blank">http://www.ej-technologies.com/download/overview.html</a><br />
<br />
<img src="http://p.blog.csdn.net/images/p_blog_csdn_net/microrain/252656/o_jprofiler.jpg" alt="" /><br />
<br />
2，冗余代码检查　--　Simian<br />
Simian UI 是一个用来发现重复代码的eclipse插件，对于改善设计，消除冗余代码很有帮助。<br />
<span style="font-weight: bold;">安装方法：</span>使用eclipse的Help-&gt;Software Update进行安装。站点地址为:http://www.integility.com/eclipse/<br />
安装后，按照提示重新启动eclipse。在希望进行分析的项目上点击右键，选择Simian-&gt;Add Simian to this project，就可以为该项目进行代码检查。<br />
<img src="http://p.blog.csdn.net/images/p_blog_csdn_net/microrain/252656/o_simian.jpg" alt="" /><br />
<br />
3，浏览器端调试工具（IE）　--　Internet Explorer Developer Toolbar<br />
微软发布了Internet Explorer Developer Toolbar Beta版。该产品让开发人员能够深入探索和理解Web页面，帮助开发者更好地创建Web应用。浏览和修改Web页的文档对象模型（DOM）。具备以下特性：<br />
－通过多种技术方式定位、选定Web页上的特定元素。<br />
－禁止或激活IE设置。<br />
－查看HTML对象的类名、ID，以及类似链接路径、tab顺序、快捷键等细节。<br />
－描绘表格、单元格、图片或选定标签的轮廓。<br />
－显示图片象素、大小、路径、替代文字等。<br />
－即时重定义浏览器窗口大小到800x600或自定义大小。<br />
－清空浏览器缓存和cookie，被清除项可从所有对象或给定域中选择。<br />
－直接访问关联W3C规范参考、IE开发组blog或其他来源。<br />
－显示设计时标尺，帮助对齐对象。<br />
该工具条可集成在IE窗口，或以浮动窗口形式存在。<br />
<br />
<span style="font-weight: bold;">下载地址：</span><a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=e59c3964-672d-4511-bb3e-2d5e1db91038&amp;displaylang=en" target="_blank">从微软官方下载</a><br />
<br />
<img src="http://p.blog.csdn.net/images/p_blog_csdn_net/microrain/252656/o_InternetExplorerDeveloperToolbar.jpg" alt="" /><br />
<br />
4，浏览器端调试工具（Firefox）　--　Firebug<br />
JavaScript, CSS,
HTML，Ajax调试工具。功能包括HTML/CSS检查，除错工具，错误控制台和命令行等。可对javascript做调试和性能分析，查看html
代码的结构，css样式动态提示，检测并显示页面错误，浏览树型结构的DOM数据，可以显示javascript的log等等功能。<br />
<br />
<span style="font-weight: bold;">下载地址：</span><a href="http://www.getfirebug.com/" target="_blank">http://www.getfirebug.com/</a><br />
<br />
<img src="http://p.blog.csdn.net/images/p_blog_csdn_net/microrain/252656/o_Firebug.jpg" alt="" /><br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/289212.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-07-31 09:10 <a href="http://www.blogjava.net/hulizhong/archive/2009/07/31/289212.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 JAVA正则表达式高级用法(分组与捕获)</title><link>http://www.blogjava.net/hulizhong/archive/2009/07/16/286949.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Thu, 16 Jul 2009 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/07/16/286949.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/286949.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/07/16/286949.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/286949.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/286949.html</trackback:ping><description><![CDATA[转 http://extjs2.javaeye.com/blog/394128<br />
<br />
正则表达式在字符串处理中经常使用，关于正则简单的用法相信有一点程序基础的人都懂得一些，这里就不介绍简单基础了。这里主要讲解一下在<span class="hilite1">JAVA</span>中实现了的正则的高级用法-分组与捕获。
<br />
<br />
&nbsp;&nbsp;&nbsp; 对于要重复单个字符，非常简单，直接在字符后卖弄加上限定符即可，例如 a+ 表示匹配1个或一个以上的a，a?表示匹配0个或1个a。这些限定符如下所示：
<br />
<br />
&nbsp; X? X，一次或一次也没有
<br />
X* X，零次或多次
<br />
X+ X，一次或多次
<br />
X{n} X，恰好 n 次
<br />
X{n,} X，至少 n 次
<br />
X{n,m} X，至少 n 次，但是不超过 m 次
<br />
<br />
<br />
<br />
<br />
<br />
但是我们如果要对多个字符进行重复怎么办呢？此时我们就要用到分组，我们可以使用小括号"()"来指定要重复的子表达式，然后对这个子表达式进行重复，例如：(abc)? 表示0个或1个abc 这里一个括号的表达式就表示一个分组。
<br />
<br />
<br />
<br />
&nbsp;&nbsp; 分组可以分为两种形式，捕获组和非捕获组。
<br />
<br />
<br />
<br />
捕获组
<br />
<br />
捕获组可以通过从左到右计算其开括号来编号。例如，在表达式 ((A)(B(C))) 中，存在四个这样的组：
<br />
<br />
1&nbsp;&nbsp;&nbsp;&nbsp; ((A)(B(C)))
<br />
2&nbsp;&nbsp;&nbsp;&nbsp; \A
<br />
3&nbsp;&nbsp;&nbsp;&nbsp; (B(C))
<br />
4&nbsp;&nbsp;&nbsp;&nbsp; (C)
<br />
<br />
组零始终代表整个表达式
<br />
<br />
之所以这样命名捕获组是因为在匹配中，保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用在表达式中使用，也可以在匹配操作完成后从匹配器检索。
<br />
<br />
<br />
<br />
Back 引用 是说在后面的表达式中我们可以使用组的编号来引用前面的表达式所捕获到的文本序列(是文本不是正则)。
<br />
<br />
<br />
<br />
例如 ([" ']).* \1&nbsp;&nbsp;
其中使用了分组，\1就是对引号这个分组的引用，它匹配包含在两个引号或者两个单引号中的所有字符串，如，"abc" 或 " ' " 或 ' "
'&nbsp; ，但是请注意，它并不会对" a'或者 'a"匹配。原因上面已经说明，Back引用只是引用文本而不是表达式。
<br />
<br />
<br />
<br />
非捕获组
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以 (?) 开头的组是纯的非捕获 组，它不捕获文本，也不针对组合计进行计数。就是说，如果小括号中以?号开头，那么这个分组就不会捕获文本，当然也不会有组的编号，因此也不存在Back 引用。
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在<span class="hilite1">Java</span>中，支持的非捕获组，有如下几种：
<br />
<br />
&nbsp;
<br />
&nbsp;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;
<br />
(?=X)&nbsp;&nbsp;&nbsp;&nbsp; X，通过零宽度的正 lookahead
<br />
(?!X)&nbsp;&nbsp;&nbsp;&nbsp; X，通过零宽度的负 lookahead
<br />
(?&lt;=X)&nbsp;&nbsp;&nbsp;&nbsp; X，通过零宽度的正 lookbehind
<br />
(?&lt;!X)&nbsp;&nbsp;&nbsp;&nbsp; X，通过零宽度的负 lookbehind
<br />
&nbsp;
<br />
<br />
<br />
<br />
<br />
这四个非捕获组用于匹配表达式X，但是不包含表达式的文本。
<br />
<br />
(?=X ) 零宽度正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。例如，\w+(?=\d) 与后跟数字的单词匹配，而不与该数字匹配。此构造不会回溯。
<br />
(?!X) 零宽度负先行断言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如，例如，\w+(?!\d) 与后不跟数字的单词匹配，而不与该数字匹配。
<br />
(?&lt;=X) 零宽度正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如，(?&lt;=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
<br />
(?&lt;!X) 零宽度负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如，(?&lt;!19)99 与不跟在 19 后面的 99 的实例匹配
<br />
<br />
<br />
<br />
<br />
<br />
<br />
举例：
<br />
<br />
<br />
<br />
上面都是理论性的介绍，这里就使用一些例子来说明一下问题：
<br />
<br />
&nbsp;&nbsp; 1、测试匹配性&nbsp;&nbsp; (?&lt;!4)56(?=9) 这里的含义就是匹配后面的文本56前面不能是4，后面必须是9组成。因此，可以匹配如下文本 5569&nbsp; ，与4569不匹配。
<br />
<br />
<br />
<br />
&nbsp; 2 、提取字符串&nbsp;&nbsp; 提取 da12bka3434bdca4343bdca234bm&nbsp;&nbsp; 提取包含在字符a和b之间的数字，但是这个a之前的字符不能是c,b后面的字符必须是d才能提取。
<br />
<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 例如这里就只有3434这个数字满足要求。那么我们怎么提取呢？
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先我们写出提取这个字符串的表达式： (?&lt;!c)a(\d+)bd&nbsp; 这里就只有一个捕获组(\d+)
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="hilite1">JAVA</span>代码片段如下：
<br />
<br />
Pattern p = Pattern.compile("(?&lt;!c)a(\\d+)bd");
<br />
Matcher m = p.matcher("da12bca3434bdca4343bdca234bm");
<br />
while(m.find()){
<br />
&nbsp;&nbsp; System.out.println(m.group(1)); //我们只要捕获组1的数字即可。结果 3434
<br />
&nbsp;&nbsp; System.out.println(m.group(0)); // 0组是整个表达式，看这里，并没有提炼出(?&lt;!c)的字符 。结果 a3434bd
<br />
}
<br />
&nbsp;&nbsp;&nbsp; 可以看到，非捕获组，最后是不会返回结果的，因为它本身并不捕获文本。
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/286949.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-07-16 09:43 <a href="http://www.blogjava.net/hulizhong/archive/2009/07/16/286949.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>netbeas 相关快捷键</title><link>http://www.blogjava.net/hulizhong/archive/2009/07/12/286482.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sun, 12 Jul 2009 13:53:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/07/12/286482.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/286482.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/07/12/286482.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/286482.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/286482.html</trackback:ping><description><![CDATA[Ctrl-F3 搜索位于插入点的词 <br />
F3/Shift-F3 在文件中查找下一个/上一个 <br />
Ctrl-F/H 在文件中查找/替换 <br />
Alt-F7 查找使用实例 <br />
Ctrl-Shift-P 在项目中查找 <br />
Alt-Shift-U 查找使用实例结果 <br />
Alt-Shift-H 关闭搜索结果突出显示 <br />
Alt-Shift-L 跳转列表中的下一个（所有文件） <br />
Alt-Shift-K 跳转列表中的上一个（所有文件） <br />
Ctrl-R 重新装入窗体 <br />
Alt-U-U 将选定内容转换为大写 <br />
Alt-U-L 将选定内容转换为小写 <br />
Alt-U-R 对选定内容切换大小写 <br />
<br />
在源代码中导航 <br />
Alt-Shift-O 转至类 <br />
Alt-Shift-E&nbsp; 转至 JUnit 测试 <br />
Alt-O 转至源代码 <br />
Alt-G 转至声明 <br />
Ctrl-B 转至超级实现 <br />
Alt-K/Alt-L 后退/前进 <br />
Ctrl-G 转至行 <br />
Ctrl-F2&nbsp; 切换添加/删除书签 <br />
F2/Shift-F2&nbsp; 下一个/上一个书签 <br />
F12/Shift-F12 下一个/上一个使用实例/编译错误 <br />
Ctrl-Shift-1/2/3 在&#8220;项目&#8221;/&#8220;文件&#8221;/&#8220;收藏夹&#8221;中选择 <br />
Ctrl-[ 将插入记号移至匹配的方括号 <br />
Ctrl-^ Ctrl-[（法语/比利时语键盘） <br />
<br />
用Java编码 <br />
Ctrl-I 覆盖方法 <br />
Alt-Shift-F/I 修复全部/选定类的导 <br />
Alt-Shift-W 以 try-catch 块围绕 <br />
Ctrl-Shift-F 重新设置选定内容的 <br />
Ctrl-D/Ctrl-T 左移/右移一个制表符 <br />
Ctrl-Shift-T/D 添加/撤消注释行 ("// <br />
Ctrl-L/K 插入下一个/上一个匹 <br />
Esc/Ctrl-空格键 关闭/打开代码完成 <br />
Ctrl-M 选择下一个参数 <br />
Shift-空格键 输入空格，不展开缩写 <br />
Alt-F1/Shift-F1 显示/搜索 Javadoc <br />
Ctrl-Shift-M&nbsp; 提取方法 <br />
Alt-U-G 将 &#8220;get&#8221; 放置到标识符前面 <br />
Alt-U-S 将 &#8220;set&#8221; 放置到标识符前面 <br />
Alt-U-I 将 &#8220;is&#8221; 放置到标识符前面 <br />
Ctrl-Backspace/Del 删除上一个/当前词 <br />
Ctrl-E 删除当前行 <br />
Ctrl-J-S/E 开始/结束录制宏 <br />
Ctrl-Shift-J&nbsp; 插入国际化字符串 <br />
Ctrl-数字键盘上的 - 折叠（隐藏）代码块 <br />
Ctrl-数字键盘上的 + 展开已折叠的代码块 <br />
Ctrl-Shift-数字键盘上的 - 折叠所有代码块 <br />
Ctrl-Shift-数字键盘上的 + 展开所有代码块 <br />
Alt-Enter 显示建议/提示 <br />
<br />
打开和切换视图 <br />
Ctrl-Shift-0 显示&#8220;搜索结果&#8221;窗口 <br />
Ctrl-0 显示源代码编辑器 <br />
Ctrl-1 显示&#8220;项目&#8221;窗口 <br />
Ctrl-2 显示&#8220;文件&#8221;窗口 <br />
Ctrl-3 显示&#8220;收藏夹&#8221;窗口 <br />
Ctrl-4 显示&#8220;输出&#8221;窗口 <br />
Ctrl-5 显示&#8220;运行环境&#8221;窗口 <br />
Ctrl-6 显示&#8220;待做事项&#8221;窗口 <br />
Ctrl-7 显示&#8220;导航&#8221;窗口 <br />
Ctrl-Shift-7 显示&#8220;属性&#8221;对话框 <br />
Ctrl-Shift-8 显示组件面板 <br />
Ctrl-8 显示&#8220;版本控制&#8221;窗口 <br />
Ctrl-9 显示&#8220;VCS 输出&#8221;窗口 <br />
Shift-F4 显示&#8220;文档&#8221;对话框 <br />
Alt-向左方向键 移动到左侧窗口 <br />
Alt-向右方向键 移动到右侧窗口 <br />
Ctrl-Tab (Ctrl-`) 在打开的文档之间切换 <br />
Shift-Escape 最大化窗口（切换） <br />
Ctrl-F4/Ctrl-W 关闭当前选定的窗口 <br />
Ctrl-Shift-F4 关闭所有窗口 <br />
Shift-F10 打开上下文菜单 <br />
<br />
编译、测试和运行 <br />
F9 编译选定的包或文件 <br />
F11 生成主项目 <br />
Shift-F11 清理并生成主项目 <br />
Ctrl-Q 设置请求参数 <br />
Ctrl-Shift-U 创建 JUnit 测试 <br />
Ctrl-F6/Alt-F6 为文件/项目运行JUnit测试 <br />
F6/Shift-F6 运行主项目/文件 <br />
<br />
调试 <br />
F5 开始调试主项目 <br />
Ctrl-Shift-F5 开始调试当前文件 <br />
Ctrl-Shift-F6 开始为文件调试测试 (JU <br />
Shift-F5/Ctrl-F5 停止/继续调试会话 <br />
F4 运行到文件中的光标位置 <br />
F7/F8 步入/越过 <br />
Ctrl-F7 步出 <br />
Ctrl-Alt-向上方向键 转至被调用的方法 <br />
Ctrl-Alt-向下方向键 转至调用方法 <br />
Ctrl-F9 计算表达式的值 <br />
Ctrl-F8 切换断点 <br />
Ctrl-Shift-F8 新建断点 <br />
Ctrl-Shift-F7 新建监视 <br />
Ctrl-Shift-5 显示 HTTP 监视器 <br />
Ctrl-Shift-0 显示&#8220;搜索结果&#8221;窗口 <br />
Alt-Shift-1 显示&#8220;局部变量&#8221;窗口 <br />
Alt-Shift-2 显示&#8220;监视&#8221;窗口 <br />
Alt-Shift-3 显示&#8220;调用栈&#8221;窗口 <br />
Alt-Shift-4 显示&#8220;类&#8221;窗口 <br />
Alt-Shift-5 显示&#8220;断点&#8221;窗口 <br />
Alt-Shift-6 显示&#8220;会话&#8221;窗口 <br />
Ctrl-Shift-6 切换到&#8220;执行&#8221;窗口 <br />
Alt-Shift-7 切换到&#8220;线程&#8221;窗口 <br />
Alt-Shift-8 切换到&#8220;源&#8221;窗口<img src ="http://www.blogjava.net/hulizhong/aggbug/286482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-07-12 21:53 <a href="http://www.blogjava.net/hulizhong/archive/2009/07/12/286482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 高效的Java异常处理框架</title><link>http://www.blogjava.net/hulizhong/archive/2009/07/11/286364.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sat, 11 Jul 2009 04:04:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/07/11/286364.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/286364.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/07/11/286364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/286364.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/286364.html</trackback:ping><description><![CDATA[一、 异常的概念和Java异常体系结构 <br />
<br />
&nbsp;&nbsp;&nbsp; 异常是程序运行过程中出现的错误。本文主要讲授的是Java语言的异常处理。Java语言的异常处理框架， <br />
&nbsp;&nbsp;&nbsp; 是Java语言健壮性的一个重要体现。 <br />
<br />
&nbsp;&nbsp;&nbsp; Java把异常当作对象来处理，并定义一个基类java.lang.Throwable作为所有异常的超类。 <br />
&nbsp;&nbsp;&nbsp; 在Java API中已经定义了许多异常类，这些异常类分为两大类，错误Error和异常Exception。 <br />
&nbsp;&nbsp;&nbsp; Java异常体系结构呈树状，其层次结构图如图 1所示：&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img height="242" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/exception1.jpg" width="407" border="0" /><br />
<br />
&nbsp;&nbsp;&nbsp; 图 1&nbsp; Java异常体系结构 <br />
<br />
&nbsp;&nbsp;&nbsp; Thorwable类所有异常和错误的超类，有两个子类Error和Exception，分别表示错误和异常。 <br />
&nbsp;&nbsp;&nbsp; 其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常， <br />
&nbsp;&nbsp;&nbsp; 这两种异常有很大的区别，也称之为不检查异常（Unchecked Exception） <br />
&nbsp;&nbsp;&nbsp; 和检查异常（Checked Exception）。下面将详细讲述这些异常之间的区别与联系： <br />
<br />
&nbsp;&nbsp;&nbsp; 1、Error与Exception <br />
<br />
&nbsp;&nbsp;&nbsp; Error是程序无法处理的错误，比如OutOfMemoryError、ThreadDeath等。这些异常发生时， <br />
&nbsp;&nbsp;&nbsp; Java虚拟机（JVM）一般会选择线程终止。 <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; Exception是程序本身可以处理的异常，这种异常分两大类运行时异常和非运行时异常。 <br />
&nbsp;&nbsp;&nbsp; 程序中应当尽可能去处理这些异常。 <br />
<br />
&nbsp;&nbsp;&nbsp; 2、运行时异常和非运行时异常 <br />
<br />
&nbsp;&nbsp;&nbsp; 运行时异常都是RuntimeException类及其子类异常，如NullPointerException、IndexOutOfBoundsException等， <br />
&nbsp;&nbsp;&nbsp; 这些异常是不检查异常，程序中可以选择捕获处理，也可以不处理。这些异常一般是由程序逻辑错误引起的， <br />
&nbsp;&nbsp;&nbsp; 程序应该从逻辑角度尽可能避免这类异常的发生。 <br />
<br />
&nbsp;&nbsp;&nbsp; 非运行时异常是RuntimeException以外的异常，类型上都属于Exception类及其子类。 <br />
&nbsp;&nbsp;&nbsp; 从程序语法角度讲是必须进行处理的异常，如果不处理，程序就不能编译通过。 <br />
&nbsp;&nbsp;&nbsp; 如IOException、SQLException等以及用户自定义的Exception异常，一般情况下不自定义检查异常。 <br />
<br />
<br />
二、 异常的捕获和处理 <br />
<br />
&nbsp;&nbsp;&nbsp; Java异常的捕获和处理是一个不容易把握的事情，如果处理不当，不但会让程序代码的可读性大大降低， <br />
&nbsp;&nbsp;&nbsp; 而且导致系统性能低下，甚至引发一些难以发现的错误。 <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; Java异常处理涉及到五个关键字，分别是：try、catch、finally、throw、throws。下面将骤一介绍， <br />
&nbsp;&nbsp;&nbsp; 通过认识这五个关键字，掌握基本异常处理知识。 <br />
<br />
&nbsp;&nbsp;&nbsp; 1、 异常处理的基本语法 <br />
&nbsp;&nbsp;&nbsp; 在java中，异常处理的完整语法是： <br />
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://spark998.javaeye.com/blog/413339#"></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&nbsp;</span><span class="keyword">try</span><span>{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span><span class="comment">//（尝试运行的）程序代码 </span><span>&nbsp;&nbsp;</span></span>
    <li><span>}</span><span class="keyword">catch</span><span>(异常类型&nbsp;异常的变量名){ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span><span class="comment">//异常处理代码 </span><span>&nbsp;&nbsp;</span></span>
    <li><span>}</span><span class="keyword">finally</span><span>{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span><span class="comment">//异常发生，方法返回之前，总是要执行的代码 </span><span>&nbsp;&nbsp;</span></span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">     try{
//（尝试运行的）程序代码
}catch(异常类型 异常的变量名){
//异常处理代码
}finally{
//异常发生，方法返回之前，总是要执行的代码
}
</pre>
<br />
<br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 以上语法有三个代码块： <br />
&nbsp;&nbsp;&nbsp; try语句块，表示要尝试运行代码，try语句块中代码受异常监控，其中代码发生异常时，会抛出异常对象。 <br />
<br />
&nbsp;&nbsp;&nbsp; catch语句块会捕获try代码块中发生的异常并在其代码块中做异常处理，catch语句带一个Throwable类型的参数， <br />
&nbsp;&nbsp;&nbsp; 表示可捕获异常类型。当try中出现异常时，catch会捕获到发生的异常，并和自己的异常类型匹配， <br />
&nbsp;&nbsp;&nbsp; 若匹配，则执行catch块中代码，并将catch块参数指向所抛的异常对象。catch语句可以有多个， <br />
&nbsp;&nbsp;&nbsp; 用来匹配多个中的一个异常，一旦匹配上后，就不再尝试匹配别的catch块了。 <br />
&nbsp;&nbsp;&nbsp; 通过异常对象可以获取异常发生时完整的JVM堆栈信息，以及异常信息和异常发生的原因等。 <br />
<br />
&nbsp;&nbsp;&nbsp; finally语句块是紧跟catch语句后的语句块，这个语句块总是会在方法返回前执行， <br />
&nbsp;&nbsp;&nbsp; 而不管是否try语句块是否发生异常。并且这个语句块总是在方法返回前执行。 <br />
&nbsp;&nbsp;&nbsp; 目的是给程序一个补救的机会。这样做也体现了Java语言的健壮性。 <br />
<br />
&nbsp;&nbsp;&nbsp; 2、 try、catch、finally三个语句块应注意的问题 <br />
&nbsp;&nbsp;&nbsp; 第一、try、catch、finally三个语句块均不能单独使用，三者可以组成 try...catch...finally、try...catch、 <br />
&nbsp;&nbsp;&nbsp; try...finally三种结构，catch语句可以有一个或多个，finally语句最多一个。 <br />
&nbsp;&nbsp;&nbsp; 第二、try、catch、finally三个代码块中变量的作用域为代码块内部，分别独立而不能相互访问。 <br />
&nbsp;&nbsp;&nbsp; 如果要在三个块中都可以访问，则需要将变量定义到这些块的外面。 <br />
&nbsp;&nbsp;&nbsp; 第三、多个catch块时候，只会匹配其中一个异常类并执行catch块代码，而不会再执行别的catch块， <br />
&nbsp;&nbsp;&nbsp; 并且匹配catch语句的顺序是由上到下。 <br />
<br />
&nbsp;&nbsp;&nbsp; 3、throw、throws关键字 <br />
&nbsp;&nbsp;&nbsp; throw关键字是用于方法体内部，用来抛出一个Throwable类型的异常。如果抛出了检查异常， <br />
&nbsp;&nbsp;&nbsp; 则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。 <br />
&nbsp;&nbsp;&nbsp; 如果所有方法都层层上抛获取的异常，最终JVM会进行处理，处理也很简单，就是打印异常消息和堆栈信息。 <br />
&nbsp;&nbsp;&nbsp; 如果抛出的是Error或RuntimeException，则该方法的调用者可选择处理该异常。有关异常的转译会在下面说明。 <br />
<br />
&nbsp;&nbsp;&nbsp; throws关键字用于方法体外部的方法声明部分，用来声明方法可能会抛出某些异常。仅当抛出了检查异常， <br />
&nbsp;&nbsp;&nbsp; 该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候，应该继续抛出， <br />
&nbsp;&nbsp;&nbsp; 而不是囫囵吞枣一般在catch块中打印一下堆栈信息做个勉强处理。下面给出一个简单例子， <br />
&nbsp;&nbsp;&nbsp; 看看如何使用这两个关键字： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://spark998.javaeye.com/blog/413339#"></a></div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;test3()&nbsp;</span><span class="keyword">throws</span><span>&nbsp;Exception{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span><span class="comment">//抛出一个检查异常 </span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">throw</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;Exception(</span><span class="string">"方法test3中的Exception"</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">    public static void test3() throws Exception{
//抛出一个检查异常
throw new Exception("方法test3中的Exception");
}
</pre>
<br />
<br />
&nbsp;&nbsp;&nbsp; 4、 Throwable类中的常用方法 <br />
&nbsp;&nbsp;&nbsp; getCause()：返回抛出异常的原因。如果 cause 不存在或未知，则返回 null。 <br />
&nbsp;&nbsp;&nbsp; getMessage()：返回异常的消息信息。 <br />
&nbsp;&nbsp;&nbsp; printStackTrace()：对象的堆栈跟踪输出至错误输出流，作为字段 System.err 的值。 <br />
<br />
<br />
<br />
三、 异常处理的一般原则 <br />
<br />
&nbsp;&nbsp;&nbsp; 1、 能处理就早处理，抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。 <br />
&nbsp;&nbsp;&nbsp; 因为对于一个应用系统来说，抛出大量异常是有问题的，应该从程序开发角度尽可能的控制异常发生的可能。 <br />
&nbsp;&nbsp;&nbsp; 2、 对于检查异常，如果不能行之有效的处理，还不如转换为RuntimeException抛出。 <br />
&nbsp;&nbsp;&nbsp; 这样也让上层的代码有选择的余地――可处理也可不处理。 <br />
&nbsp;&nbsp;&nbsp; 3、 对于一个应用系统来说，应该有自己的一套异常处理框架，这样当异常发生时，也能得到统一的处理风格， <br />
&nbsp;&nbsp;&nbsp; 将优雅的异常信息反馈给用户。 <br />
<br />
四、 异常的转译与异常链 <br />
<br />
&nbsp;&nbsp;&nbsp; 1、异常转译的原理 <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 所谓的异常转译就是将一种异常转换另一种新的异常，也许这种新的异常更能准确表达程序发生异常。 <br />
&nbsp;&nbsp;&nbsp; 在Java中有个概念就是异常原因，异常原因导致当前抛出异常的那个异常对象， <br />
&nbsp;&nbsp;&nbsp; 几乎所有带异常原因的异常构造方法都使用Throwable类型做参数，这也就为异常的转译提供了直接的支持， <br />
&nbsp;&nbsp;&nbsp; 因为任何形式的异常和错误都是Throwable的子类。比如将SQLException转换为另外一个新的异常DAOException， <br />
&nbsp;&nbsp;&nbsp; 可以这么写： <br />
<br />
&nbsp;&nbsp;&nbsp; 先自定义一个异常DAOException： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://spark998.javaeye.com/blog/413339#"></a></div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;DAOException&nbsp;</span><span class="keyword">extends</span><span>&nbsp;RuntimeException&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>/(省略了部分代码) &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;DAOException(String&nbsp;message,&nbsp;Throwable&nbsp;cause)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">super</span><span>(message,&nbsp;cause); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">      public class DAOException extends RuntimeException {
//(省略了部分代码)
public DAOException(String message, Throwable cause) {
super(message, cause);
}
}
</pre>
<br />
<br />
&nbsp;&nbsp;&nbsp; 比如有一个SQLException类型的异常对象e，要转换为DAOException，可以这么写： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://spark998.javaeye.com/blog/413339#"></a></div>
</div>
<ol class="dp-j">
    <li><span><span>DAOException&nbsp;daoEx&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;DAOException&nbsp;(&nbsp;</span><span class="string">"SQL异常"</span><span>,&nbsp;e);&nbsp;&nbsp;&nbsp;</span></span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">    DAOException daoEx = new DAOException ( "SQL异常", e);
</pre>
<br />
<br />
&nbsp;&nbsp;&nbsp; 异常转译是针对所有继承Throwable超类的类而言的，从编程的语法角度讲，其子类之间都可以相互转换。 <br />
&nbsp;&nbsp;&nbsp; 但是，从合理性和系统设计角度考虑，可将异常分为三类：Error、Exception、RuntimeException，笔者认为， <br />
&nbsp;&nbsp;&nbsp; 合理的转译关系图应该如图 2：&nbsp;<br />
<br />
&nbsp;<img height="222" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/exception2.jpg" width="337" border="0" /><br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 图 2 异常转译 <br />
<br />
&nbsp;&nbsp;&nbsp; 为什么要这么做呢？笔者认为，异常的处理存在着一套哲学思想：对于一个应用系统来说， <br />
&nbsp;&nbsp;&nbsp; 系统所发生的任何异常或者错误对操作用户来说都是系统"运行时"异常，都是这个应用系统内部的异常。 <br />
&nbsp;&nbsp;&nbsp; 这也是异常转译和应用系统异常框架设计的指导原则。在系统中大量处理非检查异常的负面影响很多， <br />
&nbsp;&nbsp;&nbsp; 最重要的一个方面就是代码可读性降低，程序编写复杂，异常处理的代码也很苍白无力。 <br />
&nbsp;&nbsp;&nbsp; 因此，很有必要将这些检查异常Exception和错误Error转换为RuntimeException异常， <br />
&nbsp;&nbsp;&nbsp; 让程序员根据情况来决定是否捕获和处理所发生的异常。 <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 图中的三条线标识转换的方向，分三种情况： <br />
<br />
&nbsp;&nbsp;&nbsp; ①：Error到Exception：将错误转换为异常，并继续抛出。例如Spring WEB框架中， <br />
&nbsp;&nbsp;&nbsp; 将org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中， <br />
&nbsp;&nbsp;&nbsp; 将捕获的错误转译为一个NestedServletException异常。这样做的目的是为了最大限度挽回因错误发生带来的负面影响。 <br />
&nbsp;&nbsp;&nbsp; 因为一个Error常常是很严重的错误，可能会引起系统挂起。 <br />
<br />
&nbsp;&nbsp;&nbsp; ②：Exception到RuntimeException：将检查异常转换为RuntimeException可以让程序代码变得更优雅， <br />
&nbsp;&nbsp;&nbsp; 让开发人员集中经理设计更合理的程序代码，反过来也增加了系统发生异常的可能性。 <br />
<br />
&nbsp;&nbsp;&nbsp; ③：Error到RuntimeException：目的还是一样的。把所有的异常和错误转译为不检查异常， <br />
&nbsp;&nbsp;&nbsp; 这样可以让代码更为简洁，还有利于对错误和异常信息的统一处理。 <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 1、 异常链 <br />
<br />
&nbsp;&nbsp;&nbsp; 异常链顾名思义就是将异常发生的原因一个传一个串起来，即把底层的异常信息传给上层，这样逐层抛出。 <br />
&nbsp;&nbsp;&nbsp; Java API文档中给出了一个简单的模型： <br />
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://spark998.javaeye.com/blog/413339#"></a></div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;lowLevelOp(); &nbsp;&nbsp;</span>
    <li><span>}&nbsp;</span><span class="keyword">catch</span><span>&nbsp;(LowLevelException&nbsp;le)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span><span class="keyword">throw</span><span>&nbsp;(HighLevelException) &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span><span class="keyword">new</span><span>&nbsp;HighLevelException().initCause(le); &nbsp;&nbsp;</span></span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">    try {
lowLevelOp();
} catch (LowLevelException le) {
throw (HighLevelException)
new HighLevelException().initCause(le);
}
</pre>
<br />
<br />
&nbsp;&nbsp;&nbsp; 当程序捕获到了一个底层异常le，在处理部分选择了继续抛出一个更高级别的新异常给此方法的调用者。 <br />
&nbsp;&nbsp;&nbsp; 这样异常的原因就会逐层传递。这样，位于高层的异常递归调用getCause()方法，就可以遍历各层的异常原因。 <br />
&nbsp;&nbsp;&nbsp; 这就是Java异常链的原理。异常链的实际应用很少，发生异常时候逐层上抛不是个好注意， <br />
&nbsp;&nbsp;&nbsp; 上层拿到这些异常又能奈之何？而且异常逐层上抛会消耗大量资源， <br />
&nbsp;&nbsp;&nbsp; 因为要保存一个完整的异常链信息. <br />
<br />
<br />
五、 设计一个高效合理的异常处理框架 <br />
<br />
&nbsp;&nbsp;&nbsp; 对于一个应用系统来说，发生所有异常在用户看来都是应用系统内部的异常。因此应该设计一套应用系统的异常框架， <br />
&nbsp;&nbsp;&nbsp; 以处理系统运行过程中的所有异常。 <br />
<br />
&nbsp;&nbsp;&nbsp; 基于这种观点，可以设计一个应用系统的异常比如叫做AppException。并且对用户来说， <br />
&nbsp;&nbsp;&nbsp; 这些异常都是运行应用系统运行时发生的，因此AppException应该继承RuntimeException， <br />
&nbsp;&nbsp;&nbsp; 这样系统中所有的其他异常都转译为AppException，当异常发生的时候，前端接收到AppExcetpion并做统一的处理。 <br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 画出异常处理框架如图 3 ：&nbsp;<br />
<br />
&nbsp;<img height="418" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/exception3.jpg" width="520" border="0" /><br />
<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 图 3 一个应用系统的异常处理框架 <br />
<br />
&nbsp;&nbsp;&nbsp; 在这个设计图中，AppRuntimeException是系统异常的基类，对外只抛出这个异常， <br />
&nbsp;&nbsp;&nbsp; 这个异常可以由前端（客户端）接收处理，当异常发生时，客户端的相关组件捕获并处理这些异常， <br />
&nbsp;&nbsp;&nbsp; 将"友好"的信息展示给客户。 <br />
<br />
&nbsp;&nbsp;&nbsp; 在AppRuntimeException下层，有各种各样的异常和错误，最终都转译为AppRuntimeException， <br />
&nbsp;&nbsp;&nbsp; AppRuntimeException下面还可以设计一些别的子类异常，比如AppDAOException、OtherException等， <br />
&nbsp;&nbsp;&nbsp; 这些都根据实际需要灵活处理。 <br />
&nbsp;&nbsp;&nbsp; 在往下就是如何将捕获的原始异常比如SQLException、HibernateException转换为更高级一点AppDAOException。 <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 有关异常框架设计这方面公认比较好的就是Spring，Spring中的所有异常都可以用org.springframework.core.NestedRuntimeException来表示，并且该基类继承的是RuntimeException。 <br />
&nbsp;&nbsp;&nbsp; Spring框架很庞大，因此设计了很多NestedRuntimeException的子类，还有异常转换的工具， <br />
&nbsp;&nbsp;&nbsp; 这些都是非常优秀的设计思想。 <br />
<br />
<br />
六、 Java异常处理总结 <br />
<br />
&nbsp;&nbsp;&nbsp; 回顾全文，总结一下Java异常处理的要点： <br />
&nbsp;&nbsp;&nbsp; 1、 异常是程序运行过程过程出现的错误，在Java中用类来描述，用对象来表示具体的异常。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Java将其区分为Error与Exception，Error是程序无力处理的错误，Exception是程序可以处理的错误。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 异常处理是为了程序的健壮性。 <br />
&nbsp;&nbsp;&nbsp; 2、 Java异常类来自于Java API定义和用户扩展。通过继承Java API异常类可以实现异常的转译。 <br />
&nbsp;&nbsp;&nbsp; 3、 异常能处理就处理，不能处理就抛出，最终没有处理的异常JVM会进行处理。 <br />
&nbsp;&nbsp;&nbsp; 4、 异常可以传播，也可以相互转译，但应该根据需要选择合理的异常转译的方向。 <br />
&nbsp;&nbsp;&nbsp; 5、 对于一个应用系统，设计一套良好的异常处理体系很重要。这一点在系统设计的时候就应该考虑到。 <br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/286364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-07-11 12:04 <a href="http://www.blogjava.net/hulizhong/archive/2009/07/11/286364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 OOM和JVM配置优化 </title><link>http://www.blogjava.net/hulizhong/archive/2009/07/11/286347.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sat, 11 Jul 2009 01:04:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/07/11/286347.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/286347.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/07/11/286347.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/286347.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/286347.html</trackback:ping><description><![CDATA[转 http://www.blogjava.net/cenwenchu/archive/2008/01/22/177082.html<br />
<br />
OOM<span style="font-family: 宋体">这个缩写就是</span>Java<span style="font-family: 宋体">程序开发过程中让人最头痛的问题：</span>Out of Memory<span style="font-family: 宋体">。在很多开发人员的开发过程中，或多或少的都会遇到这类问题，这类问题定位比较困难，往往需要根据经验来判断可能出现问题的代码。原因主要是两个：对象没有被释放（多种情况引起，往往是比较隐蔽的引用导致被</span>Hold<span style="font-family: 宋体">而无法被回收）。另一种就是真的</span>Memory<span style="font-family: 宋体">不够用了，需要增加</span>JVM<span style="font-family: 宋体">的</span>Heap<span style="font-family: 宋体">来满足应用程序的需求。最近有同事发的关于解决</span>OOM<span style="font-family: 宋体">的问题，让我了解了原来</span>OOM<span style="font-family: 宋体">除了在</span>JVM Heap<span style="font-family: 宋体">不够时会发生，在</span>Native Heap<span style="font-family: 宋体">不够的时候也会发生，同时</span>JVM Heap<span style="font-family: 宋体">和</span>Native Heap<span style="font-family: 宋体">存在着相互影响和平衡的关系，因此就仔细的去看了关于</span>OOM<span style="font-family: 宋体">和</span>JVM<span style="font-family: 宋体">配置优化的内容。</span>
<h2><span style="font-size: 12pt; line-height: 173%">OOM</span></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">在其他语言类似于</span>C,Delphi<span style="font-family: 宋体">等等由于内存都是由自己分配和管理，因此内存泄露的问题比较常见，同时也是很头痛的一件事情。而</span>Java<span style="font-family: 宋体">的对象生命周期管理都是</span>JVM<span style="font-family: 宋体">来做的，简化了开发人员的非业务逻辑的处理，但是这种自动管理回收机制也是基于一些规则的，而违背了这些规则的时候，就会造成所谓的&#8220;</span>Memory Leak<span style="font-family: 宋体">&#8221;。</span></p>
<p>OOM(Java Heap)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">错误提示：</span>java.lang.OutOfMemoryError<span style="font-family: 宋体">。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">这类</span>OOM<span style="font-family: 宋体">是由于</span>JVM<span style="font-family: 宋体">分配的给应用的</span>Heap Memory<span style="font-family: 宋体">已经被耗尽，可能是因为应用在高负荷的情况下的却需要很大的内存，因此可以通过修改</span>JVM<span style="font-family: 宋体">参数来增加</span>Java Heap Memory<span style="font-family: 宋体">（不过也不能无限制增加，后面那种</span>OOM<span style="font-family: 宋体">有可能就是因为这个原因而产生）。另一种情况是因为应用程序使用对象或者资源没有释放，导致内存消耗持续增加，最后出现</span>OOM<span style="font-family: 宋体">，这类问题引起的原因往往是应用已不需要的对象还被其他有效对象所引用，那么就无法释放，可能是业务代码逻辑造成的（异常处理不够例如</span>IO<span style="font-family: 宋体">等资源），也可能是对于第三方开源项目中资源释放了解不够导致使用以后资源没有释放（例如</span>JDBC<span style="font-family: 宋体">的</span>ResultSet<span style="font-family: 宋体">等）。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">几个容易出现问题的场景：</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<span style="font-family: 宋体">．应用的缓存或者</span>Collection<span style="font-family: 宋体">：如果应用要缓存</span>Java<span style="font-family: 宋体">对象或者是在一个</span>Collection<span style="font-family: 宋体">中保存对象，那么就要确定是否会有大量的对象存入，要做保护，以防止在大数据量下大量内存被消耗，同时要保证</span>Cache<span style="font-family: 宋体">的大小不会无限制增加。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2<span style="font-family: 宋体">．生命周期较长的对象：尽量简短对象的生命周期，现在采用对象的创建释放代价已经很低，同时作了很好的优化，要比创建一个对象长期反复使用要好。如果能够设置超时的情景下，尽量设置超时。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3<span style="font-family: 宋体">．类似于</span>JDBC<span style="font-family: 宋体">的</span>Connection Pool<span style="font-family: 宋体">，在使用</span>Pool<span style="font-family: 宋体">中的对象以后需要释放并返回，不然就会造成</span>Pool<span style="font-family: 宋体">的不断增大，在其他</span>Pool<span style="font-family: 宋体">中使用也是一样。同样</span>ResultSet<span style="font-family: 宋体">，</span>IO<span style="font-family: 宋体">这类资源的释放都需要注意。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">解决的方法就是查找错误或者是增加</span>Java Heap Memory<span style="font-family: 宋体">。对于此类问题检测工具相当多，这里就不做介绍了。</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>OOM(Native Heap)</p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">错误提示：</span>requested XXXX bytes for ChunkPool::allocate. Out of swap space<span style="font-family: 宋体">。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Native Heap Memory<span style="font-family: 宋体">是</span>JVM<span style="font-family: 宋体">内部使用的</span>Memory<span style="font-family: 宋体">，这部分的</span>Memory<span style="font-family: 宋体">可以通过</span>JDK<span style="font-family: 宋体">提供的</span>JNI<span style="font-family: 宋体">的方式去访问，这部分</span>Memory<span style="font-family: 宋体">效率很高，但是管理需要自己去做，如果没有把握最好不要使用，以防出现内存泄露问题。</span>JVM <span style="font-family: 宋体">使用</span>Native Heap Memory<span style="font-family: 宋体">用来优化代码载入（</span>JTI<span style="font-family: 宋体">代码生成），临时对象空间申请，以及</span>JVM<span style="font-family: 宋体">内部的一些操作。这次同事在压力测试中遇到的问题就是这类</span>OOM<span style="font-family: 宋体">，也就是这类</span>Memory<span style="font-family: 宋体">耗尽。同样这类</span>OOM<span style="font-family: 宋体">产生的问题也是分成正常使用耗尽和无释放资源耗尽两类。无释放资源耗尽很多时候不是程序员自身的原因，可能是引用的第三方包的缺陷，例如很多人遇到的</span>Oracle 9 JDBC<span style="font-family: 宋体">驱动在低版本中有内存泄露的问题。要确定这类问题，就需要去观察</span>Native Heap Memory<span style="font-family: 宋体">的增长和使用情况，在服务器应用起来以后，运行一段时间后</span>JVM<span style="font-family: 宋体">对于</span>Native Heap Memory<span style="font-family: 宋体">的使用会达到一个稳定的阶段，此时可以看看什么操作对于</span>Native Heap Memory<span style="font-family: 宋体">操作频繁，而且使得</span>Native Heap Memory<span style="font-family: 宋体">增长，对于</span>Native Heap Memory<span style="font-family: 宋体">的情况我还没有找到办法去检测，现在能够看到的就是为</span>JVM<span style="font-family: 宋体">启动时候增加</span>-verbose:jni<span style="font-family: 宋体">参数来观察对于</span>Native Heap Memory<span style="font-family: 宋体">的操作。另一种情况就是正常消耗</span>Native Heap Memory<span style="font-family: 宋体">，对于</span>Native Heap Memory<span style="font-family: 宋体">的使用主要取决于</span>JVM<span style="font-family: 宋体">代码生成，线程创建，用于优化的临时代码和对象产生。当正常耗尽</span>Native Heap Memory<span style="font-family: 宋体">时，那么就需要增加</span>Native Heap Memory<span style="font-family: 宋体">，此时就会和我们前面提到增加</span>java Heap Memory<span style="font-family: 宋体">的情况出现矛盾。</span></p>
<p><span style="font-family: 宋体">应用内存组合</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">对于应用来说，可分配的内存受到</span>OS<span style="font-family: 宋体">的限制，不同的</span>OS<span style="font-family: 宋体">对进程所能访问虚拟内存地址区间直接影响对于应用内存的分配，</span>32<span style="font-family: 宋体">位的操作系统通常最大支持</span>4G<span style="font-family: 宋体">的内存寻址，而</span>Linux<span style="font-family: 宋体">一般为</span>3G<span style="font-family: 宋体">，</span>Windows<span style="font-family: 宋体">为</span>2G<span style="font-family: 宋体">。然而这些大小的内存并不会全部给</span>JVM<span style="font-family: 宋体">的</span>Java Heap<span style="font-family: 宋体">使用，它主要会分成三部分：</span>Java Heap<span style="font-family: 宋体">，</span>Native Heap<span style="font-family: 宋体">，载入资源和类库等所占用的内存。那么由此可见，</span>Native Heap<span style="font-family: 宋体">和</span> Java Heap<span style="font-family: 宋体">大小配置是相互制约的，哪一部分分配多了都可能会影响到另外一部分的正常工作，因此如果通过命令行去配置，那么需要确切的了解应用使用情况，否则采用默认配置自动监测会更好的优化应用使用情况。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">同样要注意的就是进程的虚拟内存和机器的实际内存还是有区别的，对于机器来说实际内存以及硬盘提供的虚拟内存都是提供给机器上所有进程使用的，因此在设置</span>JVM<span style="font-family: 宋体">参数时，它的虚拟内存绝对不应该超过实际内存的大小。</span></p>
<p><span style="font-family: 宋体">待续</span>&#8230;&#8230;<br />
</p>
<h2><span style="font-size: 12pt; line-height: 173%"><br />
JVM</span><span style="font-size: 12pt; line-height: 173%; font-family: 黑体">优化配置</span></h2>
<br />
更多内容可访问：<a href="http://blog.csdn.net/cenwenchu79/">http://blog.csdn.net/cenwenchu79/</a><img src ="http://www.blogjava.net/hulizhong/aggbug/286347.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-07-11 09:04 <a href="http://www.blogjava.net/hulizhong/archive/2009/07/11/286347.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>大话之观察者模式</title><link>http://www.blogjava.net/hulizhong/archive/2009/07/07/285785.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 07 Jul 2009 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/07/07/285785.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/285785.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/07/07/285785.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/285785.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/285785.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;大家都知道，毛主席对游击战有个十六字诀："敌进我退，敌驻我扰，敌疲我打，敌退我追"。<br />
我对此的理解就是当敌人的形势发生了变化，我们行动也要相应变动。其实在软件开发过程中也有类式情景。当A对象的状态变化的时候，B对象的状态也要相应变化。我们常用观察者模式解决此类问题。代码如下,可能和大家平常见的观察者模式的代码实现不太一样，其实原理都是一样的。<br />
&nbsp;&nbsp;&nbsp;&nbsp;在下面的代码里，我方的情报人员显的很重要。<span style="color: red">即被观察者要有到观察者的引用。</span></p>
<p>&nbsp;public class 敌人 {<br />
&nbsp;private 我 my;</p>
<p>&nbsp;public 敌人() {<br />
&nbsp;&nbsp;my = new 我(); /* 哈哈,敌人内部的我方情报人员 */<br />
&nbsp;}</p>
<p>&nbsp;public void 进() {<br />
&nbsp;&nbsp;System.out.println("--敌进--");<br />
&nbsp;&nbsp;my.退();<br />
&nbsp;}</p>
<p>&nbsp;public void 驻() {<br />
&nbsp;&nbsp;System.out.println("--敌驻--");<br />
&nbsp;&nbsp;my.扰();<br />
&nbsp;}</p>
<p>&nbsp;public void 疲() {<br />
&nbsp;&nbsp;System.out.println("--敌疲--");<br />
&nbsp;&nbsp;my.打();<br />
&nbsp;}</p>
<p>&nbsp;public void 退() {<br />
&nbsp;&nbsp;System.out.println("--敌退--");<br />
&nbsp;&nbsp;my.追();<br />
&nbsp;}<br />
}</p>
<p><br />
&nbsp;public class 我 {<br />
&nbsp;public void 退() {<br />
&nbsp;&nbsp;System.out.println("--我退--");<br />
&nbsp;}</p>
<p>&nbsp;public void 扰() {<br />
&nbsp;&nbsp;System.out.println("--我扰--");<br />
&nbsp;}</p>
<p>&nbsp;public void 打() {<br />
&nbsp;&nbsp;System.out.println("--我打--");<br />
&nbsp;}</p>
<p>&nbsp;public void 追() {<br />
&nbsp;&nbsp;System.out.println("--我追--");<br />
&nbsp;}<br />
}</p>
<p>&nbsp;</p><img src ="http://www.blogjava.net/hulizhong/aggbug/285785.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-07-07 12:34 <a href="http://www.blogjava.net/hulizhong/archive/2009/07/07/285785.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入浅出单实例Singleton设计模式</title><link>http://www.blogjava.net/hulizhong/archive/2009/07/06/285685.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Mon, 06 Jul 2009 06:25:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/07/06/285685.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/285685.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/07/06/285685.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/285685.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/285685.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/haoel/archive/2009/03/26/4028232.aspx<br />
<br />
<pre style="text-align: center;"><strong><span style="font-size: large;">深入浅出单实例Singleton设计模式</span></strong></pre>
<p style="text-align: center;"><strong><span style="font-size: small;">陈皓</span></strong></p>
<h1>前序</h1>
<p>单实例Singleton设计模式可能是被讨论和使用的最广泛的一个设计模式了，这可能也是面试中问得最多的一个设计模式了。这个设计模式主要目的
是想在整个系统中只能出现一个类的实例。这样做当然是有必然的，比如你的软件的全局配置信息，或者是一个Factory，或是一个主控类，等等。你希望这
个类在整个系统中只能出现一个实例。当然，作为一个技术负责人的你，你当然有权利通过使用非技术的手段来达到你的目的。比如：你在团队内部明文规
定，&#8220;XX类只能有一个全局实例，如果某人使用两次以上，那么该人将被处于2000元的罚款！&#8221;（呵呵），你当然有权这么做。但是如果你的设计的是东西是
一个类库，或是一个需要提供给用户使用的API，恐怕你的这项规定将会失效。因为，你无权要求别人会那么做。所以，这就是为什么，我们希望通过使用技术的
手段来达成这样一个目的的原因。</p>
<p>本文会带着你深入整个Singleton的世界，当然，我会放弃使用C++语言而改用Java语言，因为使用Java这个语言可能更容易让我说明一些事情。</p>
<h1><br />
Singleton的教学版本</h1>
<p>这里，我将直接给出一个Singleton的简单实现，因为我相信你已经有这方面的一些基础了。我们姑且把这具版本叫做1.0版</p>
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span class="comment"><span style="color: #008200;">//&nbsp;version&nbsp;1.0 </span></span>&nbsp;&nbsp;</li>
    <li><span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;Singleton &nbsp;&nbsp;</li>
    <li class="alt">{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">final</span></strong></span>&nbsp;Singleton&nbsp;singleton&nbsp;=&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>; &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;Singleton() &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;Singleton&nbsp;getInstance() &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">if</span></strong></span>&nbsp;(singleton==&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>) &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton=&nbsp;<span class="keyword"><strong><span style="color: #006699;">new</span></strong></span>&nbsp;Singleton(); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;singleton; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">}&nbsp;&nbsp;
    <pre name="code_lighted" class="java" style="display: none;">&nbsp;</pre>
    </li>
</ol>
</div>
<p>在上面的实例中，我想说明下面几个Singleton的特点：（下面这些东西可能是尽人皆知的，没有什么新鲜的）</p>
<ol>
    <li>私有（private）的构造函数，表明这个类是不可能形成实例了。这主要是怕这个类会有多个实例。 </li>
    <li>即然这个类是不可能形成实例，那么，我们需要一个静态的方式让其形成实例：getInstance()。注意这个方法是在new自己，因为其可以访问私有的构造函数，所以他是可以保证实例被创建出来的。 </li>
    <li>在getInstance()中，先做判断是否已形成实例，如果已形成则直接返回，否则创建实例。 </li>
    <li>所形成的实例保存在自己类中的私有成员中。 </li>
    <li>我们取实例时，只需要使用Singleton.getInstance()就行了。 </li>
</ol>
<p>当然，如果你觉得知道了上面这些事情后就学成了，那我给你当头棒喝一下了，事情远远没有那么简单。</p>
<h1>Singleton的实际版本</h1>
<p>上面的这个程序存在比较严重的问题，因为是全局性的实例，所以，在多线程情况下，所有的全局共享的东西都会变得非常的危险，这个也一样，在多线程情
况下，如果多个线程同时调用getInstance()的话，那么，可能会有多个进程同时通过 (singleton==
null)的条件检查，于是，多个实例就创建出来，并且很可能造成内存泄露问题。嗯，熟悉多线程的你一定会说——&#8220;我们需要线程互斥或同步&#8221;，没错，我们
需要这个事情，于是我们的Singleton升级成1.1版，如下所示：</p>
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span class="comment"><span style="color: #008200;">//&nbsp;version&nbsp;1.1 </span></span>&nbsp;&nbsp;</li>
    <li><span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;Singleton &nbsp;&nbsp;</li>
    <li class="alt">{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">final</span></strong></span>&nbsp;Singleton&nbsp;singleton&nbsp;=&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>; &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;Singleton() &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;Singleton&nbsp;getInstance() &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">if</span></strong></span>&nbsp;(singleton==&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>) &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">synchronized</span></strong></span>&nbsp;(Singleton.<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>)&nbsp;{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton=&nbsp;<span class="keyword"><strong><span style="color: #006699;">new</span></strong></span>&nbsp;Singleton(); &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;singleton; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">}&nbsp;&nbsp;</li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&nbsp;</pre>
<p>嗯，使用了Java的synchronized方法，看起来不错哦。应该没有问题了吧？！错！这还是有问题！为什么呢？前面已经说过，如果有多个线
程同时通过(singleton==
null)的条件检查（因为他们并行运行），虽然我们的synchronized方法会帮助我们同步所有的线程，让我们并行线程变成串行的一个一个去
new，那不还是一样的吗？同样会出现很多实例。嗯，确实如此！看来，还得把那个判断(singleton==
null)条件也同步起来。于是，我们的Singleton再次升级成1.2版本，如下所示：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">&nbsp;</div>
</div>
<ol class="dp-j">
    <li class="alt"><span class="comment"><span style="color: #008200;">//&nbsp;version&nbsp;1.2 </span></span>&nbsp;&nbsp;</li>
    <li><span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;Singleton &nbsp;&nbsp;</li>
    <li class="alt">{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">final</span></strong></span>&nbsp;Singleton&nbsp;singleton&nbsp;=&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>; &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;Singleton() &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;Singleton&nbsp;getInstance() &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">synchronized</span></strong></span>&nbsp;(Singleton.<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>) &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">if</span></strong></span>&nbsp;(singleton==&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>) &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton=&nbsp;<span class="keyword"><strong><span style="color: #006699;">new</span></strong></span>&nbsp;Singleton(); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;singleton; &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&nbsp;</pre>
<p>不错不错，看似很不错了。在多线程下应该没有什么问题了，不是吗？的确是这样的，1.2版的Singleton在多线程下的确没有问题了，因为我们
同步了所有的线程。只不过嘛&#8230;&#8230;，什么？！还不行？！是的，还是有点小问题，我们本来只是想让new这个操作并行就可以了，现在，只要是进入
getInstance()的线程都得同步啊，注意，创建对象的动作只有一次，后面的动作全是读取那个成员变量，这些读取的动作不需要线程同步啊。这样的
作法感觉非常极端啊，为了一个初始化的创建动作，居然让我们达上了所有的读操作，严重影响后续的性能啊！</p>
<p>还得改！嗯，看来，在线程同步前还得加一个(singleton== null)的条件判断，如果对象已经创建了，那么就不需要线程的同步了。OK，下面是1.3版的Singleton。</p>
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span class="comment"><span style="color: #008200;">//&nbsp;version&nbsp;1.3 </span></span>&nbsp;&nbsp;</li>
    <li><span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;Singleton &nbsp;&nbsp;</li>
    <li class="alt">{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">final</span></strong></span>&nbsp;Singleton&nbsp;singleton&nbsp;=&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>; &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">private</span></strong></span>&nbsp;Singleton() &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;Singleton&nbsp;getInstance() &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">if</span></strong></span>&nbsp;(singleton==&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>) &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">synchronized</span></strong></span>&nbsp;(Singleton.<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>) &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">if</span></strong></span>&nbsp;(singleton==&nbsp;<span class="keyword"><strong><span style="color: #006699;">null</span></strong></span>) &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton=&nbsp;<span class="keyword"><strong><span style="color: #006699;">new</span></strong></span>&nbsp;Singleton(); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;singleton; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">}&nbsp;&nbsp;</li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&nbsp;</pre>
<p>感觉代码开始变得有点罗嗦和复杂了，不过，这可能是最不错的一个版本了，这个版本又叫&#8220;双重检查&#8221;Double-Check。下面是说明：</p>
<ol>
    <li>第一个条件是说，如果实例创建了，那就不需要同步了，直接返回就好了。 </li>
    <li>不然，我们就开始同步线程。 </li>
    <li>第二个条件是说，如果被同步的线程中，有一个线程创建了对象，那么别的线程就不用再创建了。 </li>
</ol>
<p>相当不错啊，干得非常漂亮！请大家为我们的1.3版起立鼓掌！</p>
<h1>Singleton的其它问题</h1>
<p>怎么？还有问题？！当然还有，请记住下面这条规则——&#8220;<strong>无论你的代码写得有多好，其只能在特定的范围内工作，超出这个范围就要出Bug了</strong>&#8221;，这是&#8220;陈式第一定理&#8221;，呵呵。你能想一想还有什么情况会让这个我们上面的代码出问题吗？</p>
<p>在C++下，我不是很好举例，但是在Java的环境下，嘿嘿，还是让我们来看看下面的一些反例和一些别的事情的讨论（<strong>当然，有些反例可能属于钻牛角尖，可能有点学院派，不过也不排除其实际可能性，就算是提个醒吧</strong>）：</p>
<p><strong>其一、Class Loader</strong>。不知道你对Java的Class
Loader熟悉吗？&#8220;类装载器&#8221;？！C++可没有这个东西啊。这是Java动态性的核心。顾名思义，类装载器是用来把类(class)装载进JVM的。
JVM规范定义了两种类型的类装载器：启动内装载器(bootstrap)和用户自定义装载器(user-defined class
loader)。
在一个JVM中可能存在多个ClassLoader，每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一个
class对象类型的实例，但是不同的ClassLoader可能拥有相同的class对象实例，这时可能产生致命的问题。如ClassLoaderA，
装载了类A的类型实例A1，而ClassLoaderB，也装载了类A的对象实例A2。逻辑上讲A1=A2，但是由于A1和A2来自于不同的
ClassLoader，它们实际上是完全不同的，如果A中定义了一个静态变量c，则c在不同的ClassLoader中的值是不同的。</p>
<p>于是，如果咱们的Singleton 1.3版本如果面对着多个Class Loader会怎么样？呵呵，多个实例同样会被多个Class
Loader创建出来，当然，这个有点牵强，不过他确实存在。难道我们还要整出个1.4版吗？可是，我们怎么可能在我的Singleton类中操作
Class Loader啊？是的，你根本不可能。在这种情况下，你能做的只有是——&#8220;保证多个Class
Loader不会装载同一个Singleton&#8221;。</p>
<p><strong>其二、序例化。</strong>如果我们的这个Singleton类是一个关于我们程序配置信息的类。我们需要它有序列化的功能，那么，当反序列化的时候，我们将无法控制别人不多次反序列化。不过，我们可以利用一下Serializable接口的readResolve()方法，比如：</p>
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;Singleton&nbsp;<span class="keyword"><strong><span style="color: #006699;">implements</span></strong></span>&nbsp;Serializable &nbsp;&nbsp;</li>
    <li>{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;...... &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;...... &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">protected</span></strong></span>&nbsp;Object&nbsp;readResolve() &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;getInstance(); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">}&nbsp;&nbsp;</li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&nbsp;</pre>
<p><strong>其三、多个Java虚拟机。</strong>如果我们的程序运行在多个Java的虚拟机中。什么？多个虚拟机？这是一种什么样的情况啊。嗯，这种情况是有点极端，不过还是可能出现，比如EJB或RMI之流的东西。要在这种环境下避免多实例，看来只能通过良好的设计或非技术来解决了。</p>
<p><strong>其四，volatile变量。</strong>关于volatile这个关键字所声明的变量可以被看作是一种
&#8220;程度较轻的同步synchronized&#8221;；与 synchronized 块相比，volatile
变量所需的编码较少，并且运行时开销也较少，但是它所能实现的功能也仅是synchronized的一部分。当然，如前面所述，我们需要的
Singleton只是在创建的时候线程同步，而后面的读取则不需要同步。所以，volatile变量并不能帮助我们即能解决问题，又有好的性能。而且，
这种变量只能在JDK 1.5+版后才能使用。</p>
<p><strong>其五、关于继承。</strong>是的，继承于Singleton后的子类也有可能造成多实例的问题。不过，因为我们早把Singleton的构造函数声明成了私有的，所以也就杜绝了继承这种事情。</p>
<p><strong>其六，关于代码重用。</strong>也话我们的系统中有很多个类需要用到这个模式，如果我们在每一个类都中有这样的代码，那么
就显得有点傻了。那么，我们是否可以使用一种方法，把这具模式抽象出去？在C++下这是很容易的，因为有模板和友元，还支持栈上分配内存，所以比较容易一
些（程序如下所示），Java下可能比较复杂一些，聪明的你知道怎么做吗？</p>
<div class="dp-highlighter">
<ol class="dp-cpp">
    <li class="alt"><span class="keyword"><strong><span style="color: #006699;">template</span></strong></span>&lt;CLASS&nbsp;T&gt;&nbsp;<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;Singleton &nbsp;&nbsp;</li>
    <li>{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>: &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;T&amp;&nbsp;Instance() &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">static</span></strong></span>&nbsp;T&nbsp;theSingleInstance;&nbsp;<span class="comment"><span style="color: #008200;">//假设T有一个protected默认构造函数 </span></span>&nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;theSingleInstance; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</li>
    <li class="alt">}; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li class="alt"><span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;OnlyOne&nbsp;:&nbsp;<span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>&nbsp;Singleton&lt;ONLYONE&gt; &nbsp;&nbsp;</li>
    <li>{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">friend</span></strong></span>&nbsp;<span class="keyword"><strong><span style="color: #006699;">class</span></strong></span>&nbsp;Singleton&lt;ONLYONE&gt;; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="datatypes">int</span>&nbsp;example_data; &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">public</span></strong></span>: &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="datatypes">int</span>&nbsp;GetExampleData()&nbsp;<span class="keyword"><strong><span style="color: #006699;">const</span></strong></span>&nbsp;{<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;example_data;} &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">protected</span></strong></span>: &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnlyOne():&nbsp;example_data(42)&nbsp;{}&nbsp;&nbsp;&nbsp;<span class="comment"><span style="color: #008200;">//&nbsp;默认构造函数 </span></span>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnlyOne(OnlyOne&amp;)&nbsp;{} &nbsp;&nbsp;</li>
    <li class="alt">}; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li class="alt"><span class="datatypes">int</span>&nbsp;main(&nbsp;) &nbsp;&nbsp;</li>
    <li>{ &nbsp;&nbsp;</li>
    <li class="alt">&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;OnlyOne::Instance().GetExampleData()&lt;&lt;&nbsp;endl; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword"><strong><span style="color: #006699;">return</span></strong></span>&nbsp;0; &nbsp;&nbsp;</li>
    <li class="alt">}&nbsp;&nbsp;</li>
</ol>
</div>
<pre name="code_lighted" class="c++" style="display: none;">本文同时发表于——酷壳：<a href="http://cocre.com/?p=265">http://cocre.com/?p=265</a></pre>
<p>&nbsp;<strong><span style="font-family: Times New Roman;">(</span></strong><strong><span style="font-family: 宋体;" lang="ZH-CN">转载时请注明作者和出处。未经许可，请勿用于商业用途</span><span style="font-family: Times New Roman;">)</span></strong></p>
<p>（全文完）</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/285685.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-07-06 14:25 <a href="http://www.blogjava.net/hulizhong/archive/2009/07/06/285685.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解析Java对象的equals()和hashCode()的使用</title><link>http://www.blogjava.net/hulizhong/archive/2009/06/22/283505.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Mon, 22 Jun 2009 01:46:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/06/22/283505.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/283505.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/06/22/283505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/283505.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/283505.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/RichardSundusky/archive/2007/02/12/1508028.aspx<br />
<p>解析Java对象的equals()和hashCode()的使用 </p>
<p>前言 </p>
<p>在Java语言中，equals()和hashCode()两个函数的使用是紧密配合的，你要是自己设计其中一个，就要设计另外一个。在多数情况 下，这两个函数是不用考虑的，直接使用它们的默认设计就可以了。但是在一些情况下，这两个函数最好是自己设计，才能确保整个程序的正常运行。最常见的是当 一个对象被加入收集对象（collection object）时，这两个函数必须自己设计。更细化的定义是：如果你想将一个对象A放入另一个收集对象B里，或者使用这个对象A为查找一个元对象在收集对 象B里位置的钥匙，并支持是否容纳，删除收集对象B里的元对象这样的操作，那么，equals()和hashCode()函数必须开发者自己定义。其他情 况下，这两个函数是不需要定义的。</p>
<p>equals(): <br />
它是用于进行两个对象的比较的，是对象内容的比较，当然也能用于进行对象参阅值的比较。什么是对象参阅值的比较？就是两个参阅变量的值得比较，我们 都知道参阅变量的值其实就是一个数字，这个数字可以看成是鉴别不同对象的代号。两个对象参阅值的比较，就是两个数字的比较，两个代号的比较。这种比较是默 认的对象比较方式，在Object这个对象中，这种方式就已经设计好了。所以你也不用自己来重写，浪费不必要的时间。</p>
<p>对象内容的比较才是设计equals()的真正目的，Java语言对equals()的要求如下，这些要求是必须遵循的。否则，你就不该浪费时间： <br />
对称性：如果x.equals(y)返回是&#8220;true&#8221;，那么y.equals(x)也应该返回是&#8220;true&#8221;。 <br />
反射性：x.equals(x)必须返回是&#8220;true&#8221;。 <br />
类推性：如果x.equals(y)返回是&#8220;true&#8221;，而且y.equals(z)返回是&#8220;true&#8221;，那么z.equals(x)也应该返回是&#8220;true&#8221;。 <br />
还有一致性：如果x.equals(y)返回是&#8220;true&#8221;，只要x和y内容一直不变，不管你重复x.equals(y)多少次，返回都是&#8220;true&#8221;。 <br />
任何情况下，x.equals(null)，永远返回是&#8220;false&#8221;；x.equals(和x不同类型的对象)永远返回是&#8220;false&#8221;。 <br />
hashCode():<br />
这 个函数返回的就是一个用来进行赫希操作的整型代号，请不要把这个代号和前面所说的参阅变量所代表的代号弄混了。后者不仅仅是个代号还具有在内存中才查找对 象的位置的功能。hashCode()所返回的值是用来分类对象在一些特定的收集对象中的位置。这些对象是HashMap, Hashtable, HashSet，等等。这个函数和上面的equals()函数必须自己设计，用来协助HashMap, Hashtable, HashSet，等等对自己所收集的大量对象进行搜寻和定位。</p>
<p>这些收集对象究竟如何工作的，想象每个元对象hashCode是一个箱子的 编码，按照编码，每个元对象就是根据hashCode()提供的代号归入相应的箱子里。所有的箱子加起来就是一个HashSet，HashMap，或 Hashtable对象，我们需要寻找一个元对象时，先看它的代码，就是hashCode()返回的整型值，这样我们找到它所在的箱子，然后在箱子里，每 个元对象都拿出来一个个和我们要找的对象进行对比，如果两个对象的内容相等，我们的搜寻也就结束。这种操作需要两个重要的信息，一是对象的 hashCode()，还有一个是对象内容对比的结果。</p>
<p>hashCode()的返回值和equals()的关系如下：</p>
<p>如果x.equals(y)返回&#8220;true&#8221;，那么x和y的hashCode()必须相等。 <br />
如果x.equals(y)返回&#8220;false&#8221;，那么x和y的hashCode()有可能相等，也有可能不等。 </p>
<p>为什么这两个规则是这样的，原因其实很简单，拿HashSet来说吧，HashSet可以拥有一个或更多的箱子，在同一个箱子中可以有一个 或更多的独特元对象（HashSet所容纳的必须是独特的元对象）。这个例子说明一个元对象可以和其他不同的元对象拥有相同的hashCode。但是一个 元对象只能和拥有同样内容的元对象相等。所以这两个规则必须成立。</p>
<p>设计这两个函数所要注意到的：<br />
如果你设计的对象类型并不使用于收集性对象，那么没有必要自己再设计这两个函数的处理方式。这是正确的面向对象设计方法，任何用户一时用不到的功能，就先不要设计，以免给日后功能扩展带来麻烦。</p>
<p>如果你在设计时想别出心裁，不遵守以上的两套规则，那么劝你还是不要做这样想入非非的事。我还没有遇到过哪一个开发者和我说设计这两个函数要违背前面说的两个规则，我碰到这些违反规则的情况时，都是作为设计错误处理。</p>
<p>当一个对象类型作为收集型对象的元对象时，这个对象应该拥有自己处理equals()，和/或处理hashCode()的设计，而且要遵守前面所说 的两种原则。equals()先要查null和是否是同一类型。查同一类型是为了避免出现ClassCastException这样的异常给丢出来。查 null是为了避免出现NullPointerException这样的异常给丢出来。</p>
<p>如果你的对象里面容纳的数据过多，那么这两个函数 equals()和hashCode()将会变得效率低。如果对象中拥有无法serialized的数据，equals()有可能在操作中出现错误。想象 一个对象x，它的一个整型数据是transient型（不能被serialize成二进制数据流）。然而equals()和hashCode()都有依靠 这个整型数据，那么，这个对象在serialization之前和之后，是否一样？答案是不一样。因为serialization之前的整型数据是有效的 数据，在serialization之后，这个整型数据的值并没有存储下来，再重新由二进制数据流转换成对象后，两者（对象在serialization 之前和之后）的状态已经不同了。这也是要注意的。</p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/RichardSundusky/archive/2007/02/12/1508028.aspx</p><img src ="http://www.blogjava.net/hulizhong/aggbug/283505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-06-22 09:46 <a href="http://www.blogjava.net/hulizhong/archive/2009/06/22/283505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何设置柱图的纵坐标数值格式(显示整数,小数,百分比)</title><link>http://www.blogjava.net/hulizhong/archive/2009/06/19/283185.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 19 Jun 2009 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/06/19/283185.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/283185.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/06/19/283185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/283185.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/283185.html</trackback:ping><description><![CDATA[<p>转 http://zhaoyl.javaeye.com/blog/200021<strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CategoryPlot categoryplot = (CategoryPlot)chart.getPlot();&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="hilite1"><span class="hilite1"><span class="hilite1">NumberAxis</span></span></span> <span class="hilite1"><span class="hilite1"><span class="hilite1">numberaxis</span></span></span> = (<span class="hilite1"><span class="hilite1"><span class="hilite1">NumberAxis</span></span></span>)categoryplot.getRangeAxis();&nbsp; </p>
<p>&nbsp;<span style="color: #0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;decimalFormat = new DecimalFormat("格式");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p><span style="color: #0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="hilite1"><span class="hilite1"><span class="hilite1">numberaxis</span></span></span>.setNumberFormatOverride(decimalFormat);</span></p>
<p>&nbsp;//格式将指定纵坐标的显示格式,可以显示整数,小数,百分比等</p>
<p>比如:</p>
<p>&nbsp;0.00&nbsp; 显示如:56.89</p>
<p>&nbsp;0.00%&nbsp; 显示如:56.89%</p>
<p>&nbsp;0&nbsp; 显示如57</p>
<p>&nbsp;0% 显示如57%</strong></p><img src ="http://www.blogjava.net/hulizhong/aggbug/283185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-06-19 10:44 <a href="http://www.blogjava.net/hulizhong/archive/2009/06/19/283185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RequestDispatcher mechanism</title><link>http://www.blogjava.net/hulizhong/archive/2009/05/05/268944.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 05 May 2009 03:59:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/05/05/268944.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/268944.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/05/05/268944.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/268944.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/268944.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; 关于<tt class="filename">RequestDispatcher</tt>机制的文章，只看懂个大概，那位E文好，给翻译一下！<br />
&nbsp;&nbsp; <br />
<p><tt class="filename">RequestDispatcher</tt> defines an object that receives
requests from the client and sends them to any resource (such as a servlet, HTML
file, or JSP file) on the server. The servlet container creates the <tt class="filename">RequestDispatcher</tt> object, which is used as a wrapper around
a server resource located at a particular path or given by a particular name.
</p>
<p>&nbsp;</p>
<p>An object implementing the <tt class="filename">RequestDispatcher</tt>
interface may be obtained via the following methods: </p>
<div class="itemizedlist">
<ul type="disc">
    <li>
    <p><tt class="function">ServletContext.getRequestDispatcher(String path)</tt> </p>
    </li>
    <li>
    <p><tt class="function">ServletContext.getNamedDispatcher(String name)</tt> </p>
    </li>
    <li>
    <p><tt class="function">ServletRequest.getRequestDispatcher(String path)</tt>
    </p>
    </li>
</ul>
</div>
<p>&nbsp;</p>
<p>The <tt class="function">ServletContext.getRequestDispatcher</tt> method takes
a <tt class="filename">String</tt> argument describing a path within the scope of
the <tt class="filename">ServletContext</tt>. This path must be relative to the
root of the <tt class="filename">ServletContext</tt> and begin with a <tt class="filename">'/'</tt>. The method uses the path to look up a servlet, using
the servlet path matching rules, wraps it with a <tt class="filename">RequestDispatcher</tt> object, and returns the resulting object.
If no servlet can be resolved based on the given path, a <tt class="filename">RequestDispatcher</tt> is provided that returns the content for
that path. </p>
<p>The <tt class="function">ServletContext.getNamedDispatcher</tt> method takes a
<tt class="filename">String</tt> argument indicating the NAME of a servlet known
to the <tt class="filename">ServletContext</tt>. If a servlet is found, it is
wrapped with a <tt class="function">RequestDispatcher</tt> object and the object
is returned. If no servlet is associated with the given name, the method must
return <tt class="filename">null</tt>. </p>
<p>To allow <tt class="filename">RequestDispatcher</tt> objects to be obtained
using relative paths that are relative to the path of the current request (not
relative to the root of the <tt class="filename">ServletContext</tt>), the <tt class="function">ServletRequest.getRequestDispatcher</tt> method is provided in
the <tt class="filename">ServletRequest</tt> interface. The behavior of this
method is similar to the method of the same name in the <tt class="filename">ServletContext</tt>. The servlet container uses information in
the request object to transform the given relative path against the current
servlet to a complete path. For example, in a context rooted at <tt class="filename">'/'</tt> and a request to <tt class="filename">/garden/tools.html</tt>, a request dispatcher obtained via <tt class="function">ServletRequest.getRequestDispatcher("header.html")</tt> will
behave exactly like a call to <tt class="function">ServletContext.getRequestDispatcher("/garden/header.html")</tt>.
</p>
<p><span class="bold"><strong><tt class="filename">RequestDispatcher</tt> creation and
using.</strong></span> </p>
<p>&nbsp;</p>
<pre class="programlisting">public class Dispatcher extends HttpServlet {<br />
public void doGet(HttpServletRequest req, HttpServletResponse res) {<br />
RequestDispatcher dispatcher =<br />
request.getRequestDispatcher("/template.jsp");<br />
if (dispatcher != null) dispatcher.forward(request, response);<br />
}<br />
}<br />
</pre>
<p><tt class="function">forward</tt> should be called before the response has been
committed to the client (before response body output has been flushed). If the
response already has been committed, this method throws an <tt class="filename">IllegalStateException</tt>. Uncommitted output in the response
buffer is automatically cleared before the forward. </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre class="programlisting">public class Dispatcher extends HttpServlet {<br />
public void doGet(HttpServletRequest req, HttpServletResponse res) {<br />
RequestDispatcher dispatcher =<br />
getServletContext().getRequestDispatcher("/banner");<br />
if (dispatcher != null) dispatcher.include(request, response);<br />
}<br />
}<br />
</pre>
<p>Includes the content of a resource (servlet, JSP page, HTML file) in the
response. In essence, this method enables programmatic server-side includes. The
<tt class="filename">ServletResponse</tt> object has its path elements and
parameters remain unchanged from the caller's. The included servlet cannot
change the response status code or set headers; any attempt to make a change is
ignored. </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre class="programlisting">package javax.servlet;<br />
public interface RequestDispatcher {<br />
public void forward(ServletRequest request, ServletResponse response)<br />
throws ServletException, java.io.IOException;<br />
public void include(ServletRequest request, ServletResponse response)<br />
throws ServletException, java.io.IOException;<br />
}<br />
</pre>
<p>&nbsp;</p>
<p>The <tt class="function">include</tt> method of the <tt class="filename">RequestDispatcher</tt> interface may be called at ANY time. The
target servlet of the include method has access to all aspects of the request
object, but its use of the response object is more limited. It can only write
information to the <tt class="filename">ServletOutputStream</tt> or <tt class="filename">Writer</tt> of the response object and commit a response by
writing content past the end of the response buffer, or by explicitly calling
the <tt class="function">flushBuffer</tt> method of the <tt class="filename">ServletResponse</tt> interface. It CANNOT set headers or call any
method that affects the headers of the response. Any attempt to do so must be
ignored. </p>
<p>The <tt class="function">forward</tt> method of the <tt class="filename">RequestDispatcher</tt> interface may be called by the calling
servlet ONLY when NO output has been committed to the client. If output data
exists in the response buffer that has not been committed, the content must be
cleared before the target servlet's service method is called. If the response
has been committed, an <tt class="filename">IllegalStateException</tt> must be
thrown. </p>
<p>The path elements of the request object exposed to the target servlet must
reflect the path used to obtain the <tt class="filename">RequestDispatcher</tt>.
The only exception to this is if the <tt class="filename">RequestDispatcher</tt>
was obtained via the <tt class="function">getNamedDispatcher</tt> method. In this
case, the path elements of the request object must reflect those of the original
request. Before the <tt class="function">forward</tt> method of the <tt class="filename">RequestDispatcher</tt> interface returns, the response content
MUST be sent and committed, and closed by the servlet container. </p>
<p>The <tt class="filename">ServletContext</tt> and <tt class="filename">ServletRequest</tt> methods that create <tt class="filename">RequestDispatcher</tt> objects using path information allow the
optional attachment of query string information to the path. For example, a
Developer may obtain a <tt class="filename">RequestDispatcher</tt> by using the
following code: </p>
<pre class="programlisting">String path = "/raisins.jsp?orderno=5";<br />
RequestDispatcher rd = context.getRequestDispatcher(path);<br />
rd.include(request, response);<br />
</pre>
<p>Parameters specified in the query string used to create the <tt class="filename">RequestDispatcher</tt> take precedence over other parameters of
the same name passed to the included servlet. The parameters associated with a
<tt class="filename">RequestDispatcher</tt> are scoped to apply only for the
duration of the <tt class="function">include</tt> or <tt class="function">forward</tt> call. </p>
<p>&nbsp;</p>
<p><span class="bold"><strong>Additional request-scoped attributes.</strong></span> </p>
<p>Except for servlets obtained by using the <tt class="function">getNamedDispatcher</tt> method, a servlet that has been invoked
by another servlet using the <tt class="function">include</tt> method of <tt class="filename">RequestDispatcher</tt> has access to the path by which it was
invoked. </p>
<p>The following request attributes must be set: </p>
<div class="itemizedlist">
<ul type="disc">
    <li>
    <p><tt class="filename">javax.servlet.include.request_uri</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.include.context_path</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.include.servlet_path</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.include.path_info</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.include.query_string</tt>
    </p>
    </li>
</ul>
</div>
<p>&nbsp;</p>
<p>These attributes are accessible from the included servlet via the <tt class="function">getAttribute</tt> method on the request object and their values
must be equal to the request URI, context path, servlet path, path info, and
query string of the INCLUDED servlet, respectively. If the request is
subsequently included, these attributes are replaced for that include. </p>
<p>If the included servlet was obtained by using the <tt class="function">getNamedDispatcher</tt> method, these attributes MUST NOT be set.
</p>
<p>Except for servlets obtained by using the <tt class="function">getNamedDispatcher</tt> method, a servlet that has been invoked
by another servlet using the <tt class="function">forward</tt> method of <tt class="filename">RequestDispatcher</tt> has access to the path of the ORIGINAL
request. </p>
<p>The following request attributes must be set: </p>
<div class="itemizedlist">
<ul type="disc">
    <li>
    <p><tt class="filename">javax.servlet.forward.request_uri</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.forward.context_path</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.forward.servlet_path</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.forward.path_info</tt> </p>
    </li>
    <li>
    <p><tt class="filename">javax.servlet.forward.query_string</tt>
    </p>
    </li>
</ul>
</div>
<p>&nbsp;</p>
<p>The values of these attributes must be equal to the return values of the <tt class="filename">HttpServletRequest</tt> methods <tt class="function">getRequestURI</tt>, <tt class="function">getContextPath</tt>, <tt class="function">getServletPath</tt>, <tt class="function">getPathInfo</tt>, <tt class="function">getQueryString</tt> respectively, invoked on the request object
passed to the first servlet object in the call chain that received the request
from the client. </p>
<p>These attributes are accessible from the forwarded servlet via the <tt class="function">getAttribute</tt> method on the request object. Note that these
attributes must always reflect the information in the original request even
under the situation that multiple forwards and subsequent includes are called.
</p>
<p>If the forwarded servlet was obtained by using the <tt class="function">getNamedDispatcher</tt> method, these attributes must not be set.
</p><img src ="http://www.blogjava.net/hulizhong/aggbug/268944.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-05-05 11:59 <a href="http://www.blogjava.net/hulizhong/archive/2009/05/05/268944.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 利用JExcelApi来动态生成excel文档 </title><link>http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 28 Apr 2009 01:02:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267825.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267825.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267825.html</trackback:ping><description><![CDATA[转 http://www.blogjava.net/eamoi/archive/2005/01/14/299.html<br />
<br />
<p>首先，请到<a href="http://www.andykhan.com/jexcelapi/index.html">http://www.andykhan.com/jexcelapi/index.html</a>下载java excel api，主页上同时有比较详细的介绍。最新版本为2.4.3，同时也可以到：<a href="http://www.andykhan.com/jexcelapi/jexcelapi_2_4_3.tar.gz">http://www.andykhan.com/jexcelapi/jexcelapi_2_4_3.tar.gz</a>下载到该最新版的API，由于该项目是开源的，所以下载的文件中已经包含了源代码，同样的，文件中也有javadoc，大家在开发中可以参考javadoc。</p>
<p>下载完毕后，我们需要把文件中的jxl.jar加入到你的开发classpath中。<br />
下图是现在要生产的excel截图：<br />
<a href="http://blog.csdn.net/beming/gallery/image/3437.aspx">http://blog.csdn.net/beming/gallery/image/3437.aspx</a><br />
</p>
<p>代码如下：</p>
<p>&nbsp;&nbsp;&nbsp;File excel = new File("d:/aming.xls");<br />
&nbsp;&nbsp;&nbsp;if(!excel.exists()){<br />
&nbsp;&nbsp;&nbsp;&nbsp;excel.createNewFile();<br />
&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;WritableWorkbook wwb = Workbook.createWorkbook(excel);<br />
&nbsp;&nbsp;&nbsp;WritableSheet ws = wwb.createSheet("testexcel",0);<br />
&nbsp;&nbsp;&nbsp;Label lable = null;<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//对中文的支持非常好<br />
&nbsp;&nbsp;&nbsp;lable = new Label(0,0,"我的中国心");<br />
&nbsp;&nbsp;&nbsp;ws.addCell(lable);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//可以定义模板格式化你的cell<br />
&nbsp;&nbsp;&nbsp;WritableFont
wf = new
WritableFont(WritableFont.ARIAL,10,WritableFont.NO_BOLD,false,UnderlineStyle.NO_UNDERLINE,
Colour.BLACK);<br />
&nbsp;&nbsp;&nbsp;WritableCellFormat wcf = new WritableCellFormat(wf);<br />
&nbsp;&nbsp;&nbsp;wcf.setBackground(Colour.WHITE);<br />
&nbsp;&nbsp;&nbsp;lable = new Label(0,1,"fdsl",wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(lable);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;wf = new WritableFont(WritableFont.TIMES,18,WritableFont.BOLD,true);<br />
&nbsp;&nbsp;&nbsp;wcf = new WritableCellFormat(wf);<br />
&nbsp;&nbsp;&nbsp;lable = new Label(0,2,"aming",wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(lable);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//cell的类型同样可以定义为数字类型<br />
&nbsp;&nbsp;&nbsp;Number nb = new Number(0,3,21.4321321);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(nb);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//支持格式化你的数字串<br />
&nbsp;&nbsp;&nbsp;NumberFormat nf = new NumberFormat("#.###");<br />
&nbsp;&nbsp;&nbsp;wcf = new WritableCellFormat(nf);<br />
&nbsp;&nbsp;&nbsp;nb = new Number(0,4,21.43254354354354,wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(nb);</p>
<p>&nbsp;&nbsp;&nbsp;//cell的类型可以为boolean类型<br />
&nbsp;&nbsp;&nbsp;Boolean bl = new Boolean(0,5,true);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(bl);</p>
<p>&nbsp;&nbsp;&nbsp;//cell的类型同样可以为日期，时间<br />
&nbsp;&nbsp;&nbsp;DateTime dt = new DateTime(0,6,new Date());<br />
&nbsp;&nbsp;&nbsp;ws.addCell(dt);</p>
<p>&nbsp;&nbsp;&nbsp;//并且可以很好格式化你的日期格式<br />
&nbsp;&nbsp;&nbsp;DateFormat df = new DateFormat("MM dd yyyy hh:mm:ss");<br />
&nbsp;&nbsp;&nbsp;wcf = new WritableCellFormat(df);<br />
&nbsp;&nbsp;&nbsp;dt = new DateTime(0,7,new Date(),wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(dt);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//开始写文件了<br />
&nbsp;&nbsp;&nbsp;wwb.write();<br />
&nbsp;&nbsp;&nbsp;wwb.close();<br />
<br />
上面的下载地址无法打开.<br />
下载请到:http://prdownloads.sourceforge.net/jexcelapi</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-28 09:02 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 web.xml配置详细说明(下)</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 24 Apr 2009 09:17:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267404.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267404.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267404.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/fenglibing/archive/2009/03/19/4005449.aspx<br />
<br />
<p>5.2 分配JSP初始化参数 <br />
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。 <br />
1）使用jsp-file而不是servlet-class。因此，WEB-INF/web.xml文件的servlet元素如下所示：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;...&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;...&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>... &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;
&lt;init-param&gt;
&lt;param-name&gt;...&lt;/param-name&gt;
&lt;param-value&gt;...&lt;/param-value&gt;
&lt;/init-param&gt;
...
&lt;/servlet&gt;</pre>
<p>2)
几乎总是分配一个明确的URL模式。对servlet，一般相应地使用以http://host/webAppPrefix/servlet/
开始的缺省URL。只需记住，使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如，在上面给出的例子中，可用URL
http://host/webAppPrefix/servlet/PageName
访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时，许多用户似乎不喜欢应用常规的servlet的URL。此外，如果
JSP页面位于服务器为其提供了目录清单的目录中（如，一个既没有index.html也没有index.jsp文件的目录），则用户可能会连接到此
JSP页面，单击它，从而意外地激活未初始化的页面。因此，好的办法是使用url-pattern（5.3节）将JSP页面的原URL与注册的
servlet名相关联。这样，客户机可使用JSP页面的普通名称，但仍然激活定制的版本。例如，给定来自项目1的servlet定义，可使用下面的
servlet-mapping定义：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/RealPage.jsp&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet-mapping&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;url-pattern&gt;/RealPage.jsp&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<p>3）JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此，使用JSP声明提供一个init方法是不合法的，必须制定jspInit方法。 <br />
为
了说明初始化JSP页面的过程，程序清单5-9给出了一个名为InitPage.jsp的JSP页面，它包含一个jspInit方法且放置于
deployDemo Web应用层次结构的顶层。一般，http://host/deployDemo/InitPage.jsp
形式的URL将激活此页面的不具有初始化参数访问权的版本，从而将对firstName和emailAddress变量显示null。但是，
web.xml文件（程序清单5-10）分配了一个注册名，然后将该注册名与URL模式/InitPage.jsp相关联。 <br />
<br />
程序清单5-9 InitPage.jsp</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!DOCTYPE HTML PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//W3C//DTD HTML 4.0 Transitional//EN"</span></span><span>&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;HTML&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;HEAD&gt;&lt;TITLE&gt;JSP Init Test&lt;/TITLE&gt;&lt;/HEAD&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;BODY BGCOLOR=</span><span class="string"><span style="color: #0000ff;">"#FDF5E6"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;H2&gt;Init Parameters:&lt;/H2&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;UL&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;LI&gt;First name: &lt;%= firstName %&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;LI&gt;Email address: &lt;%= emailAddress %&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/UL&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/BODY&gt;&lt;/HTML&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;%! &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span> String firstName, emailAddress; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> jspInit() { &nbsp;&nbsp;</span> </li>
    <li><span>ServletConfig config = getServletConfig(); &nbsp;&nbsp;</span> </li>
    <li><span>firstName = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"firstName"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>emailAddress = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"emailAddress"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>%&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;
&lt;HTML&gt;
&lt;HEAD&gt;&lt;TITLE&gt;JSP Init Test&lt;/TITLE&gt;&lt;/HEAD&gt;
&lt;BODY BGCOLOR="#FDF5E6"&gt;
&lt;H2&gt;Init Parameters:&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;First name: &lt;%= firstName %&gt;
&lt;LI&gt;Email address: &lt;%= emailAddress %&gt;
&lt;/UL&gt;
&lt;/BODY&gt;&lt;/HTML&gt;
&lt;%!
private String firstName, emailAddress;
public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
%&gt;</pre>
<p><br />
<br />
程序清单5-10 web.xml（说明JSP页面的init参数的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;InitPage&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/InitPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;firstName&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;Bill&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;emailAddress&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;gates</span><span class="annotation"><span style="color: #646464;">@oracle</span></span><span>.com&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; InitPage&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/InitPage.jsp&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&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/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;InitPage&lt;/servlet-name&gt;
&lt;jsp-file&gt;/InitPage.jsp&lt;/jsp-file&gt;
&lt;init-param&gt;
&lt;param-name&gt;firstName&lt;/param-name&gt;
&lt;param-value&gt;Bill&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
&lt;param-name&gt;emailAddress&lt;/param-name&gt;
&lt;param-value&gt;gates@oracle.com&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; InitPage&lt;/servlet-name&gt;
&lt;url-pattern&gt;/InitPage.jsp&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
<br />
5.3 提供应用范围内的初始化参数 <br />
一般，对单个地servlet或JSP页面分配初始化参数。指定的servlet或
JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是，在某些情形下，希望提供可由任意servlet或
JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。 <br />
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素，如下所示： <br />
&lt;context-param&gt; <br />
&lt;param-name&gt;support-email&lt;/param-name&gt; <br />
&lt;param-value&gt;blackhole@mycompany.com&lt;/param-value&gt; <br />
&lt;/context-param&gt; <br />
可
回忆一下，为了保证可移植性，web.xml内的元素必须以正确的次序声明。但这里应该注意，context-param元素必须出现任意与文档有关的元
素（icon、display-name或description）之后及filter、filter-mapping、listener或
servlet元素之前。 <br />
5.4 在服务器启动时装载servlet <br />
假如servlet或JSP页面有一个要花很长时间执行的
init
（servlet）或jspInit（JSP）方法。例如，假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种
情况下，在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此，可利用servlet的load-on-
startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt; &#8230; &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &#8230; &lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &lt;!-- or jsp-file --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;load-on-startup/&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt; &#8230; &lt;/servlet-name&gt;
&lt;servlet-class&gt; &#8230; &lt;/servlet-class&gt; &lt;!-- or jsp-file --&gt;
&lt;load-on-startup/&gt;
&lt;/servlet&gt;</pre>
<p>可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前
装载较少数目的servlet或JSP页面。例如，下面的servlet项（放置在Web应用的WEB-INF目录下的web.xml文件中的web-
app元素内）将指示服务器首先装载和初始化SearchServlet，然后装载和初始化由位于Web应用的result目录中的index.jsp文
件产生的 servlet。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Search&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;myPackage.SearchServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &lt;!-- or jsp-file --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;load-on-startup&gt;</span><span class="number"><span style="color: #c00000;">1</span></span><span>&lt;/load-on-startup&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Results&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;/results/index.jsp&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &lt;!-- or jsp-file --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;load-on-startup&gt;</span><span class="number"><span style="color: #c00000;">2</span></span><span>&lt;/load-on-startup&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Search&lt;/servlet-name&gt;
&lt;servlet-class&gt;myPackage.SearchServlet&lt;/servlet-class&gt; &lt;!-- or jsp-file --&gt;
&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;Results&lt;/servlet-name&gt;
&lt;servlet-class&gt;/results/index.jsp&lt;/servlet-class&gt; &lt;!-- or jsp-file --&gt;
&lt;load-on-startup&gt;2&lt;/load-on-startup&gt;
&lt;/servlet&gt;</pre>
<p><br />
<span style="text-decoration: underline;">6 声明过滤器</span> <br />
<br />
servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器，但为了使用与过滤器有关的元素，必须在web.xml中使用版本2.3的DTD。 <br />
过
滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前，
必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时，执行链中的下一个过滤器。如果没
有其他过滤器，servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权，因此，它们可以查看客户机名、查找
到来的cookie等。为了访问servlet或JSP页面的输出，过滤器可将响应对象包裹在一个替身对象（stand-in
object）中，比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后，过滤器可检查缓冲区，如有必要，就对它
进行修改，然后传送到客户机。 <br />
例如，程序清单5-11帝国难以了一个简单的过滤器，只要访问相关的servlet或JSP页面，它就截取请求并在标准输出上打印一个报告（开发过程中在桌面系统上运行时，大多数服务器都可以使用这个过滤器）。 <br />
<br />
程序清单5-11 ReportFilter.java</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.util.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple filter that prints a report on the standard output </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* whenever the associated servlet or JSP page is accessed. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> ReportFilter </span><span class="keyword"><strong><span style="color: #7f0055;">implements</span></strong></span><span> Filter { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doFilter(ServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>ServletResponse response, &nbsp;&nbsp;</span> </li>
    <li><span>FilterChain chain) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletRequest req = (HttpServletRequest)request; &nbsp;&nbsp;</span> </li>
    <li><span>System.out.println(req.getRemoteHost() + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">" tried to access "</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span>req.getRequestURL() + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">" on "</span></span><span> + </span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span> Date() + </span><span class="string"><span style="color: #0000ff;">"."</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>chain.doFilter(request,response); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> init(FilterConfig config) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException { &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> destroy() {} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
/** Simple filter that prints a report on the standard output
* whenever the associated servlet or JSP page is accessed.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}
public void init(FilterConfig config)
throws ServletException {
}
public void destroy() {}
}</pre>
<p>一旦建立了一个过滤器，可以在web.xml中利用filter元素以及filter-name（任意名称）、file-class（完全限定的类
名）和（可选的）init-params子元素声明它。请注意，元素在web.xml的web-app元素中出现的次序不是任意的；允许服务器（但不是必
需的）强制所需的次序，并且实际中有些服务器也是这样做的。但这里要注意，所有filter元素必须出现在任意filter-mapping元素之前，
filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。 <br />
例如，给定上述的ReportFilter类，可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter（位于moreservlets程序包中）相关联。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;filter&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moresevlets.ReportFilter&lt;/filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;filter&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;
&lt;/filter&gt;</pre>
<p>一旦命名了一个过滤器，可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。 <br />
首
先，可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名（此servlet名必须稍后在相同的
web.xml文件中使用servlet元素声明）关联。例如，下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName
的servlet或JSP页面，就运行名为Reporter的过滤器。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;filter-mapping&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;SomeServletName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;filter-mapping&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;servlet-name&gt;SomeServletName&lt;/servlet-name&gt;
&lt;/filter-mapping&gt;</pre>
<p>其次，可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如，相面的程序片段指示系统只要访问Web应用中的任意URL，就运行名为Reporter的过滤器。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;filter-mapping&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;filter-mapping&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;</pre>
<p>例如，程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字
PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http:
//host/webAppPrefix/UrlTest2/
开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上，程序清单5-
12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项，可看到下面的标准输出形式的调试报告（换行是
为了容易阅读）。 <br />
audit.irs.gov tried to access <br />
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html <br />
on Tue Dec 25 13:12:29 EDT 2001. <br />
<br />
程序清单5-12 Web.xml（说明filter用法的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moresevlets.ReportFilter&lt;/filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; PageName &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&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/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;filter&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;!-- ... --&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;/filter-mapping&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;
&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
7 指定欢迎页 假如用户提供了一个像http: //host/webAppPrefix/directoryName/
这样的包含一个目录名但没有包含文件名的URL，会发生什么事情呢？用户能得到一个目录表？一个错误？还是标准文件的内容？如果得到标准文件内容，是
index.html、index.jsp、default.html、default.htm或别的什么东西呢？
Welcome-file-list
元素及其辅助的welcome-file元素解决了这个模糊的问题。例如，下面的web.xml项指出，如果一个URL给出一个目录名但未给出文件名，服
务器应该首先试用index.jsp，然后再试用index.html。如果两者都没有找到，则结果有赖于所用的服务器（如一个目录列表）。
虽然许多服务器缺省遵循这种行为，但不一定必须这样。因此，明确地使用welcom-file-list保证可移植性是一种良好的习惯。 <br />
</p>
<p>8
指定处理错误的页面
现在我了解到，你在开发servlet和JSP页面时从不会犯错误，而且你的所有页面是那样的清晰，一般的程序员都不会被它们的搞糊涂。但是，是人总会犯
错误的，用户可能会提供不合规定的参数，使用不正确的URL或者不能提供必需的表单字段值。除此之外，其它开发人员可能不那么细心，他们应该有些工具来克
服自己的不足。 error-page元素就是用来克服这些问题的。它有两个可能的子元素，分别是：error-code和exception-
type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个
给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此
URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息，
这两个属性分别是：javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下，在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住，error-page出现在web.xml文件的末尾附
近，servlet、servlet-name和welcome-file-list之后即可。 <br />
</p>
<p>8.1 error-code元素
为了更好地了解error-code元素的值，可考虑一下如果不正确地输入文件名，大多数站点会作出什么反映。这样做一般会出现一个404错误信息，它表
示不能找到该文件，但几乎没提供更多有用的信息。另一方面，可以试一下在www.microsoft.com、www.ibm.com
处或者特别是在www.bea.com
处输出未知的文件名。这是会得出有用的消息，这些消息提供可选择的位置，以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。
事实上rm-error-page子元素）。由form-login-page给出的HTML表单必须具有一个j_security_check的
ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如，程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令，并且失败的登陆将
由相同目录中名为login-error.jsp的页面报告。 程序清单5-19 web.xml（说明login-config的摘录） <br />
</p>
<p>9.2
限制对Web资源的访问 现在，可以指示服务器使用何种验证方法了。"了不起，"你说道，"除非我能指定一个来收到保护的
URL，否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在
web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素，分别是：web-resource-collection、
auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。 l
web-resource-collection
此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元
素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的
HTTP命令（GET、POST等，缺省为所有方法）的http-method元素和一个提供资料的可选description元素组成。例如，下面的
Web-resource-collection项（在security-constratint元素内）指出Web应用的proprietary目录中
所有文档应该受到保护。 重要的是应该注意到，url-pattern仅适用于直接访问这些资源的客户机。特别是，它不适合于通过MVC体系结构利用
RequestDispatcher来访问的页面，或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好
处。例如，servlet可利用MVC体系结构查找数据，把它放到bean中，发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直
接访问受保护的JSP页面，而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素
可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是，这种不匀称的行为可能让开发人员放松警惕，使他们偶然对应受保护的资源提供不受限制的
访问。 l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护，但是auth-constraint元素却指出哪些用户应该具有
受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role-
name元素，以及包含（可选）一个描述角色的description元素。例如，下面web.xml中的security-constraint元素部
门规定只有指定为Administrator或Big Kahuna（或两者）的用户具有指定资源的访问权。
重要的是认识到，到此为止，这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令，完全有赖于具体的系统。
例如，Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联，正如下面例子中所示，它指出
用户joe（口令bigshot）和jane（口令enaj）属于administrator和kahuna角色。 l
user-data-constraint
这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素（合法值为NONE、
INTEGRAL或CONFIDENTIAL），并且可选地包含一个description元素。transport-guarantee为NONE值将
对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上（并且在未来的HTTP版本中），在
INTEGRAL和CONFIDENTIAL之间可能会有差别，但在当前实践中，他们都只是简单地要求用SSL。例如，下面指示服务器只允许对相关资源做
HTTPS连接： l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。 9.3 分配角色名
迄今为止，讨论已经集中到完全由容器（服务器）处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例如，容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面，但只允许bigwig用户修改此页面的参数。完成这
种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法，并据此修改访问。 Servlet的
security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如，假如编写了一个调用
request.isUserInRole（"boss"）的servlet，但后来该servlet被用在了一个其口令文件调用角色manager而不
是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级
IDE容易处理安全信息。 10 控制会话超时
如果某个会话在一定的时间内未被访问，服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直
接设置个别会话对象的超时值。如果不采用这种方法，则缺省的超时值由具体的服务器决定。但可利用session-config和session-
timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟，因此，下面的例子设置缺省会话超时值为三个小时（180分钟）。</p>
<p>
11 Web应用的文档化 越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder
Enterprise Edition、Macromedia UltraDev、Allaire JRun
Studio（写此文时，已被Macromedia收购）以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的，而且还是为可视开发环境设计的。它们包括icon、display-name和discription
等。
可回忆一下，在web.xml内以适当地次序声明web-app子元素很重要。不过，这里只要记住icon、display-name和
description是web.xml的web-app元素内的前三个合法元素即可。 l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x
16的GIF或JPEG图像，用large-icon元素指定一幅32 x 32的图像。下面举一个例子： l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。 l description
description元素提供解释性文本，如下所示： 12 关联文件与MIME类型
服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如，将会自动给予名为mom.jpg的文件一个image/jpeg的
MIME
类型。但是，假如你的Web应用具有几个不寻常的文件，你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素（具有
extension和mime-type子元素）可提供这种保证。例如，下面的代码指示服务器将application/x-fubar的MIME类型分
配给所有以.foo结尾的文件。
或许，你的Web应用希望重载（override）标准的映射。例如，下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本
（text/plain）而不是作为PostScript（application/postscript）。</p>
<p> 13 定位TLD JSP
taglib元素具有一个必要的uri属性，它给出一个TLD（Tag Library
Descriptor）文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变，但我们希望避免更改所有现有JSP页
面。此外，可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两
个子元素：taglib-uri和taglib-location。taglib-uri元素应该与用于JSP
taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如，假如你将文件chart-tags-
1.3beta.tld放在WebApp/WEB-INF/tlds中。现在，假如web.xml在web-app元素内包含下列内容。
给出这个说明后，JSP页面可通过下面的简化形式使用标签库。 14 指定应用事件监听程序
应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向
Web应用注册一个监听程序的web.xml的用法。
注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内，listener-class元
素列出监听程序的完整的限定类名，如下所示： &lt;listener&gt;
虽然listener元素的结构很简单，但请不要忘记，必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的
servlet
元素之前以及所有filter-mapping元素之后。此外，因为应用生存期监听程序是serlvet规范的2.3版本中的新内容，所以必须使用
web.xml DTD的2.3版本，而不是2.2版本。
例如，程序清单5-20给出一个名为ContextReporter的简单的监听程序，只要Web应用的Servlet-Context建立（如装载
Web应用）或消除（如服务器关闭）时，它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。
程序清单5-20 ContextReporterjava 程序清单5-21 web.xml（声明一个监听程序的摘录） 15 J2EE元素
本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍，详细内容可以参阅
http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform
Enterprise Edition版本1.3规范的第5章。 l distributable distributable
元素指出，Web应用是以这样的方式编程的：即，支持集群的服务器可安全地在多个服务器上分布Web应用。例如，一个可分布的应用必须只使用
Serializable对象作为其HttpSession对象的属性，而且必须避免用实例变量（字段）来实现持续性。distributable元素直
接出现在discription元素之后，并且不包含子元素或数据，它只是一个如下的标志。 l resource-env-ref resource
-env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref-
name元素（一个相对于java:comp/env环境的JNDI名）以及一个resource-env-type元素（指定资源类型的完全限定的
类），如下所示： l env-entry env
-entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素（一个相对于java:
comp/env环境JNDI名）、一个env-entry-value元素（项值）以及一个env-entry-type元素（java.lang程序
包中一个类型的完全限定类名，java.lang.Boolean、java.lang.String等）组成。下面是一个例子： l ejb-ref
ejb
-ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素（相对于java:
comp/env的EJB应用）、一个ejb-ref-type元素（bean的类型，Entity或Session）、一个home元素（bean的主
目录接口的完全限定名）、一个remote元素（bean的远程接口的完全限定名）以及一个可选的ejb-link元素（当前bean链接的另一个
bean的名称）组成。 l ejb-local-ref
ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外，此元素具有与ejb-ref元素相同的属
性并以相同的方式使用。</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267404.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-24 17:17 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转  web.xml配置详细说明(中)</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 24 Apr 2009 09:15:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267403.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267403.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267403.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/fenglibing/archive/2009/03/19/4005446.aspx<br />
<br />
3.2 定义定制的URL <br />
大多数服务器具有一个缺省的serlvet URL： <br />
http://host
/webAppPrefix/servlet/packageName.ServletName。虽然在开发中使用这个URL很方便，但是我们常常会希望
另一个URL用于部署。例如，可能会需要一个出现在Web应用顶层的URL（如，http:
//host/webAppPrefix/Anyname），并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外，对
许多开发人员来说，顶层URL看上去比更长更麻烦的缺省URL更简短。 <br />
事实上，有时需要使用定制的URL。比如，你可能想关闭缺省URL映射，以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL，那么你怎样访问servlet呢？这时只有使用定制的URL了。 <br />
为
了分配一个定制的URL，可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet-
name元素提供了一个任意名称，可利用此名称引用相应的servlet；url-pattern描述了相对于Web应用的根目录的URL。url-
pattern元素的值必须以斜杠（/）起始。 <br />
下面给出一个简单的web.xml摘录，它允许使用URL http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test或 <br />
http:
//host/webAppPrefix/servlet/moreservlets.TestServlet。请注意，仍然需要XML头、
DOCTYPE声明以及web-app封闭元素。此外，可回忆一下，XML元素出现地次序不是随意的。特别是，需要把所有servlet元素放在所有
servlet-mapping元素之前。
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.TestServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/UrlTest&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;url-pattern&gt;/UrlTest&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<p>URL模式还可以包含通配符。例如，下面的小程序指示服务器发送所有以Web应用的URL前缀开始，以..asp结束的请求到名为BashMS的servlet。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;msUtils.ASPTranslator&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/*.asp&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;
&lt;servlet-class&gt;msUtils.ASPTranslator&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;
&lt;url-pattern&gt;/*.asp&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<p><br />
3.3 命名JSP页面 <br />
因为JSP页面要转换成sevlet，自然希望就像命名servlet一样命名JSP页面。毕竟，JSP
页面可能会从初始化参数、安全设置或定制的URL中受益，正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的，但
存在一个关键的猜疑：即，你不知道JSP页面的实际类名（因为系统自己挑选这个名字）。因此，为了命名JSP页面，可将jsp-file元素替换为
servlet-calss元素，如下所示：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;
&lt;/servlet&gt;</pre>
<p>命名JSP页面的原因与命名servlet的原因完全相同：即为了提供一个与定制设置（如，初始化参数和安全设置）一起使用的名称，并且，以便能更
改激活
JSP页面的URL（比方说，以便多个URL通过相同页面得以处理，或者从URL中去掉.jsp扩展名）。但是，在设置初始化参数时，应该注意，JSP页
面是利用jspInit方法，而不是init方法读取初始化参数的。 <br />
例如，程序清单5-3给出一个名为TestPage.jsp的简单JSP
页面，它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配
一个注册名PageName，然后将此注册名与http://host/webAppPrefix/UrlTest2/anything
形式的URL相关联的web.xml文件（即，deployDemo/WEB-INF/web.xml）的一部分。 <br />
<br />
程序清单5-3 TestPage.jsp</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!DOCTYPE HTML PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//W3C//DTD HTML 4.0 Transitional//EN"</span></span><span>&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;HTML&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;HEAD&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;TITLE&gt; &nbsp;&nbsp;</span> </li>
    <li><span>JSP Test Page &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/TITLE&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/HEAD&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;BODY BGCOLOR=</span><span class="string"><span style="color: #0000ff;">"#FDF5E6"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;H2&gt;URI: &lt;%= request.getRequestURI() %&gt;&lt;/H2&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/BODY&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/HTML&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;
JSP Test Page
&lt;/TITLE&gt;
&lt;/HEAD&gt;
&lt;BODY BGCOLOR="#FDF5E6"&gt;
&lt;H2&gt;URI: &lt;%= request.getRequestURI() %&gt;&lt;/H2&gt;
&lt;/BODY&gt;
&lt;/HTML&gt;</pre>
<p><br />
<br />
程序清单5-4 web.xml（说明JSP页命名的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; PageName &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&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/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;
&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
<br />
4 禁止激活器servlet <br />
<br />
对servlet或JSP页面建立定制URL的一个原因是，这样做可以注册从
init（servlet）或jspInit（JSP页面）方法中读取得初始化参数。但是，初始化参数只在是利用定制URL模式或注册名访问
servlet或JSP页面时可以使用，用缺省URL http://host/webAppPrefix/servlet/ServletName
访问时不能使用。因此，你可能会希望关闭缺省URL，这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet，因为
多数服务器具有一个用缺省的servlet URL注册的标准servlet，并激活缺省的URL应用的实际servlet。 <br />
有两种禁止此缺省URL的主要方法： <br />
l 在每个Web应用中重新映射/servlet/模式。 <br />
l 全局关闭激活器servlet。 <br />
重
要的是应该注意到，虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多，但重新映射可以用一种完全可移植
的方式来完成。相反，全局禁止激活器servlet完全是针对具体机器的，事实上有的服务器（如ServletExec）没有这样的选择。下面的讨论对每
个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。 <br />
4.1 重新映射/servlet/URL模式 <br />
在
一个特定的Web应用中禁止以http://host/webAppPrefix/servlet/
开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet，并使用前一节讨论的url-pattern元素将所有匹配请求转向该
servlet。只要简单地使用： <br />
&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt; <br />
作为servlet-mapping元素中的模式即可。 <br />
例如，程序清单5-5给出了将SorryServlet servlet（程序清单5-6）与所有以http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。 <br />
<br />
程序清单5-5 web.xml（说明JSP页命名的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Sorry&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.SorryServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; Sorry &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&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/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;Sorry&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.SorryServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; Sorry &lt;/servlet-name&gt;
&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
<br />
程序清单5-6 SorryServlet.java</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple servlet used to give error messages to </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* users who try to access default servlet URLs </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* (i.e., http://host/webAppPrefix/servlet/ServletName) </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* in Web applications that have disabled this </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* behavior. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> SorryServlet </span><span class="keyword"><strong><span style="color: #7f0055;">extends</span></strong></span><span> HttpServlet { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doGet(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>response.setContentType(</span><span class="string"><span style="color: #0000ff;">"text/html"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>PrintWriter out = response.getWriter(); &nbsp;&nbsp;</span> </li>
    <li><span>String title = </span><span class="string"><span style="color: #0000ff;">"Invoker Servlet Disabled."</span></span><span>; &nbsp;&nbsp;</span> </li>
    <li><span>out.println(ServletUtilities.headWithTitle(title) + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;H2&gt;"</span></span><span> + title + </span><span class="string"><span style="color: #0000ff;">"&lt;/H2&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"Sorry, access to servlets by means of\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"URLs that begin with\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://host/webAppPrefix/servlet/\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"has been disabled.\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/BODY&gt;&lt;/HTML&gt;"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doPost(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>doGet(request, response); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e., http://host/webAppPrefix/servlet/ServletName)
* in Web applications that have disabled this
* behavior.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Invoker Servlet Disabled.";
out.println(ServletUtilities.headWithTitle(title) +
"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +
"&lt;H2&gt;" + title + "&lt;/H2&gt;\n" +
"Sorry, access to servlets by means of\n" +
"URLs that begin with\n" +
"http://host/webAppPrefix/servlet/\n" +
"has been disabled.\n" +
"&lt;/BODY&gt;&lt;/HTML&gt;");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}</pre>
<p><br />
4.2 全局禁止激活器：Tomcat <br />
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法： <br />
1．禁止激活器： Tomcat 4 <br />
Tomcat
4用与前面相同的方法关闭激活器servlet，即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在
install_dir/conf中的一个服务器专用的全局web.xml文件，而前面使用的是存放在每个Web应用的WEB-INF目录中的标准
web.xml文件。 <br />
因此，为了在Tomcat 4中关闭激活器servlet，只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可，如下所示：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!-- &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;invoker&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>--&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!--
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;invoker&lt;/servlet-name&gt;
&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
--&gt;</pre>
<p>再次提醒，应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的，此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。 <br />
2．禁止激活器：Tomcat3 <br />
在Apache
Tomcat的版本3中，通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省
servlet URL。例如，下面是禁止使用缺省servlet URL的server.xml文件的一部分。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!-- &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;RequsetInterceptor &nbsp;&nbsp;</span> </li>
    <li><span>className=</span><span class="string"><span style="color: #0000ff;">"org.apache.tomcat.request.InvokerInterceptor"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span>debug=</span><span class="string"><span style="color: #0000ff;">"0"</span></span><span> prefix=</span><span class="string"><span style="color: #0000ff;">"/servlet/"</span></span><span> /&gt; &nbsp;&nbsp;</span> </li>
    <li><span>--&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!--
&lt;RequsetInterceptor
className="org.apache.tomcat.request.InvokerInterceptor"
debug="0" prefix="/servlet/" /&gt;
--&gt;</pre>
<p><br />
5 初始化和预装载servlet与JSP页面 <br />
<br />
这里讨论控制servlet和JSP页面的启动行为的方法。特别是，说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。 <br />
5.1 分配servlet初始化参数 <br />
利
用init-param元素向servlet提供初始化参数，init-param元素具有param-name和param-value子元素。例如，
在下面的例子中，如果initServlet
servlet是利用它的注册名（InitTest）访问的，它将能够从其方法中调用getServletConfig().
getInitParameter("param1")获得"Value
1"，调用getServletConfig().getInitParameter("param2")获得"2"。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.InitServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;param1&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;value1&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;param2&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;</span><span class="number"><span style="color: #c00000;">2</span></span><span>&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.InitServlet&lt;/servlet-class&gt;
&lt;init-param&gt;
&lt;param-name&gt;param1&lt;/param-name&gt;
&lt;param-value&gt;value1&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
&lt;param-name&gt;param2&lt;/param-name&gt;
&lt;param-value&gt;2&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;/servlet&gt;</pre>
<p>在涉及初始化参数时，有几点需要注意： <br />
l 返回值。GetInitParameter的返回值总是一个String。因此，在前一个例子中，可对param2使用Integer.parseInt获得一个int。 <br />
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。 <br />
l
缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此，在这个例子中，param1和
param2初始化参数将能够在使用URL http://host/webAppPrefix/servlet/InitTest
时可用，但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet
时不能使用。 <br />
例如，程序清单5-7给出一个名为InitServlet的简单servlet，它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。 <br />
程序清单5-7 InitServlet.java</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple servlet used to illustrate servlet </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* initialization parameters. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> InitServlet </span><span class="keyword"><strong><span style="color: #7f0055;">extends</span></strong></span><span> HttpServlet { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span> String firstName, emailAddress; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> init() { &nbsp;&nbsp;</span> </li>
    <li><span>ServletConfig config = getServletConfig(); &nbsp;&nbsp;</span> </li>
    <li><span>firstName = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"firstName"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>emailAddress = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"emailAddress"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doGet(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>response.setContentType(</span><span class="string"><span style="color: #0000ff;">"text/html"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>PrintWriter out = response.getWriter(); &nbsp;&nbsp;</span> </li>
    <li><span>String uri = request.getRequestURI(); &nbsp;&nbsp;</span> </li>
    <li><span>out.println(ServletUtilities.headWithTitle(</span><span class="string"><span style="color: #0000ff;">"Init Servlet"</span></span><span>) + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;H2&gt;Init Parameters:&lt;/H2&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;UL&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;LI&gt;First name: "</span></span><span> + firstName + </span><span class="string"><span style="color: #0000ff;">"\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;LI&gt;Email address: "</span></span><span> + emailAddress + </span><span class="string"><span style="color: #0000ff;">"\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/UL&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/BODY&gt;&lt;/HTML&gt;"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to illustrate servlet
* initialization parameters.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class InitServlet extends HttpServlet {
private String firstName, emailAddress;
public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Init Servlet") +
"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +
"&lt;H2&gt;Init Parameters:&lt;/H2&gt;\n" +
"&lt;UL&gt;\n" +
"&lt;LI&gt;First name: " + firstName + "\n" +
"&lt;LI&gt;Email address: " + emailAddress + "\n" +
"&lt;/UL&gt;\n" +
"&lt;/BODY&gt;&lt;/HTML&gt;");
}
}</pre>
<p><br />
程序清单5-8 web.xml（说明初始化参数的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.InitServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;firstName&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;Larry&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;emailAddress&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;Ellison</span><span class="annotation"><span style="color: #646464;">@Microsoft</span></span><span>.com&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
</ol>
</div>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267403.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-24 17:15 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转  web.xml配置详细说明(上)</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 24 Apr 2009 09:13:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267402.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267402.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267402.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/fenglibing/archive/2009/03/19/4005441.aspx<br />
<br />
<p><strong>转自<a href="http://elevenet.javaeye.com/blog/67862">http://elevenet.javaeye.com/blog/67862</a></strong></p>
<p><strong>这篇文章还是不错的，有些老，有些地址还是直接翻译的。不是很准确。</strong></p>
<p><strong>关键字: J2EE</strong></p>
<div class="blog_content">1 定义头和根元素 <br />
<br />
部署描述符文件就像所有XML文件一样，必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。 <br />
DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本（如2.2或2.3）并指定管理此文件其余部分内容的语法的DTD(Document Type Definition，文档类型定义)。 <br />
所有部署描述符文件的顶层（根）元素为web-app。请注意，XML元素不像HTML，他们是大小写敏感的。因此，web-App和WEB-APP都是不合法的，web-app必须用小写。 <br />
<br />
2 部署描述符文件内的元素次序 <br />
<br />
XML
元素不仅是大小写敏感的，而且它们还对出现在其他元素中的次序敏感。例如，XML头必须是文件中的第一项，DOCTYPE声明必须是第二项，而web-
app元素必须是第三项。在web-app元素内，元素的次序也很重要。服务器不一定强制要求这种次序，但它们允许（实际上有些服务器就是这样做的）完全
拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。 <br />
下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如，此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意，所有这些元素都是可选的。因此，可以省略掉某一元素，但不能把它放于不正确的位置。 <br />
l icon&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; icon 元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。 <br />
l display-name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。 <br />
l description&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; description元素给出与此有关的说明性文本。 <br />
l context-param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context-param元素声明应用范围内的初始化参数。 <br />
l filter&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。 <br />
l filter-mapping&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一旦命名了一个过滤器，就要利用filter-mapping元素把它与一个或多个servlet或JSP 页面相关联。 <br />
l listener&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet API的版本2.3增加了对事件监听程序的支持，事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。 <br />
l servlet&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在向servlet或JSP页面制定初始化参数或定制URL时，必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。 <br />
l servlet-mapping&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 服务器一般为servlet提供一个缺省的URL：<a href="http://host/webAppPrefix/servlet/ServletName">http://host/webAppPrefix/servlet/ServletName</a>。但是，常常会更改这个URL，以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时，使用servlet-mapping元素。 <br />
l
session-config&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如果某个会话在一定时间内未被访问，服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法
明确设置单个会话对象的超时值，或者可利用session-config元素制定缺省超时值。 <br />
l mime-mapping&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果Web应用具有想到特殊的文件，希望能保证给他们分配特定的MIME类型，则mime-mapping元素提供这种保证。 <br />
l welcom-file-list&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时，使用哪个文件。 <br />
l error-page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error-page元素使得在返回特定HTTP状态代码时，或者特定类型的异常被抛出时，能够制定将要显示的页面。 <br />
l taglib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; taglib元素对标记库描述符文件（Tag Libraryu Descriptor file）指定别名。此功能使你能够更改TLD文件的位置，而不用编辑使用这些文件的JSP页面。 <br />
l resource-env-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resource-env-ref元素声明与资源相关的一个管理对象。 <br />
l resource-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resource-ref元素声明一个资源工厂使用的外部资源。 <br />
l security-constraint&nbsp;&nbsp;&nbsp; security-constraint元素制定应该保护的URL。它与login-config元素联合使用 <br />
l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。 <br />
l
security-role&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
security-role元素给出安全角色的一个列表，这些角色将出现在servlet元素内的security-role-ref元素的role-
name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。 <br />
l env-entry&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env-entry元素声明Web应用的环境项。 <br />
l ejb-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ejb-ref元素声明一个EJB的主目录的引用。 <br />
l ejb-local-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ejb-local-ref元素声明一个EJB的本地主目录的应用。 <br />
<br />
3 分配名称和定制的UL <br />
<br />
在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称，使用servlet-mapping元素将定制的URL与刚分配的名称相关联。 <br />
3.1 分配名称 <br />
为
了提供初始化参数，对servlet或JSP页面定义一个定制URL或分配一个安全角色，必须首先给servlet或JSP页面一个名称。可通过
servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素（在web-app元素内），如下所示：
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.TestServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;</pre>
这
表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给
servlet一个名称具有两个主要的含义。首先，初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在
URL而不是类名中使用此名称。因此，利用刚才给出的定义，URL http://host/webAppPrefix/servlet/Test
可用于 http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。 <br />
请
记住：XML元素不仅是大小写敏感的，而且定义它们的次序也很重要。例如，web-app元素内所有servlet元素必须位于所有servlet-
mapping元素（下一小节介绍）之前，而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素（如果有的话）之前。类似地，servlet
的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。 <br />
例
如，程序清单5-1给出了一个名为TestServlet的简单servlet，它驻留在moreservlets程序包中。因为此servlet是扎根
在一个名为deployDemo的目录中的Web应用的组成部分，所以TestServlet.class放在deployDemo/WEB-
INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部
分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图
5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。 <br />
<br />
程序清单5-1 TestServlet.java <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple servlet used to illustrate servlet naming </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* and custom URLs. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> TestServlet </span><span class="keyword"><strong><span style="color: #7f0055;">extends</span></strong></span><span> HttpServlet { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doGet(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>response.setContentType(</span><span class="string"><span style="color: #0000ff;">"text/html"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>PrintWriter out = response.getWriter(); &nbsp;&nbsp;</span> </li>
    <li><span>String uri = request.getRequestURI(); &nbsp;&nbsp;</span> </li>
    <li><span>out.println(ServletUtilities.headWithTitle(</span><span class="string"><span style="color: #0000ff;">"Test Servlet"</span></span><span>) + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;H2&gt;URI: "</span></span><span> + uri + </span><span class="string"><span style="color: #0000ff;">"&lt;/H2&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/BODY&gt;&lt;/HTML&gt;"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to illustrate servlet naming
* and custom URLs.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Test Servlet") +
"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +
"&lt;H2&gt;URI: " + uri + "&lt;/H2&gt;\n" +
"&lt;/BODY&gt;&lt;/HTML&gt;");
}
}</pre>
<br />
程序清单5-2 web.xml（说明servlet名称的摘录） <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- &#8230; --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.TestServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- &#8230; --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
</div>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-24 17:13 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 Java Web中的入侵检测及简单实现</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 17 Apr 2009 08:42:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/266178.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/266178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/266178.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/zhaowei001/archive/2007/12/29/2001800.aspx<br />
<br />
作者：<a href="http://www.easyjf.com/">EasyJF开源团队</a> 大峡<br />
<br />
<strong>一、简介</strong><br />
<br />
在Java
Web应用程中，特别是网站开发中，我们有时候需要为应用程序增加一个入侵检测程序来防止恶意刷新的功能，防止非法用户不断的往Web应用中重复发送数
据。当然，入侵检测可以用很多方法实现，包括软件、硬件防火墙，入侵检测的策略也很多。在这里我们主要介绍的是Java
Web应用程序中通过软件的方式实现简单的入侵检测及防御。
<p>　　该方法的实现原理很简单，就是用户访问Web系统时记录每个用户的信息，然后进行对照，并根据设定的策略(比如：1秒钟刷新页面10次)判断用户是否属于恶意刷新。</p>
<p>
我们的入侵检测程序应该放到所有Java Web程序的执行前，也即若发现用户是恶意刷新就不再继续执行Java
Web中的其它部分内容，否则就会失去了意义。这就需要以插件的方式把入侵检测的程序置入Java Web应用中，使得每次用户访问Java
Web，都先要到这个入侵检测程序中报一次到，符合规则才能放行。</p>
<p>　　Java Web应用大致分为两种，一种纯JSP(+Java
Bean)方式，一种是基于框架(如Struts、EasyJWeb等)的。第一种方式的Java Web可以通过Java
Servlet中的Filter接口实现，也即实现一个Filter接口，在其doFilter方法中插入入侵检测程序，然后再web.xml中作简单的
配置即可。在基于框架的Web应用中，由于所有应用都有一个入口，因此可以把入侵检测的程序直接插入框架入口引擎中，使框架本身支持入侵检测功能。当然,
也可以通过实现Filter接口来实现。</p>
<p>　　在EasyJWeb框架中，已经置入了简单入侵检测的程序，因此，这里我们以EasyJWeb框架为例,介绍具体的实现方法及源码，完整的代码可以在EasyJWeb源码中找到。</p>
<p>　　在基于EasyJWeb的Java Web应用中(如<a href="http://www.easyjf.com/bbs/">http://www.easyjf.com/bbs/</a>)，默认情况下你只要连续刷新页面次数过多，即会弹出如下的错误：</p>
<p>　　EasyJWeb框架友情提示!:-): <br />
<font color="#ff0000">　　您对页面的刷新太快,请等待60秒后再刷新页面！ <br />
详细请查询<a href="http://www.easyjf.com/" target="_blank">http://www.easyjf.com</a> </font></p>
<p><br />
<strong>二、用户访问信息记录UserConnect.java类</strong>　　<br />
<br />
这个类是一个简单的Java Bean，主要代表用户的信息，包括用户名、IP、第一次访问时间、最后登录时间、登录次数、用户状态等。全部<br />
<br />
代码如下：<br />
<br />
package com.easyjf.web;</p>
<p>import java.util.Date;<br />
/**<br />
*<br />
* </p>
<p>Title:用户验证信息</p>
<br />
*
<p>Description:记录用户登录信息,判断用户登录情况</p>
<br />
*
<p>Copyright: Copyright (c) 2006</p>
<br />
*
<p>Company: <a href="http://www.easyjf.com%3c/p">www.easyjf.com</a></p>
&gt;<br />
* @author 蔡世友<br />
* @version 1.0<br />
*/<br />
public class UserConnect {<br />
private String userName;<br />
private String ip;<br />
private Date firstFailureTime;<br />
private Date lastLoginTime;<br />
private int failureTimes;//用户登录失败次数<br />
private int status=0;//用户状态0表示正常,-1表示锁定<br />
public int getFailureTimes() {<br />
&nbsp;return failureTimes;<br />
}<br />
public void setFailureTimes(int failureTimes) {<br />
&nbsp;this.failureTimes = failureTimes;<br />
}<br />
public Date getFirstFailureTime() {<br />
&nbsp;return firstFailureTime;<br />
}
<p>public void setFirstFailureTime(Date firstFailureTime) {<br />
&nbsp;this.firstFailureTime = firstFailureTime;<br />
}</p>
<p>public String getIp() {<br />
&nbsp;return ip;<br />
}</p>
<p>public void setIp(String ip) {<br />
&nbsp;this.ip = ip;<br />
}</p>
<p>public Date getLastLoginTime() {<br />
&nbsp;return lastLoginTime;<br />
}</p>
<p>public void setLastLoginTime(Date lastLoginTime) {<br />
&nbsp;this.lastLoginTime = lastLoginTime;<br />
}</p>
<p>public String getUserName() {<br />
&nbsp;return userName;<br />
}</p>
<p>public void setUserName(String userName) {<br />
&nbsp;this.userName = userName;<br />
}</p>
<p>public int getStatus() {<br />
&nbsp;return status;<br />
}</p>
<p>public void setStatus(int status) {<br />
&nbsp;this.status = status;<br />
}</p>
<p>}</p>
<p><br />
<strong>三、监控线程UserConnectManage.java类</strong><br />
<br />
这是入侵检测的核心部分，主要实现具体的入侵检测、记录、判断用户信息、在线用户的刷新等功能，并提供其它应用程序使用本组件的调用接口。<br />
<br />
package com.easyjf.web;</p>
<p>import java.util.Date;<br />
import java.util.HashMap;<br />
import java.util.HashSet;<br />
import java.util.Iterator;<br />
import java.util.Map;<br />
import java.util.Set;</p>
<p>import org.apache.log4j.Logger;<br />
/**<br />
*<br />
* </p>
<p>Title:用户入侵检测信息</p>
<br />
*
<p>Description:用于判断用户刷新情况检查，默认为10秒钟之内连续连接10次为超时</p>
<br />
*
<p>Copyright: Copyright (c) 2006</p>
<br />
*
<p>Company: <a href="http://www.easyjf.com%3c/p">www.easyjf.com</a></p>
&gt;<br />
* @author 蔡世友<br />
* @version 1.0<br />
*/<br />
public class UserConnectManage {<br />
private static final Logger logger = (Logger) Logger.getLogger(UserConnectManage.class.getName());<br />
private static int maxFailureTimes=10;//最大登录失败次数<br />
private static long maxFailureInterval=10000;//毫秒，达到最大登录次数且在这个时间范围内<br />
private static long waitInterval=60000;//失败后接受连接的等待时间，默认1分钟<br />
private static int maxOnlineUser=200;//同时在线的最大数<br />
private final static Map users=new HashMap();//使用ip+userName为key存放用户登录信息UserLoginAuth<br />
private static Thread checkThread=null;<br />
private static class CheckTimeOut implements Runnable{&nbsp;<br />
private Thread parentThread;<br />
public&nbsp; CheckTimeOut(Thread parentThread)&nbsp;<br />
&nbsp;{<br />
&nbsp;&nbsp;this.parentThread=parentThread;&nbsp;<br />
&nbsp;&nbsp;synchronized(this){<br />
&nbsp;&nbsp;if(checkThread==null){&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;checkThread= new Thread(this);<br />
&nbsp;&nbsp;&nbsp;//System.out.println("创建一个新线程！");<br />
&nbsp;&nbsp;&nbsp;checkThread.start();&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;}<br />
&nbsp;}&nbsp;<br />
&nbsp;public void run() {&nbsp;&nbsp;<br />
&nbsp;&nbsp;while(true)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;if(parentThread.isAlive()){<br />
&nbsp;&nbsp;&nbsp;try{<br />
&nbsp;&nbsp;&nbsp;Thread.sleep(2000);<br />
&nbsp;&nbsp;&nbsp;int i=0;<br />
&nbsp;&nbsp;&nbsp;if(users.size()&gt;maxOnlineUser)//当达到最大用户数时清除<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;synchronized(users){//执行删除操作<br />
&nbsp;&nbsp;&nbsp;&nbsp;Iterator it=users.keySet().iterator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Set set=new HashSet();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Date now=new Date();<br />
&nbsp;&nbsp;&nbsp;&nbsp;while(it.hasNext())<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object key=it.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserConnect user=(UserConnect)users.get(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(now.getTime()-user.getFirstFailureTime().getTime()&gt;maxFailureInterval)//删除超时的用户<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set.add(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("删除了一个超时的连接"+i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(i&lt;5)//如果删除少于5个，则强行删除1/2在线记录，牺牲性能的情况下保证内存<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int num=maxOnlineUser/2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;it=users.keySet().iterator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(it.hasNext() &amp;&amp; i<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set.add(it.next());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("删除了一个多余的连接"+i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;users.keySet().removeAll(set);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;logger.info("监视程序运行结束！");&nbsp;<br />
&nbsp;}<br />
}<br />
//通过checkLoginValidate判断是否合法的登录连接，如果合法则继续，非法则执行<br />
public static boolean checkLoginValidate(String ip,String userName)//只检查认证失败次数<br />
{<br />
&nbsp;boolean ret=true;<br />
&nbsp;Date now=new Date();&nbsp;<br />
&nbsp;String key=ip+":"+userName;<br />
&nbsp;UserConnect auth=(UserConnect)users.get(key);<br />
&nbsp;if(auth==null)//把用户当前的访问信息加入到users容器中<br />
&nbsp;{<br />
&nbsp;&nbsp;auth=new UserConnect();<br />
&nbsp;&nbsp;auth.setIp(ip);<br />
&nbsp;&nbsp;auth.setUserName(userName);<br />
&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;&nbsp;users.put(key,auth);&nbsp;&nbsp;<br />
&nbsp;&nbsp;if(checkThread==null)new CheckTimeOut(Thread.currentThread());<br />
&nbsp;}&nbsp;<br />
&nbsp;else<br />
&nbsp;{<br />
&nbsp;&nbsp;if(auth.getFailureTimes()&gt;maxFailureTimes)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果在限定的时间间隔内，则返回拒绝用户连接的信息<br />
&nbsp;&nbsp;&nbsp;if((now.getTime()-auth.getFirstFailureTime().getTime())<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;ret=false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setStatus(-1);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else&nbsp;
if(auth.getStatus()==-1 &amp;&amp;
(now.getTime()-auth.getFirstFailureTime().getTime()&lt;
(maxFailureInterval+waitInterval)))//重置计数器<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;ret=false;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setStatus(0);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;//登录次数加1<br />
&nbsp;&nbsp;auth.setFailureTimes(auth.getFailureTimes()+1);<br />
&nbsp;}<br />
&nbsp;//System.out.println(key+":"+auth.getFailureTimes()+":"+ret+":"+(now.getTime()-auth.getFirstFailureTime().getTime()));<br />
&nbsp;return ret;<br />
}
<p>public static void reset(String ip,String userName)//重置用户信息<br />
{&nbsp;<br />
&nbsp;Date now=new Date();&nbsp;<br />
&nbsp;String key=ip+":"+userName;<br />
&nbsp;UserConnect auth=(UserConnect)users.get(key);<br />
&nbsp;if(auth==null)//把用户当前的访问信息加入到users容器中<br />
&nbsp;{<br />
&nbsp;&nbsp;auth=new UserConnect();<br />
&nbsp;&nbsp;auth.setIp(ip);<br />
&nbsp;&nbsp;auth.setUserName(userName);<br />
&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;&nbsp;users.put(key,auth);<br />
&nbsp;}&nbsp;<br />
&nbsp;else<br />
&nbsp;{<br />
&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;}<br />
}<br />
public static void remove(String ip,String userName)//删除用户在容器中的记录<br />
{<br />
&nbsp;String key=ip+":"+userName;<br />
&nbsp;users.remove(key);<br />
}<br />
public static void clear()//清空容器中内容<br />
{<br />
&nbsp;if(!users.isEmpty())users.clear();<br />
}<br />
public static long getMaxFailureInterval() {<br />
&nbsp;return maxFailureInterval;<br />
}</p>
<p>public static void setMaxFailureInterval(long maxFailureInterval) {<br />
&nbsp;UserConnectManage.maxFailureInterval = maxFailureInterval;<br />
}</p>
<p>public static int getMaxFailureTimes() {<br />
&nbsp;return maxFailureTimes;<br />
}</p>
<p>public static void setMaxFailureTimes(int maxFailureTimes) {<br />
&nbsp;UserConnectManage.maxFailureTimes = maxFailureTimes;<br />
}</p>
<p>public static int getMaxOnlineUser() {<br />
&nbsp;return maxOnlineUser;<br />
}</p>
<p>public static void setMaxOnlineUser(int maxOnlineUser) {<br />
&nbsp;UserConnectManage.maxOnlineUser = maxOnlineUser;<br />
}</p>
<p>public static long getWaitInterval() {<br />
&nbsp;return waitInterval;<br />
}</p>
<p>public static void setWaitInterval(long waitInterval) {<br />
&nbsp;UserConnectManage.waitInterval = waitInterval;<br />
}</p>
<p><br />
<strong>四、调用接口</strong><br />
<br />
在需要进入侵检测判断的地方，直接使用UserConnectManage类中的checkLoginValidate方法即可。如EasyJWeb的核心Servlet　<br />
<br />
com.easyjf.web.ActionServlet中调用UserConnectManage的代码：<br />
&nbsp; &nbsp;if(!UserConnectManage.checkLoginValidate(request.getRemoteAddr(),"guest"))<br />
&nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;info(request,response,new Exception("您对页面的刷新太快,请等待"+UserConnectManage.getWaitInterval()/1000+"秒<br />
<br />
后再刷新页面！"));<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return;<br />
&nbsp; &nbsp; &nbsp; &nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
<br />
<br />
<strong>五、总结</strong><br />
当然，这里提供的方法只是一个简单的实现示例，由于上面的用户信息是直接保存在内存中，若并发用户很大的时候的代码的占用，可以考虑引入数据库来记录用
户的访问信息，当然相应的执行效率肯定用降低。上面介绍的实现中，入侵检测判断的策略也只有用户访问次数及时间间隔两个元素，您还可以根据你的实现情况增
加其它的检测元素。<br />
<br />
由于水平有限，该设计上有N不合理或者需要改进的地方，恳请大家指正！</p>
<br />
<br />
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=657199</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/266178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-17 16:42 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 Java编码规范 </title><link>http://www.blogjava.net/hulizhong/archive/2009/04/03/263754.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 03 Apr 2009 07:09:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/03/263754.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/263754.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/03/263754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/263754.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/263754.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转 http://www.blogjava.net/Werther/archive/2009/04/03/263653.htmlJava编码规范&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; 1 介绍(Introduction) &nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;&nbsp;<a href='http://www.blogjava.net/hulizhong/archive/2009/04/03/263754.html'>阅读全文</a><img src ="http://www.blogjava.net/hulizhong/aggbug/263754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-03 15:09 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/03/263754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>try catch finally一点疑惑</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/01/263310.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 01 Apr 2009 03:54:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/01/263310.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/263310.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/01/263310.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/263310.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/263310.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; 在http://www.cnitblog.com/yemoo/archive/2008/06/18/45850.html文中巧用try finally;但是让我对finally执行过程有点疑惑，发现java的try catch功能和js是一样的。下面列出例子：<br />
&nbsp;&nbsp; public int test1(){<br />
&nbsp;&nbsp; int i=4;<br />
&nbsp;&nbsp; try{ return i;}finally{ i=0;System.out.println("---test----");}<br />
}<br />
&nbsp;&nbsp; 执行结果:输出---test----,test1方法返回4；我的疑惑是为什么不返回0<br />
&nbsp;&nbsp; 在QQ群里讨论的时候，有人说finally中的语句在try中的return后执行。但是如下代码执行否决了上面的结论。<br />
public int test2(){<br />
&nbsp;&nbsp; int i=4;<br />
&nbsp;&nbsp; try{ return i;}finally{ i=0;System.out.println("---test----");return i;}<br />
}<br />
&nbsp;&nbsp; 注意：在finally中多了个return i;<br />
&nbsp;&nbsp; 执行结果：输出---test----,test2方法返回0；<br />
&nbsp;&nbsp; 这个例说明了finally中的语句是在try的return执行前执行的。那么test1方法的finally中i=0了，但是为什么test1方法还返回4呢？这是我的疑惑，那位知道解释一下。<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/263310.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-01 11:54 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/01/263310.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 JAVA上加密算法的实现用例</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/23/261511.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Mon, 23 Mar 2009 06:14:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/23/261511.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261511.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/23/261511.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261511.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261511.html</trackback:ping><description><![CDATA[转 http://www.ibm.com/developerworks/cn/java/l-security/index.html<br />
<br />
<p><a name="1"><span class="atitle">第1章基础知识</span></a></p>
<p>
</p>
<p><a name="N1003D"><span class="smalltitle">1.1. 单钥密码体制</span></a></p>
<p>单钥密码体制是一种传统的加密算法，是指信息的发送方和接收方共同使用同一把密钥进行加解密。</p>
<p>通常,使用的加密算法
比较简便高效,密钥简短，加解密速度快，破译极其困难。但是加密的安全性依靠密钥保管的安全性,在公开的计算机网络上安全地传送和保管密钥是一个严峻的问题，并且如果在多用户的情况下密钥的保管安全性也是一个问题。</p>
<p>单钥密码体制的代表是美国的DES</p>
<p><a name="N1004C"><span class="smalltitle">1.2. 消息摘要</span></a></p>
<p>一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算，产生一个唯一指印（对于SHA1是产生一个20字节的二进制数组）。</p>
<p>消息摘要有两个基本属性：</p>
<ul>
    <li>两个不同的报文难以生成相同的摘要</li>
    <li>
    难以对指定的摘要生成一个报文，而由该报文反推算出该指定的摘要</li>
</ul>
<p>代表：美国国家标准技术研究所的SHA1和麻省理工学院Ronald Rivest提出的MD5
</p>
<p><a name="N10064"><span class="smalltitle">1.3.
Diffie-Hellman密钥一致协议</span></a></p>
<p>密钥一致协议是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。</p>
<p>
先决条件,允许两名用户在公开媒体上交换信息以生成"一致"的,可以共享的密钥</p>
<p>代表：指数密钥一致协议(Exponential Key Agreement Protocol)</p>
<p><a name="N10073"><span class="smalltitle">1.4. 非对称算法与公钥体系</span></a></p>
<p>1976
年，Dittie和Hellman为解决密钥管理问题，在他们的奠基性的工作"密码学的新方向"一文中，提出一种密钥交换协议，允许在不安全的媒体上通过
通讯双方交换信息，安全地传送秘密密钥。在此新思想的基础上，很快出现了非对称密钥密码体制，即公钥密码体制。在公钥体制中，加密密钥不同于解密密钥，加
密密钥公之于众，谁都可以使用；解密密钥只有解密人自己知道。它们分别称为公开密钥（Public
key）和秘密密钥（Private key）。</p>
<p>
迄今为止的所有公钥密码体系中，RSA系统是最著名、最多使用的一种。RSA公开密钥密码系统是由R.Rivest、A.Shamir和L.Adleman俊教授于1977年提出的。RSA的取名就是来自于这三位发明者的姓的第一个字母</p>
<p><a name="N1007F"><span class="smalltitle">1.5. 数字签名</span></a></p>
<p>所
谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据（或称数字指纹）进行RSA算法操作，以保证发信人无法抵赖曾发过该信息（即不可抵赖
性），同时也确保信息报文在经签名后末被篡改（即完整性）。当信息接收者收到报文后，就可以用发送者的公钥对数字签名进行验证。　</p>
<p>
在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数（HASH函数）生成的，对这些HASH函数的特殊要求是：</p>
<ol>
    <li>接受的输入报文数据没有长度限制；</li>
    <li>对任何输入报文数据生成固定长度的摘要（数字指纹）输出</li>
    <li>从报文能方便地算出摘要；</li>
    <li>
    难以对指定的摘要生成一个报文，而由该报文反推算出该指定的摘要；</li>
    <li>两个不同的报文难以生成相同的摘要</li>
</ol>
<p>代表：DSA</p>
<p><a name="2"><span class="atitle">第2章在JAVA中的实现</span></a></p>
<p>
</p>
<p><a name="N100A8"><span class="smalltitle">2.1. 相关</span></a></p>
<p>Diffie-Hellman密钥一致协议和DES程序需要JCE工具库的支持,可以到
<a href="http://java.sun.com/security/index.html">http://java.sun.com/security/index.html</a>
下载JCE,并进行安装。简易安装把 jce1.2.1\lib 下的所有内容复制到
%java_home%\lib\ext下,如果没有ext目录自行建立,再把jce1_2_1.jar和sunjce_provider.jar添加到CLASSPATH内,更详细说明请看相应用户手册
</p>
<p><a name="N100B5"><span class="smalltitle">2.2. 消息摘要MD5和SHA的使用</span></a></p>
<p>使用方法:</p>
<p>首先用生成一个MessageDigest类,确定计算方法</p>
<p>java.security.MessageDigest
alga=java.security.MessageDigest.getInstance("SHA-1");</p>
<p>添加要进行计算摘要的信息</p>
<p>alga.update(myinfo.getBytes());</p>
<p>计算出摘要</p>
<p>byte[] digesta=alga.digest();</p>
<p>发送给其他人你的信息和摘要</p>
<p>其他人用相同的方法初始化,添加信息,最后进行比较摘要是否相同</p>
<p>algb.isEqual(digesta,algb.digest())</p>
<p>相关AIP</p>
<p>java.security.MessageDigest 类</p>
<p>static getInstance(String algorithm)</p>
<p>返回一个MessageDigest对象,它实现指定的算法</p>
<p>参数:算法名,如 SHA-1 或MD5</p>
<p>void update (byte input)</p>
<p>void update (byte[] input)</p>
<p>void update(byte[] input, int offset, int len)</p>
<p>添加要进行计算摘要的信息</p>
<p>byte[] digest()</p>
<p>完成计算,返回计算得到的摘要(对于MD5是16位,SHA是20位)</p>
<p>void reset()</p>
<p>复位</p>
<p>static boolean isEqual(byte[] digesta, byte[] digestb)</p>
<p>比效两个摘要是否相同</p>
<p>代码：</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">import java.security.*;<br />
            public class myDigest {<br />
            public static void main(String[] args)  {<br />
            myDigest my=new myDigest();<br />
            my.testDigest();<br />
            }<br />
            public void testDigest()<br />
            {<br />
            try {<br />
            String myinfo="我的测试信息";<br />
            //java.security.MessageDigest alg=java.security.MessageDigest.getInstance("MD5");<br />
            java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");<br />
            alga.update(myinfo.getBytes());<br />
            byte[] digesta=alga.digest();<br />
            System.out.println("本信息摘要是:"+byte2hex(digesta));<br />
            //通过某中方式传给其他人你的信息(myinfo)和摘要(digesta) 对方可以判断是否更改或传输正常<br />
            java.security.MessageDigest algb=java.security.MessageDigest.getInstance("SHA-1");<br />
            algb.update(myinfo.getBytes());<br />
            if (algb.isEqual(digesta,algb.digest())) {<br />
            System.out.println("信息检查正常");<br />
            }<br />
            else<br />
            {<br />
            System.out.println("摘要不相同");<br />
            }<br />
            }<br />
            catch (java.security.NoSuchAlgorithmException ex) {<br />
            System.out.println("非法摘要算法");<br />
            }<br />
            }<br />
            public String byte2hex(byte[] b) //二行制转字符串<br />
            {<br />
            String hs="";<br />
            String stmp="";<br />
            for (int n=0;n&lt;b.length;n++)<br />
            {<br />
            stmp=(java.lang.Integer.toHexString(b[n] &amp; 0XFF));<br />
            if (stmp.length()==1) hs=hs+"0"+stmp;<br />
            else hs=hs+stmp;<br />
            if (n&lt;b.length-1)  hs=hs+":";<br />
            }<br />
            return hs.toUpperCase();<br />
            }<br />
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N1010D"><span class="smalltitle">2.3. 数字签名DSA</span></a></p>
<p>
</p>
<ol>
    <li>对于一个用户来讲首先要生成他的密钥对,并且分别保存
    <p>生成一个KeyPairGenerator实例</p>
    <table border="0" width="100%" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td class="code-outline">
                <pre class="displaycode">   java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance("DSA");<br />
                <!-- code sample is too wide -->    如果设定随机产生器就用如相代码初始化<br />
                SecureRandom secrand=new SecureRandom();<br />
                secrand.setSeed("tttt".getBytes()); //初始化随机产生器<br />
                keygen.initialize(512,secrand);     //初始化密钥生成器<br />
                否则<br />
                keygen.initialize(512);<br />
                生成密钥公钥pubkey和私钥prikey<br />
                KeyPair keys=keygen.generateKeyPair(); //生成密钥组<br />
                PublicKey pubkey=keys.getPublic();<br />
                PrivateKey prikey=keys.getPrivate();<br />
                分别保存在myprikey.dat和mypubkey.dat中,以便下次不在生成<br />
                (生成密钥对的时间比较长<br />
                java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));<br />
                <!-- code sample is too wide -->     out.writeObject(prikey);<br />
                out.close();<br />
                out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));<br />
                <!-- code sample is too wide -->     out.writeObject(pubkey);<br />
                out.close();<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <br />
    </li>
    <li>
    用他私人密钥(prikey)对他所确认的信息(info)进行数字签名产生一个签名数组
    <p>从文件中读入私人密钥(prikey)</p>
    <table border="0" width="100%" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td class="code-outline">
                <pre class="displaycode">   java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));<br />
                <!-- code sample is too wide -->    PrivateKey myprikey=(PrivateKey)in.readObject();<br />
                in.close();<br />
                初始一个Signature对象,并用私钥对信息签名<br />
                java.security.Signature signet=java.security.Signature.getInstance("DSA");<br />
                signet.initSign(myprikey);<br />
                signet.update(myinfo.getBytes());<br />
                byte[] signed=signet.sign();<br />
                把信息和签名保存在一个文件中(myinfo.dat)<br />
                java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));<br />
                <!-- code sample is too wide -->      out.writeObject(myinfo);<br />
                out.writeObject(signed);<br />
                out.close();<br />
                把他的公钥的信息及签名发给其它用户<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <br />
    </li>
    <li>
    其他用户用他的公共密钥(pubkey)和签名(signed)和信息(info)进行验证是否由他签名的信息
    <p>读入公钥
    <br />
    <code>java.io.ObjectInputStream in=new
    java.io.ObjectInputStream(new
    java.io.FileInputStream("mypubkey.dat"));
    <br />
    PublicKey pubkey=(PublicKey)in.readObject();
    <br />
    in.close();
    </code>
    </p>
    <p>读入签名和信息
    <br />
    <code>in=new java.io.ObjectInputStream(new
    java.io.FileInputStream("myinfo.dat"));
    <br />
    String info=(String)in.readObject();
    <br />
    byte[] signed=(byte[])in.readObject();
    <br />
    in.close();
    </code>
    </p>
    <p>初始一个Signature对象,并用公钥和签名进行验证
    <br />
    <code>java.security.Signature
    signetcheck=java.security.Signature.getInstance("DSA");
    <br />
    signetcheck.initVerify(pubkey);
    <br />
    signetcheck.update(info.getBytes());
    <br />
    if (signetcheck.verify(signed)) {
    System.out.println("签名正常");}
    </code>
    </p>
    <p>
    对于密钥的保存本文是用对象流的方式保存和传送的,也可可以用编码的方式保存.注意要
    <br />
    <code>import java.security.spec.*
    <br />
    import java.security.*
    </code>
    </p>
    <p>具休说明如下</p>
    <ul>
        <li>public key是用X.509编码的,例码如下:
        <table border="0" width="100%" cellpadding="0" cellspacing="0">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode">  byte[] bobEncodedPubKey=mypublic.getEncoded(); //生成编码<br />
                    //传送二进制编码<br />
                    //以下代码转换编码为相应key对象<br />
                    X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);<br />
                    KeyFactory keyFactory = KeyFactory.getInstance("DSA");<br />
                    PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);<br />
                    </pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br />
        <br />
        </li>
        <li>对于Private key是用PKCS#8编码,例码如下:
        <table border="0" width="100%" cellpadding="0" cellspacing="0">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode"> byte[] bPKCS=myprikey.getEncoded();<br />
                    //传送二进制编码<br />
                    //以下代码转换编码为相应key对象<br />
                    PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);<br />
                    KeyFactory keyf=KeyFactory.getInstance("DSA");<br />
                    PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);<br />
                    </pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br />
        </li>
    </ul>
    <br />
    </li>
    <li>常用API
    <p>java.security.KeyPairGenerator 密钥生成器类
    <br />
    public static KeyPairGenerator getInstance(String algorithm)
    throws NoSuchAlgorithmException
    <br />
    以指定的算法返回一个KeyPairGenerator 对象
    <br />
    参数: algorithm 算法名.如:"DSA","RSA"
    </p>
    <p>public void initialize(int keysize)
    </p>
    <p>
    以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置
    </p>
    <p>参数:keysize 算法位长.其范围必须在 512 到 1024 之间，且必须为 64
    的倍数</p>
    <p>public void initialize(int keysize, SecureRandom random)
    <br />
    以指定的长度初始化和随机发生器初始化KeyPairGenerator对象
    <br />
    参数:keysize 算法位长.其范围必须在 512 到 1024 之间，且必须为 64
    的倍数
    <br />
    random 一个随机位的来源(对于initialize(int
    keysize)使用了默认随机器
    </p>
    <p>public abstract KeyPair generateKeyPair()
    <br />
    产生新密钥对
    </p>
    <p>java.security.KeyPair 密钥对类
    <br />
    public PrivateKey getPrivate()
    <br />
    返回私钥
    </p>
    <p>public PublicKey getPublic()
    <br />
    返回公钥
    </p>
    <p>java.security.Signature 签名类
    <br />
    public static Signature getInstance(String algorithm) throws
    NoSuchAlgorithmException
    <br />
    返回一个指定算法的Signature对象
    <br />
    参数 algorithm 如:"DSA"
    </p>
    <p>public final void initSign(PrivateKey privateKey)
    <br />
    throws InvalidKeyException
    <br />
    用指定的私钥初始化
    <br />
    参数:privateKey 所进行签名时用的私钥
    </p>
    <p>public final void update(byte data)
    <br />
    throws SignatureException
    <br />
    public final void update(byte[] data)
    <br />
    throws SignatureException
    <br />
    public final void update(byte[] data, int off, int len)
    <br />
    throws SignatureException
    <br />
    添加要签名的信息
    </p>
    <p>public final byte[] sign()
    <br />
    throws SignatureException
    <br />
    返回签名的数组,前提是initSign和update
    </p>
    <p>public final void initVerify(PublicKey publicKey)
    <br />
    throws InvalidKeyException
    <br />
    用指定的公钥初始化
    <br />
    参数:publicKey 验证时用的公钥
    </p>
    <p>public final boolean verify(byte[] signature)
    <br />
    throws SignatureException
    <br />
    验证签名是否有效,前提是已经initVerify初始化
    <br />
    参数: signature 签名数组
    </p>
    <table border="0" width="100%" cellpadding="0" cellspacing="0">
        <tbody>
            <tr>
                <td class="code-outline">
                <pre class="displaycode"> */<br />
                import java.security.*;<br />
                import java.security.spec.*;<br />
                public class testdsa {<br />
                public static void main(String[] args) throws java.security.NoSuchAlgorithmException,java.lang.Exception {<br />
                <!-- code sample is too wide -->        testdsa my=new testdsa();<br />
                my.run();<br />
                }<br />
                public void run()<br />
                {<br />
                //数字签名生成密钥<br />
                //第一步生成密钥对,如果已经生成过,本过程就可以跳过,对用户来讲myprikey.dat要保存在本地<br />
                //而mypubkey.dat给发布给其它用户<br />
                if ((new java.io.File("myprikey.dat")).exists()==false) {<br />
                if (generatekey()==false) {<br />
                System.out.println("生成密钥对败");<br />
                return;<br />
                };<br />
                }<br />
                //第二步,此用户<br />
                //从文件中读入私钥,对一个字符串进行签名后保存在一个文件(myinfo.dat)中<br />
                //并且再把myinfo.dat发送出去<br />
                //为了方便数字签名也放进了myifno.dat文件中,当然也可分别发送<br />
                try {<br />
                java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));<br />
                <!-- code sample is too wide -->  PrivateKey myprikey=(PrivateKey)in.readObject();<br />
                in.close();<br />
                // java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);<br />
                <!-- code sample is too wide --> //java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpec<br />
                <!-- code sample is too wide -->  String myinfo="这是我的信息";    //要签名的信息<br />
                //用私钥对信息生成数字签名<br />
                java.security.Signature signet=java.security.Signature.getInstance("DSA");<br />
                signet.initSign(myprikey);<br />
                signet.update(myinfo.getBytes());<br />
                byte[] signed=signet.sign();  //对信息的数字签名<br />
                System.out.println("signed(签名内容)="+byte2hex(signed));<br />
                //把信息和数字签名保存在一个文件中<br />
                java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));<br />
                <!-- code sample is too wide -->  out.writeObject(myinfo);<br />
                out.writeObject(signed);<br />
                out.close();<br />
                System.out.println("签名并生成文件成功");<br />
                }<br />
                catch (java.lang.Exception e) {<br />
                e.printStackTrace();<br />
                System.out.println("签名并生成文件失败");<br />
                };<br />
                //第三步<br />
                //其他人通过公共方式得到此户的公钥和文件<br />
                //其他人用此户的公钥,对文件进行检查,如果成功说明是此用户发布的信息.<br />
                //<br />
                try {<br />
                java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));<br />
                <!-- code sample is too wide -->   PublicKey pubkey=(PublicKey)in.readObject();<br />
                in.close();<br />
                System.out.println(pubkey.getFormat());<br />
                in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));<br />
                String info=(String)in.readObject();<br />
                byte[] signed=(byte[])in.readObject();<br />
                in.close();<br />
                java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");<br />
                signetcheck.initVerify(pubkey);<br />
                signetcheck.update(info.getBytes());<br />
                if (signetcheck.verify(signed)) {<br />
                System.out.println("info="+info);<br />
                System.out.println("签名正常");<br />
                }<br />
                else  System.out.println("非签名正常");<br />
                }<br />
                catch (java.lang.Exception e) {e.printStackTrace();};<br />
                }<br />
                //生成一对文件myprikey.dat和mypubkey.dat---私钥和公钥,<br />
                //公钥要用户发送(文件,网络等方法)给其它用户,私钥保存在本地<br />
                public boolean generatekey()<br />
                {<br />
                try {<br />
                java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance("DSA");<br />
                <!-- code sample is too wide --> // SecureRandom secrand=new SecureRandom();<br />
                // secrand.setSeed("tttt".getBytes()); //初始化随机产生器<br />
                // keygen.initialize(576,secrand);     //初始化密钥生成器<br />
                keygen.initialize(512);<br />
                KeyPair keys=keygen.genKeyPair();<br />
                //  KeyPair keys=keygen.generateKeyPair(); //生成密钥组<br />
                PublicKey pubkey=keys.getPublic();<br />
                PrivateKey prikey=keys.getPrivate();<br />
                java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));<br />
                <!-- code sample is too wide -->  out.writeObject(prikey);<br />
                out.close();<br />
                System.out.println("写入对象 prikeys ok");<br />
                out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));<br />
                out.writeObject(pubkey);<br />
                out.close();<br />
                System.out.println("写入对象 pubkeys ok");<br />
                System.out.println("生成密钥对成功");<br />
                return true;<br />
                }<br />
                catch (java.lang.Exception e) {<br />
                e.printStackTrace();<br />
                System.out.println("生成密钥对失败");<br />
                return false;<br />
                };<br />
                }<br />
                public String byte2hex(byte[] b)<br />
                {<br />
                String hs="";<br />
                String stmp="";<br />
                for (int n=0;n&lt;b.length;n++)<br />
                {<br />
                stmp=(java.lang.Integer.toHexString(b[n] &amp; 0XFF));<br />
                if (stmp.length()==1) hs=hs+"0"+stmp;<br />
                else hs=hs+stmp;<br />
                if (n&lt;b.length-1)  hs=hs+":";<br />
                }<br />
                return hs.toUpperCase();<br />
                }<br />
                }</pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    <br />
    </li>
</ol>
<p><a name="N101F0"><span class="smalltitle">2.4. DESede/DES对称算法</span></a></p>
<p>首先生成密钥,并保存(这里并没的保存的代码,可参考DSA中的方法)</p>
<p>KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);</p>
<p>SecretKey deskey = keygen.generateKey();</p>
<p>用密钥加密明文(myinfo),生成密文(cipherByte)</p>
<p>Cipher c1 = Cipher.getInstance(Algorithm);</p>
<p>c1.init(Cipher.ENCRYPT_MODE,deskey);</p>
<p>byte[] cipherByte=c1.doFinal(myinfo.getBytes());</p>
<p>传送密文和密钥,本文没有相应代码可参考DSA</p>
<p>.............</p>
<p>用密钥解密密文</p>
<p>c1 = Cipher.getInstance(Algorithm);</p>
<p>c1.init(Cipher.DECRYPT_MODE,deskey);</p>
<p>byte[] clearByte=c1.doFinal(cipherByte);</p>
<p>
相对来说对称密钥的使用是很简单的,对于JCE来讲支技DES,DESede,Blowfish三种加密术</p>
<p>
对于密钥的保存各传送可使用对象流或者用二进制编码,相关参考代码如下</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">   SecretKey deskey = keygen.generateKey();<br />
            byte[] desEncode=deskey.getEncoded();<br />
            javax.crypto.spec.SecretKeySpec destmp=new javax.crypto.spec.SecretKeySpec(desEncode,Algorithm);<br />
            <!-- code sample is too wide -->   SecretKey mydeskey=destmp;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>相关API</p>
<p>KeyGenerator
在DSA中已经说明,在添加JCE后在instance进可以如下参数</p>
<p>DES,DESede,Blowfish,HmacMD5,HmacSHA1</p>
<p>javax.crypto.Cipher 加/解密器</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public static final Cipher getInstance(java.lang.String transformation)<br />
            throws java.security.NoSuchAlgorithmException,<br />
            NoSuchPaddingException</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>返回一个指定方法的Cipher对象</p>
<p>参数:transformation 方法名(可用 DES,DESede,Blowfish)</p>
<p>public final void init(int opmode, java.security.Key key)
<br />
throws java.security.InvalidKeyException
</p>
<p>用指定的密钥和模式初始化Cipher对象</p>
<p>参数:opmode 方式(ENCRYPT_MODE, DECRYPT_MODE,
WRAP_MODE,UNWRAP_MODE)</p>
<p>key 密钥</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public final byte[] doFinal(byte[] input)<br />
            throws java.lang.IllegalStateException,<br />
            IllegalBlockSizeException,<br />
            BadPaddingException<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>对input内的串,进行编码处理,返回处理后二进制串,是返回解密文还是加解文由init时的opmode决定</p>
<p>
注意:本方法的执行前如果有update,是对updat和本次input全部处理,否则是本inout的内容</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">/*<br />
            安全程序 DESede/DES测试<br />
            */<br />
            import java.security.*;<br />
            import javax.crypto.*;<br />
            public class testdes {<br />
            public static void main(String[] args){<br />
            testdes my=new testdes();<br />
            my.run();<br />
            }<br />
            public  void run() {<br />
            //添加新安全算法,如果用JCE就要把它添加进去<br />
            Security.addProvider(new com.sun.crypto.provider.SunJCE());<br />
            String Algorithm="DES"; //定义 加密算法,可用 DES,DESede,Blowfish<br />
            String myinfo="要加密的信息";<br />
            try {<br />
            //生成密钥<br />
            KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);<br />
            SecretKey deskey = keygen.generateKey();<br />
            //加密<br />
            System.out.println("加密前的二进串:"+byte2hex(myinfo.getBytes()));<br />
            System.out.println("加密前的信息:"+myinfo);<br />
            Cipher c1 = Cipher.getInstance(Algorithm);<br />
            c1.init(Cipher.ENCRYPT_MODE,deskey);<br />
            byte[] cipherByte=c1.doFinal(myinfo.getBytes());<br />
            System.out.println("加密后的二进串:"+byte2hex(cipherByte));<br />
            //解密<br />
            c1 = Cipher.getInstance(Algorithm);<br />
            c1.init(Cipher.DECRYPT_MODE,deskey);<br />
            byte[] clearByte=c1.doFinal(cipherByte);<br />
            System.out.println("解密后的二进串:"+byte2hex(clearByte));<br />
            System.out.println("解密后的信息:"+(new String(clearByte)));<br />
            }<br />
            catch (java.security.NoSuchAlgorithmException e1) {e1.printStackTrace();}<br />
            catch (javax.crypto.NoSuchPaddingException e2) {e2.printStackTrace();}<br />
            catch (java.lang.Exception e3) {e3.printStackTrace();}<br />
            }<br />
            public String byte2hex(byte[] b) //二行制转字符串<br />
            {<br />
            String hs="";<br />
            String stmp="";<br />
            for (int n=0;n&lt;b.length;n++)<br />
            {<br />
            stmp=(java.lang.Integer.toHexString(b[n] &amp; 0XFF));<br />
            if (stmp.length()==1) hs=hs+"0"+stmp;<br />
            else hs=hs+stmp;<br />
            if (n&lt;b.length-1)  hs=hs+":";<br />
            }<br />
            return hs.toUpperCase();<br />
            }<br />
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1025B"><span class="smalltitle">2.5.
Diffie-Hellman密钥一致协议</span></a></p>
<p>公开密钥密码体制的奠基人Diffie和Hellman所提出的
"指数密钥一致协议"(Exponential Key Agreement
Protocol),该协议不要求别的安全性
先决条件,允许两名用户在公开媒体上交换信息以生成"一致"的,可以共享的密钥。在JCE的中实现用户alice生成DH类型的密钥对,如果长度用1024生成的时间请,推荐第一次生成后保存DHParameterSpec,以便下次使用直接初始化.使其速度加快</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">System.out.println("ALICE: 产生 DH 对 ...");<br />
            KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");<br />
            aliceKpairGen.initialize(512);<br />
            KeyPair aliceKpair = aliceKpairGen.generateKeyPair();</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>alice生成公钥发送组bob</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>
bob从alice发送来的公钥中读出DH密钥对的初始参数生成bob的DH密钥对</p>
<p>注意这一步一定要做,要保证每个用户用相同的初始参数生成的</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">   DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();<br />
            KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");<br />
            bobKpairGen.initialize(dhParamSpec);<br />
            KeyPair bobKpair = bobKpairGen.generateKeyPair();</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>bob根据alice的公钥生成本地的DES密钥</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">   KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");<br />
            bobKeyAgree.init(bobKpair.getPrivate());<br />
            bobKeyAgree.doPhase(alicePubKey, true);<br />
            SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>bob已经生成了他的DES密钥,他现把他的公钥发给alice,</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">      byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>alice根据bob的公钥生成本地的DES密钥</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">       ,,,,,,解码<br />
            KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");<br />
            aliceKeyAgree.init(aliceKpair.getPrivate());<br />
            aliceKeyAgree.doPhase(bobPubKey, true);<br />
            SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>
bob和alice能过这个过程就生成了相同的DES密钥,在这种基础就可进行安全能信</p>
<p>
<strong>常用API</strong>
</p>
<p>java.security.KeyPairGenerator 密钥生成器类
<br />
public static KeyPairGenerator getInstance(String algorithm)
<br />
throws NoSuchAlgorithmException
<br />
以指定的算法返回一个KeyPairGenerator 对象
<br />
参数: algorithm 算法名.如:原来是DSA,现在添加了
DiffieHellman(DH)
</p>
<p>public void initialize(int keysize)
<br />
以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置
<br />
参数:keysize 算法位长.其范围必须在 512 到 1024 之间，且必须为 64
的倍数
<br />
注意:如果用1024生长的时间很长,最好生成一次后就保存,下次就不用生成了
</p>
<p>public void initialize(AlgorithmParameterSpec params)
<br />
throws InvalidAlgorithmParameterException
<br />
以指定参数初始化
</p>
<p>javax.crypto.interfaces.DHPublicKey
<br />
public DHParameterSpec getParams()
<br />
返回
<br />
java.security.KeyFactory
</p>
<p>public static KeyFactory getInstance(String algorithm)
<br />
throws NoSuchAlgorithmException
<br />
以指定的算法返回一个KeyFactory
<br />
参数: algorithm 算法名:DSH,DH
</p>
<p>public final PublicKey generatePublic(KeySpec keySpec)
<br />
throws InvalidKeySpecException
<br />
根据指定的key说明,返回一个PublicKey对象
</p>
<p>java.security.spec.X509EncodedKeySpec
<br />
public X509EncodedKeySpec(byte[] encodedKey)
<br />
根据指定的二进制编码的字串生成一个key的说明
<br />
参数:encodedKey
二进制编码的字串(一般能过PublicKey.getEncoded()生成)
<br />
javax.crypto.KeyAgreement 密码一至类
</p>
<p>public static final KeyAgreement getInstance(java.lang.String
algorithm)
<br />
throws java.security.NoSuchAlgorithmException
<br />
返回一个指定算法的KeyAgreement对象
<br />
参数:algorithm 算法名,现在只能是DiffieHellman(DH)
</p>
<p>public final void init(java.security.Key key)
<br />
throws java.security.InvalidKeyException
<br />
用指定的私钥初始化
<br />
参数:key 一个私钥
</p>
<p>public final java.security.Key doPhase(java.security.Key
key,
<br />
boolean lastPhase)
<br />
throws java.security.InvalidKeyException,
<br />
java.lang.IllegalStateException
<br />
用指定的公钥进行定位,lastPhase确定这是否是最后一个公钥,对于两个用户的
<br />
情况下就可以多次定次,最后确定
<br />
参数:key 公钥
<br />
lastPhase 是否最后公钥
</p>
<p>public final SecretKey generateSecret(java.lang.String
algorithm)
<br />
throws java.lang.IllegalStateException,
<br />
java.security.NoSuchAlgorithmException,
<br />
java.security.InvalidKeyException
<br />
根据指定的算法生成密钥
<br />
参数:algorithm 加密算法(可用 DES,DESede,Blowfish)
</p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">*/<br />
            import java.io.*;<br />
            import java.math.BigInteger;<br />
            import java.security.*;<br />
            import java.security.spec.*;<br />
            import java.security.interfaces.*;<br />
            import javax.crypto.*;<br />
            import javax.crypto.spec.*;<br />
            import javax.crypto.interfaces.*;<br />
            import com.sun.crypto.provider.SunJCE;<br />
            public class testDHKey {<br />
            public static void main(String argv[]) {<br />
            try {<br />
            testDHKey my= new testDHKey();<br />
            my.run();<br />
            } catch (Exception e) {<br />
            System.err.println(e);<br />
            }<br />
            }<br />
            private void run() throws Exception {<br />
            Security.addProvider(new com.sun.crypto.provider.SunJCE());<br />
            System.out.println("ALICE: 产生 DH 对 ...");<br />
            KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");<br />
            aliceKpairGen.initialize(512);<br />
            KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); //生成时间长<br />
            // 张三(Alice)生成公共密钥 alicePubKeyEnc 并发送给李四(Bob) ,<br />
            //比如用文件方式,socket.....<br />
            byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();<br />
            //bob接收到alice的编码后的公钥,将其解码<br />
            KeyFactory bobKeyFac = KeyFactory.getInstance("DH");<br />
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec  (alicePubKeyEnc);<br />
            PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);<br />
            System.out.println("alice公钥bob解码成功");<br />
            // bob必须用相同的参数初始化的他的DH KEY对,所以要从Alice发给他的公开密钥,<br />
            //中读出参数,再用这个参数初始化他的 DH key对<br />
            //从alicePubKye中取alice初始化时用的参数<br />
            DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();<br />
            KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");<br />
            bobKpairGen.initialize(dhParamSpec);<br />
            KeyPair bobKpair = bobKpairGen.generateKeyPair();<br />
            System.out.println("BOB: 生成 DH key 对成功");<br />
            KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");<br />
            bobKeyAgree.init(bobKpair.getPrivate());<br />
            System.out.println("BOB: 初始化本地key成功");<br />
            //李四(bob) 生成本地的密钥 bobDesKey<br />
            bobKeyAgree.doPhase(alicePubKey, true);<br />
            SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");<br />
            System.out.println("BOB: 用alice的公钥定位本地key,生成本地DES密钥成功");<br />
            // Bob生成公共密钥 bobPubKeyEnc 并发送给Alice,<br />
            //比如用文件方式,socket.....,使其生成本地密钥<br />
            byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();<br />
            System.out.println("BOB向ALICE发送公钥");<br />
            // alice接收到 bobPubKeyEnc后生成bobPubKey<br />
            // 再进行定位,使aliceKeyAgree定位在bobPubKey<br />
            KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");<br />
            x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);<br />
            PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);<br />
            System.out.println("ALICE接收BOB公钥并解码成功");<br />
            ;<br />
            KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");<br />
            aliceKeyAgree.init(aliceKpair.getPrivate());<br />
            System.out.println("ALICE: 初始化本地key成功");<br />
            aliceKeyAgree.doPhase(bobPubKey, true);<br />
            // 张三(alice) 生成本地的密钥 aliceDesKey<br />
            SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");<br />
            System.out.println("ALICE: 用bob的公钥定位本地key,并生成本地DES密钥");<br />
            if (aliceDesKey.equals(bobDesKey)) System.out.println("张三和李四的密钥相同");<br />
            //现在张三和李四的本地的deskey是相同的所以,完全可以进行发送加密,接收后解密,达到<br />
            //安全通道的的目的<br />
            /*<br />
            * bob用bobDesKey密钥加密信息<br />
            */<br />
            Cipher bobCipher = Cipher.getInstance("DES");<br />
            bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);<br />
            String bobinfo= "这是李四的机密信息";<br />
            System.out.println("李四加密前原文:"+bobinfo);<br />
            byte[] cleartext =bobinfo.getBytes();<br />
            byte[] ciphertext = bobCipher.doFinal(cleartext);<br />
            /*<br />
            * alice用aliceDesKey密钥解密<br />
            */<br />
            Cipher aliceCipher = Cipher.getInstance("DES");<br />
            aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);<br />
            byte[] recovered = aliceCipher.doFinal(ciphertext);<br />
            System.out.println("alice解密bob的信息:"+(new String(recovered)));<br />
            if (!java.util.Arrays.equals(cleartext, recovered))<br />
            throw new Exception("解密后与原文信息不同");<br />
            System.out.println("解密后相同");<br />
            }<br />
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/l-security/index.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="3"><span class="atitle">第3章小结</span></a></p>
<p>
在加密术中生成密钥对时，密钥对的当然是越长越好，但费时也越多，请从中从实际出发选取合适的长度，大部分例码中的密钥是每次运行就从新生成，在实际的情
况中是生成后在一段时间保存在文件中，再次运行直接从文件中读入，从而加快速度。当然定时更新和加强密钥保管的安全性也是必须的。</p>
<br />
<br />
<p><a name="author"><span class="atitle">关于作者</span></a></p>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td colspan="3"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="5" /></td>
        </tr>
        <tr align="left" valign="top">
            <td><br />
            </td>
            <td><img alt="" src="http://www.ibm.com/i/c.gif" width="4" height="5" /></td>
            <td width="100%">
            <p>王辉，具有八年的编程及系统管理经验，所使用的语言为C和Java
            编程语言。目前在深圳一家公司做程序员，使用C和JAVA为DB2数据库编程.</p>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/261511.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-23 14:14 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/23/261511.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 加密java源代码</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/20/261120.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 20 Mar 2009 12:07:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/20/261120.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261120.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/20/261120.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261120.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261120.html</trackback:ping><description><![CDATA[转 http://www.blogjava.net/wang123/archive/2009/03/19/260729.html<br />
<br />
<h2>
<a id="viewpost1_TitleUrl" href="../../wang123/archive/2009/03/19/260729.html">加密java源代码</a>
</h2>
Java程序的源代码很容易被别人偷看，只要有一个反编译器，任何人都可以分析别人的代码。本文讨论如何在不修改原有程序的情况下，通过加密技术保护源代码。
<p>　　<strong>一、为什么要加密?</strong></p>
<p>　　对于传统的C或C++之类的语言来说，要在Web上保护源代码是很容易的，只要不发布它就可以。遗憾的是，Java程序的源代码很容易被别人偷
看。只要有一个反编译器，任何人都可以分析别人的代码。Java的灵活性使得源代码很容易被窃取，但与此同时，它也使通过加密保护代码变得相对容易，我们
唯一需要了解的就是Java的ClassLoader对象。当然，在加密过程中，有关Java Cryptography
Extension(JCE)的知识也是必不可少的。</p>
<p>　　有几种技术可以&#8220;模糊&#8221;Java类文件，使得反编译器处理类文件的效果大打折扣。然而，修改反编译器使之能够处理这些经过模糊处理的类文件并不是什么难事，所以不能简单地依赖模糊技术来保证源代码的安全。</p>
<p>　　我们可以用流行的加密工具加密应用，比如<a href="http://www.bt285.cn/">PGP</a>(Pretty Good Privacy)或GPG(GNU Privacy Guard)。这时，最终用户在运行应用之前必须先进行解密。但解密之后，最终用户就有了一份不加密的类文件，这和事先不进行加密没有什么差别。</p>
<p>　　Java运行时装入字节码的机制隐含地意味着可以对字节码进行修改。JVM每次装入类文件时都需要一个称为ClassLoader的对象，这个
对象负责把新的类装入正在运行的JVM。JVM给ClassLoader一个包含了待装入类(比如java.lang.Object)名字的字符串，然后
由ClassLoader负责找到类文件，装入原始数据，并把它转换成一个Class对象。</p>
<p>　　我们可以通过定制ClassLoader，在类文件执行之前修改它。这种技术的应用非常广泛??在这里，它的用途是在类文件装入之时进行解密，因此可以看成是一种即时解密器。由于解密后的字节码文件永远不会保存到文件系统，所以窃密者很难得到解密后的代码。</p>
<p>　　由于把原始字节码转换成Class对象的过程完全由系统负责，所以创建定制ClassLoader对象其实并不困难，只需先获得原始数据，接着就可以进行包含解密在内的任何转换。</p>
<p>　　Java 2在一定程度上简化了定制ClassLoader的构建。在Java 2中，loadClass的缺省实现仍旧负责处理所有必需的步骤，但为了顾及各种定制的类装入过程，它还调用一个新的findClass方法。</p>
<p>　　这为我们编写定制的ClassLoader提供了一条捷径，减少了麻烦：只需覆盖findClass，而不是覆盖loadClass。这种方法避免了重复所有装入器必需执行的公共步骤，因为这一切由loadClass负责。</p>
<p>　　不过，本文的定制ClassLoader并不使用这种方法。原因很简单。如果由默认的ClassLoader先寻找经过加密的类文件，它可以找
到;但由于类文件已经加密，所以它不会认可这个类文件，装入过程将失败。因此，我们必须自己实现loadClass，稍微增加了一些工作量。</p>
<p><strong>二、定制类装入器</strong></p>
<p>　　每一个运行着的JVM已经拥有一个ClassLoader。这个<a href="http://www.guihua.org/">默认</a>的ClassLoader根据CLASSPATH环境变量的值，在本地文件系统中寻找合适的字节码文件。</p>
<p>　　应用定制ClassLoader要求对这个过程有较为深入的认识。我们首先必须创建一个定制ClassLoader类的实例，然后显式地要求它
装入另外一个类。这就强制JVM把该类以及所有它所需要的类关联到定制的ClassLoader。Listing
1显示了如何用定制ClassLoader装入类文件。</p>
<p>　　【Listing 1：利用定制的ClassLoader装入类文件】</p>
<p>以下是引用片段：</p>
<p>
<table heihgt="" align="center" bgcolor="#f3f3f3" border="1" bordercolor="#cccccc" width="550" cellpadding="3" cellspacing="0">
    <tbody>
        <tr>
            <td>　　// 首先创建一个ClassLoader对象 如 <a href="http://www.bt285.cn/">http://www.bt285.cn</a> <br />
            ClassLoader myClassLoader = new myClassLoader(); <br />
            // 利用定制ClassLoader对象装入类文件 <br />
            // 并把它转换成Class对象 <br />
            Class myClass = myClassLoader.loadClass( "mypackage.MyClass" ); <br />
            // 最后，创建该类的一个实例 <br />
            Object newInstance = myClass.newInstance(); <br />
            // 注意，MyClass所需要的所有其他类，都将通过 <br />
            // 定制的ClassLoader自动装入&nbsp;</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;　　如前所述，定制ClassLoader只需先获取类文件的数据，然后把字节码传递给运行时系统，由后者完成余下的任务。</p>
<p>　　ClassLoader有几个重要的方法。创建定制的ClassLoader时，我们只需覆盖其中的一个，即loadClass，提供获取原始类文件数据的代码。这个方法有两个参数：类的名字，以及一个表示JVM是否要求解析类名字的标记(即是否同时装入有依赖<a href="http://www.5a520.cn/">关系</a>的类)。如果这个标记是true，我们只需在返回JVM之前调用resolveClass。</p>
<p>　　【Listing 2：ClassLoader.loadClass()的一个简单实现】</p>
<p>以下是引用片段：</p>
<p>
<table heihgt="" align="center" bgcolor="#f3f3f3" border="1" bordercolor="#cccccc" width="550" cellpadding="3" cellspacing="0">
    <tbody>
        <tr>
            <td>public Class loadClass( String name, boolean resolve )&nbsp; 如：<a href="http://www.5a520.cn/">http://www.5a520.cn</a> <br />
            throws ClassNotFoundException { <br />
            try { <br />
            // 我们要创建的Class对象 <br />
            Class clasz = null; <br />
            // 必需的步骤1：如果类已经在系统缓冲之中， <br />
            // 我们不必再次装入它 <br />
            clasz = findLoadedClass( name ); <br />
            if (clasz != null) <br />
            return clasz; <br />
            // 下面是定制部分 <br />
            byte classData[] = /* 通过某种方法获取字节码数据 */; <br />
            if (classData != null) { <br />
            // 成功读取字节码数据，现在把它转换成一个Class对象 <br />
            clasz = defineClass( name, classData, 0, classData.length ); <br />
            } <br />
            // 必需的步骤2：如果上面没有成功， <br />
            // 我们尝试用默认的ClassLoader装入它 <br />
            if (clasz == null) <br />
            clasz = findSystemClass( name ); <br />
            // 必需的步骤3：如有必要，则装入相关的类 <br />
            if (resolve &amp;&amp; clasz != null) <br />
            resolveClass( clasz ); <br />
            // 把类返回给调用者 <br />
            return clasz; <br />
            } catch( IOException ie ) { <br />
            throw new ClassNotFoundException( ie.toString() ); <br />
            } catch( GeneralSecurityException gse ) { <br />
            throw new ClassNotFoundException( gse.toString() ); <br />
            } <br />
            }&nbsp;</td>
        </tr>
    </tbody>
</table>
</p>
&nbsp;&nbsp;&nbsp; Listing 2显示了一个简单的loadClass实现。代码中的大部分对所有ClassLoader对象来说都一样，但有一小部分(已通过注释标记)是特有的。在处理过程中，ClassLoader对象要用到其他几个辅助方法：
<p>　　findLoadedClass：用来进行检查，以便确认被请求的类当前还不存在。loadClass方法应该首先调用它。</p>
<p>　　defineClass：获得原始类文件字节码数据之后，调用defineClass把它转换成一个Class对象。任何loadClass实现都必须调用这个方法。</p>
<p>　　findSystemClass：提供默认ClassLoader的支持。如果用来寻找类的定制方法不能找到指定的类(或者有意地不用定制方法)，则可以调用该方法尝试默认的装入方式。这是很有用的，特别是从普通的JAR文件装入标准Java类时。</p>
<p>　　resolveClass：当JVM想要装入的不仅包括指定的类，而且还包括该类引用的所有其他类时，它会把loadClass的resolve参数设置成true。这时，我们必须在返回刚刚装入的Class对象给调用者之前调用resolveClass。</p>
<p>　　<strong>三、加密、解密</strong></p>
<p>　　Java加密扩展即Java Cryptography Extension，简称JCE。它是Sun的加密服务软件，包含了加密和密匙生成功能。JCE是JCA(Java Cryptography Architecture)的一种扩展。</p>
<p>　　JCE没有规定具体的加密算法，但提供了一个框架，加密算法的具体实现可以作为服务提供者加入。除了JCE框架之外，JCE软件包还包含了
SunJCE服务提供者，其中包括许多有用的加密算法，比如DES(Data Encryption Standard)和Blowfish。</p>
<p>　　为简单计，在本文中我们将用DES算法加密和解密字节码。下面是用JCE加密和解密数据必须遵循的基本步骤：</p>
<p>　　步骤1：生成一个安全密匙。在加密或解密任何数据之前需要有一个密匙。密匙是随同被加密的应用一起发布的一小段数据，Listing 3显示了如何生成一个密匙。 【Listing 3：生成一个密匙】</p>
<p>以下是引用片段：</p>
<p>
<table heihgt="" align="center" bgcolor="#f3f3f3" border="1" bordercolor="#cccccc" width="550" cellpadding="3" cellspacing="0">
    <tbody>
        <tr>
            <td>// DES算法要求有一个可信任的随机数源 <br />
            SecureRandom sr = new SecureRandom(); <br />
            // 为我们选择的DES算法生成一个KeyGenerator对象 <br />
            KeyGenerator kg = KeyGenerator.getInstance( "DES" ); <br />
            kg.init( sr ); <br />
            // 生成密匙 <br />
            SecretKey key = kg.generateKey(); <br />
            // 获取密匙数据 <br />
            byte rawKeyData[] = key.getEncoded(); <br />
            /* 接下来就可以用密匙进行加密或解密，或者把它保存 <br />
            为文件供以后使用 */ <br />
            doSomething( rawKeyData );&nbsp;&nbsp;</td>
        </tr>
    </tbody>
</table>
<br />
步骤2：加密数据。得到密匙之后，接下来就可以用它加密数据。除了解密的ClassLoader之外，一般还要有一个加密待发布应用的独立程序(见Listing 4)。 【Listing 4：用密匙加密原始数据】</p>
<p>以下是引用片段：</p>
<p>
<table heihgt="" align="center" bgcolor="#f3f3f3" border="1" bordercolor="#cccccc" width="550" cellpadding="3" cellspacing="0">
    <tbody>
        <tr>
            <td>// DES算法要求有一个可信任的随机数源 <br />
            SecureRandom sr = new SecureRandom(); <br />
            byte rawKeyData[] = /* 用某种方法获得密匙数据 */; <br />
            // 从原始密匙数据创建DESKeySpec对象 <br />
            DESKeySpec dks = new DESKeySpec( rawKeyData ); <br />
            // 创建一个密匙工厂，然后用它把DESKeySpec转换成 <br />
            // 一个SecretKey对象 <br />
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" ); <br />
            SecretKey key = keyFactory.generateSecret( dks ); <br />
            // Cipher对象实际完成加密操作 <br />
            Cipher cipher = Cipher.getInstance( "DES" ); <br />
            // 用密匙初始化Cipher对象 <br />
            cipher.init( Cipher.ENCRYPT_MODE, key, sr ); <br />
            // 现在，获取数据并加密 <br />
            byte data[] = /* 用某种方法获取数据 */ <br />
            // 正式执行加密操作 <br />
            byte encryptedData[] = cipher.doFinal( data ); <br />
            // 进一步处理加密后的数据 <br />
            doSomething( encryptedData );&nbsp;&nbsp;</td>
        </tr>
    </tbody>
</table>
<br />
步骤3：解密数据。运行经过加密的应用时，ClassLoader分析并解密类文件。操作步骤如Listing 5所示。 【Listing 5：用密匙解密数据】 </p>
<p>
<table heihgt="" align="center" bgcolor="#f3f3f3" border="1" bordercolor="#cccccc" width="550" cellpadding="3" cellspacing="0">
    <tbody>
        <tr>
            <td>　　// DES算法要求有一个可信任的随机数源 <br />
            SecureRandom sr = new SecureRandom(); <br />
            byte rawKeyData[] = /* 用某种方法获取原始密匙数据 */; <br />
            // 从原始密匙数据创建一个DESKeySpec对象 <br />
            DESKeySpec dks = new DESKeySpec( rawKeyData ); <br />
            // 创建一个密匙工厂，然后用它把DESKeySpec对象转换成 <br />
            // 一个SecretKey对象 <br />
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" ); <br />
            SecretKey key = keyFactory.generateSecret( dks ); <br />
            // Cipher对象实际完成解密操作 <br />
            Cipher cipher = Cipher.getInstance( "DES" ); <br />
            // 用密匙初始化Cipher对象 <br />
            cipher.init( Cipher.DECRYPT_MODE, key, sr ); <br />
            // 现在，获取数据并解密 <br />
            byte encryptedData[] = /* 获得经过加密的数据 */ <br />
            // 正式执行解密操作 <br />
            byte decryptedData[] = cipher.doFinal( encryptedData ); <br />
            // 进一步处理解密后的数据 <br />
            doSomething( decryptedData );&nbsp;&nbsp;</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<p><strong>四、应用实例</strong></p>
<p>　　前面介绍了如何加密和解密数据。要部署一个经过加密的应用，步骤如下：</p>
<p>　　步骤1：创建应用。我们的例子包含一个App主类，两个辅助类(分别称为Foo和Bar)。这个应用没有什么实际功用，但只要我们能够加密这个应用，加密其他应用也就不在话下。</p>
<p>　　步骤2：生成一个安全密匙。在命令行，利用GenerateKey工具(参见GenerateKey.java)把密匙写入一个文件： % java GenerateKey key.data</p>
<p>　　步骤3：加密应用。在命令行，利用EncryptClasses工具(参见EncryptClasses.java)加密应用的类： % java EncryptClasses key.data App.class Foo.class Bar.class</p>
<p>　　该命令把每一个.class文件替换成它们各自的加密版本。</p>
<p>　　步骤4：运行经过加密的应用。用户通过一个DecryptStart程序运行经过加密的应用。DecryptStart程序如Listing 6所示。 【Listing 6：DecryptStart.java，启动被加密应用的程序】</p>
<p>以下是引用片段：</p>
<p>
<table heihgt="" align="center" bgcolor="#f3f3f3" border="1" bordercolor="#cccccc" width="550" cellpadding="3" cellspacing="0">
    <tbody>
        <tr>
            <td>　　import java.io.*; <br />
            import java.security.*; <br />
            import java.lang.reflect.*; <br />
            import javax.crypto.*; <br />
            import javax.crypto.spec.*; <br />
            public class DecryptStart extends ClassLoader <br />
            { <br />
            // 这些对象在构造函数中设置， <br />
            // 以后loadClass()方法将利用它们解密类 <br />
            private SecretKey key; <br />
            private Cipher cipher; <br />
            // 构造函数：设置解密所需要的对象 <br />
            public DecryptStart( SecretKey key ) throws GeneralSecurityException, <br />
            IOException { <br />
            this.key = key; <br />
            String algorithm = "DES"; <br />
            SecureRandom sr = new SecureRandom(); <br />
            System.err.println( "[DecryptStart: creating cipher]" ); <br />
            cipher = Cipher.getInstance( algorithm ); <br />
            cipher.init( Cipher.DECRYPT_MODE, key, sr ); <br />
            } <br />
            // main过程：我们要在这里读入密匙，创建DecryptStart的 <br />
            // 实例，它就是我们的定制ClassLoader。 <br />
            // 设置好ClassLoader以后，我们用它装入应用实例， <br />
            // 最后，我们通过Java Reflection API调用应用实例的main方法 <br />
            static public void main( String args[] ) throws Exception { <br />
            String keyFilename = args[0]; <br />
            String appName = args[1]; <br />
            // 这些是传递给应用本身的参数 <br />
            String realArgs[] = new String[args.length-2]; <br />
            System.arraycopy( args, 2, realArgs, 0, args.length-2 ); <br />
            // 读取密匙 <br />
            System.err.println( "[DecryptStart: reading key]" ); <br />
            byte rawKey[] = Util.readFile( keyFilename ); <br />
            DESKeySpec dks = new DESKeySpec( rawKey ); <br />
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" ); <br />
            SecretKey key = keyFactory.generateSecret( dks ); <br />
            // 创建解密的ClassLoader <br />
            DecryptStart dr = new DecryptStart( key ); <br />
            // 创建应用主类的一个实例 <br />
            // 通过ClassLoader装入它 <br />
            System.err.println( "[DecryptStart: loading "+appName+"]" ); <br />
            Class clasz = dr.loadClass( appName ); <br />
            // 最后，通过Reflection API调用应用实例 <br />
            // 的main()方法 <br />
            // 获取一个对main()的引用 <br />
            String proto[] = new String[1]; <br />
            Class mainArgs[] = { (new String[1]).getClass() }; <br />
            Method main = clasz.getMethod( "main", mainArgs ); <br />
            // 创建一个包含main()方法参数的数组 <br />
            Object argsArray[] = { realArgs }; <br />
            System.err.println( "[DecryptStart: running "+appName+".main()]" ); <br />
            // 调用main() <br />
            main.invoke( null, argsArray ); <br />
            } <br />
            public Class loadClass( String name, boolean resolve ) <br />
            throws ClassNotFoundException { <br />
            try { <br />
            // 我们要创建的Class对象 <br />
            Class clasz = null; <br />
            // 必需的步骤1：如果类已经在系统缓冲之中 <br />
            // 我们不必再次装入它 <br />
            clasz = findLoadedClass( name ); <br />
            if (clasz != null) <br />
            return clasz; <br />
            // 下面是定制部分 <br />
            try { <br />
            // 读取经过加密的类文件 <br />
            byte classData[] = Util.readFile( name+".class" ); <br />
            if (classData != null) { <br />
            // 解密... <br />
            byte decryptedClassData[] = cipher.doFinal( classData ); <br />
            // ... 再把它转换成一个类 <br />
            clasz = defineClass( name, decryptedClassData, <br />
            0, decryptedClassData.length ); <br />
            System.err.println( "[DecryptStart: decrypting class "+name+"]" ); <br />
            } <br />
            } catch( FileNotFoundException fnfe ) <br />
            // 必需的步骤2：如果上面没有成功 <br />
            // 我们尝试用默认的ClassLoader装入它 <br />
            if (clasz == null) <br />
            clasz = findSystemClass( name ); <br />
            // 必需的步骤3：如有必要，则装入相关的类 <br />
            if (resolve &amp;&amp; clasz != null) <br />
            resolveClass( clasz ); <br />
            // 把类返回给调用者 <br />
            return clasz; <br />
            } catch( IOException ie ) { <br />
            throw new ClassNotFoundException( ie.toString() <br />
            ); <br />
            } catch( GeneralSecurityException gse ) { <br />
            throw new ClassNotFoundException( gse.toString() <br />
            ); <br />
            } <br />
            } <br />
            }&nbsp;</td>
        </tr>
    </tbody>
</table>
<br />
&nbsp;对于未经加密的应用，正常执行方式如下： % java App arg0 arg1 arg2</p>
<p>　　对于经过加密的应用，则相应的运行方式为： % java DecryptStart key.data App arg0 arg1 arg2</p>
<p>　　DecryptStart有两个目的。一个DecryptStart的实例就是一个实施即时解密操作的定制ClassLoader;同
时，DecryptStart还包含一个main过程，它创建解密器实例并用它装入和运行应用。示例应用App的代码包含在App.java、
Foo.java和Bar.java内。Util.java是一个文件I/O工具，本文示例多处用到了它。完整的代码请从本文最后下载。</p>
<p>　　<strong>五、注意事项</strong></p>
<p>　　我们看到，要在不修改源代码的情况下加密一个Java应用是很容易的。不过，世上没有完全安全的系统。本文的加密方式提供了一定程度的源代码保护，但对某些攻击来说它是脆弱的。</p>
<p>　　虽然应用本身经过了加密，但启动程序DecryptStart没有加密。攻击者可以反编译启动程序并修改它，把解密后的类文件保存到磁盘。降低
这种风险的办法之一是对启动程序进行高质量的模糊处理。或者，启动程序也可以采用直接编译成机器语言的代码，使得启动程序具有传统执行文件格式的安全性。</p>
<p>　　另外还要记住的是，大多数JVM本身并不安全。狡猾的黑客可能会修改JVM，从ClassLoader之外获取解密后的代码并保存到磁盘，从而绕过本文的加密技术。Java没有为此提供真正有效的补救措施。</p>
<p>　　不过应该指出的是，所有这些可能的攻击都有一个前提，这就是攻击者可以得到密匙。如果没有密匙，应用的安全性就完全取决于加密算法的安全性。虽然这种保护代码的方法称不上十全十美，但它仍不失为一种保护知识产权和敏感用户数据的有效方案。</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/261120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-20 20:07 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/20/261120.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转  java内部类的作用分析</title><link>http://www.blogjava.net/hulizhong/archive/2009/02/19/255522.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Thu, 19 Feb 2009 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/02/19/255522.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/255522.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/02/19/255522.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/255522.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/255522.html</trackback:ping><description><![CDATA[转&nbsp; http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx<br />
<br />
<div style="border-right: medium none; padding-right: 20px; border-top: windowtext 1pt solid; padding-left: 20px; background: rgb(255,244,233); padding-bottom: 1pt; font: 15px/25px tahoma; border-left: medium none; padding-top: 1pt; border-bottom: 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial; font-size-adjust: none; font-stretch: normal">
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">提起Java内部类（Inner Class）可能很多人不太熟悉，实际上类似的概念在C++里也有，那就是嵌套类（Nested Class），关于这两者的区别与联系，在下文中会有对比。内部类从表面上看，就是在类中又定义了一个类（下文会看到，内部类可以在很多地方定义），而实际上并没有那么简单，乍看上去内部类似乎有些多余，它的用处对于初学者来说可能并不是那么显著，但是随着对它的深入了解，你会发现Java的设计者在内部类身上的确是用心良苦。学会使用内部类，是掌握Java高级编程的一部分，它可以让你更优雅地设计你的程序结构。下面从以下几个方面来介绍：</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>第一次见面</strong></span></div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">view plain</a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">copy to clipboard</a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">print</a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">?</a></div>
</div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">interface</span><span>&nbsp;Contents&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span><span>&nbsp;value();&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
    <li class=""><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">interface</span><span>&nbsp;Destination&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;readLabel();&nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Goods&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Content&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Contents&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">11</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;value()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;i;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;GDestination&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Destination&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;String&nbsp;label;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;GDestination(String&nbsp;whereTo)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label&nbsp;=&nbsp;whereTo;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;readLabel()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;label;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Destination&nbsp;dest(String&nbsp;s)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;GDestination(s);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Contents&nbsp;cont()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;Content();&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
    <li class=""><span><span class="keyword">class</span><span>&nbsp;TestGoods&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Goods&nbsp;p&nbsp;=&nbsp;<span class="keyword">new</span><span>&nbsp;Goods();&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Contents&nbsp;c&nbsp;=&nbsp;p.cont();&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Destination&nbsp;d&nbsp;=&nbsp;p.dest(<span class="string">"Beijing"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class="java" style="display: none" name="code" rows="15" cols="50">public interface Contents {
int value();
}
public interface Destination {
String readLabel();
}
public class Goods {
private class Content implements Contents {
private int i = 11;
public int value() {
return i;
}
}
protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
public Destination dest(String s) {
return new GDestination(s);
}
public Contents cont() {
return new Content();
}
}
class TestGoods {
public static void main(String[] args) {
Goods p = new Goods();
Contents c = p.cont();
Destination d = p.dest("Beijing");
}
}</textarea>&nbsp;
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">在这个例子里类Content和GDestination被定义在了类Goods内部，并且分别有着protected和private修饰符来控制访问级别。Content代表着Goods的内容，而GDestination代表着Goods的目的地。它们分别实现了两个接口Content和 Destination。在后面的main方法里，直接用 Contents c和Destination d进行操作，你甚至连这两个内部类的名字都没有看见！这样，内部类的第一个好处就体现出来了 隐藏你不想让别人知道的操作，也即封装性。 </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">同时，我们也发现了在外部类作用范围之外得到内部类对象的第一个方法，那就是利用其外部类的方法创建并返回。上例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢？当然有，其语法格式如下：</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">outerObject=new outerClass(Constructor Parameters);</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters); </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">注意在创建非静态内部类对象时，一定要先创建起相应的外部类对象。至于原因，也就引出了我们下一个话题 非静态内部类对象有着指向其外部类对象的引用，对刚才的例子稍作修改：</span></div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">view plain</a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">copy to clipboard</a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">print</a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">?</a></div>
</div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Goods&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;valueRate&nbsp;=&nbsp;</span><span class="number">2</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Content&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Contents&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">11</span><span>&nbsp;*&nbsp;valueRate;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;value()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;i;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;GDestination&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Destination&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;String&nbsp;label;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;GDestination(String&nbsp;whereTo)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label&nbsp;=&nbsp;whereTo;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;readLabel()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;label;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Destination&nbsp;dest(String&nbsp;s)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;GDestination(s);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Contents&nbsp;cont()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;Content();&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class="java" style="display: none" name="code" rows="15" cols="50">public class Goods {
private int valueRate = 2;
private class Content implements Contents {
private int i = 11 * valueRate;
public int value() {
return i;
}
}
protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
public Destination dest(String s) {
return new GDestination(s);
}
public Contents cont() {
return new Content();
}
}</textarea> <br />
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">在这里我们给Goods类增加了一个private成员变量valueRate，意义是货物的价值系数，在内部类Content的方法value()计算价值时把它乘上。我们发现，value()可以访问valueRate，这也是内部类的第二个好处一个内部类对象可以访问创建它的外部类对象的内容，甚至包括私有变量！这是一个非常有用的特性，为我们在设计时提供了更多的思路和捷径。要想实现这个功能，内部类对象就必须有指向外部类对象的引用。Java编译器在创建内部类对象时，隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象，同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">有人会问，如果内部类里的一个成员变量与外部类的一个成员变量同名，也即外部类的同名成员变量被屏蔽了，怎么办？没事，Java里用如下格式表达外部类的引用：</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">outerClass.this </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">有了它，我们就不怕这种屏蔽的情况了。</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>静态内部类</strong></span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">和普通的类一样，内部类也可以有静态的。不过和非静态内部类相比，区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套类很相像了，Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上，当然从设计的角度以及以它一些细节来讲还有区别。</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">除此之外，在任何非静态内部类中，都不能有静态数据，静态方法或者又一个静态内部类（内部类的嵌套可以不止一层）。不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>局部内部类</strong> </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">是的，Java内部类也可以是局部的，它可以定义在一个方法甚至一个代码块之内。</span></div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">view plain</a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">copy to clipboard</a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">print</a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">?</a></div>
</div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Goods1&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Destination&nbsp;dest(String&nbsp;s)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class</span><span>&nbsp;GDestination&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Destination&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;String&nbsp;label;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;GDestination(String&nbsp;whereTo)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;label&nbsp;=&nbsp;whereTo;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;readLabel()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;label;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;GDestination(s);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Goods1&nbsp;g&nbsp;=&nbsp;<span class="keyword">new</span><span>&nbsp;Goods1();&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Destination&nbsp;d&nbsp;=&nbsp;g.dest(<span class="string">"Beijing"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class="java" style="display: none" name="code" rows="15" cols="50">public class Goods1 {
public Destination dest(String s) {
class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new GDestination(s);
}
public static void main(String[] args) {
Goods1 g = new Goods1();
Destination d = g.dest("Beijing");
}
}</textarea> <br />
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">上面就是这样一个例子。在方法dest中我们定义了一个内部类，最后由这个方法返回这个内部类的对象。如果我们在用一个内部类的时候仅需要创建它的一个对象并创给外部，就可以这样做。当然，定义在方法中的内部类可以使设计多样化，用途绝不仅仅在这一点。</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">下面有一个更怪的例子：</span></div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">view plain</a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">copy to clipboard</a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">print</a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">?</a></div>
</div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Goods2&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;internalTracking(</span><span class="keyword">boolean</span><span>&nbsp;b)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(b)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class</span><span>&nbsp;TrackingSlip&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;String&nbsp;id;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TrackingSlip(String&nbsp;s)&nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp;=&nbsp;s;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;getSlip()&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;id;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TrackingSlip&nbsp;ts&nbsp;=&nbsp;<span class="keyword">new</span><span>&nbsp;TrackingSlip(</span><span class="string">"slip"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;s&nbsp;=&nbsp;ts.getSlip();&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;track()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;internalTracking(<span class="keyword">true</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Goods2&nbsp;g&nbsp;=&nbsp;<span class="keyword">new</span><span>&nbsp;Goods2();&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.track();&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class="java" style="display: none" name="code" rows="15" cols="50">public class Goods2 {
private void internalTracking(boolean b) {
if (b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() {
return id;
}
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Goods2 g = new Goods2();
g.track();
}
}</textarea> <br />
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">你不能在if之外创建这个内部类的对象，因为这已经超出了它的作用域。不过在编译的时候，内部类TrackingSlip和其他类一样同时被编译，只不过它由它自己的作用域，超出了这个范围就无效，除此之外它和其他内部类并没有区别。</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>匿名内部类</strong> </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">java的匿名内部类的语法规则看上去有些古怪，不过如同匿名数组一样，当你只需要创建一个类的对象而且用不上它的名字时，使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的：</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">new interfacename(){......}; 或 new superclassname(){......}; </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">下面接着前面继续举例子：</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">view plain</a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">copy to clipboard</a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">print</a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">?</a></div>
</div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Goods3&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Contents&nbsp;cont()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;Contents()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">11</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;value()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;i;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class="java" style="display: none" name="code" rows="15" cols="50">public class Goods3 {
public Contents cont() {
return new Contents() {
private int i = 11;
public int value() {
return i;
}
};
}
}</textarea> <br />
</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象，看上去的确十分简洁。</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">在java的事件处理的匿名适配器中，匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码：</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">view plain</a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">copy to clipboard</a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">print</a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://blog.csdn.net/ilibaba/archive/2009/02/06/3866537.aspx#">?</a></div>
</div>
<ol class="dp-j">
    <li class="alt"><span><span>frame.addWindowListener(</span><span class="keyword">new</span><span>&nbsp;WindowAdapter(){&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;windowClosing(WindowEvent&nbsp;e){&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(<span class="number">0</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>});&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class="java" style="display: none" name="code" rows="15" cols="50">frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
}); </textarea> <br />
</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">有一点需要注意的是，匿名内部类由于没有名字，所以它没有构造函数（但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类，创建它的时候必须带上这些参数，并在实现的过程中使用super关键字调用相应的内容）。如果你想要初始化它的成员变量，有下面几种方法：</span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">如果是在一个方法的匿名内部类，可以利用这个方法传进你想要的参数，不过记住，这些参数必须被声明为final。 </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">将匿名内部类改造成有名字的局部内部类，这样它就可以拥有构造函数了。 </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%">在这个匿名内部类中使用初始化代码块。 </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>为什么需要内部类？</strong> </span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>java内部类有什么好处？为什么需要内部类？</strong></span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>首先举一个简单的例子，如果你想实现一个接口，但是这个接口中的一个方法和你构想的这个类中的一个方法的名称，参数相同，你应该怎么办？这时候，你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的，所以这样做可以完成所有你直接实现这个接口的功能。</strong></span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>不过你可能要质疑，更改一下方法的不就行了吗？</strong></span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>的确，以此作为设计内部类的理由，实在没有说服力。</strong></span></div>
<div style="line-height: 200%"><span style="font-size: 10pt; line-height: 200%"><strong>真正的原因是这样的，java中的内部类和接口加在一起，可以的解决常被C++程序员抱怨java中存在的一个问题 没有多继承。实际上，C++的多继承设计起来很复杂，而java通过内部类加上接口，可以很好的实现多继承的效果。</strong></span></div>
</div><img src ="http://www.blogjava.net/hulizhong/aggbug/255522.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-02-19 15:08 <a href="http://www.blogjava.net/hulizhong/archive/2009/02/19/255522.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 转 在applet中访问本地资源 </title><link>http://www.blogjava.net/hulizhong/archive/2009/02/08/253781.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sun, 08 Feb 2009 06:29:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/02/08/253781.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/253781.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/02/08/253781.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/253781.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/253781.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.blogjava.net/nokiaguy/archive/2009/02/06/253636.html">转自 http://www.blogjava.net/nokiaguy/archive/2009/02/06/253636.html</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; applet运行在沙盒中，因此，在默认情况下无法访问本地的资源。但可以通过签名的方式打破这一限制。签名的过程很简单，读者可以按如下的步骤编写一个applet程序，该程序读取了本地C盘的a.txt文件的内容，并显示在applet上，然后压缩成jar文件，并签名后进行发布。<br />
<br />
&nbsp;&nbsp;&nbsp; 下面使用的keytool和jarsigner都是JDK本身带的程序。<br />
<br />
第1步&nbsp; 编写applet程序<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.applet.</span><span style="color: #000000">*</span><span style="color: #000000">;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.awt.Graphics;<br />
<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;HelloWorld&nbsp;</span><span style="color: #0000ff">extends</span><span style="color: #000000">&nbsp;Applet<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;String&nbsp;name;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;paint(Graphics&nbsp;g)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawString(name,&nbsp;</span><span style="color: #000000">20</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">30</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;init()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">super</span><span style="color: #000000">.init();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">try</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;&nbsp;a.txt文件的内容要以UTF-8格式存储</span><span style="color: #008000"><br />
</span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.io.FileInputStream&nbsp;fis&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;java.io.FileInputStream(</span><span style="color: #000000">"</span><span style="color: #000000">c:\\a.txt</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.io.InputStreamReader&nbsp;isr&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;java.io.InputStreamReader(fis,&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">UTF-8</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.io.BufferedReader&nbsp;br&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;java.io.BufferedReader(isr);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;br.readLine();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">catch</span><span style="color: #000000">&nbsp;(Exception&nbsp;e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;e.getMessage();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
第2步:&nbsp; 编译HelloWorld.java，并压缩成hw.jar文件<br />
<br />
&nbsp;&nbsp;&nbsp; 编译HelloWorld.java后，使用如下的命令生成hw.jar<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;jar&nbsp;cvf&nbsp;hw.jar&nbsp;HelloWorld.class</span></div>
<br />
第3步：使用keytool命令生成密钥库<br />
<br />
&nbsp;&nbsp;&nbsp; 使用如下的命令生成hw.store文件：<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;keytool&nbsp;-genkey&nbsp;-keystore&nbsp;hw.store&nbsp;-alias&nbsp;helloworld&nbsp;&nbsp; <br />
</span></div>
<br />
&nbsp;&nbsp;&nbsp; 其中hw.store和helloworld是用户自定义的名字，读者可以起其他的名字。<br />
<br />
&nbsp;&nbsp;&nbsp; 在执行上面的命令后，会要求输出一个密码（输出两次，密码长度至少为6位字符），然后会要求输出一些信息，密码是必须输入的，而其他的信息如果不想输入，直接按回车即可，最后会让你确定是否生成hw.store文件，输入&#8220;y&#8221;，按回车，又让输出主密码，如果密码与刚才输出的密码相同，直接按回车。执行完命令后，会在当前目录生成一个hw.store文件。这一过程如下图所示。<br />
<div align="center"><img height="438" alt="" src="http://www.blogjava.net/images/blogjava_net/nokiaguy/applet/01.jpg" width="669" /></div>
&nbsp;&nbsp;&nbsp; 使用keytool命令生成hw.store文件的默认有效期是180天，也可以使用-validity命令行参数指定有效期，单位为天，如下面的命令指定了有效期为120天：<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000">&nbsp;keytool&nbsp;-genkey&nbsp;-keystore&nbsp;hw.store&nbsp;-alias&nbsp;helloworld&nbsp;&nbsp;&nbsp;-validity&nbsp;</span><span style="color: #000000">120</span></div>
<br />
&nbsp;&nbsp;&nbsp; 如果想查看hw.store的有效期和其他信息，可以使用如下的命令：<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000">keytool&nbsp;-list&nbsp;-v&nbsp;-alias&nbsp;helloworld&nbsp;-keystore&nbsp;hw.store</span></div>
<br />
&nbsp;&nbsp;&nbsp; 执行上面的命令后，将输出如下图所示的信息。<br />
<div align="center"><img alt="" src="http://www.blogjava.net/images/blogjava_net/nokiaguy/applet/03.jpg" /></div>
&nbsp;&nbsp;&nbsp; 上图显示的有效期是120天。<br />
<br />
第4步：使用jarsigner命令对hw.jar进行签名<br />
<br />
&nbsp;&nbsp;&nbsp; 执行如下的命令对hw.jar文件进行签名：<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000">jarsigner&nbsp;-keystore&nbsp;hw.store&nbsp;hw.jar&nbsp;helloworld</span></div>
<br />
第5步：发布与使用applet<br />
<br />
&nbsp;&nbsp;&nbsp; 将hw.jar文件放在&lt;Tomcat安装目录&gt;\webapps\test目录中，并在test目录中建立一个applet.jsp文件，代码如下：<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; background-color: #ffff00">&lt;%</span><span style="color: #000000; background-color: #f5f5f5">@&nbsp;page&nbsp;language</span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5">"</span><span style="color: #000000; background-color: #f5f5f5">java</span><span style="color: #000000; background-color: #f5f5f5">"</span><span style="color: #000000; background-color: #f5f5f5">&nbsp;contentType</span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5">"</span><span style="color: #000000; background-color: #f5f5f5">text/html;&nbsp;charset=UTF-8</span><span style="color: #000000; background-color: #f5f5f5">"</span><span style="color: #000000; background-color: #f5f5f5"><br />
&nbsp;&nbsp;&nbsp;&nbsp;pageEncoding</span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5">"</span><span style="color: #000000; background-color: #f5f5f5">UTF-8</span><span style="color: #000000; background-color: #f5f5f5">"</span><span style="color: #000000; background-color: #ffff00">%&gt;</span><span style="color: #000000"><br />
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">html</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">head</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">title</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">第一个applet</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">title</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">head</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">body</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">APPLET&nbsp;</span><span style="color: #ff0000">CODE</span><span style="color: #0000ff">="HelloWorld.class"</span><span style="color: #ff0000">&nbsp;width</span><span style="color: #0000ff">=200&nbsp;</span><span style="color: #ff0000">height</span><span style="color: #0000ff">=200&nbsp;</span><span style="color: #ff0000">ARCHIVE</span><span style="color: #0000ff">="hw.jar"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">APPLET</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">body</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">html</span><span style="color: #0000ff">&gt;</span></div>
<br />
&nbsp;&nbsp;&nbsp; 在IE地址栏中输出如下的URL：<br />
&nbsp;&nbsp;&nbsp; http://localhost:8080/test/applet.jsp<br />
<br />
&nbsp;&nbsp;&nbsp; 当第一次访问上面的URL时，会出现如下图所的对话框。<br />
<div align="center"><img alt="" src="http://www.blogjava.net/images/blogjava_net/nokiaguy/applet/05.jpg" /></div>
&nbsp;&nbsp;&nbsp; 选中对话框中的复选框，单击&#8220;运行&#8221;按钮，就可以正常运行applet了（下次再访问applet.jsp，就会会弹出这个对话框了），如果是firefox，也会弹出类似的对话框。<br />
<br />
&nbsp;&nbsp;&nbsp; 要运行这个applet，客户端需要安装jdk1.3或以上版本。<br />
<br />
&nbsp;&nbsp;&nbsp; 在linux下的firefox中试了一下，发现有时firefox无法正常显示applet。发生这种情况的原因是由于firefox未安装jdk插件，读者可按如下的方法为linux版的firefox安装jdk插件：<br />
<br />
&nbsp;&nbsp;&nbsp; jdk插件的库文件是<font id="font_word" style="font-size: 14px; font-family: 宋体,Verdana,Arial,Helvetica,sans-serif">libjavaplugin_oji.so，这个文件在&lt;JDK安装目录&gt;/</font><font id="font_word" style="font-size: 14px; font-family: 宋体,Verdana,Arial,Helvetica,sans-serif">jre/plugin/i386/ns7目录中，其中ns7根据当前jdk版本不同而有所差异，但前两个字母都为ns。在linux下，jdk一般都安装在</font><font id="font_word" style="font-size: 14px; font-family: 宋体,Verdana,Arial,Helvetica,sans-serif">/usr/java目录中。<br />
&nbsp;&nbsp;&nbsp; 找到</font><font id="font_word" style="font-size: 14px; font-family: 宋体,Verdana,Arial,Helvetica,sans-serif">libjavaplugin_oji.so文件后，再进行&lt;firefox的安装目录</font>&gt;/plugins目录中，使用如下的命令为<font id="font_word" style="font-size: 14px; font-family: 宋体,Verdana,Arial,Helvetica,sans-serif">libjavaplugin_oji.so文件添加一个</font>符号链接：<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000">ln&nbsp;-s&nbsp;&lt;jdk安装目录&gt;/jre/plugin/i386/ns7/libjavaplugin_oji.so <br />
</span></div>
<br />
&nbsp;&nbsp;&nbsp; 重启Firefox，再访问applet.jsp页面，就可以正常显示applet了。<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/253781.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-02-08 14:29 <a href="http://www.blogjava.net/hulizhong/archive/2009/02/08/253781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP在Servlet中的几个编码的作用及原理[转]</title><link>http://www.blogjava.net/hulizhong/archive/2008/08/08/220857.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 08 Aug 2008 03:18:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2008/08/08/220857.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/220857.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2008/08/08/220857.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/220857.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/220857.html</trackback:ping><description><![CDATA[<p style="text-indent: 2em">原文:http://blog.csdn.net/shenzhen_mydream/archive/2008/06/03/2507739.aspx<br />
</p>
<div class="blog_content">乱码问题好像跟我们中国程序员特别有缘，一直困扰着我们，从开始的JSP乱码问题，STRUTS乱码问题，到现在的AJAX乱码问题，无一不是搞得许多程序员焦头烂额的，整天骂XXX产品对中文支持不了，UTF-8无法使用中文啊什么的，其实这里面被骂的产品中其实99％以上是对中文支持非常好的，而出现乱码的原因只是因为自身对国际化支持以及文件编码等信息的认识不知造成的。要知道一个产品那么流行，怎么可能对中文支持不了呢，下面就开始一一帮大家解决这些问题。 <br />
<br />
1 、编码 <br />
－－ 想要解决好中文问题，对编码肯定是不能一概不懂了，编码是解决中文乱码问题的根本。 <br />
编码比较常用的有： UTF-8 ， GBK ， GB2312 ， ISO-8859-1 ，除了 iso-8859-1 之外的其它三个编码都能很好的支持中文，但它们都兼容 ISO-8859-1 的编码（就是说无论编码怎么改变，只要是 ISO-8859-1 中的字符，永远不会出现乱码）。 <br />
这四种编码中， GB2312 是中国规定的汉字编码，也可以说是简体中文的字符集编码 ; GBK 是 GB2312 的扩展 , 除了兼容 GB2312 外，它还能显示繁体中文，还有日文的假名 ; 而 UTF-8 虽然也支持中文，但却 与 GB 码不兼容（编码值不同） 。 UTF-8 使用的是可变长的 UNICODE 编码，编码可能是 1 位 16 进制（即 ISO-8859-1 中的字符，其编码也是相同的）也有可能是 2 位或 3 位的 16 进制。 UTF-8 的优点是： 1 、 与 CPU 字节顺序无关 , 可以在不同平台之间交流。 2 、容错能力高 , 任何一个字节损坏后 , 最多只会导致一个编码码位损失 , 不会链锁错误 ( 如 GB 码错一个字节就会整行乱码 ) ，所以在国际化处理中基本都是建议使用 UTF-8 作为编码。 <br />
<br />
2、文件的编码 <br />
－－虽然说只要设置了正确的编码就可以使字符正确显示了，但如果忽略了文件保存时的编码的话，那可是会让你走进迷雾中的。 <br />
文件编码最常使用的有两种：ANSI和UTF-8，光看名字估计你都可以猜到了，ANSI就是我们保存文件时使用的默认编码，而UTF-8则需自己设置。对于编码的改变，我使用的工具是NOTEPAD和ECLIPSE，NOTEPAD使用最简单，只要打开文件后在另存为中选择相应的编码就行了，而且它对编码的支持非常好;而在ECLIPSE中，只要稍微设置一下就行了，打开首选项，然后选择：常规-&gt;内容类型(ContentType)，在右边选中你想改变保存编码的文件类型，然后在下方的缺省编码中改变其值，最后点击更新（UPDATE）按钮即可。 <br />
<br />
<br />
而在其它的编辑器中，默认保存的内容都是GB2312或者GBK（NOTEPAD中对应ANSI）.而根据前面所说的UTF-8和GBK,GB2312等的编码值是不同的这一点，可以知道，如果文件使用了UTF-8，那么字符编码就必须使用UTF-8，否则编码值的不同就可能造成乱码。而这也就是为什么那么多的人使用了UTF-8编码后还会产生乱码的根本原因。（JS和JSP都是这个道理） <br />
<br />
3、JSP,STRUTS等的中文乱码解决方案 <br />
其实解决的方法只有一个： <br />
<br />
request.setCharacterEncoding(encoding); <br />
方法只有一种，但处理方式就多种多样了，初学者会在JSP页面上直接使用，而有经验的程序员会使用过滤器。而现在所要说的方法也是过滤器。这里以统一使用UTF-8作为编码作为例子说明。具体过程就不多说了，网上有很多教程。偷懒一点的，到TOMCAT中复制就行了。在TOMCAT的目录下的\webapps\jsp-examples\WEB-INF\classes\filters\找到SetCharacterEncodingFilter.java 这个类，放到你的程序中并配置好映射路径。配置好后基本上你的乱码问题就解决了。但要映射路径中需要注意的就是不能使用 '*' <br />
<br />
&lt; filter-mapping &gt; <br />
&lt; filter-name &gt; Set Character Encoding &lt;/ filter-name &gt; <br />
&lt; servlet-name &gt; * &lt;/ servlet-name &gt; <br />
&lt;/ filter-mapping &gt; <br />
像上面这样配置的话(可能也是网上大多教程的做法，想当年也是害苦了我)，可能你只有JSP的乱码解决了，要解决STRUTS的乱码需要映射 *.do 或者 servletActionName。然后在初始化参数中设置encoding的值就行了。 <br />
<br />
&lt; init-param &gt; <br />
&lt; param-name &gt; encoding &lt;/ param-name &gt; <br />
&lt; param-value &gt; UTF-8 &lt;/ param-value &gt; <br />
&lt;/ init-param &gt; <br />
当然，最重要的是要记得根据前面所说的方法，改变你所使用的编辑器保存文件的编码要与使用的字符编码一致。 <br />
而在JSP内容中，还是使用如网上教程所说的那种技俩，在所有页面的页首加入： <br />
<br />
&lt;% @ page language = "java" contentType = " text / html; charset = UTF - 8 " <br />
pageEncoding = "UTF - 8 " %&gt; <br />
至此，相信JSP,ACTION都不太可能出现乱码了。 <br />
<br />
4、资源文件的乱码解决方案 <br />
资源文件谁都知道是国际化支持不可或缺的一部分，如果资源文件都出现乱码了那还了得？其实资源文件的乱码是很好解决的，其原因也是因为使用了UTF-8做为JSP编码后，没有相应地改变资源文件的文件编码造成的，所以只要对资源文件保存的编码进行更正后，乱码问题也就解决了。当然，你的中文要使用 native2ascii 命令进行正确的转换。 <br />
<br />
5、调用JS时，JS内容乱码的解决方案。 <br />
其实JS的乱码还是跟文件的编码有关系的，如果JS中有中文的话，那JS文件保存的编码就必须跟调用此JS的页面编码相同，否则，你的所有中文都要从JSP页面传给JS才会显示正常。可以看出对于调用JS出现的乱码是最容易解决的（也是建立在前面的辛苦之下的）。 <br />
<br />
6、AJAX提交数据乱码，返回数据乱码的解决方案 <br />
随着AJAX的流行，乱码问题也开始困扰着许多刚开始使用它的程序员，幸好我之前对JSP乱码有过一点研究，在遇到AJAX后，并没有给我带来多大的困扰，在此将我的一些心得共享给大家。 <br />
万变不离其宗，AJAX的乱码问题自然跟编码有关了，其实很多人跟我一样想到了对文件编码进行设置，并且在接数据时设置了requet的编码，在返回的数据时设置了response的编码一切都以为会很顺利，可是这一切都是徒劳无功的，讨厌的乱码再一次出现在你眼前。在你试了N多种方法，包括JS自身的escape,unescape方法后，你发现乱码仍然猖狂地出现在屏幕上。 <br />
其实在试过这N多方法后，很多人都没发现，解决的方法其实很简单，而且其答案就在我们之前处理的JSP乱码之中。让我们先看一下AJAX的经典请求代码 <br />
<br />
<br />
xmlhttp.open( "post", url, async ); <br />
xmlhttp.setRequestHeader( "Content-Type", "text/html" ); <br />
xmlhttp.send( params ); <br />
通过前面的说明，不知道你现在看出端倪了没有。不知道是受了网上教程的影响还是其它方面影响，setRequestHeader并是万年不变的，也没人想过去改它，而问题就正好出在这个地方。回想一个JSP页面内容的编码设置，其中有这么一节： contentType="text/html; charset=UTF-8" <br />
现在知道问题了吧，所以我们要把第二句代码改为： xmlhttp.setRequestHeader( "Content-Type", "text/html;charset=UTF-8" ); <br />
最后别忘了在返回数据时也设置上： response.setContentType( "text/xml" ); <br />
response.setCharacterEncoding( "UTF-8" ); <br />
是不是很简单，一点都不麻烦呢？ <br />
如果要问为什么的话，其实我们可以把xmlhttp看成是一个临时页面，它由浏览器动态生成，主要作用是在后台获得请求的数据（可以看成是一个高级的iframe）。所以对于普通页面设置的编码，对它也要同样设置。而在servlet中返回数据为什么要设置contentType和encoding其道理也是一样的。众所周知，jsp的最后形态就是servlet，而jsp页首设置的那个内容其实也就是让生成的servlet中生成这么两句话： <br />
response.setContentType( "text/html" ); <br />
response.setCharacterEncoding( "UTF-8" ); <br />
而pageEncoding则是跟jvm说明了这个页面的内容要使用什么编码保存（这跟之后生成的CLASS有关系）。所以在servlet设置response的编码也是理所当然的了。 </div>
<div class="blog_content"></div>
<div class="blog_content"></div>
<div class="blog_content">
<h3><a href="http://renyangok.javaeye.com/blog/45372"><font color="#108ac6">J2ee中文问题的解决</font></a></h3>
<div class="blog_content">&nbsp;
<p><span class="postbody"><font size="2">第一，</font></span><span class="postbody"><font size="2">文件的的编码方式其实就包括两方面：存和取，</font></span><span class="postbody"><font size="2">存文件必须以一种编码存；读文件也必须以一种编码读。</font></span><span class="postbody"><font size="2">如果存取按照相同的编码方式，则不会有问题，关键就是很多时候存取的方式不一致，产生乱码。</font></span><span class="postbody"><font size="2">，如不特别设置取系统默认的编码，中文windows为GBK编码。<br />
<br />
从.java-&gt;.class过程是，先编写.java文件并按莫种编码方式保存，然后用javac方法编译此文件，注意如.java没按系统默认编码保存则要带encoding参数指明实际编码，否则出错，生成的.class文件存为系统默认编码。<br />
<br />
从.jsp-&gt;.java-&gt;.class，先存为某种编码的.jsp文件，然后tomcat根据pageEncoding读取并转化为servlet存为系统默认编码，然后同上面.java-&gt;.class过程。<br />
<br />
第二，IDE的encoding为对系统下文件打开的解码方式或保存的编码方式。特例：如果.jsp文件有&lt;%@ page language="java" pageEncoding="UTF-8"%&gt;，则eclipse会自动存为UTF-8方式，不管eclipse的encoding是什么，这也是 eclipse的聪明之处。<br />
<br />
第三，<br />
pageEncoding="UTF-8"表示此文件的编码方式，必须与此文件存储方式一致（所以eclipse会首选根据它来存文件），tomcat根据这个来读此.jsp文件并编译为servlet（至于编译成的.java和.class文件应该为tomcat服务器默认编码）。<br />
contentType="text/html;charset=UTF-8"表示当服务器给浏览器传页面文件时编码方式为UTF-8，形式为HTML。例如：<br />
&lt;%@ page language="java" pageEncoding="UTF-8"%&gt;<br />
&lt;%@ page contentType="text/html;charset=GBK"%&gt;<br />
&lt;html&gt;<br />
&nbsp;&lt;head&gt;<br />
&nbsp;&nbsp;&lt;title&gt;test&lt;/title&gt;<br />
&nbsp;&lt;/head&gt;<br />
&nbsp;&lt;body&gt;<br />
&nbsp;&nbsp;我是个好人<br />
&nbsp;&lt;/body&gt;<br />
&lt;/html&gt;<br />
</font></span></p>
<p>表示本jsp文件存为UTF-8字符集，当浏览器打开此页面后，查看原码就会发现源码为GBK字符集。<br />
<br />
第四，<br />
request.setCharacterEncoding("UTF-8")是把提交内容的字符集设为UTF－8<br />
response.setCharacterEncoding("UTF-8")可以把页面中的<font size="2">&lt;%@ page contentType="text/html;charset=iso8859-1"%&gt;换为charset=</font><font size="3">UTF-8，是给告诉浏览器我这个文件的编码方式。</font><br />
<br />
第五，表单提交：无论何种表单提交都可以在后台的java文件中通过String des = new String(s.getBytes("iso8859-1"),"UTF-8");来转换成你想要的UTF－8编码方式。但如果每处都加词句太麻烦，故分post和get两种方式区分提交（tomcat5以后分开处理，之前处理方式一样，即都可以用 request.setCharacterEncoding("UTF-8")方法处理，不过tomcat5以后get提交方法用此语句无效）。<span class="postbody"><font size="2"><br />
1,post提交的数据: <br />
程序加上org.springframework.web.filter.CharacterEncodingFilter过滤器. <br />
&lt;filter&gt; <br />
&lt;filter-name&gt;encodingFilter&lt;/filter-name&gt; <br />
&lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&lt;/filter-class&gt; <br />
&lt;init-param&gt; <br />
&lt;param-name&gt;encoding&lt;/param-name&gt; <br />
&lt;param-value&gt;UTF8&lt;/param-value&gt; <br />
&lt;/init-param&gt; <br />
&lt;init-param&gt; <br />
&lt;param-name&gt;forceEncoding&lt;/param-name&gt; <br />
&lt;param-value&gt;true&lt;/param-value&gt; <br />
&lt;/init-param&gt; <br />
&lt;/filter&gt; <br />
<br />
&lt;filter-mapping&gt; <br />
&lt;filter-name&gt;encodingFilter&lt;/filter-name&gt; <br />
&lt;url-pattern&gt;*.html&lt;/url-pattern&gt; <br />
&lt;/filter-mapping&gt; <br />
&lt;filter-mapping&gt; <br />
&lt;filter-name&gt;encodingFilter&lt;/filter-name&gt; <br />
&lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt; <br />
&lt;/filter-mapping&gt; <br />
<br />
因为规范要求浏览器提交数据都要用utf8编码,所以这里设置编码方式为UTF8. <br />
<br />
特别注意: <br />
a,这个过滤器只是简单的调用:request.setCharacterEncoding(this.encoding); <br />
在这个语句之前不能调用任何的request.getParameter()方法,否则会设置tomcat的缺省字符集为"ISO-8859-1",并且使 setCharacterEncoding的调用失效.所以在这个过滤器之前的过滤器中不能有对getParameter这类方法的调用,比较安全的做法就是把这个过滤器尽量靠前放. <br />
b,在server.xml中不能加上&lt;Valve className="org.apache.catalina.valves.RequestDumperValve"/&gt; <br />
这个value也设置tomcat的缺省字符集为"ISO-8859-1",使setCharacterEncoding的调用失效.可能其他的value也有这个问题,我没有测试过. <br />
如果要观察http请求参数,可以考虑用过滤器或者其他工具,例如ethereal(http://www.ethereal.com/) <br />
<br />
2,get提交的数据: <br />
两种情况: <br />
a,如果从地址栏直接输入汉字,则一般编码为"GBK",需要用 <br />
new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK") <br />
取出 <br />
b,如果是页面超连接连接中带的汉字,则编码根据页面编码的不同而不同,如果页面的 <br />
content="text/html; charset=utf-8",则在tomcat/conf/server.xml中的配置文件中: <br />
&lt;!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 --&gt; <br />
&lt;Connector port="8080" <br />
maxThreads="150" minSpareThreads="25" maxSpareThreads="75" <br />
enableLookups="false" redirectPort="8443" acceptCount="100" <br />
debug="0" connectionTimeout="20000" useBodyEncodingForURI="true" <br />
disableUploadTimeout="true" /&gt; <br />
<br />
加上:useBodyEncodingForURI="true"即可正常使用getParameter取出正确内容. <br />
如果content="text/html; charset=GBK",需用 <br />
new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK") <br />
取出,其他情况类似. <br />
<br />
总结: <br />
1,所有页面使用utf8编码, <br />
2,服务器加上过滤器, <br />
3,server.xml中不要使用 <br />
&lt;Valve className="org.apache.catalina.valves.RequestDumperValve"/&gt; <br />
4,server.xml文件加上useBodyEncodingForURI="true" <br />
这样应该可以搞定大多数前台的中文问题.至于地址栏输入中文,不支持也罢,一般的程序很少要求 <br />
从这里输入.<br />
<br />
第六，连接数据库</font></span>&nbsp;</p>
<p>1、mysql配置文件：<br />
修改mysql在windows\my.ini里default-character-set=utf-8</p>
<p>2、mysql里数据库和表也都设为utf8_unicode_ci</p>
<p>3、数据库连结：jdbc:mysql://localhost/mydb?useUnicode=true&amp;characterEncoding=utf-8<br />
注意，关键就在于此：此句中间是'&amp;'不是'&amp;amp;'这是因为数据库连结时，在.jsp和.java文件中应该用&amp;号，而XML文件中需要用&amp;amp</p>
<p>&nbsp;</p>
<h3><a href="http://janwer.javaeye.com/blog/150226"><font color="#108ac6">JSP/Servlet的编码原理</font></a></h3>
<p><strong>关键字: JSP,Servlet编码</strong> </p>
<div class="blog_content">
<p><font face="Verdana" size="2"><font size="3">首先，说说JSP/Servlet中的几个编码的作用</font><br />
<br />
在JSP/Servlet中主要有以下几种设置编码的方式：</font></p>
<ol>
    <li><font face="Verdana" size="2">pageEncoding ="UTF-8"</font>
    <li><font face="Verdana" size="2">contentType = "text/html;charset=UTF-8"</font>
    <li><font face="Verdana" size="2">request.setCharacterEncoding("UTF-8")</font>
    <li><font face="Verdana" size="2">response.setCharacterEncoding("UTF-8")</font> </li>
</ol>
<p><font face="Verdana" size="2">其中前两个只能用于JSP中，而后两个可以用于JSP和Servlet 中。<br />
<br />
1、pageEncoding="UTF-8"的作用是设置JSP编译成Servlet时使用的编码<br />
<br />
众所周知，JSP在服务器上是要先被编译成Servlet的。pageEncoding="UTF-8"的作用就是告诉JSP编译器在将 JSP文件编译成Servlet时使用的编码。通常，在JSP内部定义的字符串（直接在JSP中定义，而不是从浏览器提交的数据）出现乱码时，很多都是由于该参数设置错误引起的。例如，你的JSP文件是以GBK为编码保存的，而在JSP中却指定pageEncoding="UTF-8"，就会引起JSP内部定义的字符串为乱码。<br />
<br />
另外，该参数还有一个功能，就是在JSP中不指定contentType参数，也不使用response.setCharacterEncoding方法时，指定对服务器响应进行重新编码的编码。<br />
<br />
2、contentType="text/html;charset=UTF-8"的作用是指定对服务器响应进行重新编码的编码<br />
<br />
在不使用response.setCharacterEncoding方法时，用该参数指定对服务器响应进行重新编码的编码。</font></p>
<p><font face="Verdana" size="2">3、request.setCharacterEncoding("UTF-8")的作用是设置对客户端请求进行重新编码的编码。<br />
<br />
该方法用来指定对浏览器发送来的数据进行重新编码（或者称为解码）时，使用的编码。<br />
<br />
4、response.setCharacterEncoding("UTF-8")的作用是指定对服务器响应进行重新编码的编码。<br />
<br />
服务器在将数据发送到浏览器前，对数据进行重新编码时，使用的就是该编码。<br />
<br />
<font size="3">其次，要说一说浏览器是怎么样对接收和发送的数据进行编码的</font><br />
<br />
response.setCharacterEncoding("UTF-8")的作用是指定对服务器响应进行重新编码的编码。同时，浏览器也是根据这个参数来对其接收到的数据进行重新编码（或者称为解码）。所以在无论你在JSP中设置response.setCharacterEncoding("UTF-8")或者response.setCharacterEncoding ("GBK")，浏览器均能正确显示中文（前提是你发送到浏览器的数据编码是正确的，比如正确设置了pageEncoding参数等）。读者可以做个实验，在JSP中设置response.setCharacterEncoding("UTF-8")，在IE中显示该页面时，在IE的菜单中选择"查看 (V)"&#224;"编码(D)"中可以查看到是" Unicode（UTF-8）"，而在在JSP中设置response.setCharacterEncoding("GBK")，在IE中显示该页面时，在IE的菜单中选择"查看(V)"&#224;"编码(D)"中可以查看到是"简体中文（GB2312）"。<br />
<br />
浏览器在发送数据时，对URL和参数会进行URL编码，对参数中的中文，浏览器也是使response.setCharacterEncoding参数来进行URL编码的。以百度和GOOGLE为例，如果你在百度中搜索"汉字"，百度会将其编码为"%BA%BA%D7%D6"。而在GOOGLE中搜索"汉字"，GOOGLE会将其编码为"%E6%B1%89%E5%AD%97"，这是因为百度的response.setCharacterEncoding参数为GBK，而GOOGLE的的response.setCharacterEncoding参数为UTF-8。<br />
<br />
浏览器在接收服务器数据和发送数据到服务器时所使用的编码是相同的，默认情况下均为JSP页面的 response.setCharacterEncoding参数（或者contentType和pageEncoding参数），我们称其为浏览器编码。当然，在IE中可以修改浏览器编码（在IE的菜单中选择"查看(V)"&#224;"编码(D)"中修改），但通常情况下，修改该参数会使原本正确的页面中出现乱码。一个有趣的例子是，在IE中浏览GOOGLE的主页时，将浏览器编码修改为"简体中文（GB2312）"，此时，页面上的中文会变成乱码，不理它，在文本框中输入"汉字"，提交，GOOGLE会将其编码为"%BA%BA%D7%D6"，可见，浏览器在对中文进行URL编码时，使用的就是浏览器编码。<br />
<br />
弄清了浏览器是在接收和发送数据时，是如何对数据进行编码的了，我们再来看看服务器是在接收和发送数据时，是如何对数据进行编码的。<br />
<br />
对于发送数据，服务器按照response.setCharacterEncoding—&gt;contentType—&gt;pageEncoding的优先顺序，对要发送的数据进行编码。<br />
<br />
对于接收数据，要分三种情况。一种是浏览器直接用URL提交的数据，另外两种是用表单的GET和POST方式提交的数据。<br />
<br />
因为各种WEB服务器对这三种方式的处理也不相同，所以我们以Tomcat5.0为例。<br />
<br />
无论使用那种方式提交，如果参数中包含中文，浏览器都会使用当前浏览器编码对其进行URL编码。<br />
<br />
对于表单中POST方式提交的数据，只要在接收数据的JSP中正确request.setCharacterEncoding参数，即将对客户端请求进行重新编码的编码设置成浏览器编码，就可以保证得到的参数编码正确。有写读者可能会问，那如何得到浏览器编码呢？上面我们提过了，在默认请情况下，浏览器编码就是你在响应该请求的JSP页面中response.setCharacterEncoding设置的值。所以对于POST表单提交的数据，在获得数据的JSP页面中request.setCharacterEncoding要和生成提交该表单的JSP页面的 response.setCharacterEncoding设置成相同的值。<br />
<br />
对于URL提交的数据和表单中GET方式提交的数据，在接收数据的JSP中设置 request.setCharacterEncoding参数是不行的，因为在Tomcat5.0中，默认情况下使用ISO-8859-1对URL提交的数据和表单中GET方式提交的数据进行重新编码（解码），而不使用该参数对URL提交的数据和表单中GET方式提交的数据进行重新编码（解码）。要解决该问题，应该在Tomcat的配置文件的Connector标签中设置useBodyEncodingForURI或者URIEncoding属性，其中 useBodyEncodingForURI参数表示是否用request.setCharacterEncoding参数对URL提交的数据和表单中 GET方式提交的数据进行重新编码，在默认情况下，该参数为false（Tomcat4.0中该参数默认为true）；URIEncoding参数指定对所有GET方式请求（包括URL提交的数据和表单中GET方式提交的数据）进行统一的重新编码（解码）的编码。URIEncoding和 useBodyEncodingForURI区别是，URIEncoding是对所有GET方式的请求的数据进行统一的重新编码（解码），而 useBodyEncodingForURI则是根据响应该请求的页面的request.setCharacterEncoding参数对数据进行的重新编码（解码），不同的页面可以有不同的重新编码（解码）的编码。所以对于URL提交的数据和表单中GET方式提交的数据，可以修改URIEncoding 参数为浏览器编码或者修改useBodyEncodingForURI为true，并且在获得数据的JSP页面中 request.setCharacterEncoding参数设置成浏览器编码。<br />
<br />
下面总结下，以Tomcat5.0为WEB服务器时，如何防止中文乱码<br />
</font></p>
<ol>
    <li><font face="Verdana" size="2">对于同一个应用，最好统一编码，推荐为UTF-8，当然GBK也可以。</font>
    <li><font face="Verdana" size="2">正确设置JSP的pageEncoding参数</font>
    <li><font face="Verdana" size="2">在所有的JSP/Servlet中设置contentType="text/html;charset=UTF-8"或response.setCharacterEncoding("UTF-8")，从而间接实现对浏览器编码的设置。</font>
    <li><font face="Verdana" size="2">对于请求，可以使用过滤器或者在每个JSP/Servlet中设置request.setCharacterEncoding ("UTF-8")。同时，要修改Tomcat的默认配置，推荐将useBodyEncodingForURI参数设置为true，也可以将 URIEncoding参数设置为UTF-8（有可能影响其他应用，所以不推荐.）。</font> </li>
</ol>
</div>
</div>
</div><img src ="http://www.blogjava.net/hulizhong/aggbug/220857.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2008-08-08 11:18 <a href="http://www.blogjava.net/hulizhong/archive/2008/08/08/220857.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Top Ten Errors Java Programmers Make</title><link>http://www.blogjava.net/hulizhong/archive/2008/08/01/219255.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 01 Aug 2008 02:30:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2008/08/01/219255.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/219255.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2008/08/01/219255.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/219255.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/219255.html</trackback:ping><description><![CDATA[<h1 align="center">Top Ten Errors Java Programmers Make</h1>
<h3 align="center">(How to spot them. How to fix/prevent them.)</h3>
<p align="center">By David Reilly</p>
<p>Whether you program regularly in Java, and know it like the back of your hand, or whether you're new to the language or a casual programmer, you'll make mistakes. It's natural, it's human, and guess what? You'll more than likely make the same mistakes that others do, over and over again. Here's my top ten list of errors that we all seem to make at one time or another,&nbsp; how to spot them, and how to fix them.</p>
<h3>10. Accessing non-static member variables from static methods (such as main)</h3>
<p>Many programmers, particularly when first introduced to Java, have problems with accessing member variables from their <em>main</em> method. The method signature for main is marked static - meaning that we don't need to create an instance of the class to invoke the main method. For example, a Java Virtual Machine (JVM) could call the class MyApplication like this :-</p>
<blockquote>
<p>MyApplication.main ( command_line_args );</p>
</blockquote>
<p>This means, however, that there isn't an instance of MyApplication - it doesn't have any member variables to access! Take for example the following application, which will generate a compiler error message.</p>
<pre>public class StaticDemo
{
public String my_member_variable = "somedata";</pre>
<pre>        public static void main (String args[])
{
// Access a non-static member from static method
System.out.println ("This generates a compiler error" +
my_member_variable );
}
}</pre>
<p>If you want to access its member variables from a non-static method (like <em>main</em>), you must create an instance of the object. Here's a simple example of how to correctly write code to access non-static member variables, by first creating an instance of the object.</p>
<pre>public class NonStaticDemo
{
public String my_member_variable = "somedata";
public static void main (String args[])
{
NonStaticDemo demo = new NonStaticDemo();
// Access member variable of demo
System.out.println ("This WON'T generate an error" +
demo.my_member_variable );
}
}</pre>
<h3>9. Mistyping the name of a method when overriding</h3>
<p>Overriding allows programmers to replace a method's implementation with new code. Overriding is a handy feature, and most OO programmers make heavy use of it. If you use the AWT 1.1 event handling model, you'll often override listener implementations to provide custom functionality. One easy trap to fall into with overriding, is to mistype the method name. If you mistype the name, you're no longer overriding a method - you're creating an entirely new method, but with the same parameter and return type.</p>
<pre>public class MyWindowListener extends WindowAdapter {
// This should be WindowClose<strong>d</strong>
public void WindowClose(WindowEvent e) {
// Exit when user closes window
System.exit(0);
}
});
</pre>
<p>Compilers won't pick up on this one, and the problem can be quite frustrating to detect. In the past, I've looked at a method, believed that it was being called, and taken ages to spot the problem. The symptom of this error will be that your code isn't being called, or you think the method has skipped over its code. The only way to ever be certain is to add a println statement, to record a message in a log file, or to use good trace debugger (like Visual J++ or Borland JBuilder) and step through line by line. If your method still isn't being called, then it's likely you've mistyped the name.</p>
<h3>8. Comparison assignment (&nbsp; = rather than == )</h3>
<p>This is an easy error to make. If you're used other languages before, such as Pascal, you'll realize just how poor a choice this was by the language's designers. In Pascal, for example, we use the := operator for assignment, and leave = for comparison. This looks like a throwback to C/C++, from which Java draws its roots.</p>
<p>Fortunately, even if you don't spot this one by looking at code on the screen, your compiler will. Most commonly, it will report an error message like this : "Can't convert xxx to boolean", where xxx is a Java type that you're assigning instead of comparing.</p>
<h3>7. Comparing two objects ( == instead of .equals)</h3>
<p>When we use the == operator, we are actually comparing two object references, to see if they point to the same object. We cannot compare, for example, two strings for equality, using the == operator. We must instead use the .equals method, which is a method inherited by all classes from java.lang.Object.</p>
<p>Here's the correct way to compare two strings.</p>
<pre>String abc = "abc"; String def = "def";
// Bad way
if ( (abc + def) == "abcdef" )
{
&nbsp;&nbsp;&nbsp; ......
}</pre>
<pre>// Good way
if ( (abc + def).equals("abcdef") )
{
&nbsp;&nbsp; .....
}</pre>
<h3>6. Confusion over passing by value, and passing by reference</h3>
<p>This can be a frustrating problem to diagnose, because when you look at the code, you might be sure that its passing by reference, but find that its actually being passed by value. Java uses <strong>both</strong>, so you need to understand when you're passing by value, and when you're passing by reference.</p>
<p>When you pass a primitive data type, such as a char, int, float, or double, to a function then you are <strong>passing by value</strong>. That means that a copy of the data type is duplicated, and passed to the function. If the function chooses to modify that value, it will be modifying the copy only. Once the function finishes, and control is returned to the returning function, the "real" variable will be untouched, and no changes will have been saved. If you need to modify a primitive data type, make it a return value for a function, or wrap it inside an object.</p>
<p>When you pass a Java object, such as an array, a vector, or a string, to a function then you are <strong>passing by reference</strong>. Yes - a String is actually an object, not a primitive data type.&nbsp; So that means that if you pass an object to a function, you are passing a reference to it, not a duplicate. Any changes you make to the object's member variables will be permanent - which can be either good or bad, depending on whether this was what you intended.</p>
<p>On a side note, since String contains no methods to modify its contents, you might as well be passing by value.</p>
<h3>5. Writing blank exception handlers</h3>
<p>I know it's very tempting to write blank exception handlers, and to just ignore errors. But if you run into problems, and haven't written any error messages, it becomes almost impossible to find out the cause of the error. Even the simplest exception handler can be of benefit. For example, put a try { .. } catch Exception around your code, to catch ANY type of exception, and print out the message. You don't need to write a custom handler for every exception (though this is still good programming practice). Don't ever leave it blank, or you won't know what's happening.</p>
<p>For example</p>
<pre>public static void main(String args[])
{
&nbsp;&nbsp;&nbsp; try {
// Your code goes here..
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; catch (Exception e)
&nbsp;&nbsp;&nbsp; {
System.out.println ("Err - " + e );
&nbsp;&nbsp;&nbsp; }
}</pre>
<h3>4. Forgetting that Java is zero-indexed</h3>
<p>If you've come from a C/C++ background, you may not find this quite as much a problem as those who have used other languages. In Java, arrays are zero-indexed, meaning that the first element's index is actually 0. Confused? Let's look at a quick example.</p>
<pre>// Create an array of three strings
String[] strArray = new String[3];
// First element's index is actually 0
strArray[0] = "First string";
// Second element's index is actually 1
strArray[1] = "Second string";
// Final element's index is actually 2
strArray[2] = "Third and final string";</pre>
<p>In this example, we have an array of three strings, but to access elements of the array we actually subtract one. Now, if we were to try and access strArray[3], we'd be accessing the fourth element. This will case an ArrayOutOfBoundsException to be thrown - the most obvious sign of forgetting the zero-indexing rule.</p>
<p>Other areas where zero-indexing can get you into trouble is with strings. Suppose you wanted to get a character at a particular offset within a string. Using the String.charAt(int) function you can look this information up - but under Java, the String class is also zero-indexed. That means than the first character is at offset 0, and second at offset 1. You can run into some very frustrating problems unless you are aware of this - particularly if you write applications with heavy string processing. You can be working on the wrong character, and also throw exceptions at run-time. Just like the ArrayOutOfBoundsException, there is a string equivalent. Accessing beyond the bounds of a String will cause a StringIndexOutOfBoundsException to be thrown, as demonstrated by this example.</p>
<pre>public class StrDemo
{
public static void main (String args[])
{
String abc = "abc";
System.out.println ("Char at offset 0 : " + abc.charAt(0) );
System.out.println ("Char at offset 1 : " + abc.charAt(1) );
System.out.println ("Char at offset 2 : " + abc.charAt(2) );
// This line should throw a StringIndexOutOfBoundsException
System.out.println ("Char at offset 3 : " + abc.charAt(3) );
}
}</pre>
<p>Note too, that zero-indexing doesn't just apply to arrays, or to Strings. Other parts of Java are also indexed, but not always consistently. The java.util.Date, and java.util.Calendar classes start their months with 0, but days start normally with 1. This problem is demonstrated by the following application.</p>
<pre>import java.util.Date;
import java.util.Calendar;
public class ZeroIndexedDate
{
public static void main (String args[])
{
// Get today's date
Date today = new Date();
// Print return value of getMonth
System.out.println ("Date.getMonth() returns : " +
today.getMonth());
// Get today's date using a Calendar
Calendar rightNow = Calendar.getInstance();
// Print return value of get ( Calendar.MONTH )
System.out.println ("Calendar.get (month) returns : " +
rightNow.get ( Calendar.MONTH ));</pre>
<pre>        }
}</pre>
<p>Zero-indexing is only a problem if you don't realize that its occurring. If you think you're running into a problem, always consult your API documentation.</p>
<h3>3. Preventing concurrent access to shared variables by threads</h3>
<p>When writing multi-threaded applications, many programmers (myself included) often cut corners, and leave their applications and applets vulnerable to thread conflicts. When two or more threads access the same data concurrently, there exists the possibility (and Murphy's law holding, the probability) that two threads will access or modify the same data at the same time. Don't be fooled into thinking that such problems won't occur on single-threaded processors. While accessing some data (performing a read), your thread may be suspended, and another thread scheduled. It writes its data, which is then overwritten when the first thread makes its changes.</p>
<p>Such problems are not just limited to multi-threaded applications or applets. If you write Java APIs, or JavaBeans, then your code may not be thread-safe. Even if you never write a single application that uses threads, people that use your code WILL. For the sanity of others, if not yourself, you should always take precautions to prevent concurrent access to shared data.</p>
<p>How can this problem be solved? The simplest method is to make your variables private (but you do that already,&nbsp; right?) and to use synchronized accessor methods. Accessor methods allow access to private member variables, but in a controlled manner. Take the following accessor methods, which provide a safe way to change the value of a counter.</p>
<pre>public class MyCounter
{
private int count = 0; // count starts at zero
public synchronized void setCount(int amount)
{
count = amount;
}
public synchronized int getCount()
{
return count;
}
}</pre>
<h3>2. Capitalization errors</h3>
<p>This is one of the most frequent errors that we all make. It's so simple to do, and sometimes one can look at an uncapitalized variable or method and still not spot the problem. I myself have often been puzzled by these errors, because I recognize that the method or variable does exist, but don't spot the lack of capitalization.</p>
<p>While there's no silver bullet for detecting this error, you can easily train yourself to make less of them. There's a very simple trick you can learn :-
<ul>
    <li>all methods and member variables in the Java API begin with lowercase letters
    <li>all methods and member variables use capitalization where a new word begins e.g - getDoubleValue() </li>
</ul>
<p>If you use this pattern for all of your member variables and classes, and then make a conscious effort to get it right, you can gradually reduce the number of mistakes you'll make. It may take a while, but it can save some serious head scratching in the future.</p>
<h2 align="center">(drum roll)</h2>
<h2 align="center">And the number one error that Java programmers make !!!!!</h2>
<p>&nbsp;</p>
<h1 align="center">1. Null pointers!</h1>
<p>Null pointers are one of the most common errors that Java programmers make. Compilers can't check this one for you - it will only surface at runtime, and if you don't discover it, your users certainly will.</p>
<p>When an attempt to access an object is made, and the reference to that object is null, a NullPointerException will be thrown. The cause of null pointers can be varied, but generally it means that either you haven't initialized an object, or you haven't checked the return value of a function.</p>
<p>Many functions return null to indicate an error condition - but unless you check your return values, you'll never know what's happening. Since the cause is an error condition, normal testing may not pick it up - which means that your users will end up discovering the problem for you. If the API function indicates that null may be returned, be sure to check this before using the object reference!</p>
<p>Another cause is where your initialization has been sloppy, or where it is conditional. For example, examine the following code, and see if you can spot the problem.</p>
<pre>public static void main(String args[])
{
// Accept up to 3 parameters
String[] list = new String[3];
int index = 0;
while ( (index &lt; args.length) &amp;&amp; ( index &lt; 3 ) )
{
list[index++] = args[index];
}
// Check all the parameters
for (int i = 0; i &lt; list.length; i++)
{
if (list[i].equals "-help")
{
// .........
}
else
if (list[i].equals "-cp")
{
// .........
}
// else .....
}
}</pre>
<p>This code (while a contrived example), shows a common mistake. Under some circumstances, where the user enters three or more parameters, the code will run fine. If no parameters are entered, you'll get a NullPointerException at runtime. Sometimes your variables (the array of strings) will be initialized, and other times they won't. One easy solution is to check BEFORE you attempt to access a variable in an array that it is not equal to null.</p>
<h3>Summary</h3>
<p>These errors represent but some of the many that we all make. Though it is impossible to completely eliminate errors from the coding process, with care and practice you can avoid repeating the same ones. Rest assured, however, that all Java programmers encounter the same sorts of problems. It's comforting to know, that while you work late into the night tracking down an error, someone, somewhere, sometime, will make the same mistake!</p>
<div align="center">
<center>
<table width="80%" border="0">
    <tbody>
        <tr>
            <td width="100%" bgcolor="#eaf8ff">We'd like to thank the readers of the comp.lang.java.programmer newsgroup for their suggestions for the top ten. Regrettably, due to the number of submissions, not every error could be featured - but we think this "Top Ten" list represents the most popular and frequent errors people make.</td>
        </tr>
    </tbody>
</table>
</center></div><img src ="http://www.blogjava.net/hulizhong/aggbug/219255.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2008-08-01 10:30 <a href="http://www.blogjava.net/hulizhong/archive/2008/08/01/219255.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Undefined exploded archive location</title><link>http://www.blogjava.net/hulizhong/archive/2008/06/20/209502.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 20 Jun 2008 09:07:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2008/06/20/209502.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/209502.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2008/06/20/209502.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/209502.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/209502.html</trackback:ping><description><![CDATA[今天下午一个工程突然不能部署了,myeclipse提示Undefined exploded archive location<br />
在网上发现了这篇文章,解决了问题.特此感谢.<br />
http://hi.baidu.com/gisland/blog/item/5daabffb4abf4e274f4aea9b.html<br />
<table style="table-layout: fixed">
    <tbody>
        <tr>
            <td>
            <div class="cnt" id="blog_text">
            <p><font color="#3366ff"><strong>昨天在工程项目的调试中，突然发现项目不能部署了。这个问题以前都碰到过多次，都是折腾半天，重装ECLIPSE或TOMCAT、更换工作空间，胡乱搞定但未真正找到问题的根结点，今天终于找到问题的最好解决办法了，作一下备忘，顺便拿出来共享一下。</strong></font></p>
            <p><font color="#3366ff"><strong>错误信息为：<br />
            </strong></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff0000">Undefined exploded archive location</font></p>
            <p><br />
            <strong><font color="#3366ff">原因：</font></strong><br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在工程转移过程中，导致工程的<font color="#ff0000">配置文件出错</font>；</p>
            <p><br />
            <strong><font color="#3366ff">解决方法：<br />
            </font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.在工程目录下的<font color="#ff0000">.mymetadata</font>文件中可能<font color="#ff0000">webrootdir</font>被改无效了(把下面内容拷到你的.mymetadate文件中的相应位置上)；或者有可能少了这<font color="#ff0000">context-root</font>这个属性；添加上这个属性即可，内容如下：<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff0000">context-root="/上面的name属性值"<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font>2.关掉Eclipse，再启动Eclipse，接着发布工程，发布成功！</p>
            <p><strong><font color="#3366ff">相关截图：</font></strong></p>
            <p><img height="213" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/Snap1.JPG" width="442" border="0" /><strong><font color="#3366ff"></font></strong></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/209502.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2008-06-20 17:07 <a href="http://www.blogjava.net/hulizhong/archive/2008/06/20/209502.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>