﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-JAVA FOR FUN-文章分类-java</title><link>http://www.blogjava.net/mlw2000/category/13102.html</link><description>Be happy here...</description><language>zh-cn</language><lastBuildDate>Sun, 20 May 2007 19:14:19 GMT</lastBuildDate><pubDate>Sun, 20 May 2007 19:14:19 GMT</pubDate><ttl>60</ttl><item><title>抽象类与接口的区别(转载)</title><link>http://www.blogjava.net/mlw2000/articles/117769.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Wed, 16 May 2007 01:42:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/117769.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/117769.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/117769.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/117769.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/117769.html</trackback:ping><description><![CDATA[<div class=postbody>abstract&nbsp;class和<font color=#000000>interface</font>是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。&nbsp;abstract&nbsp;class和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于&nbsp;abstract&nbsp;class和interface的选择显得比较随意。<br>其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。<br>一、理解抽象类<br>abstract&nbsp;class和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstract&nbsp;class翻译而来，它表示的是一个抽象体，而abstract&nbsp;class为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？<br>在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。<br>比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。<br>在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed&nbsp;Principle)，抽象类是其中的关键所在。<br>二、从语法定义层面看abstract&nbsp;class和interface<br>在语法层面，Java语言对于abstract&nbsp;class和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。使用abstract&nbsp;class的方式定义Demo抽象类的方式如下：<br><br>abstract&nbsp;class&nbsp;Demo&nbsp;｛<br>abstract&nbsp;void&nbsp;method1();<br>abstract&nbsp;void&nbsp;method2();<br>&#8230;<br>｝<br><br><br>使用interface的方式定义Demo抽象类的方式如下：<br><br>interface&nbsp;Demo&nbsp;{<br>void&nbsp;method1();<br>void&nbsp;method2();<br>&#8230;<br>}<br><br><br>在abstract&nbsp;class方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是static&nbsp;final的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstract&nbsp;class。<br>从编程的角度来看，abstract&nbsp;class和interface都可以用来实现"design&nbsp;by&nbsp;contract"的思想。但是在具体的使用上面还是有一些区别的。<br>首先，abstract&nbsp;class在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。<br>其次，在abstract&nbsp;class的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会&nbsp;增加一些复杂性，有时会造成很大的麻烦。<br>在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstract&nbsp;class或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstract&nbsp;class来实现的，那么可能就只需要修改定义在abstract&nbsp;class中的默认行为就可以了。<br>同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了&nbsp;"one&nbsp;rule，one&nbsp;place"原则，造成代码重复，同样不利于以后的维护。因此，在abstract&nbsp;class和interface间进行选择时要非常的小心。<br>三、从设计理念层面看abstract&nbsp;class和interface<br>上面主要从语法定义和编程的角度论述了abstract&nbsp;class和interface的区别，这些层面的区别是比较低层次的、非本质的。本文将从另一个层面：abstract&nbsp;class和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。<br>前面已经提到过，abstarct&nbsp;class在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"is&nbsp;a"关系，即父类和派生类在概念本质上应该是相同的。对于interface&nbsp;来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。<br>考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstract&nbsp;class或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示：<br><br>使用abstract&nbsp;class方式定义Door：<br><br>abstract&nbsp;class&nbsp;Door&nbsp;{<br>abstract&nbsp;void&nbsp;open();<br>abstract&nbsp;void&nbsp;close()；<br>}<br><br>使用interface方式定义Door：<br><br>interface&nbsp;Door&nbsp;{<br>void&nbsp;open();<br>void&nbsp;close();<br>}<br><br><br>其他具体的Door类型可以extends使用abstract&nbsp;class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract&nbsp;class和interface没有大的区别。<br>如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstract&nbsp;class和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。<br>解决方案一：<br>简单的在Door的定义中增加一个alarm方法，如下：<br><br>abstract&nbsp;class&nbsp;Door&nbsp;{<br>abstract&nbsp;void&nbsp;open();<br>abstract&nbsp;void&nbsp;close()；<br>abstract&nbsp;void&nbsp;alarm();<br>}<br><br><br>或者<br><br>interface&nbsp;Door&nbsp;{<br>void&nbsp;open();<br>void&nbsp;close();<br>void&nbsp;alarm();<br>}<br><br><br>那么具有报警功能的AlarmDoor的定义方式如下：<br><br>class&nbsp;AlarmDoor&nbsp;extends&nbsp;Door&nbsp;{<br>void&nbsp;open()&nbsp;{&nbsp;&#8230;&nbsp;}<br>void&nbsp;close()&nbsp;{&nbsp;&#8230;&nbsp;}<br>void&nbsp;alarm()&nbsp;{&nbsp;&#8230;&nbsp;}<br>}<br><br><br>或者<br><br>class&nbsp;AlarmDoor&nbsp;implements&nbsp;Door&nbsp;｛<br>void&nbsp;open()&nbsp;{&nbsp;&#8230;&nbsp;}<br>void&nbsp;close()&nbsp;{&nbsp;&#8230;&nbsp;}<br>void&nbsp;alarm()&nbsp;{&nbsp;&#8230;&nbsp;}<br>｝<br><br><br>这种方法违反了面向对象设计中的一个核心原则ISP（Interface&nbsp;Segregation&nbsp;Priciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。<br>解决方案二：<br>既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用&nbsp;abstract&nbsp;class方式定义；两个概念都使用interface方式定义；一个概念使用abstract&nbsp;class方式定义，另一个概念使用interface方式定义。<br>显然，由于Java语言不支持多重继承，所以两个概念都使用abstract&nbsp;class方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。<br>如果两个概念都使用interface方式来定义，那么就反映出两个问题：<br>1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？<br>2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。<br>如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，&nbsp;abstract&nbsp;class在Java语言中表示一种继承关系，而继承关系在本质上是"is&nbsp;a"关系。所以对于Door这个概念，我们应该使用abstarct&nbsp;class方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示：<br><br>abstract&nbsp;class&nbsp;Door&nbsp;{<br>abstract&nbsp;void&nbsp;open();<br>abstract&nbsp;void&nbsp;close()；<br>}<br>interface&nbsp;Alarm&nbsp;{<br>void&nbsp;alarm();<br>}<br>class&nbsp;AlarmDoor&nbsp;extends&nbsp;Door&nbsp;implements&nbsp;Alarm&nbsp;{<br>void&nbsp;open()&nbsp;{&nbsp;&#8230;&nbsp;}<br>void&nbsp;close()&nbsp;{&nbsp;&#8230;&nbsp;}<br>void&nbsp;alarm()&nbsp;{&nbsp;&#8230;&nbsp;}<br>}<br><br><br>这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstract&nbsp;class表示的是"is&nbsp;a"关系，interface表示的是"like&nbsp;a"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有&nbsp;Door的功能，那么上述的定义方式就要反过来了。<br>abstract&nbsp;class和interface是Java语言中的两种定义抽象类的方式，它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理，因为它们表现了概念间的不同的关系（虽然都能够实现需求的功能）。这其实也是语言的一种的惯用法，希望读者朋友能够细细体会 </div>
<img src ="http://www.blogjava.net/mlw2000/aggbug/117769.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2007-05-16 09:42 <a href="http://www.blogjava.net/mlw2000/articles/117769.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate的性能(转贴)</title><link>http://www.blogjava.net/mlw2000/articles/105711.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Fri, 23 Mar 2007 01:31:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/105711.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/105711.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/105711.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/105711.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/105711.html</trackback:ping><description><![CDATA[
		<p>Hibernate的性能(转贴)</p>
		<p>xiecc：<br />我们的项目从去年12月份启动，采用了Struts+Hibernate的架构，一开始使用Hibernate的时候速度极快，对象操作异常方便，大家都说爽歪歪。 <br />可惜好景不长，随着我们对象关系的不断复杂，数据量的不断增加，Hibernate的性能急剧下降。具体表现为：我们在设计对象时采用了很多的one-to-many和many-to-one的关系，在取某个对象的几个简单的属性时，它会把所有关联的子对象都取出来，经常出在取一个简单属性的时候，调试窗口的ＳＱＬ语句一屏一屏地往下闪。到最后我的一个test跑完需要１２分钟。 <br />在忍无可忍之下，我们开始性能优化方案，以下我们优化所做的一些事情： <br />1、将所以one-to-many的关系里将lazy设成true <br />2、修改hibernate.properties，增加了以下两句： <br />hibernate.jdbc.fetch_size=50 <br />hibernate.jdbc.batch_size=100 <br />3、调整WebLogic的pool <br />4、利用Hibernate提供的CGLIB Proxy机制，使many-to-one关系的子对象也可以lazy initialization <br />（但是我发现调试窗口里仍会有取子对象的SQL语句，但速度确实快了）。 <br />5、利用Hibernate提供的Cache机制，对关键对象使用Cache <br />结果优化以后，我的test可以从原来的12分钟变成50秒钟跑完。 <br />原以为万事大吉了，但当我们面对客户的时候，才发现我们系统的性能还远远不够。 <br />我们现在系统试运行约两个月，经常在数据保存或者查询时等上一分钟甚至两分钟。 <br />由于客户原来的系统用asp+SQL Server写的，速度很快。二者一对比，我们就被客户骂得惨不忍睹。 <br />优化真是一件很烦人的事，在不改动系统框架的情况下，不知还有哪些提高系统性能的方法？</p>
		<p>freecode：<br />同感，虽然我不用，不懂hibernate. <br />前段时间，我们做了个项目，对一些取数的过程，采用了javascript脚本，再通过bsf编译，运行时，时间巨长，人家说以前用foxpro做的，快多了，弄得我们很没面子。</p>
		<p>dhj1：<br />我也用 Struts+Hibernate 做大型项目,在并发很高时,每天4500人次访问量的情况下,性能也相当不错. </p>
		<p>做的时间有几点考虑: </p>
		<p>1.大东东.如果很多很多的one-to-many和many-to-one的关系. 必定会影响性能,我刚学习Hibernate 时就有这种直觉,所以我们没有用one-to-many和many-to-one的关系.而是象SQL一样的去操作表的关系标识符. </p>
		<p>2.如果超大的系统,最终必须生成HTML的文件.就是有数据库中有数据更新时,自动生成一个HTML文件.大多数用户是在只读状态.在只读状态下就只去显示HTML文件,节省很多资源. </p>
		<p>3.更用CHACHE表技术,把访问量高的记录自动提到CACHE表中.</p>
		<p>xiecc：<br />谢谢dhj1给我提的建议。 <br />在hibernate网站上看到的好多资源几乎都说hibernate的性能不错，而且很多人开发的系统性能也不错，包括dhj1的，看来hibernate无罪，是我们设计得太滥了。 <br />不过还是有点疑问。 <br />1、dhj1提到的第一点很有道理。我们确实在有些关键的地方用了标识符来关联。但是我们这个系统的关联实现太多了，如果所以有东西都用标识符作关联的话，那我们的实体层设计就退化成为面向关系的ER图设计，那我们要Hibernate何用？我用感觉用Hibernate时最大的便利不是在写代码的时候用对象的操作代替SQL语句,而是在建模的时候可以用面向对象的思维把很复杂的逻辑用UML图表示出来,然后直接转化成实体。所以我们在性能影响太大的地方采用了面向对象和关系相结合的方式，但在更多的地方仍然只能采用对象关联的方式。 <br />２、生成静态ＨＴＭＬ，对特定的系统确实有用，但我们系统中的数据几乎都是动态的，所以实现起来有困难。而且我也不太清楚这样做的难度有多高，具体怎么实现，请dhj1指点迷津。 <br />３、Cache我们已经采用了。但proxy的用法我至今仍然有点迷糊。 <br />在Hibernate的文档里似乎只要在定义文件里加这么一句就可以了： <br />　&lt;class name="eg.Order" proxy="eg.Order"&gt; <br />但实际使用时我们发现这样做之后，Hibernate取数据时的SQL语句似乎一句都没有少。 <br />我们现在的想法是自已来实现Proxy类，它的接口与实体完全一样，在Proxy里SQL语句来实现取数据操作。不知是否可行？</p>
		<p>sanwa：<br />我在设计系统时,对每个对象图都会采用两套映射模型, <br />一套用于映射实际的UML,当然包括引用,这套对象图主要用于根据关联对象的属性来查询对象时使用,很少用于更新数据,除非是聚集类. <br />另一套映射,把对象引用映射为Long型的属性,传递到业务层或表示层,需要相关的对象引用时,再用这个引用来load对象.而且,对于需要load的对象可以使用代理或直接多映射一个简单类来处理. <br />另外,对于大数据量的查询或表的关联层次较深(超过三层),建议采用jdbc直接处理.</p>
		<p>方世玉：<br />我们现在也正在用hibernate进行项目开发 <br />我认为不是所有相关的表都要做one Many，many one映射的 <br />比如说一个用户和他的定购业务，我们做了双向关联。 <br />但是这个用户的话单就不能和用户做任何关联了。话单表每天都有数十万条，就是做manyone关联也非常吓人，宁可到时候用hql来查询</p>
		<p>xiecc：<br />呵呵,请教sanwa，这两套映射模型如何在一个应用中同时使用？</p>
		<p>
				<br />dhj1：<br />我用hibernate,是可以减少开发工作量. 特别在开发中或维护过程中对表结构的改动,用HIBERNATE是很方便的. </p>
		<p>做了DAO后,对表的父子关系等的处理,通过ID标识,也只是两三句程序语句.操作也很方便,而且更灵活. </p>
		<p>生成静态ＨＴＭＬ,以后我做过这样的系统,并在XXX省信息港网站上大量使用,性能当然不错,同时1400人在线(真正的在线,不是那种用网页搞个几分钟刷新一次延时的那种在线),性能也不错,开发当然会有一些难度. </p>
		<p>我说的CACHE不是说用HIBERNATE的CACHE.而是自已开发的,把访问量高的信息自动放到一个表中去,这个表保证只有100访问量最高的条记录.多于100条记录的就出去了.</p>
		<p>sanwa：<br />举个例子,比如用户的权限,在我们的系统中涉及用户（User）、权限(Acl)、组件(Component)、组件类(componentDomain)等几个对象的关联。 <br />第一套映射图，反映他们的实际关系，即对应UML模型。User和Acl的关联映射为idbag，不要直接映射为set,因为在我设计的关联表中存在代理主键，代理主键在第二套映射图中实际为用户权限的id. <br />第二套映射图，只映射用户（UserSO）和用户拥有的权限(UserAclSO)，是one to many的关系。 <br />后缀SO表示相关类的简单对象映射类。 </p>
		<p>这样，当客户端需要获取某个用户的所有权限时，直接用第二套映射图。返回的集合中就只包括Acl的id。如果要获取用户对某个组件域的权限，则用第一套映射图，用强大的HQL查询，再转换为第二套映射图返回到客户端。 <br />当更新用户的权限时，也用第二套映射图，直接操作UserSO和UserAclSO，传回更新。 </p>
		<p>使用类似的设计策略时，对many to many的关联表，都采用代理主键，而不是联合主键，这样，两套映射图都较容易存取数据。只是，多数情况下用第二套映射图。 </p>
		<p>当然,我的程序架构是Swing + Session Bean + DAO + Hibernate + DB,Swing和Session Bean的通信可以用HTTP或RMI,在我的架构中,lazy loading发挥不了多大的作用，才采取这种策略。如果在lazy loading可以发挥作用的地方，对大多数对象图，是没有必要采取这种策略的。 </p>
		<p>另外，在我的架构中，swing层有一个组件是可以根据一个ID来加载这个类的属性，直接用jdbc实现的，独立于Hibernate</p>
		<p>xiaoyu：<br />我也说上一句吧。 </p>
		<p>虽然HB是好，方便，但有关数据库设计的一些性能原则还是要考虑的。 </p>
		<p>毕竟它只是Mapping而已。 <br />所以也要设置索引等东西。</p>
		<p>jxb8901：<br />上面的proxy和cache没有任何关系，只是用于lazy loading，请看hibernate中文文档第5章： </p>
		<p>java代码:  </p>
		<p>proxy (可选): 指定一个接口，在延迟装载时作为代理使用。你可以在这里使用该类自己的名字。 </p>
		<p>coolwyc：<br />为了在性能上得到平行,对many-to-one不直接使用关连,例如:user&amp;ACL,取user时,不取ACL,要取ACL就先取user,再取ACL,毕竟应用取user的频率比取ACL高,没必要在取user是硬要把ACL一起取出来. </p>
		<p>还有其他many-to-one都和这个很类似,如果many很少的话就没问题,例如:人&amp;宠物,通常一个人只有少于等于一个宠物,这种情况取人的时候把人跟宠物一起取出对系统影响不大</p>
		<p>willmac：<br />hibernate本身的性能非常好，关键在设计本身 <br />和你的数据库本身，以及你的访问量和数据库的性质 <br />针对不同的环境一定要有不同的设计的，一套设计肯定不能适用于全部的环境 <br />我举个典型例子来说吧 <br />你的hibernate设计可以采用单session的方式，也可以采用多session的方式，应用环境不同，结果也大大的不同。当用户人群少，数据库记录两低的时候，多session的设计是非常有优势的，这就是为什么那么多人 <br />反对使用threadlocal管理session的主要理由把，的却 <br />把两种设计都放在这里，你会发现多session的性效要比 <br />threadlocal的强太多了，并且编程也异常的容易，这种例子，我不再举了，你在这个论坛上都可以下的到。可是 <br />当人群变多，数据库记录海量增长的时候，我们发现问题来了，我们做的应用无限制的吃去内存，应用的响应开始变慢，这是为什么呢？不知道大家是否理解究竟什么是session，其实从根本上说就是一张hashmap,这样大家就好理解了，当不同的用户，同时访问应用的时候，不同的人建立不同的连表，然后释放，而当并发人群急速增长的时候，于是问题就来了。那怎么办，threadlocal没有其他的办法，你要解决一个同步的问题，我们只有一个session,你不能够同时往里读取数据，然后又写入数据的，并发用户这么多，你只可以让他们一个一个得来！！！ <br />哇，这样效率不是太低了，的确，我一开始就说了，小型系统，使用threadlocal绝对是低效的做法的，可是当规模上来了之后，我们再来看，内存不再无限制的开销，cpu德负荷也降下来了，唯一一点发生变化的是，用户的客户端可能多等了0.001秒钟，一个在服务段队列的时间，这就是设计。 <br />讲了一个设计的例子，我们来看多对一，一对多，会让我们应用的效率降低么？ <br />请注意hibernate只是帮助我们完成了mapping 的工作 <br />原来的一对多也好或者多对一也好，本来就存在的，之所以让你觉得效率低，是因为在得到父亲后，还要去把每一个儿子给读出来，可是注意，你只是多执行了一条sql而已，为什么会慢的呢，我想问题还是在设计之上 </p>
		<p>
				<br /> </p>
