﻿<?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-勇于挑战</title><link>http://www.blogjava.net/gp213/</link><description>不畏艰辛</description><language>zh-cn</language><lastBuildDate>Thu, 23 Apr 2026 20:54:46 GMT</lastBuildDate><pubDate>Thu, 23 Apr 2026 20:54:46 GMT</pubDate><ttl>60</ttl><item><title>分组函数GROUP BY的扩展使用总结</title><link>http://www.blogjava.net/gp213/archive/2009/06/25/284066.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 25 Jun 2009 01:44:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/06/25/284066.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 出自http://blog.csdn.net/wizardlun/archive/2009/02/19/3912287.aspx1.ROLLUP子句&nbsp;&nbsp;&nbsp;&nbsp; ROLLUP是GROUP BY子句的扩展，它是为每一个分组返回一条合计记录，并为全部分组返回总计。下面举例说明：EG：select * from employees2;EMPLOYE...&nbsp;&nbsp;<a href='http://www.blogjava.net/gp213/archive/2009/06/25/284066.html'>阅读全文</a><img src ="http://www.blogjava.net/gp213/aggbug/284066.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-06-25 09:44 <a href="http://www.blogjava.net/gp213/archive/2009/06/25/284066.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebWork 2.1 / 2.2 与 Spring 集成方法总结</title><link>http://www.blogjava.net/gp213/archive/2009/06/23/283721.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Tue, 23 Jun 2009 04:37:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/06/23/283721.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/283721.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/06/23/283721.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/283721.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/283721.html</trackback:ping><description><![CDATA[<h4>第一部分：WebWork2.1与<a target="_self"><span style="text-decoration: underline"><strong>Spring</strong></span></a>集成</h4>
<p>两种方法：</p>
<h5>法一：先<a target="_self"><span style="text-decoration: underline"><strong><span class="hilite1">WebWork</span></strong></span></a>后Spring（利用webwork2-spring.jar集成）</h5>
<p>该方法的执行顺序是先<span class="hilite1">WebWork</span>，后Spring。<br />
即，首先在xwork.xml中找到待执行的action，如果该action依赖其他bean，再到applicationContext.xml中查找被依赖的bean。</p>
<p>WebWork2的IoC功能实现是在xwork.xml中，指定action类与其他bean的依赖关系。即：<br />
(1)xwork.xml文件<br />
&lt;action name="myAction" class="com.ryandaigle.web.actions.MyAction"&gt;<br />
&lt;external-ref name="DAO"&gt;myDAO&lt;/external-ref&gt;<br />
&lt;result name="success" type="dispatcher"&gt;<br />
&lt;param name="location"&gt;/success.jsp&lt;/param&gt;<br />
&lt;/result&gt;<br />
&lt;/action&gt;</p>
<p>(2)applicationContext.xml文件<br />
&lt;bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" /&gt;</p>
<p>具体集成步骤如下：</p>
<p>1. <a target="_self"><span style="text-decoration: underline"><strong>下载</strong></span></a>以下文件：<br />
<span class="nobr"><a title="Visit page outside Confluence" href="http://www.ryandaigle.com/pebble/images/webwork2-spring.jar" rel="nofollow">http://www.ryandaigle.com/pebble/images/webwork2-spring.jar<sup><img class="rendericon" title="点击图片可在新窗口打开" style="cursor: pointer" height="7" alt="" src="http://hi.baidu.com/fc/editor/" width="7" align="absMiddle" border="0" /></sup></a></span></p>
<p>2. 配置xwork.xml</p>
<p>2.1 在xwork.xml中定义action时，采用external-ref来指定依赖的spring bean</p>
<p>&lt;action name="myAction" class="com.foo.Action"&gt;<br />
&lt;external-ref name="someDao"&gt;someDao&lt;/external-ref&gt;</p>
<p>&lt;result name="success" type="dispatcher"&gt;<br />
&lt;param name="location"&gt;/success.jsp&lt;/param&gt;<br />
&lt;/result&gt;<br />
&lt;/action&gt;</p>
<p>2.2 在&lt;package&gt;中指定外部引用解析器</p>
<p>&lt;package name="default" extends="<span class="hilite1">webwork</span>-default"<br />
externalReferenceResolver="com.atlassian.xwork.ext.SpringServletContextReferenceResolver"/&gt;</p>
<p>SpringServletContextReferenceResolver作用是在applicationContext.xml中解析外部引用的bean</p>
<p>2.3 增加一个拦截器，允许引用作为外部资源进行解析<br />
&lt;interceptors&gt;<br />
&lt;interceptor name="reference-resolver" class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/&gt;<br />
&lt;interceptor-stack name="myDefaultWebStack"&gt;<br />
&lt;interceptor-ref name="defaultStack"/&gt;<br />
&lt;interceptor-ref name="reference-resolver"/&gt;<br />
&lt;/interceptor-stack&gt;<br />
&lt;/interceptors&gt;<br />
&lt;default-interceptor-ref name="myDefaultWebStack"/&gt;</p>
<p>3. 在web.xml中配置Spring与XWork的外部解析器在同一个Web Context中工作<br />
&lt;!--载入spring配置文件--&gt;<br />
&nbsp;&lt;context-param&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; classpath:/applicationContext.xml<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/param-value&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/context-param&gt;<br />
<br />
&lt;listener&gt;<br />
&lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;<br />
&lt;/listener&gt;</p>
<p>&lt;listener&gt;<br />
&lt;listener-class&gt;com.atlassian.xwork.ext.ResolverSetupServletContextListener&lt;/listener-class&gt;<br />
&lt;/listener&gt;</p>
<p>第一个侦听器是spring必需的，无论是否与<span class="hilite1">WebWork</span>集成，都要<a target="_self"><span style="text-decoration: underline"><strong>定义</strong></span></a>该侦听器；第二个侦听器是外部解析器所需要的，作用是通过ServletContext来取出Spring的ApplicationContext，作为<span class="hilite1">WebWork</span>与Spring的&#8220;桥梁&#8221;。</p>
<p>4.applicationContext.xml文件跟xwork.xml文件的存放路径一下，都在classpath下<br />
在applicationContext.xml中定义被引用的bean<br />
&lt;bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" /&gt;</p>
<p>说明：<br />
如果不使用WebWork2，完全使用Spring，达到上述<a target="_self"><span style="text-decoration: underline"><strong>效果</strong></span></a>，在applicationContext.xml中采取类似定义：<br />
&lt;bean id="myAction" class="com.ryandaigle.web.actions.MyAction" singleton="false"&gt;<br />
&lt;property name="DAO"&gt;<br />
&lt;ref bean="myDAO"/&gt;<br />
&lt;/property&gt;<br />
&lt;bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" /&gt;</p>
<p>&nbsp;</p>
<h5>法二：先Spring后<span class="hilite1">WebWork</span>（利用spring-xwork-integration.jar集成）</h5>
<p>该方法的执行顺序是先Spring，后<span class="hilite1">WebWork</span>。<br />
即，首先由Spring负责wire所有的依赖关系，再由XWork来执行action。</p>
<p>注意：因为XWork为每个action调用生成一个新的类实例，action在Spring的applicationContext.xml中应该<a target="_self"><span style="text-decoration: underline"><strong>配置</strong></span></a>为prototype。</p>
<p>1. 配置SpringObjectFactory</p>
<p>(0)下载<a href="https://xwork-optional.dev.java.net/files/documents/1475/11992/spring-xwork-integration.jar">https://xwork-optional.dev.java.net/files/documents/1475/11992/spring-xwork-integration.jar</a></p>
<p>(1) 可以在web.xml配置</p>
<p>&lt;listener&gt;<br />
&lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;<br />
&lt;/listener&gt;</p>
<p>&lt;listener&gt;<br />
&lt;listener-class&gt;com.opensymphony.xwork.spring.SpringObjectFactoryListener&lt;/listener-class&gt;<br />
&lt;/listener&gt;</p>
<p>(2) 或者：在applicationContext.xml配置<br />
&lt;bean id="springObjectFactory"<br />
class="com.opensymphony.xwork.spring.SpringObjectFactory"<br />
init-method="initObjectFactory"/&gt;</p>
<p>以上两种方法任选一种即可</p>
<p>2. 在applicationContext.xml中定义bean<br />
&lt;bean name="some-action" class="fully.qualified.class.name" singleton="false"&gt;<br />
&lt;property name="someProperty"&gt;&lt;ref bean="someOtherBean"/&gt;&lt;/property&gt;<br />
&lt;/bean&gt;</p>
<p>3. 在xwork.xml中定义action（注意action的class与bean的id相同）</p>
<p>&lt;action name="myAction" class="some-action"&gt;<br />
&lt;result name="success"&gt;view.jsp&lt;/result&gt;<br />
&lt;/action&gt;</p>
<p>说明：<br />
该方法与<span class="hilite1">WebWork</span> 2.2中的方法基本相同。</p>
<p>&nbsp;</p>
<h4>第二部分：WebWork2.2与Spring的集成</h4>
<p>Spring是WebWork2.2中唯一支持的IoC容器。</p>
<p>1 配置<span class="hilite1">webwork</span>.properties文件，指定spring作为<span class="hilite1">webwork</span>的IoC容器<br />
<span class="hilite1">webwork</span>.objectFactory = spring<br />
(1)默认的autowiring模式是：by name<br />
即如果applicationContext.xml文件中的bean id与xwork.xml文件中的action name相同，就<br />
(2)如果要改为其他模式：<br />
<span class="hilite1">webwork</span>.objectFactory.spring.autoWire = name|type|auto|constructor</p>
<p>2 配置web.xml文件，启动Spring侦听器<br />
&lt;listener&gt;<br />
&lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;<br />
&lt;/listener&gt;</p>
<p>3 在WEB-INF目录下增加applicationContext.xml文件<br />
例：<br />
&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"&gt;<br />
&lt;beans default-autowire="autodetect"&gt;<br />
&lt;bean id="personManager" class="com.acme.PersonManager"/&gt;<br />
...<br />
&lt;/beans&gt;</p>
<p>4 设置由Spring来初始化action<br />
4.1&nbsp;&nbsp;&nbsp;&nbsp; 在Spring的applicationContext.xml文件中配置bean（即action类）<br />
4.2&nbsp;&nbsp;&nbsp;&nbsp; 将xwork.xml中的action的class属性，由class名改为Spring中定义的bean名<br />
例如：<br />
(1)applicationContext.xml中，定义bean id是bar<br />
&lt;beans default-autowire="autodetect"&gt;<br />
&lt;bean id="bar" class="com.my.BarClass" singleton="false"/&gt;<br />
...<br />
&lt;/beans&gt;<br />
(2)xwork.xml中，action的class="bar"，而不是通常的类名<br />
&lt;package name="secure" namespace="/secure" extends="default"&gt;<br />
&lt;action name="bar" class="bar"&gt;<br />
&lt;result&gt;bar.ftl&lt;/result&gt;<br />
&lt;/action&gt;<br />
&lt;/package&gt;</p>
<img src ="http://www.blogjava.net/gp213/aggbug/283721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-06-23 12:37 <a href="http://www.blogjava.net/gp213/archive/2009/06/23/283721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebWork深入浅出 </title><link>http://www.blogjava.net/gp213/archive/2009/06/23/283697.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Tue, 23 Jun 2009 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/06/23/283697.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 前言 本篇文章并没有太多WebWork 的实战代码细节。本人非常希望能充当一名导游的角色，带领读者逐步游览WebWork的功能特性和原理。在第一章，我们将提出基于三层架构的Web层需要解决的10个问题，这是本文的纵轴。围绕着纵轴，我们按照横轴的顺序逐步描述讲解：WebWork简介、WebWork入门、WebWork原理、WebWork实战和技巧、展望WebWork未来、最后是本文的总结。基...&nbsp;&nbsp;<a href='http://www.blogjava.net/gp213/archive/2009/06/23/283697.html'>阅读全文</a><img src ="http://www.blogjava.net/gp213/aggbug/283697.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-06-23 10:11 <a href="http://www.blogjava.net/gp213/archive/2009/06/23/283697.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java反射机制(reflect)解决   （调用一个类中多个规律命名的方法的办法）</title><link>http://www.blogjava.net/gp213/archive/2009/05/14/270616.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 14 May 2009 07:27:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/05/14/270616.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/270616.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/05/14/270616.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/270616.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/270616.html</trackback:ping><description><![CDATA[在实际生产中常常会遇见这样一种情况，就是由于数据库设计或者页面设计导致的 <br />
批量操作大量规律命名的对象。 <br />
例如下面这样
<div class="dp-highlighter">
<ol class="dp-j">
    <li>
    <pre class="java" style="display: none" name="code">create table tt
    (
    Q1_CODE		VARCHAR2(3),
    Q2_CODE		VARCHAR2(3),
    .....
    Q100_CODE       VARCHAR2(3),
    );</pre>
    &nbsp; 恕本人抱怨一句，设计出这样的表的人八成是吃得有点饱。 <br />
    <br />
    呵呵于是利用工具生成了JavaBean一般就是Struts的form,有的叫做DTO,有的叫做Ao,bo,bao,dao..........众多名字。访问数据库的时候就用它们做容器。 <br />
    面对100个get方法，一一调用，太无聊了。有什么简便方法呢.如果有像JS的eval()的方法就会方便多了。可惜啊。 <br />
    不过！！<span class="hilite1">java</span>中有一种神奇的机制，娃哈哈<strong><span class="hilite1">Java</span> Reflection </strong><br />
    <br />
    以下是我参考的两个网址 <br />
    <a href="http://www-128.ibm.com/developerworks/cn/java/j-dyn0603/" target="_blank">http://www-128.ibm.com/developerworks/cn/<span class="hilite1">java</span>/j-dyn0603/</a> <br />
    <a href="http://dev.csdn.net/article/49/49876.shtm" target="_blank">http://dev.csdn.net/article/49/49876.shtm</a> <br />
    大家想要仔细了解就去看看吧。 <br />
    <br />
    现在介绍一下我的具体实现： <br />
    </li>