<img src ="http://www.blogjava.net/mlw2000/aggbug/105711.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2007-03-23 09:31 <a href="http://www.blogjava.net/mlw2000/articles/105711.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解析Java类和对象的初始化过程【转】</title><link>http://www.blogjava.net/mlw2000/articles/85162.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Sun, 03 Dec 2006 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/85162.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/85162.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/85162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/85162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/85162.html</trackback:ping><description><![CDATA[               出处：blog　责任编辑： 方舟 <!-- #EndEditable --><table height="65" cellspacing="0" cellpadding="0" width="760" align="center" border="0"><tbody><tr><td class="content" height="65"><!-- #BeginEditable "3" --><table class="content" width="100%" align="center" border="0"><tbody><tr><td class="content"><p>类的初始化和对象初始化是 JVM 管理的类型生命周期中非常重要的两个环节，Google 了一遍网络，有关类装载机制的文章倒是不少，然而类初始化和对象初始化的文章并不多，特别是从字节码和 JVM 层次来分析的文章更是鲜有所见。</p><p>本文主要对类和对象初始化全过程进行分析，通过一个实际问题引入，将源代码转换成 JVM 字节码后，对 JVM 执行过程的关键点进行全面解析，并在文中穿插入了相关 JVM 规范和 JVM 的部分内部理论知识，以理论与实际结合的方式介绍对象初始化和类初始化之间的协作以及可能存在的冲突问题。</p><p><strong>问题引入</strong></p><p>近日我在调试一个枚举类型的解析器程序，该解析器是将数据库内一万多条枚举代码装载到缓存中，为了实现快速定位枚举代码和具体枚举类别的所有枚举元素，该类在装载枚举代码的同时对其采取两种策略建立内存索引。由于该类是一个公共服务类，在程序各个层面都会使用到它，因此我将它实现为一个单例类。这个类在我调整类实例化语句位置之前运行正常，但当我把该类实例化语句调整到静态初始化语句之前时，我的程序不再为我工作了。 下面是经过我简化后的示例代码：</p><p><strong>［清单一］</strong></p><table width="100%"><tbody><tr><td class="content" bgcolor="#cccccc">package com.ccb.framework.enums;<br />import java.util.Collections;<br />import java.util.HashMap;<br />import java.util.Map;<br />public class CachingEnumResolver { <br />　//单态实例　一切问题皆由此行引起 <br />　private static final CachingEnumResolver SINGLE_ENUM_RESOLVER = new CachingEnumResolver(); <br />　/*MSGCODE-&gt;Category内存索引*/ <br />　private static Map CODE_MAP_CACHE; <br />　static {<br />　　CODE_MAP_CACHE = new HashMap(); <br />　　//为了说明问题,我在这里初始化一条数据 <br />　　CODE_MAP_CACHE.put("0","北京市"); <br />　}<br />　//private, for single instance <br />　private CachingEnumResolver() {<br />　　//初始化加载数据 引起问题，该方法也要负点责任 <br />　　initEnums();<br />　}<br />　/** * 初始化所有的枚举类型 */ <br />　public static void initEnums() { <br />　　// ~~~~~~~~~问题从这里开始暴露 ~~~~~~~~~~~//<br />　　if (null == CODE_MAP_CACHE) { <br />　　　System.out.println("CODE_MAP_CACHE为空,问题在这里开始暴露."); <br />　　　CODE_MAP_CACHE = new HashMap(); <br />　　}<br />　　CODE_MAP_CACHE.put("1", "北京市");<br />　　CODE_MAP_CACHE.put("2", "云南省"); <br />　　//..... other code...<br />　}<br />　public Map getCache() {<br />　　return Collections.unmodifiableMap(CODE_MAP_CACHE);<br />　}<br />　/** * 获取单态实例 * * @return */<br />　public static CachingEnumResolver getInstance() { <br />　　return SINGLE_ENUM_RESOLVER; <br />　}<br />　public static void main(String[] args) {<br />　　System.out.println(CachingEnumResolver.getInstance().getCache());<br />　}<br />} </td></tr></tbody></table><p>想必大家看了上面的代码后会感觉有些茫然，这个类看起来没有问题啊，这的确属于典型的饿汉式单态模式啊，怎么会有问题呢？</p><p>是的，他看起来的确没有问题，可是如果将他 run 起来时，其结果是他不会为你正确 work。运行该类，它的执行结果是：</p><p><strong>［清单二］</strong></p><table width="100%"><tbody><tr><td class="content" bgcolor="#cccccc">CODE_MAP_CACHE为空,问题在这里开始暴露.{0=北京市}</td></tr></tbody></table><p>我的程序怎么会这样？为什么在 initEnum() 方法里 CODE_MAP_CACHE 为空？为什么我输出的 CODE_MAP_CACHE 内容只有一个元素，其它两个元素呢？？？？！！</p><p>看到这里，如果是你在调试该程序，你此刻一定觉得很奇怪，难道是我的 Jvm 有问题吗？非也！如果不是，那我的程序是怎么了？这绝对不是我想要的结果。可事实上无论怎么修改 initEnum() 方法都无济于事，起码我最初是一定不会怀疑到问题可能出在创建 CachingEnumResolver 实例这一环节上。正是因为我太相信我创建 CachingEnumResolver 实例的方法，加之对 Java 类初始化与对象实例化底层原理理解有所偏差，使我为此付出了三、四个小时--约半个工作日的大好青春。</p><p>那么问题究竟出在哪里呢？为什么会出现这样的怪事呢？在解决这个问题之前，先让我们来了解一下JVM的类和对象初始化的底层机制。</p><p><strong>类的生命周期</strong><br />　<img height="243" src="http://www.uml.org.cn/j2ee/images/e892zpk371ap.jpg" width="511" /><br />　 上图展示的是类生命周期流向；在本文里，我只打算谈谈类的"初始化"以及"对象实例化"两个阶段。 </p><p><strong>类初始化</strong></p><p>类"初始化"阶段，它是一个类或接口被首次使用的前阶段中的最后一项工作，本阶段负责为类变量赋予正确的初始值。</p><p>Java 编译器把所有的类变量初始化语句和类型的静态初始化器通通收集到 &lt;clinit&gt; 方法内，该方法只能被 Jvm 调用，专门承担初始化工作。</p><p>除接口以外，初始化一个类之前必须保证其直接超类已被初始化，并且该初始化过程是由 Jvm 保证线程安全的。另外，并非所有的类都会拥有一个 &lt;clinit&gt;() 方法，在以下条件中该类不会拥有 &lt;clinit&gt;() 方法：</p><p>该类既没有声明任何类变量，也没有静态初始化语句；该类声明了类变量，但没有明确使用类变量初始化语句或静态初始化语句初始化；该类仅包含静态 final 变量的类变量初始化语句，并且类变量初始化语句是编译时常量表达式。 <br /><br />　<strong>对象初始化</strong></p><p>在类被装载、连接和初始化，这个类就随时都可能使用了。对象实例化和初始化是就是对象生命的起始阶段的活动，在这里我们主要讨论对象的初始化工作的相关特点。</p><p>Java 编译器在编译每个类时都会为该类至少生成一个实例初始化方法--即 "&lt;init&gt;()" 方法。此方法与源代码中的每个构造方法相对应，如果类没有明确地声明任何构造方法，编译器则为该类生成一个默认的无参构造方法，这个默认的构造器仅仅调用父类的无参构造器，与此同时也会生成一个与默认构造方法对应的 "&lt;init&gt;()" 方法.</p><p>通常来说，&lt;init&gt;() 方法内包括的代码内容大概为：调用另一个 &lt;init&gt;() 方法；对实例变量初始化；与其对应的构造方法内的代码。 如果构造方法是明确地从调用同一个类中的另一个构造方法开始，那它对应的 &lt;init&gt;() 方法体内包括的内容为：一个对本类的 &lt;init&gt;() 方法的调用；对应用构造方法内的所有字节码。</p><p>如果构造方法不是通过调用自身类的其它构造方法开始，并且该对象不是 Object 对象，那 &lt;init&gt;() 法内则包括的内容为：一个对父类 &lt;init&gt;() 方法的调用；对实例变量初始化方法的字节码；最后是对应构造子的方法体字节码。</p><p>如果这个类是 Object，那么它的 &lt;init&gt;() 方法则不包括对父类 &lt;init&gt;() 方法的调用。</p><p><strong>类的初始化时机</strong></p><p>本文到目前为止，我们已经大概有了解到了类生命周期中都经历了哪些阶段，但这个类的生命周期的开始阶段--类装载又是在什么时候被触发呢？类又是何时被初始化的呢？让我们带着这三个疑问继续去寻找答案。</p><p>Java 虚拟机规范为类的初始化时机做了严格定义："initialize on first active use"--" 在首次主动使用时初始化"。这个规则直接影响着类装载、连接和初始化类的机制--因为在类型被初始化之前它必须已经被连接，然而在连接之前又必须保证它已经被装载了。</p><p>在与初始化时机相关的类装载时机问题上，Java 虚拟机规范并没有对其做严格的定义，这就使得 JVM 在实现上可以根据自己的特点提供采用不同的装载策略。我们可以思考一下 Jboss AOP 框架的实现原理，它就是在对你的 class 文件装载环节做了手脚--插入了 AOP 的相关拦截字节码，这使得它可以对程序员做到完全透明化，哪怕你用 new 操作符创建出的对象实例也一样能被 AOP 框架拦截--与之相对应的 Spring AOP，你必须通过他的 BeanFactory 获得被 AOP 代理过的受管对象，当然 Jboss AOP 的缺点也很明显--他是和 JBOSS 服务器绑定很紧密的，你不能很轻松的移植到其它服务器上。嗯~……，说到这里有些跑题了，要知道 AOP 实现策略足可以写一本厚厚的书了，嘿嘿，就此打住。</p><p>说了这么多，类的初始化时机就是在"在首次主动使用时"，那么，哪些情形下才符合首次主动使用的要求呢？</p><p>首次主动使用的情形：</p><p>·创建某个类的新实例时--new、反射、克隆或反序列化； </p><p>·调用某个类的静态方法时； </p><p>·使用某个类或接口的静态字段或对该字段赋值时（final字段除外）； </p><p>·调用Java的某些反射方法时 </p><p>·初始化某个类的子类时 </p><p>·在虚拟机启动时某个含有main()方法的那个启动类。 </p><p>除了以上几种情形以外，所有其它使用JAVA类型的方式都是被动使用的，他们不会导致类的初始化。</p><p><strong>我的问题究竟出在哪里</strong></p><p>好了，了解了JVM的类初始化与对象初始化机制后，我们就有了理论基础，也就可以理性的去分析问题了。</p><p>下面让我们来看看前面[清单一]的JAVA源代码反组译出的字节码：</p><p><strong>［清单三］</strong></p><table width="100%"><tbody><tr><td class="content" bgcolor="#cccccc">public class com.ccb.framework.enums.CachingEnumResolver extendsjava.lang.Object{<br />　static {};<br />　Code: 0: new #2; <br />　//class CachingEnumResolver<br />　3: dup<br />　4: invokespecial #14; <br />　//Method "&lt;init&gt;":()V ① <br />　7: putstatic #16; <br />　//Field SINGLE_ENUM_RESOLVER:Lcom/ccb/framework/enums/CachingEnumResolver; <br />　10: new #18;<br />　//class HashMap ② <br />　13: dup <br />　14: invokespecial #19;<br />　//Method java/util/HashMap."&lt;init&gt;":()V <br />　17: putstatic #21;<br />　//Field CODE_MAP_CACHE:Ljava/util/Map;<br />　20: getstatic #21; <br />　//Field CODE_MAP_CACHE:Ljava/util/Map; <br />　23: ldc #23; <br />　//String 0 <br />　25: ldc #25; <br />　//String 北京市 <br />　27: invokeinterface #31, 3;<br />　//InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; ③ <br />　32: pop 33: returnprivate com.ccb.framework.enums.CachingEnumResolver(); <br />　Code: 0: aload_0 1: invokespecial #34; <br />　//Method java/lang/Object."&lt;init&gt;":()V 4: invokestatic #37; <br />　//Method initEnums:()V ④ 7: returnpublic static void initEnums(); <br />　Code: 0: getstatic #21; <br />　//Field CODE_MAP_CACHE:Ljava/util/Map; <br />　⑤ 3: ifnonnull 24 6: getstatic #44;<br />　//Field java/lang/System.out:Ljava/io/PrintStream; <br />　9: ldc #46;<br />　//String CODE_MAP_CACHE为空,问题在这里开始暴露. <br />　11: invokevirtual #52;<br />　//Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: new #18;<br />　//class HashMap 17: dup 18: invokespecial #19; <br />　//Method java/util/HashMap."&lt;init&gt;":()V ⑥ 21: putstatic #21;<br />　//Field CODE_MAP_CACHE:Ljava/util/Map;<br />　24: getstatic #21; <br />　//Field CODE_MAP_CACHE:Ljava/util/Map;<br />　27: ldc #54; <br />　//String 1 29: ldc #25; <br />　//String 北京市 31: invokeinterface #31, 3;<br />　//InterfaceMethod java/util/Map.put:(Ljava/lang/Object;<br />　Ljava/lang/Object;)Ljava/lang/Object; <br />　⑦ 36: pop 37: getstatic #21; <br />　//Field CODE_MAP_CACHE:Ljava/util/Map; <br />　40: ldc #56;<br />　//String 2 42: ldc #58;<br />　//String 云南省 44: invokeinterface #31, 3;<br />　//InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;<br />　⑧ 49: pop 50: returnpublic java.util.Map getCache();<br />　Code: 0: getstatic #21; <br />　//Field CODE_MAP_CACHE:Ljava/util/Map;<br />　3: invokestatic #66; <br />　//Method java/util/Collections.unmodifiableMap:(Ljava/util/Map;)Ljava/util/Map; <br />　6: areturnpublic static com.ccb.framework.enums.CachingEnumResolver getInstance(); <br />　Code: 0: getstatic #16;<br />　//Field SINGLE_ENUM_RESOLVER:Lcom/ccb/framework/enums/CachingEnumResolver; <br />　⑨ 3: areturn} </td></tr></tbody></table><p>如果上面［清单一］显示，清单内容是在 JDK1.4 环境下的字节码内容，可能这份清单对于很大部分兄弟来说确实没有多少吸引力，因为这些 JVM 指令确实不像源代码那样漂亮易懂。但它的的确确是查找和定位问题最直接的办法，我们想要的答案就在这份 JVM 指令清单里。</p><p>现在，让我们对该类从类初始化到对象实例初始化全过程分析[清单一]中的代码执行轨迹。</p><p>如前面所述，类初始化是在类真正可用时的最后一项前阶工作，该阶段负责对所有类正确的初始化值，此项工作是线程安全的，JVM会保证多线程同步。</p><p><strong>第1步：</strong>调用类初始化方法 CachingEnumResolver.&lt;clinit&gt;()，该方法对外界是不可见的，换句话说是 JVM 内部专用方法，&lt;clinit&gt;() 内包括了 CachingEnumResolver 内所有的具有指定初始值的类变量的初始化语句。要注意的是并非每个类都具有该方法，具体的内容在前面已有叙述。</p><p><strong>第2步：</strong>进入 &lt;clinit&gt;() 方法内，让我们看字节码中的 "①" 行，该行与其上面两行组合起来代表 new 一个 CachingEnumResolver 对象实例，而该代码行本身是指调用 CachingEnumResolver 类的 &lt;init&gt;（）方法。每一个 Java 类都具有一个 &lt;init&gt;() 方法，该方法是 Java 编译器在编译时生成的，对外界不可见，&lt;init&gt;() 方法内包括了所有具有指定初始化值的实例变量初始化语句和java类的构造方法内的所有语句。对象在实例化时，均通过该方法进行初始化。然而到此步，一个潜在的问题已经在此埋伏好，就等着你来犯了。</p><p><strong>第3步：</strong>让我们顺着执行顺序向下看，"④" 行，该行所在方法就是该类的构造器，该方法先调用父类的构造器 &lt;init&gt;() 对父对象进行初始化，然后调用 CachingEnumResolver.initEnum() 方法加载数据。</p><p><strong>第4步：</strong>"⑤" 行，该行获取 "CODE_MAP_CACHE" 字段值，其运行时该字段值为 null。注意，问题已经开始显现了。（作为程序员的你一定是希望该字段已经被初始化过了，而事实上它还没有被初始化）。通过判断，由于该字段为 NULL，因此程序将继续执行到 "⑥" 行，将该字段实例化为 HashMap()。</p><p><strong>第5步：</strong>在 "⑦"、"⑧" 行，其功能就是为 "CODE_MAP_CACHE" 字段填入两条数据。</p><p><strong>第6步：</strong>退出对象初始化方法 &lt;init&gt;()，将生成的对象实例初始化给类字段 "SINGLE_ENUM_RESOLVER"。（注意，此刻该对象实例内的类变量还未初始化完全，刚才由 &lt;init&gt;() 调用 initEnum() 方法赋值的类变量 "CODE_MAP_CACHE" 是 &lt;clinit&gt;() 方法还未初始化字段，它还将在后面的类初始化过程再次被覆盖）。</p><p><strong>第7步：</strong>继续执行 &lt;clinit&gt;（）方法内的后继代码，"②" 行，该行对 "CODE_MAP_CACHE" 字段实例化为 HashMap 实例（注意：在对象实例化时已经对该字段赋值过了，现在又重新赋值为另一个实例，此刻，"CODE_MAP_CACHE"变量所引用的实例的类变量值被覆盖，到此我们的疑问已经有了答案）。</p><p><strong>第8步：</strong>类初始化完毕，同时该单态类的实例化工作也完成。</p><p>通过对上面的字节码执行过程分析，或许你已经清楚了解到导致错误的深层原因了，也或许你可能早已被上面的分析过程给弄得晕头转向了，不过也没折，虽然我也可以从源代码的角度来阐述问题，但这样不够深度，同时也会有仅为个人观点、不足可信之嫌。 </p><p><strong>如何解决</strong></p><p>要解决上面代码所存在的问题很简单，那就是将 "SINGLE_ENUM_RESOLVER" 变量的初始化赋值语句转移到 getInstance() 方法中去即可。换句话说就是要避免在类还未初始化完成时从内部实例化该类或在初始化过程中引用还未初始化的字段。</p><p><strong>写在最后</strong></p><p>静下浮燥之心，仔细思量自己是否真的掌握了本文主题所引出的知识，如果您觉得您已经完全或基本掌握了，那么很好，在最后，我将前面的代码稍做下修改，请思考下面两组程序是否同样会存在问题呢？</p><p><strong>程序一</strong></p><table width="100%"><tbody><tr><td class="content" bgcolor="#cccccc">public class CachingEnumResolver { <br />　public static Map CODE_MAP_CACHE; <br />　static { <br />　　CODE_MAP_CACHE = new HashMap(); <br />　　//为了说明问题,我在这里初始化一条数据 <br />　　CODE_MAP_CACHE.put("0","北京市"); <br />　　initEnums();<br />　} </td></tr></tbody></table><p><strong>程序二</strong></p><table width="100%"><tbody><tr><td class="content" bgcolor="#cccccc">public class CachingEnumResolver { <br />　private static final CachingEnumResolver SINGLE_ENUM_RESOLVER; <br />　public static Map CODE_MAP_CACHE;<br />　static { <br />　　CODE_MAP_CACHE = new HashMap(); <br />　　//为了说明问题,我在这里初始化一条数据 <br />　　CODE_MAP_CACHE.put("0","北京市"); <br />　　SINGLE_ENUM_RESOLVER = new CachingEnumResolver(); <br />　　initEnums();<br />　} </td></tr></tbody></table><p>最后，一点关于 JAVA 群体的感言：时下正是各种开源框架盛行时期，Spring 更是大行其道，吸引着一大批 JEE 开发者的眼球（我也是 fans 中的一员）。然而，让我们仔细观察一下--以 Spring 群体为例，在那么多的 Spring fans 当中，有多少人去研究过 Spring 源代码？又有多少人对 Spring 设计思想有真正深入了解呢？当然，我是没有资格以这样的口吻来说事的，我只是想表明一个观点--学东西一定要"正本清源"。</p></td></tr></tbody></table></td></tr></tbody></table><img src ="http://www.blogjava.net/mlw2000/aggbug/85162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-12-03 10:49 <a href="http://www.blogjava.net/mlw2000/articles/85162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>test error ok</title><link>http://www.blogjava.net/mlw2000/articles/81963.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Sat, 18 Nov 2006 13:09:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/81963.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/81963.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/81963.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/81963.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/81963.html</trackback:ping><description><![CDATA[
		<p>public class Main {<br />    private static final String L = "Decompiling this copyrighted software is a violation of both your license agreement and the Digital Millenium Copyright Act of 1998 (<a href="http://www.loc.gov/copyright/legislation/dmca.pdf">http://www.loc.gov/copyright/legislation/dmca.pdf</a>). Under section 1204 of the DMCA, penalties range up to a $500,000 fine or up to five years imprisonment for a first offense. Think about it; pay for a license, avoid prosecution, and feel better about yourself.";<br />    public static void main(String[] args) {<br />          //"csdn"为用户名。<br />          String userId = "csdn";<br />          <br />          //其中491（4.9.1）/400（4.0）/401（4.1）/501（5.1.0），（）内为版本号<br />          //9912310代表注册码过期时间2099/12/31<br />          <br />          //MyEclipse4.1 GA <br />          //String need = userId.substring(0,1) + "YE3MP-401-00-9912310";<br />          //MyEclipse4.0.0 GA<br />          //String need = userId.substring(0,1) + "YE3MP-400-01-9912310";<br />          //MyEclipse4.9.1<br />          //String need = userId.substring(0,1) + "YE3MP-491-01-9912310";<br />          //MyEclipse5.1.0 GA<br />          String need = userId.substring(0,1) + "YE3MP-501-00-9912310";<br />          String dx = need + L + userId;<br />          int suf = decode(dx);<br />          //System.out.println("SUF:"+suf);<br />          String code = need + suf;<br />          System.out.println(change(code));<br />    }</p>
		<p>    static int decode(String s) {<br />          int i = 0;<br />          char ac[] = s.toCharArray();<br />          int j = 0;<br />          for(int k = ac.length; j &lt; k; j++) {<br />                i = 31 * i + ac[j];<br />          }</p>
		<p>          return Math.abs(i);<br />    }</p>
		<p>    static String change(String s) {<br />          byte abyte0[] = s.getBytes();<br />          char ac[] = new char[s.length()];<br />          int i = 0;<br />          for(int k = abyte0.length; i &lt; k; i++) {<br />                //..............................  <br />             int j = abyte0;<br />                if(j &gt;= 48 &amp;&amp; j &lt;= 57)<br />                    j = ((j - 48) + 5) % 10 + 48;<br />                else<br />                if(j &gt;= 65 &amp;&amp; j &lt;= 90)<br />                    j = ((j - 65) + 13) % 26 + 65;<br />                else<br />                if(j &gt;= 97 &amp;&amp; j &lt;= 122)<br />                    j = ((j - 97) + 13) % 26 + 97;<br />                //.....................................<br />                ac = (char)j;<br />          }</p>
		<p>          return String.valueOf(ac);<br />    }<br />} </p>
<img src ="http://www.blogjava.net/mlw2000/aggbug/81963.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-11-18 21:09 <a href="http://www.blogjava.net/mlw2000/articles/81963.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA的反射 (转)</title><link>http://www.blogjava.net/mlw2000/articles/72765.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Fri, 29 Sep 2006 03:56:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/72765.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/72765.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/72765.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/72765.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/72765.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<font size="3">JAVA的反射库提供了一个非常丰富且精心设计的工具集，以便编写能够动态操纵JAVA代码的程序，这项功能被大量的应用在JavaBeans中，他是JAVA的组建体系结构。</font>
		</div>
		<div class="postText">
				<p>
						<font size="3">JAVA中的代理可以在运行时创建一个实现一组接口的新类，这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用，对于应用程序设计人员来说，遇到这种情况的机会很少。然而对于系统程序设计人员来说，代理带来的灵活性却十分重要。                                                              －－－摘自《JAVA核心技术卷2》</font>
				</p>
				<p>
						<font size="3">有一些初学者对反射和代理有一点害怕，觉得这是高深的技术，不敢去碰，其实任何东西都没有那么复杂，只要你去接触，就会有收获，这篇文章主要讲解了反射和代理、AOP方面的知识，但是非常基本，如果有朋友不太明白可以发表评论，我会认真的解答的，下面我贴上两个程序的代码，很简单大家看一下。</font>
				</p>
				<p>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
				<p>/**<br /> *这个程序的功能是通过反射技术得到一个类（SrcClass）的两个String类型的成员变量<br /> *然后经过判断对字符串进行处理<br /> *这个题目的特点：通过对API的阅读了解Class类的使用方法<br /> *1、getDeclaredFields()得到所有的成员变量<br /> *2、Field类中的一些方法getType（）<br /> *3、Field类继承于AccessibleObject类，有一个setAccessible（）方法，查看API得到他的用途<br /> **/<br />import java.lang.reflect.Field;<br />class SrcClass<br />{<br /> private String name = "BeiJing.2008";//定义来两个String类型的变量，访问修饰都是私有的<br /> private String eMail = "<a href="mailto:BeiJing.2008@163.com">BeiJing.2008@163.com</a>";<br /> private int age = 25;<br /> <br /> <br /> public String toString()//覆盖父类的toString方法，打印数据<br /> {<br />  return "Name:[ " + this.name + " ] EMail: [ " + this.eMail + " ] Age: [ " + this.age + " ]";<br /> }<br />}</p>
				<p>public class TestClass <br />{</p>
				<p>
						<br /> public static void main(String[] args) <br /> {<br />  SrcClass srcclass = new SrcClass();//首先实例化一个SrcClass类的实例，用于给amendString方法传值<br />  TestClass testclass = new TestClass();//再实力化一个TestClass的实例<br />  <br />  System.out.println(srcclass);//打印一遍原始信息</p>
				<p>  <br />  testclass.amendString(srcclass);//调用amendString方法<br />  System.out.println(srcclass);//打印一遍修改后的信息</p>
				<p> }<br /> <br /> <br /> public void amendString(Object cls)//用于修改通过代理得到srcclass的String成员变量，进行替换处理<br /> {<br />  try<br />  {<br />   Field[] fidles = cls.getClass().getDeclaredFields();//得到srcclass.getClass()类中的所有成员变量<br />   for(Field field : fidles)//增强for循环<br />   {<br />    if(field.getType().equals(java.lang.String.class))//判断这个成员变量的类型是不是String类型的<br />    {                                                 //如果返回true就进行处理<br />     field.setAccessible(true);//设置这个变量不进行访问权限检查（我在SrcClass里设置的变量为private）<br />     String newString = (String)field.get(cls);//通过Field类的get方法得到String成员变量<br />     <br />     field.set(cls,newString.replaceAll("BeiJing","China"));//进行替换，再调用Field类的set方法进行修改<br />    }<br />   }<br />  }<br />  <br />  catch(SecurityException e)//<br />  {<br />   e.printStackTrace();<br />  } <br />  <br />  catch (IllegalArgumentException e)<br />  {<br />   <br />   e.printStackTrace();<br />  } <br />  <br />  catch (IllegalAccessException e) <br />  {<br />   <br />   e.printStackTrace();<br />  }<br />  catch(Exception e)<br />  {<br />   e.printStackTrace();<br />  }<br /> }</p>
				<p>}</p>
				<p>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
				<p>下面就是代理了，实现代理必须有4个类（一个接口3各类）</p>
				<p>public interface FooInterface {<br />    public String printMassage();<br />}</p>
				<p>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
				<p>class ImplementFoo implements FooInterface {<br />    <br />      public String printMassage()<br />    {<br />               return "This is ImplementFoo ...";<br />    }<br />}</p>
				<p> </p>
				<p>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
				<p>import java.lang.reflect.InvocationHandler;<br />import java.lang.reflect.Method;<br />import java.util.Date;</p>
				<p>class ImplementInvocationHandler implements InvocationHandler <br />{<br />    private Object foo;</p>
				<p>    public ImplementInvocationHandler(Object foo) //获得将要运行类的实际对象<br />    {<br />       this.foo = foo;    <br />    }</p>
				<p>    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable //<br />    {<br />        System.out.println("You want use " + proxy.getClass().getName() + "." + </p>
				<p>                                          method.getName() + " at " + new Date());<br />        return   method.invoke(foo);//方法调用<br />    }<br />    <br />}</p>
				<p>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
				<p>import java.lang.reflect.InvocationHandler;<br />import java.lang.reflect.InvocationTargetException;<br />import java.lang.reflect.Proxy;<br />/**<br /> *   InvocationHandler handler = new MyInvocationHandler(...);<br />     Class proxyClass = Proxy.getProxyClass(<br />         Foo.class.getClassLoader(), new Class[] { Foo.class });<br />     Foo f = (Foo) proxyClass.<br />         getConstructor(new Class[] { InvocationHandler.class }).<br />         newInstance(new Object[] { handler });</p>
				<p> *<br /> */<br />public class TestProxy {<br />    <br /> <br />    public static void main(String[] args)<br />    {<br />        ImplementFoo foo = new ImplementFoo();//实例化一个实现了FooInterface（自己定义的）接口的类<br />        InvocationHandler handler = new ImplementInvocationHandler(foo);<br />        //自己编写实现InvocationHandler接口的类<br />        //并且将他实例化,ImplementInvocationHandler类中有一个构造函数需要接受一个ImplementFoo类的对象，  因为我们将来<br />        //要使用ImplementFoo.printMassage方法，ImplementInvocationHandler类方法invoke中的method.invoke(foo)<br />        //需要知道他将调用谁的printMassage方法，所以我们要给ImplementInvocationHandler类传递一个ImplementFoo类的对象<br />        <br />         Class proxyClass = Proxy.getProxyClass(FooInterface.class.getClassLoader(),FooInterface.class);<br />        //此时我们就要制造出一个代理类了，使用Proxy类的静态方法getProxyClass，查看API得到答案<br />        <br />          FooInterface f;<br />        try <br />        {<br />       <br />            f =  (FooInterface) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(handler);<br />            System.out.println(f.hashCode());//动态实例化出一个代理类，并且调用他的hashCode、printMassage方法<br />            System.out.println(f.printMassage());<br />           <br />        } catch (SecurityException ex) {<br />            ex.printStackTrace();<br />        } catch (IllegalArgumentException ex) {<br />            ex.printStackTrace();<br />        } catch (IllegalAccessException ex) {<br />            ex.printStackTrace();<br />        } catch (InstantiationException ex) {<br />            ex.printStackTrace();<br />        } catch (NoSuchMethodException ex) {<br />            ex.printStackTrace();<br />        } catch (InvocationTargetException ex) {<br />            ex.printStackTrace();<br />        }<br />  <br />    }<br />    <br />}</p>
		</div>
<img src ="http://www.blogjava.net/mlw2000/aggbug/72765.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-09-29 11:56 <a href="http://www.blogjava.net/mlw2000/articles/72765.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Reflection (JAVA反射)</title><link>http://www.blogjava.net/mlw2000/articles/71703.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Mon, 25 Sep 2006 03:57:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/71703.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/71703.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/71703.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/71703.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/71703.html</trackback:ping><description><![CDATA[
		<p>Reflection 是 Java 程序开发语言的特征之一，它允许运行中的 Java 程序对自身进行检查，或者说“自审”，并能直接操作程序的内部属性。例如，使用它能获得 Java 类中各成员的名称并显示出来。<br /></p>
		<p class="partingline">[separator]</p>
		<br />Java 的这一能力在实际应用中也许用得不是很多，但是在其它的程序设计语言中根本就不存在这一特性。例如，Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。<br /><br />JavaBean 是 reflection 的实际应用之一，它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。<br /><br /><br /><br />1. 一个简单的例子<br /><br />考虑下面这个简单的例子，让我们看看 reflection 是如何工作的。<br /><br />import java.lang.reflect.*;<br />public class DumpMethods {<br />   public static void main(String args[]) {<br />       try {<br />           Class c = Class.forName(args[0]);<br />           Method m[] = c.getDeclaredMethods();<br />           for (int i = 0; i &lt; m.length; i++)<br />               System.out.println(m[i].toString());<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />按如下语句执行：<br /><br />java DumpMethods java.util.Stack<br /><br />它的结果输出为：<br /><br />public java.lang.Object java.util.Stack.push(java.lang.Object)<br /><br />public synchronized java.lang.Object java.util.Stack.pop()<br /><br />public synchronized java.lang.Object java.util.Stack.peek()<br /><br />public boolean java.util.Stack.empty()<br /><br />public synchronized int java.util.Stack.search(java.lang.Object)<br /><br />这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。<br /><br />这个程序使用 Class.forName 载入指定的类，然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。<br /><br />2.开始使用 Reflection<br /><br />用于 reflection 的类，如 Method，可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤：第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中，用 java.lang.Class 类来描述类和接口等。<br /><br />下面就是获得一个 Class 对象的方法之一：<br /><br />Class c = Class.forName("java.lang.String");<br /><br />这条语句得到一个 String 类的类对象。还有另一种方法，如下面的语句：<br /><br />Class c = int.class;<br /><br />或者<br /><br />Class c = Integer.TYPE;<br /><br />它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。<br /><br />第二步是调用诸如 getDeclaredMethods 的方法，以取得该类中定义的所有方法的列表。<br /><br />一旦取得这个信息，就可以进行第三步了——使用 reflection API 来操作这些信息，如下面这段代码：<br /><br />Class c = Class.forName("java.lang.String");<br /><br />Method m[] = c.getDeclaredMethods();<br /><br />System.out.println(m[0].toString());<br /><br />它将以文本方式打印出 String 中定义的第一个方法的原型。<br /><br />在下面的例子中，这三个步骤将为使用 reflection 处理特殊应用程序提供例证。<br /><br />模拟 instanceof 操作符<br /><br />得到类信息之后，通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如，Class.isInstance 方法可以用于模拟 instanceof 操作符：<br /><br />class A {<br />}<br /><br />public class instance1 {<br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("A");<br />           boolean b1 = cls.isInstance(new Integer(37));<br />           System.out.println(b1);<br />           boolean b2 = cls.isInstance(new A());<br />           System.out.println(b2);<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />在这个例子中创建了一个 A 类的 Class 对象，然后检查一些对象是否是 A 的实例。Integer(37) 不是，但 new A() 是。<br /><br />3.找出类的方法<br /><br />找出一个类中定义了些什么方法，这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法：<br /><br />import java.lang.reflect.*;<br /><br />public class method1 {<br />   private int f1(Object p, int x) throws NullPointerException {<br />       if (p == null)<br />           throw new NullPointerException();<br />       return x;<br />   }<br /><br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("method1");<br />           Method methlist[] = cls.getDeclaredMethods();<br />           for (int i = 0; i &lt; methlist.length; i++) {<br />               Method m = methlist[i];<br />               System.out.println("name = " + m.getName());<br />               System.out.println("decl class = " + m.getDeclaringClass());<br />               Class pvec[] = m.getParameterTypes();<br />               for (int j = 0; j &lt; pvec.length; j++)<br />                   System.out.println("param #" + j + " " + pvec[j]);<br />               Class evec[] = m.getExceptionTypes();<br />               for (int j = 0; j &lt; evec.length; j++)<br />                   System.out.println("exc #" + j + " " + evec[j]);<br />               System.out.println("return type = " + m.getReturnType());<br />               System.out.println("-----");<br />           }<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />这个程序首先取得 method1 类的描述，然后调用 getDeclaredMethods 来获取一系列的 Method 对象，它们分别描述了定义在类中的每一个方法，包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods，你还能获得继承来的各个方法的信息。<br /><br />取得了 Method 对象列表之后，要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型，都可以由描述类的对象按顺序给出。<br /><br />输出的结果如下：<br /><br />name = f1<br /><br />decl class = class method1<br /><br />param #0 class java.lang.Object<br /><br />param #1 int<br /><br />exc #0 class java.lang.NullPointerException<br /><br />return type = int<br /><br />-----<br /><br />name = main<br /><br />decl class = class method1<br /><br />param #0 class [Ljava.lang.String;<br /><br />return type = void<br /><br />-----<br /><br /><br />4.获取构造器信息<br /><br />获取类构造器的用法与上述获取方法的用法类似，如：<br /><br />import java.lang.reflect.*;<br /><br />public class constructor1 {<br />   public constructor1() {<br />   }<br /><br />   protected constructor1(int i, double d) {<br />   }<br /><br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("constructor1");<br />           Constructor ctorlist[] = cls.getDeclaredConstructors();<br />           for (int i = 0; i &lt; ctorlist.length; i++) {<br />               Constructor ct = ctorlist[i];<br />               System.out.println("name = " + ct.getName());<br />               System.out.println("decl class = " + ct.getDeclaringClass());<br />               Class pvec[] = ct.getParameterTypes();<br />               for (int j = 0; j &lt; pvec.length; j++)<br />                   System.out.println("param #" + j + " " + pvec[j]);<br />               Class evec[] = ct.getExceptionTypes();<br />               for (int j = 0; j &lt; evec.length; j++)<br />                   System.out.println("exc #" + j + " " + evec[j]);<br />               System.out.println("-----");<br />           }<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />这个例子中没能获得返回类型的相关信息，那是因为构造器没有返回类型。<br /><br />这个程序运行的结果是：<br /><br />name = constructor1<br /><br />decl class = class constructor1<br /><br />-----<br /><br />name = constructor1<br /><br />decl class = class constructor1<br /><br />param #0 int<br /><br />param #1 double<br /><br />-----<br /><br />5.获取类的字段(域)<br /><br />找出一个类中定义了哪些数据字段也是可能的，下面的代码就在干这个事情：<br /><br /><br />import java.lang.reflect.*;<br /><br />public class field1 {<br />   private double d;<br />   public static final int i = 37;<br />   String s = "testing";<br /><br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("field1");<br />           Field fieldlist[] = cls.getDeclaredFields();<br />           for (int i = 0; i &lt; fieldlist.length; i++) {<br />               Field fld = fieldlist[i];<br />               System.out.println("name = " + fld.getName());<br />               System.out.println("decl class = " + fld.getDeclaringClass());<br />               System.out.println("type = " + fld.getType());<br />               int mod = fld.getModifiers();<br />               System.out.println("modifiers = " + Modifier.toString(mod));<br />               System.out.println("-----");<br />           }<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier，它也是一个 reflection 类，用来描述字段成员的修饰语，如“private int”。这些修饰语自身由整数描述，而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个程序的输出是：<br /><br />name = d<br /><br />decl class = class field1<br /><br />type = double<br /><br />modifiers = private<br /><br />-----<br /><br />name = i<br /><br />decl class = class field1<br /><br />type = int<br /><br />modifiers = public static final<br /><br />-----<br /><br />name = s<br /><br />decl class = class field1<br /><br />type = class java.lang.String<br /><br />modifiers =<br /><br />-----<br /><br />和获取方法的情况一下，获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields)，或者也可以取得父类中定义的字段 (getFields) 。<br /><br /><br />6.根据方法的名称来执行方法<br /><br />文本到这里，所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情，比如执行一个指定了名称的方法。下面的示例演示了这一操作：<br /><br />import java.lang.reflect.*;<br />public class method2 {<br />   public int add(int a, int b) {<br />       return a + b;<br />   }<br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("method2");<br />           Class partypes[] = new Class[2];<br />           partypes[0] = Integer.TYPE;<br />           partypes[1] = Integer.TYPE;<br />           Method meth = cls.getMethod("add", partypes);<br />           method2 methobj = new method2();<br />           Object arglist[] = new Object[2];<br />           arglist[0] = new Integer(37);<br />           arglist[1] = new Integer(47);<br />           Object retobj = meth.invoke(methobj, arglist);<br />           Integer retval = (Integer) retobj;<br />           System.out.println(retval.intvalue());<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />假如一个程序在执行的某处的时候才知道需要执行某个方法，这个方法的名称是在程序的运行过程中指定的 (例如，JavaBean 开发环境中就会做这样的事)，那么上面的程序演示了如何做到。<br /><br />上例中，getMethod 用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的 Method 对象之后，在正确的对象实例中执行它。执行该方法的时候，需要提供一个参数列表，这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象，它封装了返回值 84。<br /><br />7.创建新的对象<br /><br />对于构造器，则不能像执行方法那样进行，因为执行一个构造器就意味着创建了一个新的对象 (准确的说，创建一个对象的过程包括分配内存和构造对象)。所以，与上例最相似的例子如下：<br /><br />import java.lang.reflect.*;<br /><br />public class constructor2 {<br />   public constructor2() {<br />   }<br /><br />   public constructor2(int a, int b) {<br />       System.out.println("a = " + a + " b = " + b);<br />   }<br /><br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("constructor2");<br />           Class partypes[] = new Class[2];<br />           partypes[0] = Integer.TYPE;<br />           partypes[1] = Integer.TYPE;<br />           Constructor ct = cls.getConstructor(partypes);<br />           Object arglist[] = new Object[2];<br />           arglist[0] = new Integer(37);<br />           arglist[1] = new Integer(47);<br />           Object retobj = ct.newInstance(arglist);<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />根据指定的参数类型找到相应的构造函数并执行它，以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象，而不是在编译的时候创建对象，这一点非常有价值。<br /><br />8.改变字段(域)的值<br /><br />reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它，下面的例子可以说明这一点：<br /><br />import java.lang.reflect.*;<br /><br />public class field2 {<br />   public double d;<br /><br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("field2");<br />           Field fld = cls.getField("d");<br />           field2 f2obj = new field2();<br />           System.out.println("d = " + f2obj.d);<br />           fld.setDouble(f2obj, 12.34);<br />           System.out.println("d = " + f2obj.d);<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />这个例子中，字段 d 的值被变为了 12.34。<br /><br />9.使用数组<br /><br />本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型，一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的：<br /><br />import java.lang.reflect.*;<br /><br />public class array1 {<br />   public static void main(String args[]) {<br />       try {<br />           Class cls = Class.forName("java.lang.String");<br />           Object arr = Array.newInstance(cls, 10);<br />           Array.set(arr, 5, "this is a test");<br />           String s = (String) Array.get(arr, 5);<br />           System.out.println(s);<br />       } catch (Throwable e) {<br />           System.err.println(e);<br />       }<br />   }<br />}<br /><br />例中创建了 10 个单位长度的 String 数组，为第 5 个位置的字符串赋了值，最后将这个字符串从数组中取得并打印了出来。<br /><br />下面这段代码提供了一个更复杂的例子：<br /><br />import java.lang.reflect.*;<br /><br />public class array2 {<br />   public static void main(String args[]) {<br />       int dims[] = new int[]{5, 10, 15};<br />       Object arr = Array.newInstance(Integer.TYPE, dims);<br />       Object arrobj = Array.get(arr, 3);<br />       Class cls = arrobj.getClass().getComponentType();<br />       System.out.println(cls);<br />       arrobj = Array.get(arrobj, 5);<br />       Array.setInt(arrobj, 10, 37);<br />       int arrcast[][][] = (int[][][]) arr;<br />       System.out.println(arrcast[3][5][10]);<br />   }<br />}<br />例中创建了一个 5 x 10 x 15 的整型数组，并为处于 [3][5][10] 的元素赋了值为 37。注意，多维数组实际上就是数组的数组，例如，第一个 Array.get 之后，arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素，即长度为 15 的数组，并使用 Array.setInt 为它的第 10 个元素赋值。<br /><br />注意创建数组时的类型是动态的，在编译时并不知道其类型。<br /><img src ="http://www.blogjava.net/mlw2000/aggbug/71703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-09-25 11:57 <a href="http://www.blogjava.net/mlw2000/articles/71703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA 正则表达式实现（很好）</title><link>http://www.blogjava.net/mlw2000/articles/71702.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Mon, 25 Sep 2006 03:56:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/71702.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/71702.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/71702.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/71702.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/71702.html</trackback:ping><description><![CDATA[
		<p>JAVA 正则表达式实现<br /><br />偶尔搜到的东东，不记得是那位的Bolg了。很不错的文章。。。。<br />1黑暗岁月<br />有一个String，如何查询其中是否有y和f字符？最黑暗的办法就是：<br />程序1：我知道if、for语句和charAt()啊。<br />class Test{<br />public static void main(String args[]) {<br />String str="For my money, the important thing "+<br />"about the meeting was bridge-building";<br />char x='y';<br />char y='f';<br />boolean result=false;<br />for(int i=0;i&lt;str.length();i++){<br />char z=str.charAt(i); //System.out.println(z);<br />if(x==z||y==z) {<br />result=true;<br />break;<br />}<br />else result=false;<br />}<br />System.out.println(result);<br />}<br />}</p>
		<p> </p>
		<p class="partingline">[separator]</p>
		<p>
				<br />好像很直观，但这种方式难以应付复杂的工作。如查询一段文字中，是否有is？是否有thing或ting等。这是一个讨厌的工作。<br />2 Java的java.util.regex包<br />按照面向对象的思路，把希望查询的字符串如is、thing或ting封装成一个对象，以这个对象作为模板去匹配一段文字，就更加自然了。作为模板的那个东西就是下面要讨论的正则表达式。先不考虑那么复杂，看一个例子：<br />程序2：不懂。先看看可以吧？<br />import java.util.regex.*;<br />class Regex1{<br />public static void main(String args[]) {<br />String str="For my money, the important thing "+<br />"about the meeting was bridge-building";<br />String regEx="a|f"; //表示a或f<br />Pattern p=Pattern.compile(regEx);<br />Matcher m=p.matcher(str);<br />boolean result=m.find();<br />System.out.println(result);<br />}<br />}<br />如果str匹配regEx，那么result为true，否则为flase。如果想在查找时忽略大小写，则可以写成：<br />Pattern p=Pattern.compile(regEx,Pattern.CASE_INSENSITIVE);<br />虽然暂时不知道Pattern（模板、模式）和Matcher（匹配器）的细节，程序的感觉就比较爽，如果先查询is、后来又要查询thing或ting，我们只需要修改一下模板Pattern，而不是考虑if语句和for语句，或者通过charAt()。<br />1、写一个特殊的字符串??正则表达式如a|f。<br />2、将正则表达式编译成一个模板：p<br />3、用模板p去匹配字符串str。<br />思路清楚了，现在看Java是如何处理的（Java程序员直到JDK1.4才能使用这些类。<br />3 Pattern类与查找<br />①public final class java.util.regex.Pattern是正则表达式编译后的表达法。下面的语句将创建一个Pattern对象并赋值给句柄p：Pattern p=Pattern.compile(regEx);<br />有趣的是，Pattern类是final类，而且它的构造器是private。也许有人告诉你一些设计模式的东西，或者你自己查有关资料。这里的结论是：Pattern类不能被继承，我们不能通过new创建Pattern类的对象。<br />因此在Pattern类中，提供了2个重载的静态方法，其返回值是Pattern对象（的引用）。如：<br />public static Pattern compile(String regex) {<br />return new Pattern(regex, 0);<br />}<br />当然，我们可以声明Pattern类的句柄，如Pattern p=null；<br />②p.matcher(str)表示以用模板p去生成一个字符串str的匹配器，它的返回值是一个Matcher类的引用，为什么要这个东西呢？按照自然的想法，返回一个boolean值不行吗？<br />我们可以简单的使用如下方法：<br />boolean result=Pattern.compile(regEx).matcher(str).find();<br />呵呵，其实是三个语句合并的无句柄方式。无句柄常常不是好方式。后面再学习Matcher类吧。先看看regEx??这个怪咚咚。<br />4 正则表达式之限定符<br />正则表达式（Regular Expression）是一种生成字符串的字符串。晕吧。比如说，String regEx="me+";这里字符串me+能够生成的字符串是：me、mee、meee、meeeeeeeeee等等，一个正则表达式可能生成无穷的字符串，所以我们不可能（有必要吗？）输出正则表达式产生的所有东西。<br />反过来考虑，对于字符串：me、mee、meee、meeeeeeeeee等等，我们能否有一种语言去描述它们呢？显然，正则表达式语言是这种语言，它是一些字符串的模式??简洁而深刻的描述。<br />我们使用正则表达式，用于字符串查找、匹配、指定字符串替换、字符串分割等等目的。<br /><br />生成字符串的字符串??正则表达式，真有些复杂，因为我们希望由普通字符（例如字符 a 到 z）以及特殊字符（称为元字符）描述任意的字符串，而且要准确。<br />先搞几个正则表达式例子：<br />程序3：我们总用这个程序测试正则表达式。<br />import java.util.regex.*;<br />class Regex1{<br />public static void main(String args[]) {<br />String str="For my money, the important thing "；<br />String regEx="ab*";<br />boolean result=Pattern.compile(regEx).matcher(str).find();<br />System.out.println(result);<br />}<br />}//ture<br />①"ab*"??能匹配a、ab、abb、abbb……。所以，*表示前面字符可以有零次或多次。如果仅仅考虑查找，直接用"a"也一样。但想想替换的情况。 问题regEx="abb*"结果如何？<br />②"ab+"??能匹配ab、abb、abbb……。等价于"abb*"。问题regEx="or+"结果如何？<br />③"or?"??能匹配o和or。? 表示前面字符可以有零次或一次。<br />这些限定符*、+、?方便地表示了其前面字符(子串)出现的次数（我们用{}来描述）：<br />x* 零次或多次 ≡{0,}<br />x+ 一次或多次 ≡{1,}<br />x? 零次或一次 ≡{0,1}<br />x{n} n次（n&gt;0）<br />x{n,m} 最少n次至最多m次（0&lt;n&lt;m）<br />x{n,} 最少n次,<br /><br />现在我们知道了连续字符串的查找、匹配。下面的是一些练习题：<br />①查找粗体字符串（不要求精确或要求精确匹配），写出其正则表达式：<br />str regEX(不要求精确) regEX(要求精确) 试一试<br />abcffd b或bcff或bcf*或bc*或bc+ bcff或bcf{2} bc{3}<br />gooooogle o{1,}、o+ o{5}<br />banana (an)+ (an){2}a、a(na) {2}<br /><br /><br /><br />②正则表达式匹配字符串，输出是什么？<br />5替换（删除）、Matcher类<br />现在我们可能厌烦了true/false，我们看看替换。如把book，google替换成bak（这个文件后缀名，在EditPlus中还行）、look或goooogle。<br />程序4：字符串的替换。<br />import java.util.regex.*;<br />class Regex1{<br />public static void main(String args[]) {<br />String regEx="a+";//表示一个或多个a<br />String str="abbbaaa an banana hhaana";<br />Pattern p=Pattern.compile(regEx);<br />Matcher m=p.matcher(str);<br />String s=m.replaceAll("⊙⊙"); // ("") 删除<br />System.out.println(s);<br />}<br />}<br />这个程序与前面的程序的区别，在于使用了m.replaceAll(String)方法。看来Matcher类还有点用处。<br />① public final class Matcher是一个匹配器。可以把他看成一个人，一手拿着模子（Pattern类的对象），一手拿着一个字符序列（CharSequence），通过解释该模子而对字符序列进行匹配操作（match operations）。常常我们这样编程：“喂，模子p，你和字符串str一起创建一个匹配器对象”。即Matcher m=p.matcher(str);<br />② m可以进行一些操作，如public String replaceAll(String replacement)，它以replacement替换所有匹配的字符串。<br />6正则表达式之特殊字符<br />我们熟悉这样一个字符串"\n" 如：System.out.print(s+"\nbbb");这是Java中常用的转移字符之一。其实转移字符就是一种正则表达式，它使用了特殊字符 \ 。<br /><br />下面是正则表达式中常用的特殊字符：<br />匹配次数符号 * + ？ {n}、{n,}、{n,m}<br />“或”符号 | 程序2已经使用过了<br />句点符号 . 句点符号匹配所有字符（一个），包括空格、Tab字符甚至换行符。<br />方括号 [ ] 仅仅匹配方括号其中的字符)<br />圆括号 () 分组，圆括号中的字符视为一个整体。<br />连字符 - 表示一个范围。<br />“否”符号 ^ 表示不希望被匹配的字符（排除）<br />我们一下子学不了太多的东西，这不是正则表达式的全部内容和用法。但已经够我们忙活的了。我们用程序4 验证。(⊙⊙表示替换的字符)<br />① regEx为下列字符串时，能够表示什么？<br />regEx 匹配 测试用str<br />(a|b){2} aa、ab、bb、ba aabbfooaabfooabfoob<br />a[abc]b aab、abb、acb 3dfacb5ooyfo6abbfooaab<br />. all string 3dfac<br />a. aa、ax……等等 3dfacgg<br />d[^j]a daa、d9a等等，除dja 3dfacggdjad5a<br />[d-g][ac]c dac、ecc、gac等 3dfacggggccad5c<br />[d-g].{2}c d⊙⊙c…… 3dfacggggccad5c<br />g{1,10} g、ggg…… 3dfacggggccad5c<br />[a|c][^a] 3dfacggggccad5c<br /><br />② 下列字符串如何用regEx表示？<br />测试用str 匹配 regEx<br />aabbfoaoabfooafobob a⊙⊙b a..b<br />aabbfoaaobfooafbob a⊙b、除aab a[^a]b、<br />gooooooogle oooo……变成oo o{2,20}<br />一本书中的“tan”、“ten”、“tin”和“ton” t.n、t[aeio]n<br />abcaccbcbaacabccaa 删除ac、ca (ca)|(ac)<br />abccbcbaabca 再删除ab、ba 结果ccbcca（如何与上面的合并）<br /><br /><br /><br />注：<br />1、String str="一本书中的tan、ten、tin和ton";<br />输出： 一本书中的⊙⊙、⊙⊙、⊙⊙和⊙⊙<br />2、String str=" abcaccbcbaacabccaa "; 输出：ccbcca<br />程序5：if、for语句和charAt()，886。<br />import java.util.regex.*;<br />class Regex1{<br />public static void main(String args[]) {<br />String str="abcaccbcbaacabccaa";<br />String regEx="(ac)|(ca)";<br />Pattern p=Pattern.compile(regEx);<br />Matcher m=p.matcher(str);<br />String s=m.replaceAll("");//⊙⊙<br />regEx="(ab)|(ba)";<br />p=Pattern.compile(regEx);<br />s=p.matcher(s).replaceAll("");<br /><br />System.out.print(s+"\n");<br />}<br />}<br /><br />7 开始<br />好像我们知道了一些正则表达式与 Java的知识，事实上，我们才刚刚开始。这里列出我们知道的东西，也说一点我们不知道的东西。<br />① Java在JDK1.4引入了（java.util.regex包）以支持正则表达式，包中有两个类，分别是Pattern和Matcher。它们都有很多的方法，我们还不知道。String类中的split、matches方法等等也使用到了正则表达式。StringTokenizer是否没有用处了？<br />② 正则表达式是一门语言。有许多正则表达式语法、选项和特殊字符，在Pattern.java源文件中大家可以查看。可能比想象中的要复杂。系统学习正则表达式的历史、语法、全部特殊字符（相当于Java中的关键字的地位），组合逻辑是下一步的事情。<br />③ 正则表达式是文本处理的重要技术，在Perl、PHP、Python、JavaScript、Java、C＃中被广泛支持。被列为“保证你现在和未来不失业的十种关键技术”，呵呵，信不信由你<br /></p>
<img src ="http://www.blogjava.net/mlw2000/aggbug/71702.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-09-25 11:56 <a href="http://www.blogjava.net/mlw2000/articles/71702.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java正则表达式详解</title><link>http://www.blogjava.net/mlw2000/articles/71700.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Mon, 25 Sep 2006 03:53:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/71700.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/71700.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/71700.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/71700.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/71700.html</trackback:ping><description><![CDATA[
		<table width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td class="t" align="middle" colspan="2">
										<strong>Java正则表达式详解</strong>
								</td>
						</tr>
						<tr>
								<td class="c" id="tdContent" colspan="2">
										<p>如果你曾经用过Perl或任何其他内建正则表达式支持的语言，你一定知道用正则表达式处理文本和匹配模式是多么简单。如果你不熟悉这个术语，那么“正则表达式”（Regular Expression）就是一个字符构成的串，它定义了一个用来搜索匹配字符串的模式。<br />许多语言，包括Perl、PHP、Python、JavaScript和JScript，都支持用正则表达式处理文本，一些文本编辑器用正则表达式实现高级“搜索-替换”功能。那么Java又怎样呢？本文写作时，一个包含了用正则表达式进行文本处理的Java规范需求（Specification Request）已经得到认可，你可以期待在JDK的下一版本中看到它。<br />然而，如果现在就需要使用正则表达式，又该怎么办呢？你可以从Apache.org下载源代码开放的Jakarta-ORO库。本文接下来的内容先简要地介绍正则表达式的入门知识，然后以Jakarta-ORO API为例介绍如何使用正则表达式。</p>
										<p class="partingline">[separator]</p>
										<p>
												<br />
												<strong>一、正则表达式基础知识</strong>
												<br />我们先从简单的开始。假设你要搜索一个包含字符“cat”的字符串，搜索用的正则表达式就是“cat”。如果搜索对大小写不敏感，单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说：<br /><br /><strong>1.1 句点符号</strong><br />假设你在玩英文拼字游戏，想要找出三个字母的单词，而且这些单词必须以“t”字母开头，以“n”字母结束。另外，假设有一本英文字典，你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式，你可以使用一个通配符——句点符号“.”。这样，完整的表达式就是“t.n”，它匹配“tan”、“ten”、“tin”和“ton”，还匹配“t#n”、“tpn”甚至“t n”，还有其他许多无意义的组合。这是因为句点符号匹配所有字符，包括空格、Tab字符甚至换行符：<br /><br /><strong>1.2 方括号符号</strong><br />为了解决句点符号匹配范围过于广泛这一问题，你可以在方括号（“[]”）里面指定看来有意义的字符。此时，只有方括号里面指定的字符才参与匹配。也就是说，正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配，因为在方括号之内你只能匹配单个字符：<br /><br /><strong>1.3 “或”符号</strong><br />如果除了上面匹配的所有单词之外，你还想要匹配“toon”，那么，你可以使用“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”，使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号，因为方括号只允许匹配单个字符；这里必须使用圆括号“()”。圆括号还可以用来分组，具体请参见后面介绍。<br /><br /><strong>1.4 表示匹配次数的符号</strong><br />表一显示了表示匹配次数的符号，这些符号用来确定紧靠该符号左边的符号出现的次数：<br /><br /><br />假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中，连字符（“-”）有着特殊的意义，它表示一个范围，比如从0到9。因此，匹配社会安全号码中的连字符号时，它的前面要加上一个转义字符“\”。<br /><br />图一：匹配所有123-12-1234形式的社会安全号码<br /><br />假设进行搜索的时候，你希望连字符号可以出现，也可以不出现——即，999-99-9999和999999999都属于正确的格式。这时，你可以在连字符号后面加上“？”数量限定符号，如图二所示：<br /><br />图二：匹配所有123-12-1234和123121234形式的社会安全号码<br /><br />下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]”，再加上字母部分“[A-Z]”。图三显示了完整的正则表达式。<br /><br /><br />图三：匹配典型的美国汽车牌照号码，如8836KV<br /><br /><strong>1.5 “否”符号</strong><br />“^”符号称为“否”符号。如果用在方括号内，“^”表示不想要匹配的字符。例如，图四的正则表达式匹配所有单词，但以“X”字母开头的单词除外。<br /><br />图四：匹配所有单词，但“X”开头的除外<br /><br /><strong>1.6 圆括号和空白符号</strong><br />假设要从格式为“June 26, 1951”的生日日期中提取出月份部分，用来匹配该日期的正则表达式可以如图五所示：<br /><br />五：匹配所有Moth DD,YYYY格式的日期<br /><br />新出现的“\s”符号是空白符号，匹配所有的空白字符，包括Tab字符。如果字符串正确匹配，接下来如何提取出月份部分呢？只需在月份周围加上一个圆括号创建一个组，然后用ORO API（本文后面详细讨论）提取出它的值。修改后的正则表达式如图六所示：<br /><br /><br />图六：匹配所有Month DD,YYYY格式的日期，定义月份值为第一个组<br /><br /><strong>1.7 其它符号</strong><br />为简便起见，你可以使用一些为常见正则表达式创建的快捷符号。如表二所示：<br />表二：常用符号<br /><br /><br />例如，在前面社会安全号码的例子中，所有出现“[0-9]”的地方我们都可以使用“\d”。修改后的正则表达式如图七所示<br /><br />图七：匹配所有123-12-1234格式的社会安全号码<br /><br />二、Jakarta-ORO库<br />有许多源代码开放的正则表达式库可供Java程序员使用，而且它们中的许多支持Perl 5兼容的正则表达式语法。我在这里选用的是Jakarta-ORO正则表达式库，它是最全面的正则表达式API之一，而且它与Perl 5正则表达式完全兼容。另外，它也是优化得最好的API之一。<br />Jakarta-ORO库以前叫做OROMatcher，Daniel Savarese大方地把它赠送给了Jakarta Project。你可以按照本文最后参考资源的说明下载它。<br />我首先将简要介绍使用Jakarta-ORO库时你必须创建和访问的对象，然后介绍如何使用Jakarta-ORO API。<br />▲ PatternCompiler对象<br />首先，创建一个Perl5Compiler类的实例，并把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实现，允许你把正则表达式编译成用来匹配的Pattern对象。<br /><br />▲ Pattern对象<br />要把正则表达式编译成Pattern对象，调用compiler对象的compile()方法，并在调用参数中指定正则表达式。例如，你可以按照下面这种方式编译正则表达式“t[aeio]n”：<br /><br /><br />默认情况下，编译器创建一个大小写敏感的模式（pattern）。因此，上面代码编译得到的模式只匹配“tin”、“tan”、 “ten”和“ton”，但不匹配“Tin”和“taN”。要创建一个大小写不敏感的模式，你应该在调用编译器的时候指定一个额外的参数：<br /><br /><br />创建好Pattern对象之后，你就可以通过PatternMatcher类用该Pattern对象进行模式匹配。<br />▲ PatternMatcher对象<br />PatternMatcher对象根据Pattern对象和字符串进行匹配检查。你要实例化一个Perl5Matcher类并把结果赋值给PatternMatcher接口。Perl5Matcher类是PatternMatcher接口的一个实现，它根据Perl 5正则表达式语法进行模式匹配：<br /><br />使用PatternMatcher对象，你可以用多个方法进行匹配操作，这些方法的第一个参数都是需要根据正则表达式进行匹配的字符串：<br />boolean matches(String input, Pattern pattern)：当输入字符串和正则表达式要精确匹配时使用。换句话说，正则表达式必须完整地描述输入字符串。<br />boolean matchesPrefix(String input, Pattern pattern)：当正则表达式匹配输入字符串起始部分时使用。<br />boolean contains(String input, Pattern pattern)：当正则表达式要匹配输入字符串的一部分时使用（即，它必须是一个子串）。<br />另外，在上面三个方法调用中，你还可以用PatternMatcherInput对象作为参数替代String对象；这时，你可以从字符串中最后一次匹配的位置开始继续进行匹配。当字符串可能有多个子串匹配给定的正则表达式时，用PatternMatcherInput对象作为参数就很有用了。用PatternMatcherInput对象作为参数替代String时，上述三个方法的语法如下：<br />boolean matches(PatternMatcherInput input, Pattern pattern)<br />boolean matchesPrefix(PatternMatcherInput input, Pattern pattern)<br />boolean contains(PatternMatcherInput input, Pattern pattern)<br />三、应用实例<br />下面我们来看看Jakarta-ORO库的一些应用实例。<br /><strong>3.1 日志文件处理</strong><br />任务：分析一个Web服务器日志文件，确定每一个用户花在网站上的时间。在典型的BEA WebLogic日志文件中，日志记录的格式如下：<br /><br />分析这个日志记录，可以发现，要从这个日志文件提取的内容有两项：IP地址和页面访问时间。你可以用分组符号（圆括号）从日志记录提取出IP地址和时间标记。<br />首先我们来看看IP地址。IP地址有4个字节构成，每一个字节的值在0到255之间，各个字节通过一个句点分隔。因此，IP地址中的每一个字节有至少一个、最多三个数字。图八显示了为IP地址编写的正则表达式：<br /><br />图八：匹配IP地址<br /><br />IP地址中的句点字符必须进行转义处理（前面加上“\”），因为IP地址中的句点具有它本来的含义，而不是采用正则表达式语法中的特殊含义。句点在正则表达式中的特殊含义本文前面已经介绍。<br />日志记录的时间部分由一对方括号包围。你可以按照如下思路提取出方括号里面的所有内容：首先搜索起始方括号字符（“[”），提取出所有不超过结束方括号字符（“]”）的内容，向前寻找直至找到结束方括号字符。图九显示了这部分的正则表达式。<br /><br /><br />图九：匹配至少一个字符，直至找到“]”<br /><br />现在，把上述两个正则表达式加上分组符号（圆括号）后合并成单个表达式，这样就可以从日志记录提取出IP地址和时间。注意，为了匹配“- -”（但不提取它），正则表达式中间加入了“\s-\s-\s”。完整的正则表达式如图十所示。<br /><br />图十：匹配IP地址和时间标记<br /><br />现在正则表达式已经编写完毕，接下来可以编写使用正则表达式库的Java代码了。<br />为使用Jakarta-ORO库，首先创建正则表达式字符串和待分析的日志记录字符串：<br /><br />这里使用的正则表达式与图十的正则表达式差不多完全相同，但有一点例外：在Java中，你必须对每一个向前的斜杠（“\”）进行转义处理。图十不是Java的表示形式，所以我们要在每个“\”前面加上一个“\”以免出现编译错误。遗憾的是，转义处理过程很容易出现错误，所以应该小心谨慎。你可以首先输入未经转义处理的正则表达式，然后从左到右依次把每一个“\”替换成“\”。如果要复检，你可以试着把它输出到屏幕上。<br />初始化字符串之后，实例化PatternCompiler对象，用PatternCompiler编译正则表达式创建一个Pattern对象：<br /><br />现在，创建PatternMatcher对象，调用PatternMatcher接口的contain()方法检查匹配情况：<br /><br />下来，利用PatternMatcher接口返回的MatchResult对象，输出匹配的组。由于logEntry字符串包含匹配的内容，你可以看到类如下面的输出：<br /><br /><strong>3.2 HTML处理实例一</strong><br />下面一个任务是分析HTML页面内FONT标记的所有属性。HTML页面内典型的FONT标记如下所示：<br /><br />程序将按照如下形式，输出每一个FONT标记的属性：<br /><br />在这种情况下，我建议你使用两个正则表达式。第一个如图十一所示，它从字体标记提取出“"face="Arial, Serif" size="+2" color="red"”。<br /><br />图十一：匹配FONT标记的所有属性<br /><br />第二个正则表达式如图十二所示，它把各个属性分割成名字-值对。<br /><br />图十二：匹配单个属性，并把它分割成名字-值对<br /><br />分割结果为：<br /><br />现在我们来看看完成这个任务的Java代码。首先创建两个正则表达式字符串，用Perl5Compiler把它们编译成Pattern对象。编译正则表达式的时候，指定Perl5Compiler.CASE_INSENSITIVE_MASK选项，使得匹配操作不区分大小写。<br />接下来，创建一个执行匹配操作的Perl5Matcher对象<br /><br />假设有一个String类型的变量html，它代表了HTML文件中的一行内容。如果html字符串包含FONT标记，匹配器将返回true。此时，你可以用匹配器对象返回的MatchResult对象获得第一个组，它包含了FONT的所有属性：<br /><br />接下来创建一个PatternMatcherInput对象。这个对象允许你从最后一次匹配的位置开始继续进行匹配操作，因此，它很适合于提取FONT标记内属性的名字-值对。创建PatternMatcherInput对象，以参数形式传入待匹配的字符串。然后，用匹配器实例提取出每一个FONT的属性。这通过指定PatternMatcherInput对象（而不是字符串对象）为参数，反复地调用PatternMatcher对象的contains()方法完成。PatternMatcherInput对象之中的每一次迭代将把它内部的指针向前移动，下一次检测将从前一次匹配位置的后面开始。<br />本例的输出结果如下：<br /><br /><strong>3.3 HTML处理实例二</strong><br />下面我们来看看另一个处理HTML的例子。这一次，我们假定Web服务器从widgets.acme.com移到了newserver.acme.com。现在你要修改一些页面中的链接：<br /><br />执行这个搜索的正则表达式如图十三所示：<br /><br />图十三：匹配修改前的链接<br /><br />如果能够匹配这个正则表达式，你可以用下面的内容替换图十三的链接：<br /><br />注意#字符的后面加上了。Perl正则表达式语法用、等表示已经匹配且提取出来的组。图十三的表达式把所有作为一个组匹配和提取出来的内容附加到链接的后面。<br />现在，返回Java。就象前面我们所做的那样，你必须创建测试字符串，创建把正则表达式编译到Pattern对象所必需的对象，以及创建一个PatternMatcher对<br /><br />接下来，用com.oroinc.text.regex包Util类的substitute()静态方法进行替换，输出结果字符串：<br /><br />Util.substitute()方法的语法如下：<br /><br />这个调用的前两个参数是以前创建的PatternMatcher和Pattern对象。第三个参数是一个Substiution对象，它决定了替换操作如何进行。本例使用的是Perl5Substitution对象，它能够进行Perl5风格的替换。第四个参数是想要进行替换操作的字符串，最后一个参数允许指定是否替换模式的所有匹配子串（Util.SUBSTITUTE_ALL），或只替换指定的次数。<br />【结束语】在这篇文章中，我为你介绍了正则表达式的强大功能。只要正确运用，正则表达式能够在字符串提取和文本修改中起到很大的作用。另外，我还介绍了如何在Java程序中通过Jakarta-ORO库利用正则表达式。至于最终采用老式的字符串处理方式（使用StringTokenizer，charAt，和substring），还是采用正则表达式，这就有待你自己决定了。</p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/mlw2000/aggbug/71700.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-09-25 11:53 <a href="http://www.blogjava.net/mlw2000/articles/71700.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎样创建和使用日期</title><link>http://www.blogjava.net/mlw2000/articles/71691.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Mon, 25 Sep 2006 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/71691.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/71691.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/71691.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/71691.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/71691.html</trackback:ping><description><![CDATA[
		<h4 class="TextColor1" id="subjcns!3C3DD8BEC2AFC122!122" style="MARGIN-BOTTOM: 0px">
				<strong>
						<font face="宋体" size="5">计算Java日期</font>
				</strong>
				<br />学习怎样创建和使用日期 <br /><strong>概要</strong><br />    不管你是处理财务交易还是计划着下一步的行动，你都要知道怎样在Java中建立，使用和显示日期。这需要你简单的查阅一下相应类的API参考：一个日期可以创建3个相关类的对象。这篇文章告诉你你想要知道的内容。（3,000字）<br /><strong>作者：</strong>Robert Nielsen <br /><strong>翻译：</strong>Cocia Lin</h4>
		<div id="msgcns!3C3DD8BEC2AFC122!122">
				<div>
						<p>    Java统计从1970年1月1日起的毫秒的数量表示日期。也就是说，例如，1970年1月2日，是在1月1日后的86，400，000毫秒。同样的，1969年12月31日是在1970年1月1日前86，400，000毫秒。Java的Date类使用long类型纪录这些毫秒值.因为long是有符号整数，所以日期可以在1970年1月1日之前，也可以在这之后。Long类型表示的最大正值和最大负值可以轻松的表示290，000，000年的时间，这适合大多数人的时间要求。<br /><strong>Date 类<br />   </strong>Date类可以在java.util包中找到，用一个long类型的值表示一个指定的时刻。它的一个有用的构造函数是Date(),它创建一个表示创建时刻的对象。getTime()方法返回Date对象的long值。在下面的程序中，我使用Date()构造函数创建一个表示程序运行时刻的对象，并且利用getTime()方法找到这个日期代表的毫秒数量：</p>
						<p>import java.util.*;</p>
						<p>
								<br />public class Now {<br />   public static void main(String[] args) {<br />      Date now = new Date();<br />      long nowLong = now.getTime();<br />      System.out.println("Value is " + nowLong);<br />   }<br />}<br /><br />当我运行这个程序后，我得到972,568,255,150.快速确认一下这个数字，起码在一个合理的范围：它不到31年，这个数值相对1970年1月1日到我写这篇文章的时间来说，是合理的。计算机是这个毫秒值表示时间，人们可不愿意说" 我将在996,321,998,34见到你。"幸运的是，Java提供了一个转换Date对象到字符串的途径，表示成传统的形式。我们在下一节讨论DateFormat类，它直观的建立日期字符串。 <br />DateFormat类<br /> DateFormat类的一个目标是建立一个人们能够识别的字符串。然而，因为语言的差别，不是所有的人希望看到严格的相同格式的日期。法国人更喜欢看到"25 decembre 2000,",但是美国人习惯看到"December 25,2000."所以一个DateFormat的实例创建以后，这个对象包含了日期的显示格式的信息。如果使用用户电脑区域设置缺省的格式，你可以象下面那样，创建DateFormat对象，使用getDateInstance()方法：</p>
						<p>DateFormat df = DateFormat.getDateInstance();   <br /><br />DateFormat类在java.text包中可以找到。<br /><strong>转换成字符串<br /></strong>你可以使用format()方法转换Date对象为一个字符串。下面的示例程序说明了这个问题：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class NowString {<br />   public static void main(String[] args) {<br />      Date now = new Date();<br />      DateFormat df = DateFormat.getDateInstance();<br />      String s = df.format(now);<br />      System.out.println("Today is " + s);<br />   }<br />}  <br /><br />在上面的代码中，展示了没有参数，使用缺省格式的getDateInstance()方法。Java还提供了几个选择日期格式，你可以通过使用重载的getDateInstance(int style)获得。出于方便的原因，DateFormat提供了几种预置的常量，你可以使用这些常量参数。下面是几个SHORT, MEDIUM, LONG, 和FULL类型的示例：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class StyleDemo {<br />   public static void main(String[] args) {<br />      Date now = new Date();</p>
						<p>      DateFormat df =  DateFormat.getDateInstance();<br />      DateFormat df1 = DateFormat.getDateInstance(DateFormat.SHORT);<br />      DateFormat df2 = DateFormat.getDateInstance(DateFormat.MEDIUM);<br />      DateFormat df3 = DateFormat.getDateInstance(DateFormat.LONG);<br />      DateFormat df4 = DateFormat.getDateInstance(DateFormat.FULL); <br />      String s =  df.format(now);<br />      String s1 = df1.format(now);<br />      String s2 = df2.format(now);<br />      String s3 = df3.format(now);<br />      String s4 = df4.format(now);</p>
						<p>      System.out.println("(Default) Today is " + s);<br />      System.out.println("(SHORT)   Today is " + s1);<br />      System.out.println("(MEDIUM)  Today is " + s2);<br />      System.out.println("(LONG)    Today is " + s3);<br />      System.out.println("(FULL)    Today is " + s4);<br />   }<br />}<br /><br />程序输出如下：</p>
						<p>(Default) Today is Nov 8, 2000<br />(SHORT)   Today is 11/8/00<br />(MEDIUM)  Today is Nov 8, 2000<br />(LONG)    Today is November 8, 2000<br />(FULL)    Today is Wednesday, November 8, 2000<br /><br />同样的程序，在我的电脑上使用缺省设置运行后，改变区域设置为瑞典，输出如下：</p>
						<p>(Default) Today is 2000-nov-08<br />(SHORT)   Today is 2000-11-08<br />(MEDIUM)  Today is 2000-nov-08<br />(LONG)    Today is den 8 november 2000<br />(FULL)    Today is den 8 november 2000     <br />  <br />从这里，你能看到，瑞典的月份不是大写的（虽然November还是november）.还有，LONG和FULL版本在瑞典语中是一样的，但是美国英语却不同。另外，有趣的是，瑞典语单词的星期三,onsdag，没有包含在FULL日期里，英语却包括。 <br />注意你能够使用getDateInstance()方法改变DateFormat实例的语种；但是，在上面的例子中，是通过改变Windows98的控制面板的区域设置做到的。不同的地方的区域设置不同，结果就不同，这样有好处，也有不足，Java程序员应该了解这些。一个好处是Java程序员可以只写一行代码就可以显示日期，而且世界不同地区的电脑运行同样的程序会有不用的日期格式。 但是这也是一个缺点，当程序员希望显示同一种格式的时--这也有可取之处，举例来说，在程序中混合输出文本和日期，如果文本是英文，我们就不希望日期格式是其他的格式，象德文或是西班牙文。如果程序员依靠日期格式编程，日期格式将根据运行程序所在电脑的区域设置不用而不同。 <br /><font size="4"><strong>解析字符串</strong></font><br /> 通过parse()方法，DateFormat能够以一个字符串创立一个Date对象。这个方法能抛出ParseException异常，所以你必须使用适当的异常处理技术。下面的例子程序通过字符串创建Date对象：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class ParseExample {<br />   public static void main(String[] args) {<br />      String ds = "November 1, 2000";<br />      DateFormat df = DateFormat.getDateInstance();<br />      try {<br />         Date d = df.parse(ds);<br />      }<br />      catch(ParseException e) {<br />         System.out.println("Unable to parse " + ds);<br />      }<br />   }<br />}<br /><br />在创建一个任意的日期时parse()方法很有用。我将通过另一种方法创建一个任意得日期。同时，你将看到怎样进行基本日期计算，例如计算90天后的另一天。你可以使用GregorianCalendar类来完成这个任务。 <br /><strong>GregorianCalendar类</strong><br /> 创建一个代表任意日期的一个途径使用GregorianCalendar类的构造函数，它包含在java.util包中：</p>
						<p>GregorianCalendar(int year, int month, int date) <br /><br />注意月份的表示，一月是0，二月是1，以此类推，是12月是11。因为大多数人习惯于使用单词而不是使用数字来表示月份，这样程序也许更易读，父类Calendar使用常量来表示月份：JANUARY, FEBRUARY,等等。所以，创建Wilbur 和 Orville制造第一架动力飞机的日期（December 17, 1903），你可以使用：</p>
						<p>GregorianCalendar firstFlight = new GregorianCalendar(1903, Calendar.DECEMBER, 17);  <br />出于清楚的考虑，你应该使用前面的形式。但是，你也应该学习怎样阅读下面的短格式。下面的例子同样表示December 17,1903（记住，在短格式中，11表示December）</p>
						<p>GregorianCalendar firstFlight = new GregorianCalendar(1903, 11, 17);   <br /><br />在上一节中，你学习了转换Date对象到字符串。这里，你可以做同样的事情；但是首先，你需要将GregorianCalendar对象转换到Date。要做到这一点，你可以使用getTime()方法，从它得父类Calendar继承而来。GetTime()方法返回GregorianCalendar相应的Date对象。你能够创建GregorianCalendar对象，转换到Date对象，得到和输出相应的字符串这样一个过程。下面是例子： </p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class Flight {</p>
						<p>   public static void main(String[] args) {<br />      GregorianCalendar firstFlight = new GregorianCalendar(1903, Calendar.DECEMBER, 17);    <br />      Date d = firstFlight.getTime();<br />      DateFormat df = DateFormat.getDateInstance();<br />      String s = df.format(d);<br />      System.out.println("First flight was " + s);<br />   }<br />}<br /><br />有时候创建一个代表当前时刻的GregorianCalendar类的实例是很有用的。你可以简单的使用没有参数的GregorianCalendar构造函数，象这样：</p>
						<p>GregorianCalendar thisday = new GregorianCalendar();<br /><br />一个输出今天日期的例子程序，使用GregorianCalendar对象：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>class Today {<br />   public static void main(String[] args) {<br />      GregorianCalendar thisday = new GregorianCalendar(); <br />      Date d = thisday.getTime();<br />      DateFormat df = DateFormat.getDateInstance();<br />      String s = df.format(d);<br />      System.out.println("Today is " + s);<br />   }<br />}<br /><br />注意到，Date()构造函数和GregorianCalendar()构造函数很类似：都创建一个对象，条件简单，代表今天。<br /><strong>日期处理<br /></strong>GregorianCalendar类提供处理日期的方法。一个有用的方法是add().使用add()方法，你能够增加象年，月数，天数到日期对象中。要使用add()方法，你必须提供要增加的字段，要增加的数量。一些有用的字段是DATE, MONTH, YEAR, 和 WEEK_OF_YEAR。下面的程序使用add()方法计算未来80天的一个日期。在Jules的&lt;环球80天&gt;是一个重要的数字，使用这个程序可以计算Phileas Fogg从出发的那一天1872年10月2日后80天的日期：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class World {<br />   public static void main(String[] args) {<br />      GregorianCalendar worldTour = new GregorianCalendar(1872, Calendar.OCTOBER, 2);<br />      worldTour.add(GregorianCalendar.DATE, 80);<br />      Date d = worldTour.getTime();<br />      DateFormat df = DateFormat.getDateInstance();<br />      String s = df.format(d);<br />      System.out.println("80 day trip will end " + s);<br />   }<br />}<br /><br />这个例子是想象的，但在一个日期上增加天数是一个普遍的操作：影碟可以租3天，图书馆可以借书21天，商店经常需要将购买的物品在30天内卖出。下面的程序演示了使用年计算：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class Mortgage {<br />   public static void main(String[] args) {<br />      GregorianCalendar mortgage = new GregorianCalendar(1997, Calendar.MAY, 18);<br />      mortgage.add(Calendar.YEAR, 15);<br />      Date d = mortgage.getTime();<br />      DateFormat df = DateFormat.getDateInstance();<br />      String s = df.format(d);<br />      System.out.println("15 year mortgage amortized on " + s);    }<br />}<br /><br />    add()一个重要的副作用是它改变的原来的日期。有时候，拥有原始日期和修改后的日期很重要。不幸的是，你不能简单的创建一个GregorianCalendar对象，设置它和原来的相等（equal）。原因是两个变量指向同一个Date()对象地址。如果Date对象改变，两个变量就指向改变后的日期对象。代替这种做法，应该创建一个新对象。下面的程序示范了这种做法：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class ThreeDates {<br />   public static void main(String[] args) {<br />      GregorianCalendar gc1 = new GregorianCalendar(2000, Calendar.JANUARY, 1);<br />      GregorianCalendar gc2 = gc1;<br />      GregorianCalendar gc3 = new GregorianCalendar(2000, Calendar.JANUARY, 1);<br />      //Three dates all equal to January 1, 2000</p>
						<p>      gc1.add(Calendar.YEAR, 1);<br />      <a><u><font color="#0000ff">file://gc1</font></u></a> and gc2 are changed</p>
						<p>      DateFormat df = DateFormat.getDateInstance();</p>
						<p>      Date d1 = gc1.getTime();<br />      Date d2 = gc2.getTime();<br />      Date d3 = gc3.getTime();</p>
						<p>      String s1 = df.format(d1);<br />      String s2 = df.format(d2);<br />      String s3 = df.format(d3);</p>
						<p>      System.out.println("gc1 is " + s1);<br />      System.out.println("gc2 is " + s2);<br />      System.out.println("gc3 is " + s3);<br />   }<br />}<br /><br />    程序运行后，gc1和gc2被变成2001年（因为两个对象指向同一个Date，而Date已经被改变了）。对象gc3指向一个单独的Date，它没有被改变。<br />计算复习日期<br />在这节，你将看到一个依据现实世界的例子。这个详细的程序计算过去一个具体的日期。例如，你阅读这篇文章，你想要记住一个印象深刻的知识点。如果你没有照片一样的记忆力，你就要定期的复习这些新资料，这将帮助你记住它。关于复习系统，Kurt Hanks 和 Gerreld L. Pulsipher在他们的&lt; Five Secrets to Personal Productivity个人能力的5个秘密&gt;中有讨论，建议看过第一眼后马上回顾一下，然后是1天后，1个星期后，1个月后，3个月后，1年后。我的这篇文章，你要马上回顾一下，从现在算起，再就是明天，然后是1个星期，1个月，3个月，1年后。我们的程序将计算这些日期。<br />这个程序非常有用的，它将是PIM(Personal Information Manager个人信息管理器)的一个组成部分，并将确定复习时间。在下面的程序中，getDates()方法对一个返回日期数组（复习日期）的电子软件很有用。另外，你可以返回单独的一个日期，使用getFirstDay(),getOneDay(),getOneWeek(),getOnMonth()和getOneYear().当时间范围超出这个PIM的ReviewDates的计算范围时ReviewDates类演示了怎样计算时间段。现在，你可以容易的修改它用来处理你需要的时间段，象图书馆借书，录影带租赁和抵押计算。首先，ReviewDates类显示在下面：</p>
						<p>import java.util.*;<br />import java.text.*;</p>
						<p>public class ReviewDates {<br />   private GregorianCalendar firstDay, oneDay, oneWeek, oneMonth, oneQuarter, oneYear;<br />   final int dateArraySize = 6;</p>
						<p>   ReviewDates(GregorianCalendar gcDate) {<br />      int year = gcDate.get(GregorianCalendar.YEAR);<br />      int month = gcDate.get(GregorianCalendar.MONTH);<br />      int date = gcDate.get(GregorianCalendar.DATE);</p>
						<p>      firstDay = new GregorianCalendar(year, month, date);<br />      oneDay = new GregorianCalendar(year, month, date);<br />      oneWeek = new GregorianCalendar(year, month, date);<br />      oneMonth = new GregorianCalendar(year, month, date);<br />      oneQuarter = new GregorianCalendar(year, month, date);<br />      oneYear = new GregorianCalendar(year, month, date);</p>
						<p>      oneDay.add(GregorianCalendar.DATE, 1);<br />      oneWeek.add(GregorianCalendar.DATE, 7);<br />      oneMonth.add(GregorianCalendar.MONTH, 1);<br />      oneQuarter.add(GregorianCalendar.MONTH, 3);<br />      oneYear.add(GregorianCalendar.YEAR, 1);<br />   }</p>
						<p>   ReviewDates() {<br />      this(new GregorianCalendar());<br />   }</p>
						<p>   public void listDates() {<br />      DateFormat df = DateFormat.getDateInstance(DateFormat.LONG); <br />      Date startDate = firstDay.getTime();<br />      Date date1 = oneDay.getTime();<br />      Date date2 = oneWeek.getTime();<br />      Date date3 = oneMonth.getTime();<br />      Date date4 = oneQuarter.getTime();<br />      Date date5 = oneYear.getTime();</p>
						<p>      String ss =  df.format(startDate);<br />      String ss1 = df.format(date1);<br />      String ss2 = df.format(date2);<br />      String ss3 = df.format(date3);<br />      String ss4 = df.format(date4);<br />      String ss5 = df.format(date5);</p>
						<p>      System.out.println("Start date is " + ss);<br />      System.out.println("Following review dates are:");<br />      System.out.println(ss1);<br />      System.out.println(ss2);<br />      System.out.println(ss3);<br />      System.out.println(ss4);<br />      System.out.println(ss5);<br />      System.out.println();<br />   }</p>
						<p>   public GregorianCalendar[] getDates() {<br />      GregorianCalendar[] memoryDates = new GregorianCalendar[dateArraySize];<br />      memoryDates[0] = firstDay;<br />      memoryDates[1] = oneDay;<br />      memoryDates[2] = oneWeek;<br />      memoryDates[3] = oneMonth;<br />      memoryDates[4] = oneQuarter;<br />      memoryDates[5] = oneYear;<br />      return memoryDates;<br />   }</p>
						<p>   public GregorianCalendar getFirstDay() {<br />      return this.firstDay;<br />   }</p>
						<p>   public GregorianCalendar getOneDay() {<br />      return this.oneDay;<br />   }</p>
						<p>   public GregorianCalendar getOneWeek() {<br />      return this.oneWeek;<br />   }</p>
						<p>   public GregorianCalendar getOneMonth() {<br />      return this.oneMonth;<br />   }</p>
						<p>   public GregorianCalendar getOneQuarter() {<br />      return this.oneQuarter;<br />   }</p>
						<p>   public GregorianCalendar getOneYear() {<br />      return this.oneYear;<br />   }<br />} <br /><br />下面是使用ReviewDates类列出复习日期的例子程序：</p>
						<p>import java.util.*;</p>
						<p>public class ShowDates {<br />   public static void main(String[] args) {<br />      ReviewDates rd = new ReviewDates();<br />      rd.listDates();</p>
						<p>      GregorianCalendar gc = new GregorianCalendar(2001, Calendar.JANUARY, 15);<br />      ReviewDates jan15 = new ReviewDates(gc);<br />      jan15.listDates();<br />   }<br />}<br /><br /><strong>总结</strong><br /> 这篇文章介绍了关于日期处理的3个重要的类：Date,DateFormat,GregorianCalendar.这些类让你创建日期，转换成字符串，和计算日期基本元素。处理Java中的日期问题，这篇文章只是冰山一角。可是，我在这里介绍的类和方法不仅仅是你学习高级技术的跳板，这些类和方法本身就可以处理很多通常的日期相关的任务<br /></p>
				</div>
		</div>
<img src ="http://www.blogjava.net/mlw2000/aggbug/71691.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-09-25 11:30 <a href="http://www.blogjava.net/mlw2000/articles/71691.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中一些关于日期、日期格式、日期的解析和日期的计算[转贴]</title><link>http://www.blogjava.net/mlw2000/articles/71689.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Mon, 25 Sep 2006 03:27:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/71689.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/71689.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/71689.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/71689.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/71689.html</trackback:ping><description><![CDATA[
		<div>
				<h3>Java中一些关于日期、日期格式、日期的解析和日期的计算[转贴]</h3>
				<span>sunmoon | 2005年04月01日, 11:45</span>
				<p>Java中一些关于日期、日期格式、日期的解析和日期的计算 <br /></p>
				<p>
						<font size="2">Java 语言的Calendar(日历),Date(日期), 和DateFormat(日期格式)组成了Java标准的一个基本但是非常重要的部分. 日期是商业逻辑计算一个关键的部分. 所有的开发者都应该能够计算未来的日期, 定制日期的显示格式, 并将文本数据解析成日期对象. 我们写了两篇文章, 这是第一篇, 我们将大概的学习日期, 日期格式, 日期的解析和日期的计算. </font>
				</p>
				<p>
						<font size="2">我们将讨论下面的类: </font>
				</p>
				<p>
						<font size="2">1、具体类(和抽象类相对)java.util.Date <br />2、抽象类java.text.DateFormat 和它的一个具体子类,java.text.SimpleDateFormat <br />3、抽象类java.util.Calendar 和它的一个具体子类,java.util.GregorianCalendar </font>
				</p>
				<p>
						<font size="2">具体类可以被实例化, 但是抽象类却不能. 你首先必须实现抽象类的一个具体子类. </font>
				</p>
				<p>
						<font size="2">Date 类从Java 开发包(JDK) 1.0 就开始进化, 当时它只包含了几个取得或者设置一个日期数据的各个部分的方法, 比如说月, 日, 和年. 这些方法现在遭到了批评并且已经被转移到了Calendar类里去了, 我们将在本文中进一步讨论它. 这种改进旨在更好的处理日期数据的国际化格式. 就象在JDK 1.1中一样, Date 类实际上只是一个包裹类, 它包含的是一个长整型数据, 表示的是从GMT(格林尼治标准时间)1970年, 1 月 1日00:00:00这一刻之前或者是之后经历的毫秒数. </font>
				</p>
				<p>
						<font size="2">一、创建一个日期对象 </font>
				</p>
				<p>
						<font size="2">让我们看一个使用系统的当前日期和时间创建一个日期对象并返回一个长整数的简单例子. 这个时间通常被称为Java 虚拟机(JVM)主机环境的系统时间. <br />//------------------------------------------------------<br />import java.util.Date; </font>
				</p>
				<p>
						<font size="2">public class DateExample1 <br />{ <br />public static void main(String[] args)<br />{ <br />// Get the system date/time <br />Date date = new Date(); </font>
				</p>
				<p>
						<font size="2">System.out.println(date.getTime()); <br />} <br />} <br />//------------------------------------------------------</font>
				</p>
				<p>
						<font size="2">在星期六, 2001年9月29日, 下午大约是6:50的样子, 上面的例子在系统输出设备上显示的结果是 1001803809710. 在这个例子中,值得注意的是我们使用了Date 构造函数创建一个日期对象, 这个构造函数没有接受任何参数. 而这个构造函数在内部使用了System.currentTimeMillis() 方法来从系统获取日期. </font>
				</p>
				<p>
						<font size="2">那么, 现在我们已经知道了如何获取从1970年1月1日开始经历的毫秒数了. 我们如何才能以一种用户明白的格式来显示这个日期呢? 在这里类java.text.SimpleDateFormat 和它的抽象基类 java.text.DateFormat 就派得上用场了. </font>
				</p>
				<p>
						<font size="2">二、日期数据的定制格式 </font>
				</p>
				<p>
						<font size="2">假如我们希望定制日期数据的格式, 比方星期六-9月-29日-2001年. 下面的例子展示了如何完成这个工作: </font>
				</p>
				<p>
						<font size="2">//------------------------------------------------------<br />import java.text.SimpleDateFormat; <br />import java.util.Date; </font>
				</p>
				<p>
						<font size="2">public class DateExample2 <br />{ </font>
				</p>
				<p>
						<font size="2">public static void main(String[] args) <br />{ </font>
				</p>
				<p>
						<font size="2">SimpleDateFormat bartDateFormat = <br />new SimpleDateFormat("EEEE-MMMM-dd-yyyy"); </font>
				</p>
				<p>
						<font size="2">Date date = new Date(); </font>
				</p>
				<p>
						<font size="2">System.out.println(bartDateFormat.format(date)); <br />} <br />} <br />//------------------------------------------------------</font>
				</p>
				<p>
						<font size="2">只要通过向SimpleDateFormat 的构造函数传递格式字符串"EEE-MMMM-dd-yyyy", 我们就能够指明自己想要的格式. 你应该可以看见, 格式字符串中的ASCII 字符告诉格式化函数下面显示日期数据的哪一个部分. EEEE是星期, MMMM是月, dd是日, yyyy是年. 字符的个数决定了日期是如何格式化的.传递"EE-MM-dd-yy"会显示 Sat-09-29-01. 请察看Sun 公司的Web 站点获取日期格式化选项的完整的指示.</font>
				</p>
				<p>
						<font size="2">三、将文本数据解析成日期对象 </font>
				</p>
				<p>
						<font size="2">假设我们有一个文本字符串包含了一个格式化了的日期对象, 而我们希望解析这个字符串并从文本日期数据创建一个日期对象. 我们将再次以格式化字符串"MM-dd-yyyy" 调用SimpleDateFormat类, 但是这一次, 我们使用格式化解析而不是生成一个文本日期数据. 我们的例子, 显示在下面, 将解析文本字符串"9-29-2001"并创建一个值为001736000000 的日期对象. </font>
				</p>
				<p>
						<font size="2">//------------------------------------------------------<br />import java.text.SimpleDateFormat; <br />import java.util.Date; </font>
				</p>
				<p>
						<font size="2">public class DateExample3 <br />{ </font>
				</p>
				<p>
						<font size="2">public static void main(String[] args) <br />{ <br />// Create a date formatter that can parse dates of <br />// the form MM-dd-yyyy. <br />SimpleDateFormat bartDateFormat = <br />new SimpleDateFormat("MM-dd-yyyy"); </font>
				</p>
				<p>
						<font size="2">// Create a string containing a text date to be parsed. <br />String dateStringToParse = "9-29-2001"; </font>
				</p>
				<p>
						<font size="2">try { <br />// Parse the text version of the date. <br />// We have to perform the parse method in a <br />// try-catch construct in case dateStringToParse <br />// does not contain a date in the format we are expecting. <br />Date date = bartDateFormat.parse(dateStringToParse); </font>
				</p>
				<p>
						<font size="2">// Now send the parsed date as a long value <br />// to the system output. <br />System.out.println(date.getTime()); <br />} <br />catch (Exception ex) { <br />System.out.println(ex.getMessage()); <br />} <br />} <br />} <br />//------------------------------------------------------</font>
				</p>
				<p>
						<font size="2">四、使用标准的日期格式化过程 </font>
				</p>
				<p>
						<font size="2">既然我们已经可以生成和解析定制的日期格式了, 让我们来看一看如何使用内建的格式化过程. 方法 DateFormat.getDateTimeInstance() 让我们得以用几种不同的方法获得标准的日期格式化过程. 在下面的例子中, 我们获取了四个内建的日期格式化过程. 它们包括一个短的, 中等的, 长的, 和完整的日期格式. </font>
				</p>
				<p>
						<font size="2">//------------------------------------------------------<br />import java.text.DateFormat; <br />import java.util.Date; </font>
				</p>
				<p>
						<font size="2">public class DateExample4 <br />{ </font>
				</p>
				<p>
						<font size="2">public static void main(String[] args) <br />{ <br />Date date = new Date(); </font>
				</p>
				<p>
						<font size="2">DateFormat shortDateFormat = <br />DateFormat.getDateTimeInstance( <br />DateFormat.SHORT, <br />DateFormat.SHORT); </font>
				</p>
				<p>
						<font size="2">DateFormat mediumDateFormat = <br />DateFormat.getDateTimeInstance( <br />DateFormat.MEDIUM, <br />DateFormat.MEDIUM); </font>
				</p>
				<p>
						<font size="2">DateFormat longDateFormat = <br />DateFormat.getDateTimeInstance( <br />DateFormat.LONG, <br />DateFormat.LONG); </font>
				</p>
				<p>
						<font size="2">DateFormat fullDateFormat = <br />DateFormat.getDateTimeInstance( <br />DateFormat.FULL, <br />DateFormat.FULL); </font>
				</p>
				<p>
						<font size="2">System.out.println(shortDateFormat.format(date)); <br />System.out.println(mediumDateFormat.format(date)); <br />System.out.println(longDateFormat.format(date)); <br />System.out.println(fullDateFormat.format(date)); <br />} <br />} <br />//------------------------------------------------------</font>
				</p>
				<p>
						<font size="2">注意我们在对 getDateTimeInstance的每次调用中都传递了两个值. 第一个参数是日期风格, 而第二个参数是时间风格. 它们都是基本数据类型int(整型). 考虑到可读性, 我们使用了DateFormat 类提供的常量: SHORT, MEDIUM, LONG, 和 FULL. 要知道获取时间和日期格式化过程的更多的方法和选项, 请看Sun 公司Web 站点上的解释. </font>
				</p>
				<p>
						<font size="2">运行我们的例子程序的时候, 它将向标准输出设备输出下面的内容: <br />9/29/01 8:44 PM <br />Sep 29, 2001 8:44:45 PM <br />September 29, 2001 8:44:45 PM EDT <br />Saturday, September 29, 2001 8:44:45 PM EDT</font>
				</p>
				<p>
						<font size="2">五、Calendar 类 </font>
				</p>
				<p>
						<font size="2">我们现在已经能够格式化并创建一个日期对象了, 但是我们如何才能设置和获取日期数据的特定部分呢, 比如说小时, 日, 或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类. 就如我们前面提到的那样, Calendar 类中的方法替代了Date 类中被人唾骂的方法. </font>
				</p>
				<p>
						<font size="2">假设你想要设置, 获取, 和操纵一个日期对象的各个部分, 比方一个月的一天或者是一个星期的一天. 为了演示这个过程, 我们将使用具体的子类 java.util.GregorianCalendar. 考虑下面的例子, 它计算得到下面的第十个星期五是13号. </font>
				</p>
				<p>
						<font size="2">//------------------------------------------------------<br />import java.util.GregorianCalendar; <br />import java.util.Date; <br />import java.text.DateFormat; </font>
				</p>
				<p>
						<font size="2">public class DateExample5 <br />{ </font>
				</p>
				<p>
						<font size="2">public static void main(String[] args) <br />{ <br />DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL); </font>
				</p>
				<p>
						<font size="2">// Create our Gregorian Calendar. <br />GregorianCalendar cal = new GregorianCalendar(); <br /><br />// Set the date and time of our calendar <br />// to the system&amp;s date and time <br />cal.setTime(new Date()); </font>
				</p>
				<p>
						<font size="2">System.out.println("System Date: " + <br />dateFormat.format(cal.getTime())); </font>
				</p>
				<p>
						<font size="2">// Set the day of week to FRIDAY <br />cal.set(GregorianCalendar.DAY_OF_WEEK, <br />GregorianCalendar.FRIDAY); <br />System.out.println("After Setting Day of Week to Friday: " + <br />dateFormat.format(cal.getTime())); </font>
				</p>
				<p>
						<font size="2">int friday13Counter = 0; <br /><br />while (friday13Counter &lt;= 10) <br />{ </font>
				</p>
				<p>
						<font size="2">// Go to the next Friday by adding 7 days. <br />cal.add(GregorianCalendar.DAY_OF_MONTH, 7); </font>
				</p>
				<p>
						<font size="2">// If the day of month is 13 we have <br />// another Friday the 13th. <br />if (cal.get(GregorianCalendar.DAY_OF_MONTH) == 13) <br />{ <br />friday13Counter++; <br />System.out.println(dateFormat.format(cal.getTime())); <br />} <br />} <br />} <br />} <br />//------------------------------------------------------</font>
				</p>
				<p>
						<font size="2">在这个例子中我们作了有趣的函数调用: <br />cal.set(GregorianCalendar.DAY_OF_WEEK, <br />GregorianCalendar.FRIDAY); </font>
				</p>
				<p>
						<font size="2">和: <br />cal.add(GregorianCalendar.DAY_OF_MONTH, 7); </font>
				</p>
				<p>
						<font size="2">set 方法能够让我们通过简单的设置星期中的哪一天这个域来将我们的时间调整为星期五. 注意到这里我们使用了常量 DAY_OF_WEEK 和 FRIDAY来增强代码的可读性. add 方法让我们能够在日期上加上数值. 润年的所有复杂的计算都由这个方法自动处理. </font>
				</p>
				<p>
						<font size="2">我们这个例子的输出结果是: <br />System Date: Saturday, September 29, 2001 <br />当我们将它设置成星期五以后就成了: Friday, September 28, 2001 <br />Friday, September 13, 2002 <br />Friday, December 13, 2002 <br />Friday, June 13, 2003 <br />Friday, February 13, 2004 <br />Friday, August 13, 2004 <br />Friday, May 13, 2005 <br />Friday, January 13, 2006 <br />Friday, October 13, 2006 <br />Friday, April 13, 2007 <br />Friday, July 13, 2007 <br />Friday, June 13, 2008 </font>
				</p>
				<p>
						<font size="2">六、时间掌握在你的手里 </font>
				</p>
				<p>
						<font size="2">有了这些Date 和Calendar 类的例子, 你应该能够使用 java.util.Date, java.text.SimpleDateFormat, 和 java.util.GregorianCalendar 创建许多方法了. </font>
				</p>
		</div>
<img src ="http://www.blogjava.net/mlw2000/aggbug/71689.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-09-25 11:27 <a href="http://www.blogjava.net/mlw2000/articles/71689.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符串转日期或日期转字符串(转)</title><link>http://www.blogjava.net/mlw2000/articles/71679.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Mon, 25 Sep 2006 03:11:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/71679.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/71679.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/71679.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/71679.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/71679.html</trackback:ping><description><![CDATA[
		<p>
				<font size="3">有一些网友问我字符串转日期或日期转字符串要如何做，本来已经在留言板回答了，但觉得似乎有满多的网友有这种困扰，因此我把它整理整理贴出来。 
<p></p></font>
				<a ?="">鲲鹏网</a>
		</p>
		<p>
		</p>
		<p>
				<font size="3">
						<p>
						</p>
				</font>
		</p>
		<p>
		</p>
		<font size="3">
				<a ?="">鲲鹏网</a>
		</font>
		<br />
		<p>
				<font size="3">在这篇文章中，我用的API是SimpleDateFormat，它是属于java.text.SimpleDateFormat，所以请记得import进来！ 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font size="3">
						<p>
						</p>
				</font>
		</p>
		<p>
		</p>
		<font size="3">
				<br />
		</font>
		<p>
				<font size="3">用法：</font>
		</p>
		<p>
				<br />
		</p>
		<p>
		</p>
		<p>
		</p>
		<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)">
				<!--<font size="2">http://www.kpwang.com</font><br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->SimpleDateFormat sdf  =   new  SimpleDateFormat( " yyyy-MM-dd HH:mm:ss " );  </div>
		<br />
		<p>
				<font size="3">这一行最重要，它确立了转换的格式，yyyy是完整的公元年，MM是月份，dd是日期，至于HH:mm:ss就不需要我再解释了吧！</font>
		</p>
		<p>
				<font size="3">
						<br />
						<p>
						</p>
				</font>
		</p>
		<p>
		</p>
		<font size="3">ps:为什么有的格式大写，有的格式小写，那是怕避免混淆，例如MM是月份，mm是分；HH是24小时制，而hh是12小时制 
<p></p><br /><br /></font>
		<p>
				<font size="3">1.字符串转日期： 
<p></p></font>
		</p>
		<p>
		</p>
		<font size="3">　<br />2002-10-8 15:30:22要把它转成日期，可以用</font>
		<br />
		<p>
		</p>
		<br />
		<p>
		</p>
		<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)">
				<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->Date date = sdf.parse( " 2002-10-8 15:30:22 " ); </div>
		<br />
		<p>
		</p>
		<font size="3">2.日期转字符串 
<p></p><br /></font>
		<p>
				<font size="3">
						<br />
				</font>
		</p>
		<p>
				<font size="3">假如把今天的日期转成字符串可用</font>
		</p>
		<p>
				<br />
		</p>
		<p>
		</p>
		<p>
		</p>
		<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)">
				<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->String datestr = sdf.format( new  Date());  </div>
		<br />
		<p>
				<font size="3">　这个字符串的内容便类似2002-10-08 14:55:38 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font size="3">
						<p>
						</p>
				</font>
		</p>
		<p>
		</p>
		<font size="3">
				<br />
		</font>
		<p>
				<font size="3">透过这个API我们便可以随心所欲的将日期转成我们想要的字符串格式，例如希望将日期输出成<?XML:NAMESPACE PREFIX = ST1 /?><st1:chsdate year="2002" month="10" day="08" islunardate="False" isrocdate="False" w:st="on">2002年10月08日</st1:chsdate>， 
<p></p></font>
		</p>
		<p>
		</p>
		<font size="3">
				<br />
		</font>
		<p>
				<font size="3">我们可以这么写：</font>
		</p>
		<p>
				<br />
		</p>
		<p>
		</p>
		<p>
		</p>
		<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)">
				<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->SimpleDateFormat sdf  =   new  SimpleDateFormat( " yyyy年MM月dd日 " );<br />    String datestr = sdf.format( new  Date());  </div>
		<br />
		<br />
		<p>
				<font size="3">datestr便会依照我们设定的格式输出</font>
		</p>
<img src ="http://www.blogjava.net/mlw2000/aggbug/71679.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-09-25 11:11 <a href="http://www.blogjava.net/mlw2000/articles/71679.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ThreadLocal的设计与使用</title><link>http://www.blogjava.net/mlw2000/articles/65047.html</link><dc:creator>mlw2000</dc:creator><author>mlw2000</author><pubDate>Tue, 22 Aug 2006 04:58:00 GMT</pubDate><guid>http://www.blogjava.net/mlw2000/articles/65047.html</guid><wfw:comment>http://www.blogjava.net/mlw2000/comments/65047.html</wfw:comment><comments>http://www.blogjava.net/mlw2000/articles/65047.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mlw2000/comments/commentRss/65047.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mlw2000/services/trackbacks/65047.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="98%" align="center" border="0">
				<tbody>
						<tr>
								<td height="50">
										<div align="center">
												<b>
														<font color="#990000" size="3">ThreadLocal的设计与使用 </font>
												</b>
										</div>
								</td>
						</tr>
						<tr>
								<td valign="top" height="30">
										<div align="center">来源：<font color="#669999"> 天极网 </font>  作者：<font color="#669999"> srx81 </font></div>
								</td>
						</tr>
						<tr>
								<td>
										<table cellspacing="0" cellpadding="0" width="95%" align="center" border="0">
												<tbody>
														<tr>
																<td valign="top">
																		<span class="font10" id="zoom">
																				<p>　　早在Java 1.2推出之时，Java平台中就引入了一个新的支持：java.lang.ThreadLocal，给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序，虽然ThreadLocal非常有用，但是似乎现在了解它、使用它的朋友还不多。 </p>
																				<p>　　ThreadLocal是什么</p>
																				<p>　　ThreadLocal是什么呢？其实ThreadLocal并非是一个线程的本地实现版本，它并不是一个Thread，而是thread local variable（线程局部变量）。也许把它命名为ThreadLocalVar更加合适。线程局部变量（ThreadLocal）其实的功用非常简单，就是为每一个使用该变量的线程都提供一个变量值的副本，是每一个线程都可以独立地改变自己的副本，而不会和其它线程的副本冲突。从线程的角度看，就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明，在其它的一些语言编译器实现（如IBM XL FORTRAN）中，它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持，而是提供了一个ThreadLocal的类来提供支持，所以，在Java中编写线程局部变量的代码相对比较笨拙，这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。</p>
																				<p>　　ThreadLocal的设计</p>
																				<p>　　首先看看ThreadLocal的接口：</p>
																				<p>　　Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值void set(Object value); // 设置当前线程的线程局部变量副本的值</p>
																				<p>　　ThreadLocal有3个方法，其中值得注意的是initialValue()，该方法是一个protected的方法，显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值，这个方法是一个延迟调用方法，在一个线程第1次调用get()或者set(Object)时才执行，并且仅执行1次。ThreadLocal中的确实实现直接返回一个null：</p>
																				<p>protected Object initialValue() { return null; } </p>
																				<p>　　ThreadLocal是如何做到为每一个线程维护变量的副本的呢？其实实现的思路很简单，在ThreadLocal类中有一个Map，用于存储每一个线程的变量的副本。比如下面的示例实现：</p>
																				<p>public class ThreadLocal<br />{<br />　private Map values = Collections.synchronizedMap(new HashMap());<br />　public Object get()<br />　{<br />　　Thread curThread = Thread.currentThread(); <br />　　Object o = values.get(curThread); <br />　　if (o == null &amp;&amp; !values.containsKey(curThread))<br />　　{<br />　　　o = initialValue();<br />　　　values.put(curThread, o); <br />　　}<br />　　return o; <br />　}</p>
																				<p>　public void set(Object newValue)<br />　{<br />　　values.put(Thread.currentThread(), newValue);<br />　}</p>
																				<p>　public Object initialValue()<br />　{<br />　　return null; <br />　}<br />} </p>
																				<p>　　当然，这并不是一个工业强度的实现，但JDK中的ThreadLocal的实现总体思路也类似于此。</p>
																				<p>　　ThreadLocal的使用</p>
																				<p>　　如果希望线程局部变量初始化其它值，那么需要自己实现ThreadLocal的子类并重写该方法，通常使用一个内部匿名类对ThreadLocal进行子类化，比如下面的例子，SerialNum类为每一个类分配一个序号：</p>
																				<p>public class SerialNum <br />{<br />　// The next serial number to be assigned</p>
																				<p>　private static int nextSerialNum = 0; <br />　private static ThreadLocal serialNum = new ThreadLocal() <br />　{<br />　　protected synchronized Object initialValue() <br />　　{<br />　　　return new Integer(nextSerialNum++);<br />　　}<br />　};</p>
																				<p>　public static int get() <br />　{<br />　　return ((Integer) (serialNum.get())).intValue(); <br />　}<br />} </p>
																				<p>　　SerialNum类的使用将非常地简单，因为get()方法是static的，所以在需要获取当前线程的序号时，简单地调用：</p>
																				<p>int serial = SerialNum.get(); </p>
																				<p>　　即可。</p>
																				<p>　　在线程是活动的并且ThreadLocal对象是可访问的时，该线程就持有一个到该线程局部变量副本的隐含引用，当该线程运行结束后，该线程拥有的所以线程局部变量的副本都将失效，并等待垃圾收集器收集。</p>
																				<p>　　ThreadLocal与其它同步机制的比较</p>
																				<p>　　ThreadLocal和其它同步机制相比有什么优势呢？ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突，在普通的同步机制中，是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的，使用这种同步机制需要很细致地分析在什么时候对变量进行读写，什么时候需要锁定某个对象，什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问，ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本，从而隔离了多个线程的数据，每一个线程都拥有自己的变量副本，从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象，在编写多线程代码时，可以把不安全的整个变量封装进ThreadLocal，或者把该对象的特定于线程的状态封装进ThreadLocal。</p>
																				<p>　　由于ThreadLocal中可以持有任何类型的对象，所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本（1.5）将模版的引入，新的支持模版参数的ThreadLocal&lt;T&gt;类将从中受益。也可以减少强制类型转换，并将一些错误检查提前到了编译期，将一定程度地简化ThreadLocal的使用。</p>
																				<p>　　总结</p>
																				<p>　　当然ThreadLocal并不能替代同步机制，两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问，是为了多个线程之间进行通信的有效方式；而ThreadLocal是隔离多个线程的数据共享，从根本上就不在多个线程之间共享资源（变量），这样当然不需要对多个线程进行同步了。所以，如果你需要进行多个线程之间进行通信，则使用同步机制；如果需要隔离多个线程之间的共享冲突，可以使用ThreadLocal，这将极大地简化你的程序，使程序更加易读、简洁。<br /></p>
																		</span>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/mlw2000/aggbug/65047.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mlw2000/" target="_blank">mlw2000</a> 2006-08-22 12:58 <a href="http://www.blogjava.net/mlw2000/articles/65047.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>