</ol>
</div>
<div class="dp-highlighter">
<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;main(String&nbsp;args[])&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class&nbsp;c&nbsp;=&nbsp;Class.forName(</span><span class="string">"com.TtDto"</span><span>); &nbsp;&nbsp;</span></span></li>
    <li><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这里面的类名要写全，连包名一起写 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method&nbsp;m[]&nbsp;=&nbsp;c.getDeclaredMethods(); &nbsp;&nbsp;</span></li>
    <li><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //method的方法有很多下面会讲一讲 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TtDto&nbsp;methobj&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;TtDto(); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>&nbsp;(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">0</span><span>;&nbsp;i&nbsp;&lt;&nbsp;m.length;&nbsp;i++)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"name&nbsp;=&nbsp;"</span><span>&nbsp;+&nbsp;m[i].getName()); &nbsp;&nbsp;</span></span></li>
    <li><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //打印出来的名字同羊可以用来做处理，String向怎么处理就怎么处理。 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;Object&nbsp;retobj&nbsp;=&nbsp;m[i].invoke(methobj,&nbsp;null); </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这部才是非常重要的调用，但是你可以通过上一步的处理来决定是否进行调用来完成奇怪的调用。 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">catch</span><span>&nbsp;(Throwable&nbsp;e)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println(e); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
</ol>
</div>
<br />
<br />
这样你就看到这个类下面的所有方法了。把注释掉的代码用上就可以调用你看到的方法，了哈哈，怎么样，很好玩吧。 <br />
下面讲两个方法：
<div class="quote_div">public Object <a href="http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/lang/reflect/Method.html#invoke(java.lang.Object,%20java.lang.Object...)url" target="_blank">invoke</a>(Object obj,Object... args) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws IllegalAccessException, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IllegalArgumentException</div>
如果基础方法是静态的，那么可以忽略指定的 obj 参数。该参数可以为 null。 <br />
<br />
如果基础方法所需的形参数为 0，则所提供的 args 数组长度可以为 0 或 null。 <br />
<br />
参数： <br />
obj - 从中调用基础方法的对象 <br />
args - 用于方法调用的参数 <br />
返回： <br />
使用参数 args 在 obj 上指派该对象所表示方法的结果 <br />
可以查阅JDK文档。 <br />
getDeclaredMethods和getMethods差不多都是返回一个方法的数组。当然了你也可以按照方法的名字来调用方法 <br />
<img src ="http://www.blogjava.net/gp213/aggbug/270616.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-05-14 15:27 <a href="http://www.blogjava.net/gp213/archive/2009/05/14/270616.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中对HashMap的深度分析</title><link>http://www.blogjava.net/gp213/archive/2009/05/14/270595.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 14 May 2009 06:25:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/05/14/270595.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/270595.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/05/14/270595.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/270595.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/270595.html</trackback:ping><description><![CDATA[<p>在Java的世界里，无论类还是各种数据，其结构的处理是整个程序的逻辑以及性能的关键。由于本人接触了一个有关性能与逻辑同时并存的问题，于是就开始研究这方面的问题。找遍了大大小小的论坛，也把《Java 虚拟机规范》，《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》，和《Thinking in Java》翻了也找不到很好的答案，于是一气之下把JDK的src 解压出来研究，扩然开朗，遂写此文，跟大家分享感受和顺便验证我理解还有没有漏洞。 这里就拿HashMap来研究吧。 </p>
<p>HashMap可谓JDK的一大实用工具，把各个Object映射起来，实现了&#8220;键－－值&#8221;对应的快速存取。但实际里面做了些什么呢？ </p>
<p>在这之前，先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量，其默认值是 16&#215;0.75＝12； 这个很重要，对效率很一定影响！当存入HashMap的对象超过这个容量时，HashMap 就会重新构造存取表。这就是一个大问题，我后面慢慢介绍，反正，如果你已经知道你大概要存放多少个对象，最好设为该实际容量的能接受的数字。 </p>
<p><strong>两个关键的方法，put和get：</strong> </p>
<p>先有这样一个概念，HashMap是声明了 Map，Cloneable, Serializable 接口，和继承了 AbstractMap 类，里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现，当然还有一个很重要的继承了Map.Entry 的 Entry 内部类，由于大家都有源代码，大家有兴趣可以看看这部分，我主要想说明的是 Entry 内部类。它包含了hash，value，key 和next 这四个属性，很重要。put的源码如下 </p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#999999" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>public Object put(Object key, Object value) {
            Object k = maskNull(key); </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>这个就是判断键值是否为空，并不很深奥，其实如果为空，它会返回一个static Object 作为键值，这就是为什么HashMap允许空键值的原因。</p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#999999" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>int hash = hash(k);
            int i = indexFor(hash, table.length);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>这连续的两步就是 HashMap 最牛的地方！研究完我都汗颜了，其中 hash 就是通过 key 这个Object的 hashcode 进行 hash，然后通过 indexFor 获得在Object table的索引值。 </p>
<p>table？不要惊讶，其实HashMap也神不到哪里去，它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法，我跟JDK的作者 Doug 联系过，他建议我看看《The art of programing vol3》可恨的是，我之前就一直在找，我都找不到，他这样一提，我就更加急了，可惜口袋空空啊！ </p>
<p>不知道大家有没有留意 put 其实是一个有返回的方法，它会把相同键值的 put 覆盖掉并返回旧的值！如下方法彻底说明了 HashMap 的结构，其实就是一个表加上在相应位置的Entry的链表：</p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#999999" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>for (Entry e = table[i]; e != null; e = e.next) {
            if (e.hash == hash &amp;&amp; eq(k, e.key)) {
            Object oldvalue = e.value;
            e.value = value; //把新的值赋予给对应键值。
            e.recordAccess(this); //空方法，留待实现
            return oldvalue; //返回相同键值的对应的旧的值。
            }
            }
            modCount++; //结构性更改的次数
            addEntry(hash, k, value, i); //添加新元素，关键所在！
            return null; //没有相同的键值返回
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
我们把关键的方法拿出来分析： <br />
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#999999" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>void addEntry(int hash, Object key, Object value, int bucketIndex) {
            table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引，如：key＝&#8220;33&#8221;和key＝Object g的hash都是－8901334，那它经过indexfor之后的索引一定都为i，这样在new的时候这个Entry的next就会指向这个原本的table[i]，再有下一个也如此，形成一个链表，和put的循环对定e.next获得旧的值。到这里，HashMap的结构，大家也十分明白了吧？ <br />
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#999999" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>if (size++ &gt;= threshold) //这个threshold就是能实际容纳的量
            resize(2 * table.length); //超出这个容量就会将Object table重构
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>所谓的重构也不神，就是建一个两倍大的table（我在别的论坛上看到有人说是两倍加1，把我骗了），然后再一个个indexfor进去！注意！！这就是效率！！如果你能让你的HashMap不需要重构那么多次，效率会大大提高！ </p>
<p>说到这里也差不多了，get比put简单得多，大家，了解put，get也差不了多少了。对于collections我是认为，它是适合广泛的，当不完全适合特有的，如果大家的程序需要特殊的用途，自己写吧，其实很简单。（作者是这样跟我说的，他还建议我用LinkedHashMap,我看了源码以后发现，LinkHashMap其实就是继承HashMap的，然后override相应的方法，有兴趣的同人，自己looklook）建个 Object table，写相应的算法，就ok啦。 </p>
<p>举个例子吧，像 Vector，list 啊什么的其实都很简单，最多就多了的同步的声明，其实如果要实现像Vector那种，插入，删除不多的，可以用一个Object table来实现，按索引存取，添加等。 </p>
<p>如果插入，删除比较多的，可以建两个Object table，然后每个元素用含有next结构的，一个table存，如果要插入到i，但是i已经有元素，用next连起来，然后size＋＋，并在另一个table记录其位置。</p>
<img src ="http://www.blogjava.net/gp213/aggbug/270595.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-05-14 14:25 <a href="http://www.blogjava.net/gp213/archive/2009/05/14/270595.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HashMap原理及冲突之简谈</title><link>http://www.blogjava.net/gp213/archive/2009/05/14/270586.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 14 May 2009 05:32:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/05/14/270586.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/270586.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/05/14/270586.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/270586.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/270586.html</trackback:ping><description><![CDATA[<p style="text-indent: 21pt"><span style="font-family: 宋体">了解</span>HashMap<span style="font-family: 宋体">原理对于日后的缓存机制多少有些认识。在网络中也有很多方面的帖子，但是很多都是轻描淡写，很少有把握的比较准确的信息，在这里试着不妨说解一二。</span></p>
<p>&nbsp;&nbsp;&nbsp;<span style="font-family: 宋体">对于</span>HashMap<span style="font-family: 宋体">主要以键值</span>(key-value)<span style="font-family: 宋体">的方式来体现，笼统的说就是采用</span>key<span style="font-family: 宋体">值的哈希算法来，外加取余最终获取索引，而这个索引可以认定是一种地址，既而把相应的</span>value<span style="font-family: 宋体">存储在地址指向内容中。这样说或许比较概念化，也可能复述不够清楚，来看列式更加清晰：</span></p>
<div style="border-right: windowtext 0.5pt solid; padding-right: 4pt; border-top: windowtext 0.5pt solid; padding-left: 4pt; background: #d9d9d9; padding-bottom: 1pt; border-left: windowtext 0.5pt solid; padding-top: 1pt; border-bottom: windowtext 0.5pt solid">
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; hash=key.hashCode();//------------------------1</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; index=hash%table.lenth;//table<span style="font-family: 宋体">表示当前对象的长度</span>-----------------------2</p>
</div>
<p><span style="font-family: 宋体">其实最终就是这两个式子决定了值得存储位置。但是以上两个表达式还有欠缺。为什么这么说？例如在</span>key.hashCode()<span style="font-family: 宋体">后可能存在是一个负整数，你会问：是啊，那这个时候怎么办呢？所以在这里就需要进一步加强改造式子</span>2<span style="font-family: 宋体">了，修改后的：</span></p>
<div style="border-right: windowtext 0.5pt solid; padding-right: 4pt; border-top: windowtext 0.5pt solid; padding-left: 4pt; background: #d9d9d9; padding-bottom: 1pt; border-left: windowtext 0.5pt solid; padding-top: 1pt; border-bottom: windowtext 0.5pt solid">
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; index=<span style="font-family: 宋体">（</span>hash&amp;Ox7FFFFFFF)%table.lenth;</p>
</div>
<p><span style="font-family: 宋体">到这里又迷惑了，为什么上面是这样的呢？对于先前我们谈到在</span>hash<span style="font-family: 宋体">有可能产生负数的情况，这里我们使用当前的</span>hash<span style="font-family: 宋体">做一个&#8220;与&#8221;操作，在这里需要和</span>int<span style="font-family: 宋体">最大的值相&#8220;与&#8221;。这样的话就可以保证数据的统一性，把有符号的数值给&#8220;与&#8221;掉。而一般这里我们把二进制的数值转换成</span>16<span style="font-family: 宋体">进制的就变成了：</span>Ox7FFFFFFF<span style="font-family: 宋体">。（注：与操作的方式为，不同为</span>0<span style="font-family: 宋体">，相同为</span>1<span style="font-family: 宋体">）。而对于</span>hashCode()<span style="font-family: 宋体">的方法一般有：</span></p>
<div style="border-right: windowtext 0.5pt solid; padding-right: 4pt; border-top: windowtext 0.5pt solid; padding-left: 4pt; background: #d9d9d9; padding-bottom: 1pt; border-left: windowtext 0.5pt solid; padding-top: 1pt; border-bottom: windowtext 0.5pt solid">
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp; public int hashCode(){</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int hash=0,offset,len=count;</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[]&nbsp;var=value;</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;len;i++){</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h=31*hash+var[offset++];</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return hash;</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
</div>
<p style="text-indent: 21pt"><span style="font-family: 宋体">说道这里大家可能会说，到这里算完事了吧。但是你可曾想到如果数据都采用上面的方式，最终得到的可能</span>index<span style="font-family: 宋体">会相同怎么办呢？如果你想到的话，那恭喜你</span>!<span style="font-family: 宋体">又增进一步了，这里就是要说到一个名词：冲突率。是的就是前面说道的一旦</span>index<span style="font-family: 宋体">有相同怎么办？数据又该如何存放呢，而且这个在数据量非常庞大的时候这个基率更大。这里按照算法需要明确的一点：每个键（</span>key<span style="font-family: 宋体">）被散列分布到任何一个数组索引的可能性相同，而且不取决于其它键分布的位置。这句话怎么理解呢？从概率论的角度，也就是说如果</span>key<span style="font-family: 宋体">的个数达到一个极限，每个</span>key<span style="font-family: 宋体">分布的机率都是均等的。更进一步就是：即便</span>key1<span style="font-family: 宋体">不等于</span>key2<span style="font-family: 宋体">也还是可能</span>key1.hashCode()=key2.hashCode()<span style="font-family: 宋体">。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">对于早期的解决冲突的方法有折叠法（</span>folding)<span style="font-family: 宋体">，例如我们在做系统的时候有时候会采用部门编号附加到某个单据标号后，这里比如产生一个</span>9<span style="font-family: 宋体">～</span>11<span style="font-family: 宋体">位的编码。通过对半折叠做。</span></p>
<p><span style="font-family: 宋体">现在的策略有：</span></p>
<p style="margin-left: 18pt; text-indent: -18pt; tab-stops: list 18.0pt">1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family: 宋体">键式散列</span>&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt; tab-stops: list 18.0pt">2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family: 宋体">开放地址法</span></p>
<p><span style="font-family: 宋体">在了解这两个策略前，我们先定义清楚几个名词解释：</span></p>
<div style="border-right: windowtext 0.5pt solid; padding-right: 4pt; border-top: windowtext 0.5pt solid; padding-left: 4pt; background: #d9d9d9; padding-bottom: 1pt; border-left: windowtext 0.5pt solid; padding-top: 1pt; border-bottom: windowtext 0.5pt solid">
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">threshold[<span style="font-family: 宋体">阀值</span>]<span style="font-family: 宋体">，对象大小的边界值</span>;</p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">loadFactor[<span style="font-family: 宋体">加载因子</span>]=n/m<span style="font-family: 宋体">；其中</span>n<span style="font-family: 宋体">代表对象元素个数，</span>m<span style="font-family: 宋体">表示当前表的容积最大值</span></p>
<p style="border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; border-left: medium none; padding-top: 0cm; border-bottom: medium none">threshold=(int)table.length*loadFactor</p>
</div>
<p><span style="font-family: 宋体">清晰了这几个定义，我们再来看具体的解决方式</span></p>
<p><span style="font-family: 宋体">键式散列：</span></p>
<p style="text-indent: 27pt"><span style="font-family: 宋体">我们直接看一个实例，这样就更加清晰它的工作方式，从而避免文字定义。我们这里还是来举一个图书编号的例子，下面比如有这样一些编号：</span></p>
<p style="text-indent: 27pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: black">78938</span><span style="color: black">-000</span><span style="color: black">0</span></p>
<p style="text-indent: 27pt"><span style="color: black">&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;</span>45678-0001</p>
<p style="text-indent: 27pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 72678-0002</p>
<p style="text-indent: 27pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24678-0001</p>
<p style="text-indent: 27pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;16678-0001</p>
<p style="text-indent: 27pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 98678-0003</p>
<p style="text-indent: 27pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 85678-0002</p>
<p style="text-indent: 27pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 45232-0004</p>
<p><span style="font-family: 宋体">步骤：</span></p>
<p style="margin-left: 18pt; text-indent: -18pt; tab-stops: list 18.0pt">1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family: 宋体">把编号作为</span>key,<span style="font-family: 宋体">即：</span>int hash=key.hashCode();</p>
<p style="margin-left: 18pt; text-indent: -18pt; tab-stops: list 18.0pt">2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>int index=hash%<span style="font-family: 宋体">表大小；</span></p>
<p style="margin-left: 18pt; text-indent: -18pt; tab-stops: list 18.0pt">3.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family: 宋体">逐步按顺序插入对象中</span></p>
<p><span style="font-family: 宋体">现在问题出现了：对于编号通过散列算法后很可能产生相同的索引值，意味着存在冲突。</span></p>
<p style="text-align: center" align="center"><img title="点击图片可在新窗口打开" style="cursor: pointer" height="436" alt="" src="http://space.itpub.net/attachments/2008/09/14734416_200809181404441.jpg" width="617" border="0" /></p>
<p><span style="font-family: 宋体">解释上面的操作：如果对于</span>key.hashCode()<span style="font-family: 宋体">产生了冲突（比如途中对于插入</span>24678-0001<span style="font-family: 宋体">对于通过哈希算法后可能产生的</span>index<span style="font-family: 宋体">或许也是</span>501<span style="font-family: 宋体">），既而把相应的前驱有相同的</span>index<span style="font-family: 宋体">的对象指向当前引用。这也就是大家认定的单链表方式。以此类推</span>&#8230;</p>
<p><span style="font-family: 宋体">而这里存在冲突对象的元素放在</span>Entry<span style="font-family: 宋体">对象中，</span>Entry<span style="font-family: 宋体">具有以下一些属性：</span></p>
<p>int hash;</p>
<p>Object key;</p>
<p>Entry next;</p>
<p><span style="font-family: 宋体">对于</span>Entry<span style="font-family: 宋体">对象就可以直接追溯到链表数据结构体中查阅。</span></p>
<p><span style="font-family: 宋体">开放地址法：</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: list 21.0pt">1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family: 宋体">线性地址探测法：</span></p>
<p><span style="font-family: 宋体">如何理解这个概念呢，一句话：就是通过算法规则在对象地址</span>N+1<span style="font-family: 宋体">中查阅找到为</span>NULL<span style="font-family: 宋体">的索引内容。</span></p>
<p><span style="font-family: 宋体">处理方式：如果</span>index<span style="font-family: 宋体">索引与当前的</span>index<span style="font-family: 宋体">有冲突，即把当前的索引</span>index+1<span style="font-family: 宋体">。如果在</span>index+1<span style="font-family: 宋体">已经存在占位现象（</span>index+1<span style="font-family: 宋体">的内容不为</span>NULL<span style="font-family: 宋体">）试图接着</span>index+2<span style="font-family: 宋体">执行。。。直到找到索引为内容为</span>NULL<span style="font-family: 宋体">的为止。这种处理方式也叫：线性地址探测法</span>(offset-of-1)</p>
<p><span style="font-family: 宋体">如果采用线性地址探测法会带来一个效率的不良影响。现在我们来分析这种方式会带来哪些不良因素。大家试想下如果一个非常庞大的数据存储在</span>Map<span style="font-family: 宋体">中，假如在某些记录集中有一些数据非常相似（他们产生的索引在内存的某个块中非常的密集），也就是说他们产生的索引地址是一个连续数值，而造成数据成块现象。另一个致命的问题就是在数据删除后，如果再次查询可能无法定到下一个连续数字，这个又是一个什么概念呢？例如以下图片就很好的说明开发地址散列如何把数据按照算法插入到对象中：</span></p>
<p style="text-align: center" align="center"><img title="点击图片可在新窗口打开" style="cursor: pointer" height="502" alt="" src="http://space.itpub.net/attachments/2008/09/14734416_200809181404442.jpg" width="512" border="0" /></p>
<p><span style="font-family: 宋体">对于上图的注释步骤说明：</span></p>
<p style="line-height: 119%; text-align: center" align="center"><span style="font-family: 宋体">从数据&#8220;</span>78938-0000<span style="font-family: 宋体">&#8221;开始通过哈希算法按顺序依次插入到对象中，例如</span>78938-0000<span style="font-family: 宋体">通过换</span></p>
<p style="line-height: 119%"><span style="font-family: 宋体">算得到索引为</span>0<span style="font-family: 宋体">，当前所指内容为</span>NULL<span style="font-family: 宋体">所以直接插入；</span><span style="color: black">45678-0001</span><span style="color: black; font-family: 宋体">同样通过换算得到索引为地址</span><span style="color: black">501</span><span style="color: black; font-family: 宋体">所指内容，当前内容为</span><span style="color: black">NULL</span><span style="color: black; font-family: 宋体">所以也可以插入；</span><span style="color: black">72678-0002</span><span style="color: black; font-family: 宋体">得到索引</span><span style="color: black">502</span><span style="color: black; font-family: 宋体">所指内容，当前内容为</span><span style="color: black">NULL</span><span style="color: black; font-family: 宋体">也可以插入；请注意当</span><span style="color: black">24678-0001</span><span style="color: black; font-family: 宋体">得到索引也为</span><span style="color: black">501</span><span style="color: black; font-family: 宋体">，当前地址所指内容为</span><span style="color: black">45678-0001</span><span style="color: black; font-family: 宋体">。即表示当前数据存在冲突，则直接对地址</span><span style="color: black">501+1=502</span><span style="color: black; font-family: 宋体">所指向内容为</span><span style="color: black">72678-0002</span><span style="color: black; font-family: 宋体">不为</span><span style="color: black">NULL</span><span style="color: black; font-family: 宋体">也不允许插入，再次对索引</span><span style="color: black">502+1=503</span><span style="color: black; font-family: 宋体">所指内容为</span><span style="color: black">NULL</span><span style="color: black; font-family: 宋体">允许插入。。。依次类推只要对于索引存在冲突现象，则逐次下移位知道索引地址所指为</span><span style="color: black">NULL</span><span style="color: black; font-family: 宋体">；如果索引不冲突则还是按照算法放入内容。对于这样的对象是一种插入方式，接下来就是我们的删除</span><span style="color: black">(remove)</span><span style="color: black; font-family: 宋体">方法了。按照常理对于删除，方式基本区别不大。但是现在问题又出现了，如果删除的某个数据是一个存在冲突索引的内容，带来后续的问题又会接踵而来。那是什么问题呢？我们还是同样来看看图示的描述，对于图</span><span style="color: black">-2</span><span style="color: black; font-family: 宋体">中如果删除</span><span style="color: black">(remove)</span><span style="color: black; font-family: 宋体">数据</span><span style="color: black">24678-0001</span><span style="color: black; font-family: 宋体">的方法如下图所示：</span></p>
<p style="line-height: 119%; text-align: center" align="center"><img title="点击图片可在新窗口打开" style="cursor: pointer" height="502" alt="" src="http://space.itpub.net/attachments/2008/09/14734416_200809181404443.jpg" width="512" border="0" /></p>
<p style="line-height: 119%"><span style="font-family: 宋体">对于我们会想当然的觉得只要把指向数据置为</span>NULL<span style="font-family: 宋体">就可以</span>,<span style="font-family: 宋体">这样的做法对于删除来说当然是没有问题的。如果再次定位检索数据</span>16678-0001<span style="font-family: 宋体">不会成功，因为这个时候以前的链路已经堵上了，但是需要检索的数据事实上又存在。那我们如何来解决这个问题呢？对于</span>JDK<span style="font-family: 宋体">中的</span>Entry<span style="font-family: 宋体">类中的方法存在一个：</span>boolean markedForRemoval;<span style="font-family: 宋体">它就是一个典型的删除标志位，对于对象中如果需要删除时，我们只是对于它做一个&#8220;软删除&#8221;即置一个标志位为</span>true<span style="font-family: 宋体">就可以。而插入时，默认状态为</span>false<span style="font-family: 宋体">就可以。这样的话就变成以下图所示：</span></p>
<p style="line-height: 119%; text-align: center" align="center"><img title="点击图片可在新窗口打开" style="cursor: pointer" height="502" alt="" src="http://space.itpub.net/attachments/2008/09/14734416_200809181404444.jpg" width="512" border="0" /></p>
<p style="line-height: 119%"><span style="font-family: 宋体">通过以上方式更好的解决冲突地址删除数据无法检索其他链路数据问题了。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; line-height: 119%; tab-stops: list 21.0pt">2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family: 宋体">双散列（余商法）</span></p>
<p style="margin-left: 21pt; text-indent: 21pt; line-height: 119%"><span style="font-family: 宋体">在了解开放地址散列的时候我们一直在说解决方法，但是大家都知道一个数据结构的完善更多的是需要高效的算法。这当中我们却没有涉及到。接下来我们就来看看在开放地址散列中它存在的一些不足以及如何改善这样的方法，既而达到无论是在方法的解决上还是在算法的复杂度上更加达到高效的方案。</span></p>
<p style="margin-left: 21pt; text-indent: 21pt; line-height: 119%"><span style="font-family: 宋体">在图</span>2-1<span style="font-family: 宋体">中类似这样一些数据插入进对象，存在冲突采用不断移位加一的方式，直到找到不为</span>NULL<span style="font-family: 宋体">内容的索引地址。也正是由于这样一种可能加大了时间上的变慢。大家是否注意到像图这样一些数据目前呈现出一种连续索引的插入，而且是一种成块是的数据。如果数据量非常的庞大，或许这种可能性更大。尽管它解决了冲突，但是对于数据检索的时间度来说，我们是不敢想象的。所有分布到同一个索引</span>index<span style="font-family: 宋体">上的</span>key<span style="font-family: 宋体">保持相同的路径：</span>index,index+1,index+2&#8230;<span style="font-family: 宋体">依此类推。更加糟糕的是索引键值的检索需要从索引开始查找。正是这样的原因，对于线性探索法我们需要更进一步的改进。而刚才所描述这种成块出现的数据也就定义成：簇。而这样一种现象称之为：主簇现象。</span></p>
<p style="line-height: 119%"><span style="font-family: 宋体">（主簇：就是冲突处理允许簇加速增长时出现的现象）而开放式地址冲突也是允许主簇现象产生的。那我们如何来避免这种主簇现象呢？这个方式就是我们要来说明的：双散列解决冲突法了。主要的方式为：</span></p>
<p style="background: #e5e5e5; margin-left: 21pt; text-indent: -21pt; line-height: 119%; tab-stops: list 21.0pt"><span style="font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span>int hash=key.hasCode();</p>
<p style="background: #e5e5e5; margin-left: 21pt; text-indent: -21pt; line-height: 119%; tab-stops: list 21.0pt"><span style="font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span>int index=(hash&amp;Ox7FFFFFFF)%table.length;</p>
<p style="background: #e5e5e5; margin-left: 21pt; text-indent: -21pt; line-height: 119%; tab-stops: list 21.0pt"><span style="font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: 宋体">按照以上方式得到索引存在冲突，则开始对当前索引移位，而移位方式为：</span></p>
<p style="background: #e5e5e5; line-height: 119%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ffset=(hash&amp;Ox7FFFFFFF)/table.length;</p>
<p style="background: #e5e5e5; margin-left: 21pt; text-indent: -21pt; line-height: 119%; tab-stops: list 21.0pt"><span style="font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: 宋体">如果第一次移位还存在同样的冲突，则继续：当前冲突索引位置（索引号</span>+<span style="font-family: 宋体">余数）</span>%<span style="font-family: 宋体">表</span>.length</p>
<p style="background: #e5e5e5; margin-left: 21pt; text-indent: -21pt; line-height: 119%; tab-stops: list 21.0pt"><span style="font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: 宋体">如果存在的余数恰好是表的倍数，则作偏移位置为一下移，依此类推</span></p>
<p style="text-indent: 21pt; line-height: 119%"><span style="font-family: 宋体">这样双散列冲突处理就避免了主簇现象。至于</span>HashSet<span style="font-family: 宋体">的原理基本和它是一致的，这里不再复述。在这里其实还是主要说了一些简单的解决方式，而且都是在一些具体参数满足条件下的说明，像一旦数据超过初始值该需要</span>rehash<span style="font-family: 宋体">，加载因子一旦大于</span>1.0<span style="font-family: 宋体">是何种情况等等。还有很多问题都可以值得我们更加进一步讨论的，比如：在</span><a onclick="javascript:tagshow(event, 'java');" href="javascript:;" target="_self"><u><strong>java</strong></u></a>.util.HashMap<span style="font-family: 宋体">中的加载因子为什么会是</span>0.75<span style="font-family: 宋体">，而它默认的初始大小为什么又是</span>16<span style="font-family: 宋体">等等这些问题都还值得说明。要说明这些问题可能又需要更加详尽的说明清楚。<br />
<br />
<br />
源码分析:HashMap<br />
<br />
</p>
<p>HashMap是Java新Collection Framework中用来代替HashTable的一个实现，HashMap和HashTable的区别是： HashMap是未经同步的，而且允许null值。HashTable继承Dictionary，而且使用了Enumeration，所以被建议不要使用。<br />
HashMap的声明如下：<br />
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable <br />
有关AbstractMap：<a href="http://blog.csdn.net/treeroot/archive/2004/09/20/110343.aspx">http://blog.csdn.net/treeroot/archive/2004/09/20/110343.aspx</a> <br />
有关Map：<a href="http://blog.csdn.net/treeroot/archive/2004/09/20/110331.aspx">http://blog.csdn.net/treeroot/archive/2004/09/20/110331.aspx</a> <br />
有关Cloneable：<a href="http://blog.csdn.net/treeroot/archive/2004/09/07/96936.aspx">http://blog.csdn.net/treeroot/archive/2004/09/07/96936.aspx</a> <br />
这个类比较复杂，这里只是重点分析了几个方法，特别是后面涉及到很多内部类都没有解释<br />
不过都比较简单。</p>
<p>static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小</p>
<p>static final int MAXIMUM_CAPACITY = 1 &lt;&lt; 30; 最大初始化大小</p>
<p>static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子</p>
<p>transient Entry[] table; 一个Entry类型的数组，数组的长度为2的指数。</p>
<p>transient int size; 映射的个数</p>
<p>int threshold; 下一次扩容时的值</p>
<p>final float loadFactor; 加载因子</p>
<p>transient volatile int modCount; 修改次数</p>
<p>public HashMap(int initialCapacity, float loadFactor) {<br />
　　 if (initialCapacity &lt; 0) <br />
　　　　 throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity); <br />
　　 if (initialCapacity &gt; MAXIMUM_CAPACITY) <br />
　　　　 initialCapacity = MAXIMUM_CAPACITY; <br />
　　 if (loadFactor &lt;= 0 || Float.isNaN(loadFactor)) <br />
　　　　 throw new IllegalArgumentException("Illegal load factor: " +loadFactor); <br />
　　 int capacity = 1;<br />
　　 while (capacity &lt; initialCapacity)<br />
　　　　 capacity &lt;&lt;= 1; <br />
　　 this.loadFactor = loadFactor; <br />
　　 threshold = (int)(capacity * loadFactor); <br />
　　 table = new Entry[capacity]; <br />
　　 init();<br />
}</p>
<p>public HashMap(int initialCapacity) { <br />
　　 this(initialCapacity, DEFAULT_LOAD_FACTOR);<br />
}</p>
<p>public HashMap() { <br />
　　 this.loadFactor = DEFAULT_LOAD_FACTOR; <br />
　　 threshold = (int)(DEFAULT_INITIAL_CAPACITY);<br />
　　　　　　注意：这里应该是一个失误！ 应该是：threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor); <br />
　　 table = new Entry[DEFAULT_INITIAL_CAPACITY]; <br />
　　　init(); <br />
}</p>
<p>public HashMap(Map m) { <br />
　　 this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), 　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　DEFAULT_LOAD_FACTOR); <br />
　　 putAllForCreate(m); <br />
}</p>
<p>void init() {}</p>
<p>static final Object NULL_KEY = new Object();</p>
<p>static Object maskNull(Object key){ <br />
　　 return (key == null ? NULL_KEY : key);<br />
}</p>
<p>static Object unmaskNull(Object key) {<br />
　　 return (key == NULL_KEY ? null : key);<br />
}</p>
<p>static int hash(Object x) {<br />
　　 int h = x.hashCode(); <br />
　　 h += ~(h &lt;&lt; 9);<br />
　　 h ^= (h &gt;&gt;&gt; 14);<br />
　　 h += (h &lt;&lt; 4);<br />
　　 h ^= (h &gt;&gt;&gt; 10);<br />
return h; <br />
}<br />
在HashTable中没有这个方法，也就是说HashTable中是直接用对象的hashCode值，但是HashMap做了改进 用这个算法来获得哈希值。</p>
<p>static boolean eq(Object x, Object y) {<br />
　 　return x == y || x.equals(y);<br />
}</p>
<p>static int indexFor(int h, int length) {<br />
　　 return h &amp; (length-1);<br />
}<br />
根据哈希值和数组的长度来返回该hash值在数组中的位置，只是简单的与关系。</p>
<p>public int size() {<br />
　　 return size; <br />
}</p>
<p>public boolean isEmpty() { <br />
　　return size == 0; <br />
}</p>
<p>public Object get(Object key) {<br />
　　 Object k = maskNull(key); <br />
　　 int hash = hash(k); <br />
　　 int i = indexFor(hash, table.length);<br />
　　 Entry e = table[i]; <br />
　　 while (true) {<br />
　　　　 if (e == null) return e; <br />
　　　　 if (e.hash == hash &amp;&amp; eq(k, e.key)) return e.value; <br />
　　　　 e = e.next;<br />
　　 }<br />
}<br />
<span class="code">这个方法是获取数据的方法，首先获得哈希值，这里把null值掩饰了，并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null，表示HashMap中不存在这个映射。 否则的话遍历整个链表，这里找到了就返回,如果没有找到就遍历到链表末尾，返回null。这里的比较是这样的：e.hash==hash &amp;&amp; eq(k,e.key) 也就是说如果hash不同就肯定认为不相等，eq就被短路了，只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true，他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true，但是hashCode不一样，那么在HashMap 里就认为他们不相等。</span></p>
<p>public boolean containsKey(Object key) {<br />
　　 Object k = maskNull(key); <br />
　　 int hash = hash(k);<br />
　　 int i = indexFor(hash, table.length);<br />
　　 Entry e = table[i]; <br />
　　 while (e != null) {<br />
　　　　 if (e.hash == hash &amp;&amp; eq(k, e.key)) return true;<br />
　　　　 e = e.next;<br />
　　 }<br />
　　return false; <br />
}<br />
这个方法比上面的简单，先找到哈希位置，再遍历整个链表，如果找到就返回true。</p>
<p style="text-indent: 21pt; line-height: 119%">Entry getEntry(Object key) { <br />
　　 Object k = maskNull(key); <br />
　　 int hash = hash(k); <br />
　　 int i = indexFor(hash, table.length); <br />
　　 Entry e = table[i]; <br />
　　 while (e != null &amp;&amp; !(e.hash == hash &amp;&amp; eq(k, e.key))) <br />
　　　　 e = e.next;<br />
　　 return e; <br />
}<br />
这个方法根据key值返回Entry节点，也是先获得索引位置，再遍历链表，如果没有找到返回的是null。 </p>
<p>public Object put(Object key, Object value) { <br />
　　 Object k = maskNull(key);<br />
　　 int hash = hash(k);<br />
　　 int i = indexFor(hash, table.length); <br />
　　 for (Entry e = table[i]; e != null; e = e.next) { <br />
　　　　 if (e.hash == hash &amp;&amp; eq(k, e.key)) {<br />
　　　　　　 Object oldValue = e.value; <br />
　　　　　　 e.value = value;<br />
　　　　　　 e.recordAccess(this);<br />
　　　　　　 return oldValue;<br />
　　　　 } <br />
　　 }<br />
　　 modCount++; <br />
　　 addEntry(hash, k, value, i); <br />
　　 return null;<br />
}<br />
<span class="code">首先获得hash索引位置，如果该位置的引用为null，那么直接插入一个映射，返回null。如果此处的引用不是null，必须遍历链表，如果找到一个相同的key，那么就更新该value，同时返回原来的value值。如果遍历完了没有找到，说明该key值不存在，还是插入一个映射。如果hash值足够离散的话，也就是说该索引没有被使用的话，那么不不用遍历链表了。相反，如果hash值不离散，极端的说如果是常数的话，所有的映射都会在这一个链表上，效率会极其低下。这里举一个最简单的例子，写两<br />
个不同的类作为key插入到HashMap中，效率会远远不同。<br />
class Good{<br />
　　int i;<br />
　　public Good(int i){<br />
　　　this.i=i;<br />
　　}<br />
　　public boolean equals(Object o){<br />
　　　return (o instanceof Good) &amp;&amp; (this.i==((Good)o).i)<br />
　　}<br />
　　public int hashCode(){<br />
　　　return i;<br />
　　}<br />
} <br />
class Bad{<br />
　　int i;<br />
　　public Good(int i){<br />
　　　　this.i=i;<br />
　　}<br />
　　public boolean equals(Object o){<br />
　　　　return (o instanceof Good) &amp;&amp; (this.i==((Good)o).i)</p>
</span></span>
<img src ="http://www.blogjava.net/gp213/aggbug/270586.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-05-14 13:32 <a href="http://www.blogjava.net/gp213/archive/2009/05/14/270586.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java操作Excel、PDF文件</title><link>http://www.blogjava.net/gp213/archive/2009/05/14/270584.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 14 May 2009 05:20:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/05/14/270584.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/270584.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/05/14/270584.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/270584.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/270584.html</trackback:ping><description><![CDATA[<p>相关文章:&nbsp;&nbsp;<br />
<a onclick="javascript:tagshow(event, 'Java');" href="javascript:;" target="_self"><u><strong>Java</strong></u></a>操作Excel之理解JXL--读取Excel模板动态写入数据并生成Excel<br />
Java解析Excel文件<br />
Java操作Excel之理解JXL--读取Excel</p>
<p>推荐圈子: GT-Grid<br />
更多相关推荐 下面这些是在开发中用到的一些东西，有的代码贴的不是完整的，只是贴出了关于操作EXCEL的代码：</p>
<p><br />
jxl是一个*国人写的java操作excel的工具, 在<a onclick="javascript:tagshow(event, '%BF%AA%D4%B4');" href="javascript:;" target="_self"><u><strong>开源</strong></u></a>世界中，有两套比较有影响的API可供使用，一个是POI，一个是jExcelAPI。其中功能相对POI比较弱一点。但jExcelAPI对中文支持非常好，API是纯Java的， 并不依赖Windows系统，即使运行在Linux下，它同样能够正确的处理Excel文件。 另外需要说明的是，这套API对图形和图表的支持很有限，而且仅仅识别PNG格式。</p>
<p>使用如下：</p>
<p>搭建环境</p>
<p>将下载后的文件解包，得到jxl.jar，放入classpath，<a onclick="javascript:tagshow(event, '%B0%B2%D7%B0');" href="javascript:;" target="_self"><u><strong>安装</strong></u></a>就完成了。</p>
<p>基本操作</p>
<p>一、创建文件</p>
<p>拟生成一个名为&#8220;test.xls&#8221;的Excel文件，其中第一个工作表被命名为<br />
&#8220;第一页&#8221;，大致效果如下：<br />
Java代码<br />
package&nbsp; test;&nbsp;&nbsp;<br />
&nbsp;<br />
// 生成Excel的类&nbsp;&nbsp;&nbsp;<br />
import&nbsp; java.io.File;&nbsp;&nbsp;<br />
&nbsp;<br />
import&nbsp; jxl.Workbook;&nbsp;&nbsp;<br />
import&nbsp; jxl.write.Label;&nbsp;&nbsp;<br />
import&nbsp; jxl.write.WritableSheet;&nbsp;&nbsp;<br />
import&nbsp; jxl.write.WritableWorkbook;&nbsp;&nbsp;<br />
&nbsp;<br />
public&nbsp;&nbsp; class&nbsp; CreateExcel&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; static&nbsp;&nbsp; void&nbsp; main(String args[])&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 打开文件&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableWorkbook book&nbsp; =&nbsp; Workbook.createWorkbook( new&nbsp; File( " test.xls " ));&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 生成名为&#8220;第一页&#8221;的工作表，参数0表示这是第一页&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableSheet sheet&nbsp; =&nbsp; book.createSheet( " 第一页 " ,&nbsp; 0 );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 在Label对象的构造子中指名单元格位置是第一列第一行(0,0)&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 以及单元格内容为test&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label label&nbsp; =&nbsp;&nbsp; new&nbsp; Label( 0 ,&nbsp; 0 ,&nbsp; " test " );&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 将定义好的单元格添加到工作表中&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sheet.addCell(label);&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**/ /*&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 生成一个保存数字的单元格 必须使用Number的完整包路径，否则有语法歧义 单元格位置是第二列，第一行，值为789.123&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jxl.write.Number number&nbsp; =&nbsp;&nbsp; new&nbsp; jxl.write.Number( 1 ,&nbsp; 0 ,&nbsp; 555.12541 );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sheet.addCell(number);&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 写入数据并关闭文件&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.write();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.close();&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; catch&nbsp; (Exception e)&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;package&nbsp; test;</p>
<p>&nbsp;// 生成Excel的类<br />
&nbsp;import&nbsp; java.io.File;</p>
<p>&nbsp;import&nbsp; jxl.Workbook;<br />
&nbsp;import&nbsp; jxl.write.Label;<br />
&nbsp;import&nbsp; jxl.write.WritableSheet;<br />
&nbsp;import&nbsp; jxl.write.WritableWorkbook;</p>
<p>&nbsp;public&nbsp;&nbsp; class&nbsp; CreateExcel&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; static&nbsp;&nbsp; void&nbsp; main(String args[])&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 打开文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableWorkbook book&nbsp; =&nbsp; Workbook.createWorkbook( new&nbsp; File( " test.xls " ));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 生成名为&#8220;第一页&#8221;的工作表，参数0表示这是第一页<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableSheet sheet&nbsp; =&nbsp; book.createSheet( " 第一页 " ,&nbsp; 0 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 在Label对象的构造子中指名单元格位置是第一列第一行(0,0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 以及单元格内容为test<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label label&nbsp; =&nbsp;&nbsp; new&nbsp; Label( 0 ,&nbsp; 0 ,&nbsp; " test " );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 将定义好的单元格添加到工作表中<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sheet.addCell(label);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**/ /*<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 生成一个保存数字的单元格 必须使用Number的完整包路径，否则有语法歧义 单元格位置是第二列，第一行，值为789.123<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jxl.write.Number number&nbsp; =&nbsp;&nbsp; new&nbsp; jxl.write.Number( 1 ,&nbsp; 0 ,&nbsp; 555.12541 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sheet.addCell(number);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 写入数据并关闭文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.write();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.close();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; catch&nbsp; (Exception e)&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
&nbsp;</p>
<p>&nbsp;&nbsp; 编译执行后，会产生一个Excel文件。</p>
<p>三、读取文件</p>
<p>以刚才我们创建的Excel文件为例，做一个简单的读取操作，程序代码如下：<br />
Java代码<br />
package&nbsp; test;&nbsp;&nbsp;<br />
&nbsp;<br />
// 读取Excel的类&nbsp;&nbsp;&nbsp;<br />
import&nbsp; java.io.File;&nbsp;&nbsp;<br />
&nbsp;<br />
import&nbsp; jxl.Cell;&nbsp;&nbsp;<br />
import&nbsp; jxl.Sheet;&nbsp;&nbsp;<br />
import&nbsp; jxl.Workbook;&nbsp;&nbsp;<br />
&nbsp;<br />
public&nbsp;&nbsp; class&nbsp; ReadExcel&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; static&nbsp;&nbsp; void&nbsp; main(String args[])&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Workbook book&nbsp; =&nbsp; Workbook.getWorkbook( new&nbsp; File( " test.xls " ));&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 获得第一个工作表对象&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sheet sheet&nbsp; =&nbsp; book.getSheet( 0 );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 得到第一列第一行的单元格&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cell cell1&nbsp; =&nbsp; sheet.getCell( 0 ,&nbsp; 0 );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String result&nbsp; =&nbsp; cell1.getContents();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(result);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.close();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; catch&nbsp; (Exception e)&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;package&nbsp; test;</p>
<p>&nbsp;// 读取Excel的类<br />
&nbsp;import&nbsp; java.io.File;</p>
<p>&nbsp;import&nbsp; jxl.Cell;<br />
&nbsp;import&nbsp; jxl.Sheet;<br />
&nbsp;import&nbsp; jxl.Workbook;</p>
<p>&nbsp;public&nbsp;&nbsp; class&nbsp; ReadExcel&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; static&nbsp;&nbsp; void&nbsp; main(String args[])&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Workbook book&nbsp; =&nbsp; Workbook.getWorkbook( new&nbsp; File( " test.xls " ));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 获得第一个工作表对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sheet sheet&nbsp; =&nbsp; book.getSheet( 0 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 得到第一列第一行的单元格<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cell cell1&nbsp; =&nbsp; sheet.getCell( 0 ,&nbsp; 0 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String result&nbsp; =&nbsp; cell1.getContents();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(result);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; catch&nbsp; (Exception e)&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
&nbsp;</p>
<p>&nbsp; 程序执行结果：test</p>
<p>四、修改文件<br />
利用jExcelAPI可以修改已有的Excel文件，修改Excel文件的时候，除了打开文件的方式不同之外，<br />
其他操作和创建Excel是一样的。下面的例子是在我们已经生成的Excel文件中添加一个工作表：<br />
Java代码<br />
package&nbsp; test;&nbsp;&nbsp;<br />
&nbsp;<br />
import&nbsp; java.io.File;&nbsp;&nbsp;<br />
&nbsp;<br />
import&nbsp; jxl.Workbook;&nbsp;&nbsp;<br />
import&nbsp; jxl.write.Label;&nbsp;&nbsp;<br />
import&nbsp; jxl.write.WritableSheet;&nbsp;&nbsp;<br />
import&nbsp; jxl.write.WritableWorkbook;&nbsp;&nbsp;<br />
&nbsp;<br />
public&nbsp;&nbsp; class&nbsp; UpdateExcel&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; static&nbsp;&nbsp; void&nbsp; main(String args[])&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; Excel获得文件&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Workbook wb&nbsp; =&nbsp; Workbook.getWorkbook( new&nbsp; File( " test.xls " ));&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 打开一个文件的副本，并且指定数据写回到原文件&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableWorkbook book&nbsp; =&nbsp; Workbook.createWorkbook( new&nbsp; File( " test.xls " ),&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wb);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 添加一个工作表&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableSheet sheet&nbsp; =&nbsp; book.createSheet( " 第二页 " ,&nbsp; 1 );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sheet.addCell( new&nbsp; Label( 0 ,&nbsp; 0 ,&nbsp; " 第二页的<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target="_self"><u><strong>测试</strong></u></a>数据 " ));&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.write();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.close();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; catch&nbsp; (Exception e)&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;package&nbsp; test;</p>
<p>&nbsp;import&nbsp; java.io.File;</p>
<p>&nbsp;import&nbsp; jxl.Workbook;<br />
&nbsp;import&nbsp; jxl.write.Label;<br />
&nbsp;import&nbsp; jxl.write.WritableSheet;<br />
&nbsp;import&nbsp; jxl.write.WritableWorkbook;</p>
<p>&nbsp;public&nbsp;&nbsp; class&nbsp; UpdateExcel&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; static&nbsp;&nbsp; void&nbsp; main(String args[])&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; Excel获得文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Workbook wb&nbsp; =&nbsp; Workbook.getWorkbook( new&nbsp; File( " test.xls " ));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 打开一个文件的副本，并且指定数据写回到原文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableWorkbook book&nbsp; =&nbsp; Workbook.createWorkbook( new&nbsp; File( " test.xls " ),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wb);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 添加一个工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableSheet sheet&nbsp; =&nbsp; book.createSheet( " 第二页 " ,&nbsp; 1 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sheet.addCell( new&nbsp; Label( 0 ,&nbsp; 0 ,&nbsp; " 第二页的测试数据 " ));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.write();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; catch&nbsp; (Exception e)&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
&nbsp;</p>
<p>其他操作</p>
<p>一、 数据格式化</p>
<p>在Excel中不涉及复杂的数据类型，能够比较好的处理字串、数字和日期已经能够满足一般的应用。</p>
<p>1、 字串格式化</p>
<p>字符串的格式化涉及到的是字体、粗细、字号等元素，这些功能主要由WritableFont和<br />
WritableCellFormat类来负责。假设我们在生成一个含有字串的单元格时，使用如下语句，<br />
为方便叙述，我们为每一行命令加了编号：<br />
Java代码<br />
WritableFont font1 =&nbsp;&nbsp;&nbsp;<br />
&nbsp;new&nbsp; WritableFont(WritableFont.TIMES, 16 ,WritableFont.BOLD); ①&nbsp;&nbsp;<br />
&nbsp;<br />
WritableCellFormat format1 = new&nbsp; WritableCellFormat(font1); ②&nbsp;&nbsp;<br />
&nbsp;<br />
Label label = new&nbsp; Label( 0 , 0 ,&#8221;data&nbsp; 4&nbsp; test&#8221;,format1) ③&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;其中①指定了字串格式：字体为TIMES，字号16，加粗显示。WritableFont有非常丰富的&nbsp;&nbsp;<br />
构造子，供不同情况下使用，jExcelAPI的java-doc中有详细列表，这里不再列出。&nbsp;&nbsp;<br />
&nbsp;<br />
②处代码使用了WritableCellFormat类，这个类非常重要，通过它可以指定单元格的各种&nbsp;&nbsp;<br />
属性，后面的单元格格式化中会有更多描述。&nbsp;&nbsp;<br />
&nbsp;<br />
③处使用了Label类的构造子，指定了字串被赋予那种格式。&nbsp;&nbsp;<br />
&nbsp;<br />
在WritableCellFormat类中，还有一个很重要的方法是指定数据的对齐方式，比如针对我们&nbsp;&nbsp;<br />
上面的实例，可以指定：&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp; // 把水平对齐方式指定为居中&nbsp;&nbsp;&nbsp;<br />
&nbsp;format1.setAlignment(jxl.format.Alignment.CENTRE);&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;// 把垂直对齐方式指定为居中&nbsp;&nbsp;&nbsp;<br />
&nbsp;format1.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);&nbsp;&nbsp;</p>
<p>&nbsp;WritableFont font1 =<br />
&nbsp; new&nbsp; WritableFont(WritableFont.TIMES, 16 ,WritableFont.BOLD); ①</p>
<p>&nbsp;WritableCellFormat format1 = new&nbsp; WritableCellFormat(font1); ②</p>
<p>&nbsp;Label label = new&nbsp; Label( 0 , 0 ,&#8221;data&nbsp; 4&nbsp; test&#8221;,format1) ③</p>
<p>&nbsp;<br />
&nbsp; 其中①指定了字串格式：字体为TIMES，字号16，加粗显示。WritableFont有非常丰富的<br />
&nbsp;构造子，供不同情况下使用，jExcelAPI的java-doc中有详细列表，这里不再列出。</p>
<p>&nbsp;②处代码使用了WritableCellFormat类，这个类非常重要，通过它可以指定单元格的各种<br />
&nbsp;属性，后面的单元格格式化中会有更多描述。</p>
<p>&nbsp;③处使用了Label类的构造子，指定了字串被赋予那种格式。</p>
<p>&nbsp;在WritableCellFormat类中，还有一个很重要的方法是指定数据的对齐方式，比如针对我们<br />
&nbsp;上面的实例，可以指定：</p>
<p>&nbsp;&nbsp; // 把水平对齐方式指定为居中<br />
&nbsp; format1.setAlignment(jxl.format.Alignment.CENTRE);</p>
<p>&nbsp; // 把垂直对齐方式指定为居中<br />
&nbsp; format1.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);<br />
&nbsp;<br />
二、单元格操作</p>
<p>Excel中很重要的一部分是对单元格的操作，比如行高、列宽、单元格合并等，所幸jExcelAPI<br />
提供了这些支持。这些操作相对比较简单，下面只介绍一下相关的API。</p>
<p>1、 合并单元格<br />
Java代码<br />
WritableSheet.mergeCells( int&nbsp; m, int&nbsp; n, int&nbsp; p, int&nbsp; q);&nbsp;&nbsp;&nbsp;<br />
&nbsp;<br />
// 作用是从(m,n)到(p,q)的单元格全部合并，比如：&nbsp;&nbsp;&nbsp;<br />
WritableSheet sheet = book.createSheet(&#8220;第一页&#8221;, 0 );&nbsp;&nbsp;<br />
&nbsp;<br />
// 合并第一列第一行到第六列第一行的所有单元格&nbsp;&nbsp;&nbsp;<br />
sheet.mergeCells( 0 , 0 , 5 , 0 );&nbsp;&nbsp;</p>
<p>&nbsp; WritableSheet.mergeCells( int&nbsp; m, int&nbsp; n, int&nbsp; p, int&nbsp; q);</p>
<p>&nbsp; // 作用是从(m,n)到(p,q)的单元格全部合并，比如：<br />
&nbsp; WritableSheet sheet = book.createSheet(&#8220;第一页&#8221;, 0 );</p>
<p>&nbsp; // 合并第一列第一行到第六列第一行的所有单元格<br />
&nbsp; sheet.mergeCells( 0 , 0 , 5 , 0 );<br />
&nbsp;<br />
合并既可以是横向的，也可以是纵向的。合并后的单元格不能再次进行合并，否则会触发异常。</p>
<p>2、 行高和列宽<br />
Java代码<br />
&nbsp;WritableSheet.setRowView( int&nbsp; i, int&nbsp; height);&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;// 作用是指定第i+1行的高度，比如：&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;// 将第一行的高度设为200&nbsp;&nbsp;&nbsp;<br />
&nbsp;sheet.setRowView( 0 , 200 );&nbsp;&nbsp;<br />
&nbsp;<br />
WritableSheet.setColumnView( int&nbsp; i, int&nbsp; width);&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;// 作用是指定第i+1列的宽度，比如：&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;// 将第一列的宽度设为30&nbsp;&nbsp;&nbsp;<br />
&nbsp;sheet.setColumnView( 0 , 30 );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;</p>
<p>&nbsp; WritableSheet.setRowView( int&nbsp; i, int&nbsp; height);</p>
<p>&nbsp; // 作用是指定第i+1行的高度，比如：</p>
<p>&nbsp; // 将第一行的高度设为200<br />
&nbsp; sheet.setRowView( 0 , 200 );</p>
<p>&nbsp;WritableSheet.setColumnView( int&nbsp; i, int&nbsp; width);</p>
<p>&nbsp; // 作用是指定第i+1列的宽度，比如：</p>
<p>&nbsp; // 将第一列的宽度设为30<br />
&nbsp; sheet.setColumnView( 0 , 30 );<br />
&nbsp;<br />
&nbsp;</p>
<p>jExcelAPI还有其他的一些功能，比如插入图片等，这里就不再一一介绍，读者可以自己探索。</p>
<p>其中：如果读一个excel，需要知道它有多少行和多少列，如下操作：<br />
Java代码<br />
Workbook book&nbsp; =&nbsp; Workbook.getWorkbook( new&nbsp; File( " 测试1.xls " ));&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 获得第一个工作表对象&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sheet sheet&nbsp; =&nbsp; book.getSheet( 0 );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; 得到第一列第一行的单元格&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp; columnum&nbsp; =&nbsp; sheet.getColumns(); //&nbsp; 得到列数&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp; rownum&nbsp; =&nbsp; sheet.getRows(); //&nbsp; 得到行数&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(columnum);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(rownum);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp; ( int&nbsp; i&nbsp; =&nbsp;&nbsp; 0 ; i&nbsp; &lt;&nbsp; rownum; i ++ ) //&nbsp; 循环进行读写&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp; ( int&nbsp; j&nbsp; =&nbsp;&nbsp; 0 ; j&nbsp; &lt;&nbsp; columnum; j ++ )&nbsp;&nbsp; {&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cell cell1&nbsp; =&nbsp; sheet.getCell(j, i);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String result&nbsp; =&nbsp; cell1.getContents();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(result);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print( " \t " );&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; book.close();&nbsp;&nbsp;<br />
</p>
<img src ="http://www.blogjava.net/gp213/aggbug/270584.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-05-14 13:20 <a href="http://www.blogjava.net/gp213/archive/2009/05/14/270584.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java 反射机制系列</title><link>http://www.blogjava.net/gp213/archive/2009/05/14/270583.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 14 May 2009 05:17:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/05/14/270583.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/270583.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/05/14/270583.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/270583.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/270583.html</trackback:ping><description><![CDATA[<a onclick="javascript:tagshow(event, 'Java');" href="javascript:;" target="_self"><u><strong>Java</strong></u></a> 反射机制是指Java程序可以在执行期载入，探知，使用编译期间完全未知的classes.这句话可能有点难以理解，我们可以通过一个例子来看。在Java程序中我们经常会用到这样一条语句来创建一个对象。Date date = new Date();在这条语句中date的类型(Java.util.Date)在编译时 已经确定。那么，有没有办法使我们把对象类型的确定时间由编译转到运行，答案是肯定的。这就是Java反射机制所提供的便利。而且它不单单可以生成对象还可以获取Field，对Field设值，及调用方法等。
<p>　　谈及Java反射机制就一定要知道一个名为&#8220;Class&#8221;的类，它是Java反射机制的基础。&#8220;Class&#8221;和其它类一样继承于Object类，它的实例对象用来描述Java运行时的一种类型，接口，或者原始类型(比如int).&#8220;Class&#8221;的实例要由JVM创建，它没有公用的构造方法。下面我们来看一下如何获得"Class"类实例。</p>
<p>　　主要有三种方法。</p>
<p>　　一，通过Class类的静态方法forName获取。Class cla = Class.forName("java.lang.String");</p>
<p>　　二，通过.Type或.class属性获得。Class cla = String.class;Class cla1 = int.Type;</p>
<p>　　三，通过实例变量的getClass方法获得。String s = ""; Class cla = s.getClass();</p>
<p>　　如上所示实例对象cla就是对String类型的描述，通过它我们就可以创建一个String实例，并调用其中的方法。下一篇我将通过一个例子来展示如何使用Java反射机制。</p>
下面我以顾客买相机为例来说明<a onclick="javascript:tagshow(event, 'Java');" href="javascript:;" target="_self"><u><strong>Java</strong></u></a>反射机制的应用。例子中涉及的类和接口有：
<p>　　Camera接口：定义了takePhoto()方法。</p>
<p>　　Camera01类：一种照相机的类型，实现Camera接口。</p>
<p>　　Camera02类：另一种照相机的类型，实现Camera接口。</p>
<p>　　Seller类：卖照相机。</p>
<p>　　Customer类：买相机，有main方法。</p>
<p>　　所有类都放在com包里</p>
<p>　　程序如下：</p>
<p>　　public interface Camera {</p>
<p>　　//声明照相机必须可以拍照</p>
<p>　　public void takePhoto();</p>
<p>　　}</p>
<p>　　public class Camera01 implements Camera {</p>
<p>　　private final int prefixs =300;//300万象素</p>
<p>　　private final double ptionZoom=3.5; //3.5倍变焦</p>
<p>　　public void takePhoto() {</p>
<p>　　System.out.println("Camera01 has taken a photo");</p>
<p>　　}</p>
<p>　　}</p>
<p>　　类似的有</p>
<p>　　public class Camera02 implements Camera {</p>
<p>　　private final int prefixs =400;</p>
<p>　　private final double ptionZoom=5;</p>
<p>　　public void takePhoto() {</p>
<p>　　System.out.println("Camera02 has taken a photo");</p>
<p>　　}</p>
<p>　　}</p>
<p>　　顾客出场了</p>
<p>　　public class Customer {</p>
<p>　　public static void main(String[] args){</p>
<p>　　//找到一个售货员</p>
<p>　　Seller seller = new Seller();</p>
<p>　　//向售货员询问两种相机的信息</p>
<p>　　seller.getDescription("com.Camera01");</p>
<p>　　seller.getDescription("com.Camera02");</p>
<p>　　//觉得Camera02比较好，叫售货员拿来看</p>
<p>　　Camera camera =(Camera)seller.getCamera("com.Camera02");</p>
<p>　　//让售货员拍张照试一下</p>
<p>　　seller.testFuction(camera, "takePhoto");</p>
<p>　　}</p>
<p>　　}</p>
<p>　　Seller类通过Java反射机制实现</p>
<p>　　import java.lang.reflect.Field;</p>
<p>　　import java.lang.reflect.Method;</p>
<p>　　public class Seller {</p>
<p>　　//向顾客描述商品信息</p>
<p>　　public void getDescription(String type){</p>
<p>　　try {</p>
<p>　　Class cla = Class.forName(type);</p>
<p>　　//生成一个实例对象，在编译时我们并不知道obj是什么类型。</p>
<p>　　Object bj = cla.newInstance();</p>
<p>　　//获得type类型所有已定义类变量及方法。</p>
<p>　　Field[] fileds = cla.getDeclaredFields();</p>
<p>　　Method[]methods = cla.getDeclaredMethods();</p>
<p>　　System.out.println("The arguments of this Camera is:");</p>
<p>　　for(int i=0;i<fileds.length;i++){< p>
<p>　　fileds[i].setAccessible(true);</p>
<p>　　//输出类变量的定义及obj实例中对应的值</p>
<p>　　System.out.println(fileds[i]+":"+fileds[i].get(obj));</p>
<p>　　}</p>
<p>　　System.out.println("The function of this Camera:");</p>
<p>　　for(int i=0;i<methods.length;i++){< p>
<p>　　//输出类中方法的定义</p>
<p>　　System.out.println(methods[i]);</p>
<p>　　}</p>
<p>　　System.out.println();</p>
<p>　　} catch (Exception e) {</p>
<p>　　System.out.println("Sorry , no such type");</p>
<p>　　}</p>
<p>　　}</p>
<p>　　//使用商品的某个功能</p>
<p>　　public void testFuction(Object obj,String function){</p>
<p>　　try {</p>
<p>　　Class cla = obj.getClass();</p>
<p>　　//获得cla类中定义的无参方法。</p>
<p>　　Method m = cla.getMethod(function, null);</p>
<p>　　//调用obj中名为function的无参方法。</p>
<p>　　m.invoke(obj, null);</p>
<p>　　} catch (Exception e) {</p>
<p>　　System.out.println("Sorry , no such function");</p>
<p>　　}</p>
<p>　　}</p>
<p>　　//拿商品给顾客</p>
<p>　　public Object getCamera(String type){</p>
<p>　　try {</p>
<p>　　Class cla = Class.forName(type);</p>
<p>　　Object bj = cla.newInstance();</p>
<p>　　return obj;</p>
<p>　　} catch (Exception e) {</p>
<p>　　System.out.println("Sorry , no such type");</p>
<p>　　return null;</p>
<p>　　}</p>
<p>　　}</p>
<p>　　}</p>
上一篇中，通过例子我们知道了如何利用反射机制创建对象，获得类变量和调用方法等。创建对象的语句是 Class cla = Class.forName(type); Object bj = cla.newInstance(); 这里newInstance()实际上是使用了该类的默认无参构造方法。如果我们要调用其它的构造方法就要稍微复杂一点。比如我们要创建一个StringBuffer对象，用new 操作符应该是StringBuffer br = new StringBuffer("example");用反射机制则要有以下步骤。
<p>　　首先，获得StringBuffer类的描述。</p>
<p>　　Class cla = Class.forName("<a onclick="javascript:tagshow(event, 'java');" href="javascript:;" target="_self"><u><strong>java</strong></u></a>.lang.StringBuffer");</p>
<p>　　其次，要创建参数类型数组Class[] 。</p>
<p>　　Class[] paraTypes = new Class[1];paraTypes[0]=String.class;</p>
<p>　　然后，通过cla 和 paraTypes 获得Constructor 对象。</p>
<p>　　Constructor constructor = cla.getConstructor(paraTypes);</p>
<p>　　接着，创建传入的参数列表Object[]。</p>
<p>　　Object[] paraLists = new Object[1]; paraLists[0]="color";</p>
<p>　　最后，得到我们所要得对象。Object bj = constructor.newInstance(paraLists);</p>
<p>　　如果我们paraTypes及paraLists设为null或长度为0，就可以用上述步骤调用StringBuffer的无参构造方法。类似地，我们可以调用对象中的有参方法。比如我们做如下操作br.insert(4, 'u');用反射机制实现如下。</p>
<p>　　Class[] paratypes = new Class[]{int.class,char.class};</p>
<p>　　Method method = cla.getMethod("insert", paratypes);</p>
<p>　　Object[] paralists = new Object[]{4,'u'};</p>
<p>　　method.invoke(obj, paralists);</p>
<p>　　反射机制给予我们运行时才确定对象类型的便利，然而它也有显著的缺点。</p>
<p>　　1，代码笨拙冗长。比如本来一句br.insert(4, 'u');可以解决的问题现在要用到四句。</p>
<p>　　2，损失了编译时类型检查的好处。这使得你要对付更多的异常。</p>
<p>　　3，性能损失。用反射机制运行的时间更久。</p>
<p>　　&lt;<effective Java>&gt;中给出的建议是&#8220;普通应用不应在运行时刻以映像方式访问对象，只是在很有限的情况下使用&#8220;。那么在什么地方会用到反射机制呢。已有的较熟悉应用是我们的IDE及一些框架。比如<a onclick="javascript:tagshow(event, 'eclipse');" href="javascript:;" target="_self"><u><strong>eclipse</strong></u></a>,编程时ctrl+space弹出的建议就是用到反射机制。比如Spring读取配置文件后生成对应的对象。还有RPC系统也会用到。对于一般的应用软件，你可以在工厂方法中用到它。</p>
<img src ="http://www.blogjava.net/gp213/aggbug/270583.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-05-14 13:17 <a href="http://www.blogjava.net/gp213/archive/2009/05/14/270583.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP连接数据库方法大全</title><link>http://www.blogjava.net/gp213/archive/2009/05/14/270578.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 14 May 2009 04:54:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/05/14/270578.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/270578.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/05/14/270578.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/270578.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/270578.html</trackback:ping><description><![CDATA[一、<a onclick="javascript:tagshow(event, 'jsp');" href="javascript:;" target="_self"><u><strong>jsp</strong></u></a>连接Oracle8/8i/9i数据库（用thin模式）<br />
　　testoracle.jsp如下：<br />
　　&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
　　&lt;%@ page import="<a onclick="javascript:tagshow(event, 'java');" href="javascript:;" target="_self"><u><strong>java</strong></u></a>.sql.*"%&gt;<br />
　　&lt;html&gt;<br />
　　&lt;body&gt;<br />
　　&lt;%Class.forName("<a onclick="javascript:tagshow(event, 'oracle');" href="javascript:;" target="_self"><u><strong>oracle</strong></u></a>.jdbc.driver.OracleDriver").newInstance();<br />
　　String url="jdbc:oracle:thin:@localhost:1521:orcl";<br />
　　//orcl为你的<a onclick="javascript:tagshow(event, '%CA%FD%BE%DD%BF%E2');" href="javascript:;" target="_self"><u><strong>数据库</strong></u></a>的SID<br />
　　String user="scott";<br />
　　String password="tiger";<br />
　　Connection conn= DriverManager.getConnection(url,user,password);<br />
　　Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />
　　String <a onclick="javascript:tagshow(event, 'sql');" href="javascript:;" target="_self"><u><strong>sql</strong></u></a>="select * from test";<br />
　　ResultSet rs=stmt.executeQuery(sql);<br />
　　while(rs.next()) {%&gt;<br />
　　您的第一个字段内容为：&lt;%=rs.getString(1)%&gt;<br />
　　您的第二个字段内容为：&lt;%=rs.getString(2)%&gt;<br />
　　&lt;%}%&gt;<br />
　　&lt;%out.print("数据库操作成功，恭喜你\");%&gt;<br />
　　&lt;%rs.close();<br />
　　stmt.close();<br />
　　conn.close();<br />
　　%&gt;<br />
　　&lt;/body&gt;<br />
　　&lt;/html&gt;<br />
　　<br />
　　二、jsp连接Sql Server7.0/2000数据库<br />
　　testsqlserver.jsp如下：<br />
　　&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
　　&lt;%@ page import="java.sql.*"%&gt;<br />
　　&lt;html&gt;<br />
　　&lt;body&gt;<br />
　　&lt;%Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();<br />
　　String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=pubs";<br />
　　//pubs为你的数据库的<br />
　　String user="sa";<br />
　　String password="";<br />
　　Connection conn= DriverManager.getConnection(url,user,password);<br />
　　Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />
　　String sql="select * from test";<br />
　　ResultSet rs=stmt.executeQuery(sql);<br />
　　while(rs.next()) {%&gt;<br />
　　您的第一个字段内容为：&lt;%=rs.getString(1)%&gt;<br />
　　您的第二个字段内容为：&lt;%=rs.getString(2)%&gt;<br />
　　&lt;%}%&gt;<br />
　　&lt;%out.print("数据库操作成功，恭喜你\");%&gt;<br />
　　&lt;%rs.close();<br />
　　stmt.close();<br />
　　conn.close();<br />
　　%&gt;<br />
　　&lt;/body&gt;<br />
　　&lt;/html&gt;<br />
　　<br />
　　三、jsp连接<a onclick="javascript:tagshow(event, 'DB2');" href="javascript:;" target="_self"><u><strong>DB2</strong></u></a>数据库<br />
　　testdb2.jsp如下：<br />
　　&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
　　&lt;%@ page import="java.sql.*"%&gt;<br />
　　&lt;html&gt;<br />
　　&lt;body&gt;<br />
　　&lt;%Class.forName("com.ibm.db2.jdbc.app.DB2Driver ").newInstance();<br />
　　String url="jdbc:db2://localhost:5000/sample";<br />
　　//sample为你的数据库名<br />
　　String user="admin";<br />
　　String password="";<br />
　　Connection conn= DriverManager.getConnection(url,user,password);<br />
　　Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />
　　String sql="select * from test";<br />
　　ResultSet rs=stmt.executeQuery(sql);<br />
　　while(rs.next()) {%&gt;<br />
　　您的第一个字段内容为：&lt;%=rs.getString(1)%&gt;<br />
　　您的第二个字段内容为：&lt;%=rs.getString(2)%&gt;<br />
　　&lt;%}%&gt;<br />
　　&lt;%out.print("数据库操作成功，恭喜你\");%&gt;<br />
　　&lt;%rs.close();<br />
　　stmt.close();<br />
　　conn.close();<br />
　　%&gt;<br />
　　&lt;/body&gt;<br />
　　&lt;/html&gt;<br />
　　<br />
　　四、jsp连接Informix数据库<br />
　　testinformix.jsp如下：<br />
　　&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
　　&lt;%@ page import="java.sql.*"%&gt;<br />
　　&lt;html&gt;<br />
　　&lt;body&gt;<br />
　　&lt;%Class.forName("com.informix.jdbc.IfxDriver").newInstance();<br />
　　String url =<br />
　　"jdbc:informix-sqli://123.45.67.89:1533/testDB:INFORMIXSERVER=myserver;<br />
　　user=testuser;password=testpassword";<br />
　　//testDB为你的数据库名<br />
　　Connection conn= DriverManager.getConnection(url);<br />
　　Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />
　　String sql="select * from test";<br />
　　ResultSet rs=stmt.executeQuery(sql);<br />
　　while(rs.next()) {%&gt;<br />
　　您的第一个字段内容为：&lt;%=rs.getString(1)%&gt;<br />
　　您的第二个字段内容为：&lt;%=rs.getString(2)%&gt;<br />
　　&lt;%}%&gt;<br />
　　&lt;%out.print("数据库操作成功，恭喜你\");%&gt;<br />
　　&lt;%rs.close();<br />
　　stmt.close();<br />
　　conn.close();<br />
　　%&gt;<br />
　　&lt;/body&gt;<br />
　　&lt;/html&gt;<br />
　　<br />
　　五、jsp连接Sybase数据库<br />
　　testmysql.jsp如下：<br />
　　&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
　　&lt;%@ page import="java.sql.*"%&gt;<br />
　　&lt;html&gt;<br />
　　&lt;body&gt;<br />
　　&lt;%Class.forName("com.sybase.jdbc.SybDriver").newInstance();<br />
　　String url =" jdbc:sybase:Tds:localhost:5007/tsdata";<br />
　　//tsdata为你的数据库名<br />
　　Properties sysProps = System.getProperties();<br />
　　SysProps.put("user","userid");<br />
　　SysProps.put("password","user_password");<br />
　　Connection conn= DriverManager.getConnection(url, SysProps);<br />
　　Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />
　　String sql="select * from test";<br />
　　ResultSet rs=stmt.executeQuery(sql);<br />
　　while(rs.next()) {%&gt;<br />
　　您的第一个字段内容为：&lt;%=rs.getString(1)%&gt;<br />
　　您的第二个字段内容为：&lt;%=rs.getString(2)%&gt;<br />
　　&lt;%}%&gt;<br />
　　&lt;%out.print("数据库操作成功，恭喜你\");%&gt;<br />
　　&lt;%rs.close();<br />
　　stmt.close();<br />
　　conn.close();<br />
　　%&gt;<br />
　　&lt;/body&gt;<br />
　　&lt;/html&gt;<br />
　　<br />
　　六、jsp连接<a onclick="javascript:tagshow(event, 'MySQL');" href="javascript:;" target="_self"><u><strong>MySQL</strong></u></a>数据库<br />
　　testmysql.jsp如下：<br />
　　&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
　　&lt;%@ page import="java.sql.*"%&gt;<br />
　　&lt;html&gt;<br />
　　&lt;body&gt;<br />
　　&lt;%Class.forName("org.gjt.mm.mysql.Driver").newInstance();<br />
　　String url ="jdbc:mysql://localhost/softforum?user=soft&amp;password=soft1234&amp;useUnicode=true&amp;characterEncoding=8859_1"<br />
　　//testDB为你的数据库名<br />
　　Connection conn= DriverManager.getConnection(url);<br />
　　Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />
　　String sql="select * from test";<br />
　　ResultSet rs=stmt.executeQuery(sql);<br />
　　while(rs.next()) {%&gt;<br />
　　您的第一个字段内容为：&lt;%=rs.getString(1)%&gt;<br />
　　您的第二个字段内容为：&lt;%=rs.getString(2)%&gt;<br />
　　&lt;%}%&gt;<br />
　　&lt;%out.print("数据库操作成功，恭喜你\");%&gt;<br />
　　&lt;%rs.close();<br />
　　stmt.close();<br />
　　conn.close();<br />
　　%&gt;<br />
　　&lt;/body&gt;<br />
　　&lt;/html&gt;<br />
　　<br />
　　七、jsp连接PostgreSQL数据库<br />
　　testmysql.jsp如下：<br />
　　&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
　　&lt;%@ page import="java.sql.*"%&gt;<br />
　　&lt;html&gt;<br />
　　&lt;body&gt;<br />
　　&lt;%Class.forName("org.postgresql.Driver").newInstance();<br />
　　String url ="jdbc:postgresql://localhost/soft"<br />
　　//soft为你的数据库名<br />
　　String user="myuser";<br />
　　String password="mypassword";<br />
　　Connection conn= DriverManager.getConnection(url,user,password);<br />
　　Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />
　　String sql="select * from test";<br />
　　ResultSet rs=stmt.executeQuery(sql);<br />
　　while(rs.next()) {%&gt;<br />
　　您的第一个字段内容为：&lt;%=rs.getString(1)%&gt;<br />
　　您的第二个字段内容为：&lt;%=rs.getString(2)%&gt;<br />
　　&lt;%}%&gt;<br />
　　&lt;%out.print("数据库操作成功，恭喜你\");%&gt;<br />
　　&lt;%rs.close();<br />
　　stmt.close();<br />
　　conn.close();<br />
　　%&gt;<br />
　　&lt;/body&gt;<br />
　　&lt;/html&gt; <br />
<img src ="http://www.blogjava.net/gp213/aggbug/270578.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-05-14 12:54 <a href="http://www.blogjava.net/gp213/archive/2009/05/14/270578.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 中 Vector、ArrayList、List 使用深入剖析</title><link>http://www.blogjava.net/gp213/archive/2009/05/14/270573.html</link><dc:creator>郭鹏</dc:creator><author>郭鹏</author><pubDate>Thu, 14 May 2009 03:43:00 GMT</pubDate><guid>http://www.blogjava.net/gp213/archive/2009/05/14/270573.html</guid><wfw:comment>http://www.blogjava.net/gp213/comments/270573.html</wfw:comment><comments>http://www.blogjava.net/gp213/archive/2009/05/14/270573.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gp213/comments/commentRss/270573.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gp213/services/trackbacks/270573.html</trackback:ping><description><![CDATA[<p>线性表，链表，哈希表是常用的数据结构，在进行<a onclick="javascript:tagshow(event, 'Java');" href="javascript:;" target="_self"><u><strong>Java</strong></u></a>开发时，JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述，向读者阐述各个类的作用以及如何正确使用这些类。</p>
<p>Collection<br />
├List<br />
│├LinkedList<br />
│├ArrayList<br />
│└Vector<br />
│　└Stack<br />
└Set<br />
Map<br />
├Hashtable<br />
├HashMap<br />
└WeakHashMap</p>
<p>Collection接口<br />
　　Collection是最基本的集合接口，一个Collection代表一组Object，即Collection的元素（Elements）。一些 Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类， Java SDK提供的类都是继承自Collection的&#8220;子接口&#8221;如List和Set。<br />
　　所有实现Collection接口的类都必须提供两个标准的构造函数：无参数的构造函数用于创建一个空的Collection，有一个Collection参数的构造函数用于创建一个新的 Collection，这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。<br />
　　如何遍历Collection中的每一个元素？不论Collection的实际类型如何，它都支持一个iterator()的方法，该方法返回一个迭代子，使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下：<br />
　　　　Iterator it = collection.iterator(); // 获得一个迭代子<br />
　　　　while(it.hasNext()) {<br />
　　　　　　Object bj = it.next(); // 得到下一个元素<br />
　　　　}<br />
　　由Collection接口派生的两个接口是List和Set。</p>
<p>List接口<br />
　　List是有序的Collection，使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引（元素在List中的位置，类似于数组下标）来访问List中的元素，这类似于Java的数组。<br />
和下面要提到的Set不同，List允许有相同的元素。<br />
　　除了具有Collection接口必备的iterator()方法外，List还提供一个listIterator()方法，返回一个 ListIterator接口，和标准的Iterator接口相比，ListIterator多了一些add()之类的方法，允许添加，删除，设定元素，还能向前或向后遍历。<br />
　　实现List接口的常用类有LinkedList，ArrayList，Vector和Stack。</p>
<p>LinkedList类<br />
　　LinkedList实现了List接口，允许null元素。此外LinkedList提供额外的get，remove，insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈（stack），队列（queue）或双向队列（deque）。<br />
　　注意LinkedList没有同步方法。如果多个线程同时访问一个List，则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List：<br />
　　　　List list = Collections.synchronizedList(new LinkedList(...));</p>
<p>ArrayList类<br />
　　ArrayList实现了可变大小的数组。它允许所有元素，包括null。ArrayList没有同步。<br />
size，isEmpty，get，set方法运行时间为常数。但是add方法开销为分摊的常数，添加n个元素需要O(n)的时间。其他的方法运行时间为线性。<br />
　　每个ArrayList实例都有一个容量（Capacity），即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加，但是增长算法并没有定义。当需要插入大量元素时，在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。<br />
　　和LinkedList一样，ArrayList也是非同步的（unsynchronized）。</p>
<p>Vector类<br />
　　Vector非常类似ArrayList，但是Vector是同步的。由Vector创建的Iterator，虽然和ArrayList创建的 Iterator是同一接口，但是，因为Vector是同步的，当一个Iterator被创建而且正在被使用，另一个线程改变了Vector的状态（例如，添加或删除了一些元素），这时调用Iterator的方法时将抛出ConcurrentModificationEx<wbr>ception，因此必须捕获该异常。</p>
<p>Stack 类<br />
　　Stack继承自Vector，实现一个后进先出的堆栈。Stack提供5个额外的方法使得 Vector得以被当作堆栈使用。基本的push和pop方法，还有peek方法得到栈顶的元素，empty方法<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target="_self"><u><strong>测试</strong></u></a>堆栈是否为空，search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。</p>
<p>Set接口<br />
　　Set是一种不包含重复的元素的Collection，即任意的两个元素e1和e2都有e1.equals(e2)=false，Set最多有一个null元素。<br />
　　很明显，Set的构造函数有一个约束条件，传入的Collection参数不能包含重复的元素。<br />
　　请注意：必须小心操作可变对象（Mutable Object）。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。</p>
<p>Map接口<br />
　　请注意，Map没有继承Collection接口，Map提供key到value的映射。一个Map中不能包含相同的key，每个key只能映射一个 value。Map接口提供3种集合的视图，Map的内容可以被当作一组key集合，一组value集合，或者一组key-value映射。</p>
<p>Hashtable类<br />
　　Hashtable继承Map接口，实现一个key-value映射的哈希表。任何非空（non-null）的对象都可作为key或者value。<br />
　　添加数据使用put(key, value)，取出数据使用get(key)，这两个基本操作的时间开销为常数。<br />
Hashtable 通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大，这会影响像get和put这样的操作。<br />
使用Hashtable的简单示例如下，将1，2，3放到Hashtable中，他们的key分别是&#8221;one&#8221;，&#8221;two&#8221;，&#8221;three&#8221;：<br />
　　　　Hashtable numbers = new Hashtable();<br />
　　　　numbers.put(&#8220;one&#8221;, new Integer(1));<br />
　　　　numbers.put(&#8220;two&#8221;, new Integer(2));<br />
　　　　numbers.put(&#8220;three&#8221;, new Integer(3));<br />
　　要取出一个数，比如2，用相应的key：<br />
　　　　Integer n = (Integer)numbers.get(&#8220;two&#8221;);<br />
　　　　System.out.println(&#8220;two = &#8221; + n);<br />
　　由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置，因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object，如果你用自定义的类当作key的话，要相当小心，按照散列函数的定义，如果两个对象相同，即obj1.equals(obj2)=true，则它们的hashCode必须相同，但如果两个对象不同，则它们的hashCode不一定不同，如果两个不同对象的hashCode相同，这种现象称为冲突，冲突会导致操作哈希表的时间开销增大，所以尽量定义好的hashCode()方法，能加快哈希表的操作。<br />
　　如果相同的对象有不同的hashCode，对哈希表的操作会出现意想不到的结果（期待的get方法返回null），要避免这种问题，只需要牢记一条：要同时复写equals方法和hashCode方法，而不要只写其中一个。<br />
　　Hashtable是同步的。</p>
<p>HashMap类<br />
　　HashMap和Hashtable类似，不同之处在于HashMap是非同步的，并且允许null，即null value和null key。，但是将HashMap视为Collection时（values()方法可返回Collection），其迭代子操作时间开销和HashMap的容量成比例。因此，如果迭代操作的性能相当重要的话，不要将HashMap的初始化容量设得过高，或者load factor过低。</p>
<p>WeakHashMap类<br />
　　WeakHashMap是一种改进的HashMap，它对key实行&#8220;弱引用&#8221;，如果一个key不再被外部所引用，那么该key可以被GC回收。</p>
<p>总结<br />
　　如果涉及到堆栈，队列等操作，应该考虑用List，对于需要快速插入，删除元素，应该使用LinkedList，如果需要快速随机访问元素，应该使用ArrayList。<br />
　　如果程序在单线程环境中，或者访问仅仅在一个线程中进行，考虑非同步的类，其效率较高，如果多个线程可能同时操作一个类，应该使用同步的类。<br />
　　要特别注意对哈希表的操作，作为key的对象要正确复写equals和hashCode方法。<br />
　　尽量返回接口而非实际的类型，如返回List而非ArrayList，这样如果以后需要将ArrayList换成LinkedList时，客户端<a onclick="javascript:tagshow(event, '%B4%FA%C2%EB');" href="javascript:;" target="_self"><u><strong>代码</strong></u></a>不用改变。这就是针对抽象<a onclick="javascript:tagshow(event, '%B1%E0%B3%CC');" href="javascript:;" target="_self"><u><strong>编程</strong></u></a>。</p>
<p>同步性<br />
Vector 是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的，因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率，所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择，这样可以避免由于同步带来的不必要的性能开销。</p>
<p>数据增长<br />
从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候，如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度，Vector缺省情况下自动增长原来一倍的数组长度， ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用 Vector有一些优势，因为你可以通过设置集合的初始化大小来避免不必要的资源开销。</p>
<p>使用模式<br />
在ArrayList和Vector中，从一个指定的位置（通过索引）查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的，这个时间我们用 O(1)表示。但是，如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长：O(n-i)，其中n代表集合中元素的个数，i代表元素增加或移除元素的索引位置。为什么会这样呢？以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢？</p>
<p>这意味着，你只是查找特定位置的元素或只在集合的末端增加、移除元素，那么使用Vector或ArrayList都可以。如果是其他操作，你最好选择其他的集合操作类。比如，LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的?O(1)，但它在索引一个元素的使用缺比较慢－O(i),其中i是索引的位置.使用 ArrayList也很容易，因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象，所有你要明白它也会带来额外的开销。</p>
<p>最后，在《Practical Java》一书中Peter Haggar建议使用一个简单的数组（Array）来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。</p>
<p>Java 中 Vector、ArrayList、List 使用深入剖析 - 程序杂烩</p>
<img src ="http://www.blogjava.net/gp213/aggbug/270573.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gp213/" target="_blank">郭鹏</a> 2009-05-14 11:43 <a href="http://www.blogjava.net/gp213/archive/2009/05/14/270573.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>