<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>北风技术专栏 &#187; 张维亮</title>
	<atom:link href="http://column.ibeifeng.com/wp-feed.php?author_name=zhangweiliang&#038;feed=feed" rel="self" type="application/rss+xml" />
	<link>http://column.ibeifeng.com</link>
	<description>用技术点亮生活中每个亮点</description>
	<pubDate>Sun, 25 Jul 2010 12:28:34 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.2</generator>
	<language>en</language>
			<item>
		<title>Spring集成XFire开发WebService</title>
		<link>http://column.ibeifeng.com/zhangweiliang/20090225270.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/20090225270.shtml#comments</comments>
		<pubDate>Wed, 25 Feb 2009 14:08:45 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[Web开发]]></category>

		<category><![CDATA[spring]]></category>

		<category><![CDATA[WebService]]></category>

		<category><![CDATA[XFire]]></category>

		<guid isPermaLink="false">http://column.ibeifeng.com/?p=270</guid>
		<description><![CDATA[Spring是目前最流行的JavaEE Framework，但是使用Spring的Spring-WS开发WebService却十分繁琐。XFire是一个简化WebService开发的开源项目，通过Spring和XFire的结合可以大大简化基于Spring Framework的应用中的WebService开发。
Spring和XFire可以通过多种方式结合，下文介绍的是笔者常用的一种简单而实用的方法。所用的Spring版本为2.0,XFire版本为1.2.6。

1、配置XFire Servlet
在web.xml中加入如下配置：

＜servlet＞
＜servlet-name＞XFireServlet＜/servlet-name＞
＜servlet-class＞
org.codehaus.xfire.spring.XFireSpringServlet
＜/servlet-class＞
＜/servlet＞
＜servlet-mapping＞
＜servlet-name＞XFireServlet＜/servlet-name＞
＜url-pattern＞/servlet/XFireServlet/*＜/url-pattern＞
＜/servlet-mapping＞
＜servlet-mapping＞
＜servlet-name＞XFireServlet＜/servlet-name＞
＜url-pattern＞/services/*＜/url-pattern＞
＜/servlet-mapping＞

2 配置Spring的监听器，同基于spring的Web项目一样Spring的监听器是必不可少的。

＜context-param＞
＜param-name＞contextConfigLocation＜/param-name＞
＜param-value＞
classpath:org/codehaus/xfire/spring/xfire.xml,
/WEB-INF/applicationContext.xml
＜/param-value＞
＜/context-param＞
＜listener＞
＜listener-class＞
org.springframework.web.context.ContextLoaderListener
＜/listener-class＞
＜/listener＞

以下是完整的web.xml配置文件

＜?xml version="1.0" encoding="UTF-8"?＞
＜web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
[url]http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd[/url]&#8220;＞
＜context-param＞
＜param-name＞contextConfigLocation＜/param-name＞
＜param-value＞
classpath:org/codehaus/xfire/spring/xfire.xml,
/WEB-INF/applicationContext.xml
＜/param-value＞
＜/context-param＞
＜listener＞
＜listener-class＞
org.springframework.web.context.ContextLoaderListener
＜/listener-class＞
＜/listener＞
＜servlet＞
＜servlet-name＞XFireServlet＜/servlet-name＞
＜servlet-class＞
org.codehaus.xfire.spring.XFireSpringServlet
＜/servlet-class＞
＜/servlet＞
＜servlet-mapping＞
＜servlet-name＞XFireServlet＜/servlet-name＞
＜url-pattern＞/servlet/XFireServlet/*＜/url-pattern＞
＜/servlet-mapping＞
＜servlet-mapping＞
＜servlet-name＞XFireServlet＜/servlet-name＞
＜url-pattern＞/services/*＜/url-pattern＞
＜/servlet-mapping＞
＜/web-app＞

3 定义接口及实现服务
定义接口，这个接口中定义要通过WebService暴露的方法

package org.ccsoft;
publicinterface HelloWS {
public String sayHello(String sb);
}

实现服务

package org.ccsoft;
publicclass HelloWSImp implements HelloWS {
public String sayHello(String sb) {
// TODO Auto-generated method stub
return"Hello "+sb;
}
}

4 配置服务
将上文中实现的服务，加入到spring的配置文件中。

＜?xml version="1.0" encoding="UTF-8"?＞
＜beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]&#8220;＞
＜bean id=&#8221;helloWS&#8221; class=&#8221;org.ccsoft.HelloWSImp&#8221;/＞
＜bean name=&#8221;helloService&#8221; class=&#8221;org.codehaus.xfire.spring.ServiceBean&#8221;＞
＜property name=&#8221;serviceBean&#8221; ref=&#8221;helloWS&#8221;/＞
＜property name=&#8221;serviceClass&#8221; value=&#8221;org.ccsoft.HelloWS&#8221;/＞
＜property name=&#8221;inHandlers&#8221;＞
＜list＞
＜ref bean=&#8221;addressingHandler&#8221;/＞
＜/list＞
＜/property＞
＜/bean＞
＜bean id=&#8221;addressingHandler&#8221; class=&#8221;org.codehaus.xfire.addressing.AddressingInHandler&#8221;/＞
＜/beans＞

]]></description>
			<content:encoded><![CDATA[<p>Spring是目前最流行的JavaEE Framework，但是使用Spring的Spring-WS开发WebService却十分繁琐。XFire是一个简化WebService开发的开源项目，通过Spring和XFire的结合可以大大简化基于Spring Framework的应用中的WebService开发。</p>
<p>Spring和XFire可以通过多种方式结合，下文介绍的是笔者常用的一种简单而实用的方法。所用的Spring版本为2.0,XFire版本为1.2.6。</p>
<p><span id="more-270"></span></p>
<p><strong>1、配置XFire Servlet</strong><br />
在web.xml中加入如下配置：</p>
<p><code><br />
＜servlet＞<br />
＜servlet-name＞XFireServlet＜/servlet-name＞<br />
＜servlet-class＞<br />
org.codehaus.xfire.spring.XFireSpringServlet<br />
＜/servlet-class＞<br />
＜/servlet＞<br />
＜servlet-mapping＞<br />
＜servlet-name＞XFireServlet＜/servlet-name＞<br />
＜url-pattern＞/servlet/XFireServlet/*＜/url-pattern＞<br />
＜/servlet-mapping＞<br />
＜servlet-mapping＞<br />
＜servlet-name＞XFireServlet＜/servlet-name＞<br />
＜url-pattern＞/services/*＜/url-pattern＞<br />
＜/servlet-mapping＞<br />
</code></p>
<p><strong>2 配置Spring的监听器，同基于spring的Web项目一样Spring的监听器是必不可少的。</strong></p>
<p><code><br />
＜context-param＞<br />
＜param-name＞contextConfigLocation＜/param-name＞<br />
＜param-value＞<br />
classpath:org/codehaus/xfire/spring/xfire.xml,<br />
/WEB-INF/applicationContext.xml<br />
＜/param-value＞<br />
＜/context-param＞<br />
＜listener＞<br />
＜listener-class＞<br />
org.springframework.web.context.ContextLoaderListener<br />
＜/listener-class＞<br />
＜/listener＞<br />
</code></p>
<p><strong>以下是完整的web.xml配置文件</strong></p>
<p><code><br />
＜?xml version="1.0" encoding="UTF-8"?＞<br />
＜web-app version="2.4"<br />
xmlns="http://java.sun.com/xml/ns/j2ee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee<br />
[url]http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd[/url]&#8220;＞<br />
＜context-param＞<br />
＜param-name＞contextConfigLocation＜/param-name＞<br />
＜param-value＞<br />
classpath:org/codehaus/xfire/spring/xfire.xml,<br />
/WEB-INF/applicationContext.xml<br />
＜/param-value＞<br />
＜/context-param＞<br />
＜listener＞<br />
＜listener-class＞<br />
org.springframework.web.context.ContextLoaderListener<br />
＜/listener-class＞<br />
＜/listener＞<br />
＜servlet＞<br />
＜servlet-name＞XFireServlet＜/servlet-name＞<br />
＜servlet-class＞<br />
org.codehaus.xfire.spring.XFireSpringServlet<br />
＜/servlet-class＞<br />
＜/servlet＞<br />
＜servlet-mapping＞<br />
＜servlet-name＞XFireServlet＜/servlet-name＞<br />
＜url-pattern＞/servlet/XFireServlet/*＜/url-pattern＞<br />
＜/servlet-mapping＞<br />
＜servlet-mapping＞<br />
＜servlet-name＞XFireServlet＜/servlet-name＞<br />
＜url-pattern＞/services/*＜/url-pattern＞<br />
＜/servlet-mapping＞<br />
＜/web-app＞<br />
</code></p>
<p><strong>3 定义接口及实现服务</strong></p>
<p>定义接口，这个接口中定义要通过WebService暴露的方法</p>
<p><code><br />
package org.ccsoft;</p>
<p>publicinterface HelloWS {<br />
public String sayHello(String sb);<br />
}<br />
</code></p>
<p>实现服务</p>
<p><code><br />
package org.ccsoft;</p>
<p>publicclass HelloWSImp implements HelloWS {<br />
public String sayHello(String sb) {<br />
// TODO Auto-generated method stub<br />
return"Hello "+sb;<br />
}<br />
}<br />
</code></p>
<p><strong>4 配置服务</strong></p>
<p>将上文中实现的服务，加入到spring的配置文件中。</p>
<p><code><br />
＜?xml version="1.0" encoding="UTF-8"?＞<br />
＜beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="<br />
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]&#8220;＞</p>
<p>＜bean id=&#8221;helloWS&#8221; class=&#8221;org.ccsoft.HelloWSImp&#8221;/＞<br />
＜bean name=&#8221;helloService&#8221; class=&#8221;org.codehaus.xfire.spring.ServiceBean&#8221;＞<br />
＜property name=&#8221;serviceBean&#8221; ref=&#8221;helloWS&#8221;/＞<br />
＜property name=&#8221;serviceClass&#8221; value=&#8221;org.ccsoft.HelloWS&#8221;/＞<br />
＜property name=&#8221;inHandlers&#8221;＞<br />
＜list＞<br />
＜ref bean=&#8221;addressingHandler&#8221;/＞<br />
＜/list＞<br />
＜/property＞<br />
＜/bean＞</p>
<p>＜bean id=&#8221;addressingHandler&#8221; class=&#8221;org.codehaus.xfire.addressing.AddressingInHandler&#8221;/＞<br />
＜/beans＞<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/20090225270.shtml/feed</wfw:commentRss>
		</item>
		<item>
		<title>Java编程思想：面向对象的逻辑思维方法</title>
		<link>http://column.ibeifeng.com/zhangweiliang/20081110207.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/20081110207.shtml#comments</comments>
		<pubDate>Mon, 10 Nov 2008 00:27:25 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[综合]]></category>

		<category><![CDATA[Java]]></category>

		<category><![CDATA[对象]]></category>

		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://column.ibeifeng.com/?p=207</guid>
		<description><![CDATA[什么是面向对象的设计思想?也许有不少初学者对这个概念还有许多不明白的地方，特别是这个处于新旧思想交替的时代，许多人刚刚学完现在看来是快要淘汰的只是面向过程的语言。他们的脑子还没有脱离面向过程思想的束缚，抬头却发现，“面向对象”早已经流行开来，这个陌生的词漫天飞舞。随便拿起一本流行计算机技术书籍，那里会没有“面向对象”的字眼!于是心中便惶惑了：什么叫“面向对象”?不感兴趣者，一带而过;有志于在这方面发展的，匆忙找到一本有关书籍来啃究竟什么是“面向对象”。然而，要突破思想的束缚，说难也不难，说到要深刻认识却也不容易。笔者在做了一些轻量级的工作后，颇以为有点心得，不怕贻笑大方，写出已供广大同行批评指正。

“对象(Object)”一词，早在十九世纪就有现象学大师胡塞尔提出并定义。对象是世界中的物体在人脑中的映象，是人的意识之所以为意识的反映，是做为一种概念而存在的先念的东西，它还包括了人的意愿。举例说明吧。当我们认识到一种新的物体，它叫树，于是在我们的意识当中就形成了树的概念。这个概念会一直存在于我们的思维当中，并不会因为这棵树被砍掉而消失。这个概念就是现实世界当中的物体在我们意识当中的映象。我们对它还可以有我们自己的意愿，虽然我们并不需要付诸实现——只要在你的脑中想着把这棵树砍掉做成桌子、凳子等——我们就把它叫做意向。于是，对象就是客观世界中物体在人脑中的映象及人的意向。只要这个对象存在我们的思维意识当中，我们就可以籍此判断同类的东西。譬如，当我们看到另外一棵树是，并不会因为所见的第一棵树不在了失去了供参照的模板而不认识这棵树了。当我们接触某些新事物时，我们的意识就会为这些事物确立一个对象。当然这个过程是怎么形成的，那就不是我们所能讨论的问题了。上面所说的对象研究的是一般意义上的问题，因而它可以外推到一切事物。我们经常所说的“对象”，一班指的是解决信息领域内所遇到问题的方法。特别是应用软件技术来决问题的方法。如我们经常碰到的面向对象的编程(Object-Oriented Programming)、面向对象的分析(Object-Oriented Analysis)、面向对象的设计(Object-Oriented Design)等。应用前面所介绍的关于对象的概念，可以对这些问题做进一步的分析。在面对较复杂的系统，我们可以将它作为一个对象来进行分析。一个系统(解决某个问题的全套解决方案)作为一个对象，可以由多个部分组成。同样，这个对象也可以由多个对象组成。对于同类的事物，可以由一个对象来表示。这样做的益处是显而易见的，它灵活而高效，可以大大减轻设计人员的工作量，简化实际的模型。举一个例子。在关系型数据库的设计当中，我们可以把一个元组当作对象，给它定义一组操作方法。这些方法将适用于所有元组，从而我们不必在更大的范围内去细致的考虑不同的元组(如判断一个元素是否合法)：因为它们有一组公共的面向本身的方法，它们“自己”可以“解决”自己的问题。更上一层的对象可以是一个表、视图等。表对象在元组对象的基础上又有它们自己的方法，如增加、删除等。从这个层面上讲，它也只需要做“自己”的事情，因为有元组对象的支持，它无须去考虑像元素是否合法这类的事情。甚至，有时为了满足我们还可以将元素或表群当作时对象并定义它们自己的方法。这样，更能显示面向对象的优势。
上面所讨论的可以说是面向对象的分析方法。在具体的设计过程当中，还应该采用适当的方式。因为面向对象的思想固然很先进，如果做得不好的话，同样不能达到预期的效果。这主要表现在处理对象与对象的关系上没有做好，对象与对象的层次不分明。如上面所举得关系型数据库得例子，如果在元组层面上得对象过多得考虑一个表对象得因素，或一个表层面上对象过多地考虑一个元组对象的因素，甚至去考虑元素层面上的因素，这些都不是好的面向对象的设计方法。这一点，在语言实现方面，Java比C++更有优势，因为它不允许多重继承，从而使对象之间的关系更明确。谁也不会否认C++的功能更强大，但是它也要由次付出巨大代价——当现在代码库发展到一定程度、一定规模时，个对象之间的层次关系将变得异常复杂，给后继使用者得学习、理解带来很大的困难，应用上很难把握。另外，虽然C++具备面向对象的处理能力，但它还是保留了很多面向过程的东西。用C++完全可以不用面向对象的思想来进行程序设计，当然人们不会这样去做——除了那些只是把C++看成是C扩充的初学者，这就为以后的发展埋下了隐患。在者一方面，Java的限制更多一点。就者一点还远远不够。搞开发的是人，开发方法是由人决定的。要应用面向对象的方法开发出优秀的软件，必须要求开发人员具有良好的面向对象的思想。好的工程师可以利用适当的工具开发出优秀的软件——而不在乎他所使用的语言工具——Java、C++、Object Pascal、Ada等。
要能应用面向对象的思想熟练得做好系统分析、设计、实现(编程)，首先要求开发人员知道什么是对象，什么是面向对象得概念，否则是谈不上应用面向对象得思想搞软件开了。希望读者能在看到我在前面的不太严密、详细的论述之后可以对“对象”这个概念由一个初步的了解，同时也希望有不同看法的同行提出批评。其次，还要求开发人员有比较丰富的开发经验。否则，光谈理论，那是不能真正体会“面向对象”的含义的。尽管如此，我们还是可以定出一般的规则的。抛开生命周期的其它阶段，对一个确立的系统一般可以进行以下几个过程：
一、首先应用面向对象的思想进行系统分析。仔细的划分系统的各个部分，明确它们之间的层次关系，然后将各个部分作为一个对象进行功能上的分析。例如，要设计一个用户界面，它可能由几个主要的窗体组成：主窗体MainFrame、功能性子窗体FunctionFrame以及对话对话框Dialog。其中，MainFrame中可能还包括菜单、工具条、文本框、状态条等Windows组件。对于要完成一些重要功能的组件，我们可以单独将它作为一个对象看待，在具体的实现中，用单独一个类来表示。而一些并不是很重要的、只是在过程中需要用到的组件，就可以将它们集成到其他对象中去。要明确每个对象自己的任务——不要让它有缺陷，也不能越殂代庖。各个对象之间的关系通过过程、内容、功能等耦合来实现。
实际上，如果你是用Java进行开发，你会发现swing组件本身就非常好用，因为它们之间的关系是很分明的——你无须去理解分清象C++中的组件那样复杂的关系。在开发你自己的组件时，也需要力求达到这样的效果。
二、其次时应用面向对象的思想进行系统设计。其实在真正做好了系统分析的工作之后，进行设计就比较轻松了。这段时间只要进一步确定各个对象的功能以及各个对象之间的关系。为了能够更好地帮助实现人员明白各个对象之间的关系，可以利用一些工具将这些组件的关系表示出来，统一建模语言(Uniformed Module Language，UML)就是这样的一种好东西。它不仅可以现在帮助开发人员了解整个系统，也为以后的维护工作提供一个档案文件，给以后的工作提供巨大的方便。
三、实现(编码)。这一点就不用多说了。在我的下一篇文章中，将会给出一个比较简单的实例，希望会给读者一个启发。
在搞软件开发时，编码不是最重要的。分析、设计才是最重要的。也许当我们接受了一些更先进的思想之后，会更加理解这一点了。希望我们中国的软件开发人员能应用面向对象的思想开发出具有国际竞争力的优秀软件。
]]></description>
			<content:encoded><![CDATA[<p>什么是面向对象的设计思想?也许有不少初学者对这个概念还有许多不明白的地方，特别是这个处于新旧思想交替的时代，许多人刚刚学完现在看来是快要淘汰的只是面向过程的语言。他们的脑子还没有脱离面向过程思想的束缚，抬头却发现，“面向对象”早已经流行开来，这个陌生的词漫天飞舞。随便拿起一本流行计算机技术书籍，那里会没有“面向对象”的字眼!于是心中便惶惑了：什么叫“面向对象”?不感兴趣者，一带而过;有志于在这方面发展的，匆忙找到一本有关书籍来啃究竟什么是“面向对象”。然而，要突破思想的束缚，说难也不难，说到要深刻认识却也不容易。笔者在做了一些轻量级的工作后，颇以为有点心得，不怕贻笑大方，写出已供广大同行批评指正。</p>
<p><span id="more-207"></span></p>
<p>“对象(Object)”一词，早在十九世纪就有现象学大师胡塞尔提出并定义。对象是世界中的物体在人脑中的映象，是人的意识之所以为意识的反映，是做为一种概念而存在的先念的东西，它还包括了人的意愿。举例说明吧。当我们认识到一种新的物体，它叫树，于是在我们的意识当中就形成了树的概念。这个概念会一直存在于我们的思维当中，并不会因为这棵树被砍掉而消失。这个概念就是现实世界当中的物体在我们意识当中的映象。我们对它还可以有我们自己的意愿，虽然我们并不需要付诸实现——只要在你的脑中想着把这棵树砍掉做成桌子、凳子等——我们就把它叫做意向。于是，对象就是客观世界中物体在人脑中的映象及人的意向。只要这个对象存在我们的思维意识当中，我们就可以籍此判断同类的东西。譬如，当我们看到另外一棵树是，并不会因为所见的第一棵树不在了失去了供参照的模板而不认识这棵树了。当我们接触某些新事物时，我们的意识就会为这些事物确立一个对象。当然这个过程是怎么形成的，那就不是我们所能讨论的问题了。上面所说的对象研究的是一般意义上的问题，因而它可以外推到一切事物。我们经常所说的“对象”，一班指的是解决信息领域内所遇到问题的方法。特别是应用软件技术来决问题的方法。如我们经常碰到的面向对象的编程(Object-Oriented Programming)、面向对象的分析(Object-Oriented Analysis)、面向对象的设计(Object-Oriented Design)等。应用前面所介绍的关于对象的概念，可以对这些问题做进一步的分析。在面对较复杂的系统，我们可以将它作为一个对象来进行分析。一个系统(解决某个问题的全套解决方案)作为一个对象，可以由多个部分组成。同样，这个对象也可以由多个对象组成。对于同类的事物，可以由一个对象来表示。这样做的益处是显而易见的，它灵活而高效，可以大大减轻设计人员的工作量，简化实际的模型。举一个例子。在关系型数据库的设计当中，我们可以把一个元组当作对象，给它定义一组操作方法。这些方法将适用于所有元组，从而我们不必在更大的范围内去细致的考虑不同的元组(如判断一个元素是否合法)：因为它们有一组公共的面向本身的方法，它们“自己”可以“解决”自己的问题。更上一层的对象可以是一个表、视图等。表对象在元组对象的基础上又有它们自己的方法，如增加、删除等。从这个层面上讲，它也只需要做“自己”的事情，因为有元组对象的支持，它无须去考虑像元素是否合法这类的事情。甚至，有时为了满足我们还可以将元素或表群当作时对象并定义它们自己的方法。这样，更能显示面向对象的优势。</p>
<p>上面所讨论的可以说是面向对象的分析方法。在具体的设计过程当中，还应该采用适当的方式。因为面向对象的思想固然很先进，如果做得不好的话，同样不能达到预期的效果。这主要表现在处理对象与对象的关系上没有做好，对象与对象的层次不分明。如上面所举得关系型数据库得例子，如果在元组层面上得对象过多得考虑一个表对象得因素，或一个表层面上对象过多地考虑一个元组对象的因素，甚至去考虑元素层面上的因素，这些都不是好的面向对象的设计方法。这一点，在语言实现方面，Java比C++更有优势，因为它不允许多重继承，从而使对象之间的关系更明确。谁也不会否认C++的功能更强大，但是它也要由次付出巨大代价——当现在代码库发展到一定程度、一定规模时，个对象之间的层次关系将变得异常复杂，给后继使用者得学习、理解带来很大的困难，应用上很难把握。另外，虽然C++具备面向对象的处理能力，但它还是保留了很多面向过程的东西。用C++完全可以不用面向对象的思想来进行程序设计，当然人们不会这样去做——除了那些只是把C++看成是C扩充的初学者，这就为以后的发展埋下了隐患。在者一方面，Java的限制更多一点。就者一点还远远不够。搞开发的是人，开发方法是由人决定的。要应用面向对象的方法开发出优秀的软件，必须要求开发人员具有良好的面向对象的思想。好的工程师可以利用适当的工具开发出优秀的软件——而不在乎他所使用的语言工具——Java、C++、Object Pascal、Ada等。</p>
<p>要能应用面向对象的思想熟练得做好系统分析、设计、实现(编程)，首先要求开发人员知道什么是对象，什么是面向对象得概念，否则是谈不上应用面向对象得思想搞软件开了。希望读者能在看到我在前面的不太严密、详细的论述之后可以对“对象”这个概念由一个初步的了解，同时也希望有不同看法的同行提出批评。其次，还要求开发人员有比较丰富的开发经验。否则，光谈理论，那是不能真正体会“面向对象”的含义的。尽管如此，我们还是可以定出一般的规则的。抛开生命周期的其它阶段，对一个确立的系统一般可以进行以下几个过程：</p>
<p>一、首先应用面向对象的思想进行系统分析。仔细的划分系统的各个部分，明确它们之间的层次关系，然后将各个部分作为一个对象进行功能上的分析。例如，要设计一个用户界面，它可能由几个主要的窗体组成：主窗体MainFrame、功能性子窗体FunctionFrame以及对话对话框Dialog。其中，MainFrame中可能还包括菜单、工具条、文本框、状态条等Windows组件。对于要完成一些重要功能的组件，我们可以单独将它作为一个对象看待，在具体的实现中，用单独一个类来表示。而一些并不是很重要的、只是在过程中需要用到的组件，就可以将它们集成到其他对象中去。要明确每个对象自己的任务——不要让它有缺陷，也不能越殂代庖。各个对象之间的关系通过过程、内容、功能等耦合来实现。</p>
<p>实际上，如果你是用Java进行开发，你会发现swing组件本身就非常好用，因为它们之间的关系是很分明的——你无须去理解分清象C++中的组件那样复杂的关系。在开发你自己的组件时，也需要力求达到这样的效果。</p>
<p>二、其次时应用面向对象的思想进行系统设计。其实在真正做好了系统分析的工作之后，进行设计就比较轻松了。这段时间只要进一步确定各个对象的功能以及各个对象之间的关系。为了能够更好地帮助实现人员明白各个对象之间的关系，可以利用一些工具将这些组件的关系表示出来，统一建模语言(Uniformed Module Language，UML)就是这样的一种好东西。它不仅可以现在帮助开发人员了解整个系统，也为以后的维护工作提供一个档案文件，给以后的工作提供巨大的方便。</p>
<p>三、实现(编码)。这一点就不用多说了。在我的下一篇文章中，将会给出一个比较简单的实例，希望会给读者一个启发。</p>
<p>在搞软件开发时，编码不是最重要的。分析、设计才是最重要的。也许当我们接受了一些更先进的思想之后，会更加理解这一点了。希望我们中国的软件开发人员能应用面向对象的思想开发出具有国际竞争力的优秀软件。</p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/20081110207.shtml/feed</wfw:commentRss>
		</item>
		<item>
		<title>详谈基于JSON的高级AJAX开发技术</title>
		<link>http://column.ibeifeng.com/zhangweiliang/20081029205.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/20081029205.shtml#comments</comments>
		<pubDate>Wed, 29 Oct 2008 10:32:41 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[ajax]]></category>

		<category><![CDATA[json]]></category>

		<category><![CDATA[程序开发]]></category>

		<guid isPermaLink="false">http://column.ibeifeng.com/?p=205</guid>
		<description><![CDATA[本文将解释如何使用AJAX和JSON分析器在客户端和服务器之间创建复杂的JSON数据传输层。
一、 引言
毫无疑问，AJAX已经成为当今Web开发中一种强有力的用户交互技术，但是它的许多可能性应用仍然鲜为人知。在本文中，我们将来共同探讨如何使用JavaScript对象标志(JSON)和JSON分析器在服务器和客户端AJAX引擎之间创建复杂而强有力的JSON数据传输层。我们将详细讨论如何创建一组对象(在其它语言中经常被当作一个包)，如何把这些对象串行化为JSON以发送到服务器端，以及如何把服务器端JSON反串行化为客户端JavaScript对象。

提示：你可以在Douglas Crockford的网站上找到本文中使用的JSON分析器。
在继续阅读前，本文假定你已经掌握了JavaScript技术并且了解如何创建一个基本的AJAX引擎，并经由AJAX技术向服务器端发出请求和从服务器端接收响应。为了更好地理解本文中示例，你需要下载本文相应的源码文件。
二、 开始
为了进一步抽象我们的AJAX请求并有助于我们以后在不同的应用程序间共享AJAX引擎代码，本文使用了一个我自己创建的AJAX引擎。为了使用这个引擎，我们简单地导入三个JavaScript文件并且向一个名为AjaxUpdater的对象发出请求。然后，由该引擎来负责处理其它任务，包括把响应代理到在该请求中指定的回调方法中。下面的示例展示了我们如何使用这个引擎发出请求以及导入相关的文件：
以下是引用片段：

＜script type="text/javascript"src="javascript/model/Ajax.js"＞＜/script＞
＜script type="text/javascript" src="javascript/model/HTTP.js"＞＜/script＞
＜script type="text/javascript" src="javascript/model/AjaxUpdater.js"＞＜/script＞
＜script type="text/javascript"＞
document.load = AjaxUpdater.Update(’GET’， URL， callback);
＜/script＞

首先，让我们来讨论JavaScript对象。
三、 JavaScript对象
JavaScript以前经常被误解，似乎它主要用于实现客户浏览器端图形效果。其实，JavaScript是一种强有力的语言，特别当它与AJAX以及一个应用程序的服务器端相结合时;但是，即使在客户端，JavaScript也能够实现远比你预料得多的多的功能。面向对象的JavaScript就是一个示例，它能够使我们创建对象，扩展内在对象，甚至能够把我们的对象创建成包以达到更容易的管理之目的。
在本文示例中，我们将创建三个对象：Auto，Car和Wheel。其中，每一个都是简单的对象;在此，我们仅使用它们来展示如何创建一个基本包。
首先，Auto对象被声明为一个新的对象：
以下是引用片段：

var Auto = new Object();

注意，这个Auto对象将用作Car对象的父类。因此，Car对象将成为Auto对象的一个属性，只不过它被分离到另一个文件中以更易于管理(这个概念经常被用于其它面向对象的语言中，但是在JavaScript中却并不经常提起它)。下面是这个Car对象相应的代码：
以下是引用片段：

Auto.Car = new Object();
Auto.Car.color = "#fff";
Auto.Car.setColor = function(_color)
{
Auto.Car.color = _color;
}
Auto.Car.setColor("#333");

如你所见，该Car对象是Auto对象的一个子对象—这分明是一种类对象层次结构。这个对象有一个名为color的属性和一个用于设置它的方法。在此，我们把color属性设置为灰色以覆盖掉缺省的白色。当在后面我们串行化该对象时请牢记住这个事实。
下一个对象，Wheel，是Car的一个子对象：
以下是引用片段：

Auto.Car.Wheel = new Object(); Auto.Car.Wheel.color = "#000";

在此，Wheel是一个基本对象，但是它展示了对象层次中的又一个层。这个对象有一个称为color的缺省值为黑色(“#000”)的属性。
下面，让我们来分析一下为什么这些对象如此重要以及我们是如何使用它们提供的简单的属性的。
四、 把JavaScript对象串行化为JSON
借助于JSON分析器，我们可以很容易地把刚才创建的JavaScript对象串行化为JSON。首先，我们需要下载该分析器的一个副本，并且要把它添加到文档中。下面是我在本文示例中用于导入该脚本的相应的代码：
以下是引用片段：

＜script type="text/javascript" src="javascript/utils/jsonparser.js"＞＜/script＞

我已经把该分析器添加到我的javascript目录，即一个称为utils的子目录下。
下面是最终的包括到其中用于导入适当的JavaScript文件的代码片断：
以下是引用片段：

＜script type="text/javascript" src="javascript/Auto.js"＞＜/script＞
＜script type="text/javascript" src="javascript/Car.js"＞＜/script＞
＜script type="text/javascript" src="javascript/Wheel.js"＞＜/script＞
＜script type="text/javascript" src="javascript/utils/jsonparser.js"＞＜/script＞
＜script type="text/javascript" src="javascript/model/Ajax.js"＞＜/script＞
＜script type="text/javascript" src="javascript/model/HTTP.js"＞＜/script＞
＜script type="text/javascript" src="javascript/model/AjaxUpdater.js"＞＜/script＞

在导入适当的文件后，我们可以通过把两个div元素和一个onload事件简单地添加到HTML文档中开始串行化。这两个div元素将分别拥有ID：body和loading。其中，这个loading标签将由AJAX引擎使用来指示进度情况，而body标签将用于显示消息。
onload事件相应于body元素并且设置它的innerHTML属性为JavaScript对象(作为一个串行化的JSON字符串)。为了实现这一目的，我在Auto对象上使用了jsonparser.js文件内的toJSONString方法：
以下是引用片段：

＜body onload="document.getElementById(’body’).innerHTML = ’＜b＞Local objects [...]]]></description>
			<content:encoded><![CDATA[<p>本文将解释如何使用AJAX和JSON分析器在客户端和服务器之间创建复杂的JSON数据传输层。</p>
<p><strong>一、 引言</strong></p>
<p>毫无疑问，AJAX已经成为当今Web开发中一种强有力的用户交互技术，但是它的许多可能性应用仍然鲜为人知。在本文中，我们将来共同探讨如何使用JavaScript对象标志(JSON)和JSON分析器在服务器和客户端AJAX引擎之间创建复杂而强有力的JSON数据传输层。我们将详细讨论如何创建一组对象(在其它语言中经常被当作一个包)，如何把这些对象串行化为JSON以发送到服务器端，以及如何把服务器端JSON反串行化为客户端JavaScript对象。</p>
<p><span id="more-205"></span></p>
<p>提示：你可以在Douglas Crockford的网站上找到本文中使用的JSON分析器。</p>
<p>在继续阅读前，本文假定你已经掌握了JavaScript技术并且了解如何创建一个基本的AJAX引擎，并经由AJAX技术向服务器端发出请求和从服务器端接收响应。为了更好地理解本文中示例，你需要下载本文相应的源码文件。</p>
<p><strong>二、 开始</strong></p>
<p>为了进一步抽象我们的AJAX请求并有助于我们以后在不同的应用程序间共享AJAX引擎代码，本文使用了一个我自己创建的AJAX引擎。为了使用这个引擎，我们简单地导入三个JavaScript文件并且向一个名为AjaxUpdater的对象发出请求。然后，由该引擎来负责处理其它任务，包括把响应代理到在该请求中指定的回调方法中。下面的示例展示了我们如何使用这个引擎发出请求以及导入相关的文件：</p>
<p>以下是引用片段：</p>
<p><code><br />
＜script type="text/javascript"src="javascript/model/Ajax.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/model/HTTP.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/model/AjaxUpdater.js"＞＜/script＞<br />
＜script type="text/javascript"＞<br />
document.load = AjaxUpdater.Update(’GET’， URL， callback);<br />
＜/script＞<br />
</code></p>
<p>首先，让我们来讨论JavaScript对象。</p>
<p><strong>三、 JavaScript对象</strong></p>
<p>JavaScript以前经常被误解，似乎它主要用于实现客户浏览器端图形效果。其实，JavaScript是一种强有力的语言，特别当它与AJAX以及一个应用程序的服务器端相结合时;但是，即使在客户端，JavaScript也能够实现远比你预料得多的多的功能。面向对象的JavaScript就是一个示例，它能够使我们创建对象，扩展内在对象，甚至能够把我们的对象创建成包以达到更容易的管理之目的。</p>
<p>在本文示例中，我们将创建三个对象：Auto，Car和Wheel。其中，每一个都是简单的对象;在此，我们仅使用它们来展示如何创建一个基本包。</p>
<p>首先，Auto对象被声明为一个新的对象：</p>
<p>以下是引用片段：</p>
<p><code><br />
var Auto = new Object();<br />
</code></p>
<p>注意，这个Auto对象将用作Car对象的父类。因此，Car对象将成为Auto对象的一个属性，只不过它被分离到另一个文件中以更易于管理(这个概念经常被用于其它面向对象的语言中，但是在JavaScript中却并不经常提起它)。下面是这个Car对象相应的代码：</p>
<p>以下是引用片段：</p>
<p><code><br />
Auto.Car = new Object();<br />
Auto.Car.color = "#fff";<br />
Auto.Car.setColor = function(_color)<br />
{<br />
Auto.Car.color = _color;<br />
}<br />
Auto.Car.setColor("#333");<br />
</code></p>
<p>如你所见，该Car对象是Auto对象的一个子对象—这分明是一种类对象层次结构。这个对象有一个名为color的属性和一个用于设置它的方法。在此，我们把color属性设置为灰色以覆盖掉缺省的白色。当在后面我们串行化该对象时请牢记住这个事实。</p>
<p>下一个对象，Wheel，是Car的一个子对象：</p>
<p>以下是引用片段：</p>
<p><code><br />
Auto.Car.Wheel = new Object(); Auto.Car.Wheel.color = "#000";<br />
</code></p>
<p>在此，Wheel是一个基本对象，但是它展示了对象层次中的又一个层。这个对象有一个称为color的缺省值为黑色(“#000”)的属性。</p>
<p>下面，让我们来分析一下为什么这些对象如此重要以及我们是如何使用它们提供的简单的属性的。</p>
<p><strong>四、 把JavaScript对象串行化为JSON</strong></p>
<p>借助于JSON分析器，我们可以很容易地把刚才创建的JavaScript对象串行化为JSON。首先，我们需要下载该分析器的一个副本，并且要把它添加到文档中。下面是我在本文示例中用于导入该脚本的相应的代码：</p>
<p>以下是引用片段：</p>
<p><code><br />
＜script type="text/javascript" src="javascript/utils/jsonparser.js"＞＜/script＞<br />
</code></p>
<p>我已经把该分析器添加到我的javascript目录，即一个称为utils的子目录下。</p>
<p>下面是最终的包括到其中用于导入适当的JavaScript文件的代码片断：</p>
<p>以下是引用片段：</p>
<p><code><br />
＜script type="text/javascript" src="javascript/Auto.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/Car.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/Wheel.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/utils/jsonparser.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/model/Ajax.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/model/HTTP.js"＞＜/script＞<br />
＜script type="text/javascript" src="javascript/model/AjaxUpdater.js"＞＜/script＞<br />
</code></p>
<p>在导入适当的文件后，我们可以通过把两个div元素和一个onload事件简单地添加到HTML文档中开始串行化。这两个div元素将分别拥有ID：body和loading。其中，这个loading标签将由AJAX引擎使用来指示进度情况，而body标签将用于显示消息。</p>
<p>onload事件相应于body元素并且设置它的innerHTML属性为JavaScript对象(作为一个串行化的JSON字符串)。为了实现这一目的，我在Auto对象上使用了jsonparser.js文件内的toJSONString方法：</p>
<p>以下是引用片段：</p>
<p><code><br />
＜body onload="document.getElementById(’body’).innerHTML = ’＜b＞Local objects serialized as JSON＜/b＞Auto Object: ’+ Auto.toJSONString();"＞<br />
</code></p>
<p>这段代码使用了Auto对象及其所有的子对象，并且使用JSON分析器的toJSONString方法把它们串行化为一个JSON字符串。然后，该数据可以被用作服务器端的一种数据交换格式。</p>
<p>你可能还记得，在前面我们曾调用了一个称为setColor的方法来改变Car对象的颜色。当时，我使用它是因为我想向你展示串行化能够在运行时刻的任何点上实现，而且还为了反映出对象中最新的数据。</p>
<p>如果你仔细分析一下onload事件，你会注意到，Car和Wheel对象都包装在方括号内，这些方括号代表了父对象(即Auto)。这意味着，该串行化的JavaScript对象能够在运行时刻被发送到服务器端以存储最新的数据，并且也可以在应用程序启动时从服务器端进行接收以便从数据库中检索多数的当前数据。最精彩的部分在于，为了创建一种“无缝”的过程，所有与服务器之间实现的数据交换都可以使用JSON技术来实现。</p>
<p>下面，让我们来看一下相同的数据是如何从服务器端接收的，以及它们是如何被使用最新的数据(典型地，来源于一个数据库)串行化为客户端JavaScript对象的。</p>
<p><strong>五、 把JSON反串行化为客户端JavaScript对象</strong></p>
<p>在本文中，我简单地把一个静态文件创建为JSON响应，但是在实际开发中，你可以把这些数据存储在一个数据库中并且使用一种服务器端语言返回它。基于这一能力，我们就可以轻松地创建一种强有力的数据交换过程!在前面，我们已经分析了这一串行化过程。凭基本的AJAX体验，你应该能够理解数据是如何被寄送到服务器端的。现在，让我们着手讨论反串行化的问题。首先来看一个针对本文示例提供的静态JSON文件。这个文件其实是我们在上一节中串行化的数据：</p>
<p>以下是引用片段：</p>
<p><code><br />
{"Car":{"color":"#333"，"Wheel":{"color":"#000"}}}<br />
</code></p>
<p>作为一个请求JSON文件的示例，当我们点击下列链接时将请求这个串行化的Auto对象：</p>
<p>以下是引用片段：</p>
<p><code><br />
＜a href="javascript:AjaxUpdater.Update(’GET’， ’json/data.js’， displayResponse);"＞Get remote JSON＜/a＞<br />
</code></p>
<p>一旦接收到响应消息，我们的称为displayResponse回调方法就会被激活，然后，我们就能够反串行化并开始使用这些对象：</p>
<p>以下是引用片段：</p>
<p><code><br />
＜script type="text/javascript"＞<br />
function displayResponse()<br />
{<br />
if(Ajax.checkReadyState(’loading’) == "OK")<br />
{<br />
var Auto = Ajax.request.responseText.parseJSON();<br />
document.getElementById("body").innerHTML += " ＜b＞Remote JSON unserialized＜/b＞";<br />
document.getElementById("body").innerHTML += " Car color: "+Auto.Car.color;<br />
document.getElementById("body").innerHTML += " Wheel color: "+Auto.Car.Wheel.color;<br />
}<br />
}<br />
＜/script＞<br />
</code></p>
<p>这是相当激动人心的一部分!一旦我们拥有了该responseText，我们就能够简单地使用JSON分析器中的parseJSON方法来从串行化的数据中重建我们的Auto对象。借助于这个新的Auto对象，我们就可以调用相应的子对象。这个特征允许我们在服务器和客户端来回发送对象—而不必进行大量的分析工作，然而在以前标准的XML响应情况下我们将却必须这样做。这样以来，我们就可以创建出能够基于AJAX技术来保留自身状态的客户端JavaScript对象。</p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/20081029205.shtml/feed</wfw:commentRss>
		</item>
		<item>
		<title>浅谈在Java语言中究竟是传值还是传引用</title>
		<link>http://column.ibeifeng.com/zhangweiliang/20081021183.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/20081021183.shtml#comments</comments>
		<pubDate>Tue, 21 Oct 2008 07:27:56 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://column.ibeifeng.com/?p=183</guid>
		<description><![CDATA[首先，推荐对Java有一定理解的同仁一本书《Practical Java》。
在《Practical Java》中也有一个章节介绍Java中关于传值和传引用的问题，堪称经典。
《Practical Java》
在Java中，事实上底层工作原理不存在传引用的概念，这也象《Practical Java》中所说的那样，Java中只有传值。这句话理解起来需要费一定的周折。

熟悉C的程序员都用过指针，对指针可谓爱之深恨之切。指针是指向一块内存地址的内存数据（有些拗口），也就是说指针本身是一个占用4字节内存的int（32 位系统内），而这个int值恰恰又是另一块内存的地址。比如&#8221;hello&#8221;这个字串，存放在@0&#215;0000F000这个地址到@0&#215;0000F005这段内存区域内（包括0&#215;00的结束字节）。而在@0&#215;0000FFF0到@0&#215;0000FFF03这四个字节内存放着一个int，这个int的值是 @0&#215;0000F000。这样就形成了一个指向&#8221;hello&#8221;字串的指针。
在Java中，很多人说没有指针，事实上，在Java更深层次里，到处都是大师封装好的精美绝伦的指针。为了更容易的讲解Java中关于类和类型的调用，Java中出现了值与引用的说法。浅显的来说，我们可以认为 Java中的引用与C中的指针等效（其实差别非常非常大，但是为了说明我们今天的问题，把他们理解为等效是没有任何问题的）。
所谓传引用的说法是为了更好的讲解调用方式。基于上面对指针的理解，我们不难看出，指针其实也是一个int值，所谓传引用，我们是复制了复制了指针的int值进行传递。为了便于理解，我们可以姑且把指针看作一种数据类型，透明化指针的int特性，从而提出传引用的概念。
重申一遍：Java中只有传值。
1.所谓传值和传引用
传值和传引用的问题一直是Java里争论的话题。与C++不同的，Java里面没有指针的概念，Java的设计者巧妙的对指针的操作进行了管理。事实上，在懂C++的Java程序员眼中，Java到处都是精美绝伦的指针。
下面举个简单的例子，说明什么是传值，什么是传引用。
//例1

void method1(){
int x=0;
this.change(x);
System.out.println(x);
}
void int change(int i){
i=1;
}
很显然的，在mothod1中执行了change(x)后，x的值并不会因为change方法中将输入参数赋值为1而变成1，也就是说在执行change(x)后，x的值z依然是0。这是因为x传递给change(int i)的是值。这就是最简单的传值。
同样的，进行一点简单的变化。
//例2

void method1(){
StringBuffer x=new StringBuffer("Hello");
this.change(x);
System.out.println(x);
}
void int change(StringBuffer i){
i.append(&#8221; world!&#8221;);
}
看起来没什么变化，但是这次mothed1中执行了change (x)后，x的值不再是&#8221;Hello&#8221;了，而是变成了&#8221;Hello world!&#8221;。这是因为x传递给change(i)的是x的引用。这是最经典的传引用。
似乎有些奇怪了，两段程序没有特别的不同，可是为什么一个传的是值而另一个传的是引用呢？&#8230;&#8230;
2.非要搞清楚传值还是传引用的问题吗？
搞清楚这自然是有必要的，不然我也不需要写这么多了，不过的确没有到&#8221;非要&#8221;的地步。
首先，如果我们不太关心什么是传值什么是传引用，我们一样能写出漂亮的代码，但是这些代码在运行过程中可能会存在着极大的隐患。
全局变量是让大家深恶痛绝（又难以割舍）的东西，原因就是使用全局变量要特别注意数据的保护。如果在多线程的程序里使用全局变量简直就等于跟自己过不去。不了解传值和传引用的问题，跟使用全局变量不考虑数据保护的罪过是不相上下下的，甚至有时候比它还要糟。你会莫名其妙，为什么我的返回参数没有起作用，为什么我传进去的参数变成了这样&#8230;&#8230;？
一个例子：
//例3

void mothed1(){
int x=0;
int y=1;
switchValue(x,y);
System.out.println("x="+x);
System.out.println("y="+y);
}
void switchValue(int a,int b){
int c=a;
a=b;
b=c;
}
上面是一个交换a,b值的函数，看起来似乎蛮正确的，但是这个函数永远也不会完成你想要的工作。
还有一个例子：
//例4

StringBuffer a=new StringBuffer("I am a ");
StringBuffer b=a;
a.append("after append");
a=b;
System.out.println("a="+a);

在编程过程中，经常会遇到这种情况，一个变量的值要被临时改变一下，等用完之后再恢复到开始的值。就好像上面的例子，a为了保持它的值，使用b=a做赋值，之后a被改变，再之后a把暂存在b里面的值取回来。这是我们一厢情愿的想法，而事实上，这段代码执行后，你会发现a的值已经改变了。
以上是两个最简单的例子，真正的程序开发过程中，比这要复杂的情况每天都会遇到。
3.类型和类
Java 提出的思想，在Java里面任何东西都是类。但是Java里面同时还有简单数据类型：int,byte,char,boolean，与这些数据类型相对应的类是Integer，Byte，Character，Boolean，这样做依然不会破坏Java关于任何东西都是类的提法。
这里提到数据类型和类似乎和我们要说的传值和传引用的问题无关，但这是我们分辨传值和传引用的基础。
4.试图分辨传值还是传引用
为什么是&#8221;试图分辨&#8221;呢？很简单，传值和传引用的问题无处不在，但是似乎还没有人能正统的给出标准，怎样的就是值拷贝调用，怎样的就是引用调用。面对这个问题，我们更多的应该是来自平时积累对Java的理解。
回过头来，我们分析一下上面的几个例子：
先看例1，即使你不明白为什么，但是你应该知道这样做肯定不会改变x的值。为了方便说明，我们给例子都加上行号。
//例1

1 void method1(){
2 int x=0;
3 this.change(x);
4 }
5
6 void int change(int i){
7 i=7;
8}

让我们从内存的存储方式看一下x和I之间到底是什么关系。
在执行到第2行的时候，变量x指向一个存放着int 0的内存地址。
变量x&#8212;-&#62;[存放值0]
执行第3行调用change(x)方法的时候，内存中是这样的情形：x把自己值在内存中复制一份，然后变量i指向这个被复制出来的0。
变量x&#8212;-&#62;[存放值0]
↓进行了一次值复制
变量x&#8212;-&#62;[存放值0]
这时候再执行到第7行的时候，变量i的被赋值为7，而这一步的操作已经跟x没有任何关系了。
变量x&#8212;-&#62;[存放值0]
变量x&#8212;-&#62;[存放值7]
说到这里应该已经理解为什么change(x)不能改变x的值了吧？因为这个例子是传值的。
那么，试着分析一下为什么例三中的switchValue()方法不能完成变量值交换的工作？
再看例2。
//例2

1void method1(){
2 StringBuffer x=new StringBuffer("Hello");
3 this.change(x);
4}
5
6void [...]]]></description>
			<content:encoded><![CDATA[<p>首先，推荐对Java有一定理解的同仁一本书《Practical Java》。</p>
<p>在《Practical Java》中也有一个章节介绍Java中关于传值和传引用的问题，堪称经典。</p>
<p><strong>《Practical Java》</strong></p>
<p>在Java中，事实上底层工作原理不存在传引用的概念，这也象《Practical Java》中所说的那样，Java中只有传值。这句话理解起来需要费一定的周折。</p>
<p><span id="more-183"></span></p>
<p>熟悉C的程序员都用过指针，对指针可谓爱之深恨之切。指针是指向一块内存地址的内存数据（有些拗口），也就是说指针本身是一个占用4字节内存的int（32 位系统内），而这个int值恰恰又是另一块内存的地址。比如&#8221;hello&#8221;这个字串，存放在@0&#215;0000F000这个地址到@0&#215;0000F005这段内存区域内（包括0&#215;00的结束字节）。而在@0&#215;0000FFF0到@0&#215;0000FFF03这四个字节内存放着一个int，这个int的值是 @0&#215;0000F000。这样就形成了一个指向&#8221;hello&#8221;字串的指针。</p>
<p>在Java中，很多人说没有指针，事实上，在Java更深层次里，到处都是大师封装好的精美绝伦的指针。为了更容易的讲解Java中关于类和类型的调用，Java中出现了值与引用的说法。浅显的来说，我们可以认为 Java中的引用与C中的指针等效（其实差别非常非常大，但是为了说明我们今天的问题，把他们理解为等效是没有任何问题的）。</p>
<p>所谓传引用的说法是为了更好的讲解调用方式。基于上面对指针的理解，我们不难看出，指针其实也是一个int值，所谓传引用，我们是复制了复制了指针的int值进行传递。为了便于理解，我们可以姑且把指针看作一种数据类型，透明化指针的int特性，从而提出传引用的概念。</p>
<p>重申一遍：Java中只有传值。</p>
<p><strong>1.所谓传值和传引用</strong></p>
<p>传值和传引用的问题一直是Java里争论的话题。与C++不同的，Java里面没有指针的概念，Java的设计者巧妙的对指针的操作进行了管理。事实上，在懂C++的Java程序员眼中，Java到处都是精美绝伦的指针。</p>
<p>下面举个简单的例子，说明什么是传值，什么是传引用。</p>
<p>//例1<br />
<code><br />
void method1(){<br />
int x=0;<br />
this.change(x);<br />
System.out.println(x);<br />
}</code></p>
<p>void int change(int i){<br />
i=1;<br />
}</p>
<p>很显然的，在mothod1中执行了change(x)后，x的值并不会因为change方法中将输入参数赋值为1而变成1，也就是说在执行change(x)后，x的值z依然是0。这是因为x传递给change(int i)的是值。这就是最简单的传值。</p>
<p>同样的，进行一点简单的变化。</p>
<p>//例2<br />
<code><br />
void method1(){<br />
StringBuffer x=new StringBuffer("Hello");<br />
this.change(x);<br />
System.out.println(x);<br />
}</code></p>
<p>void int change(StringBuffer i){<br />
i.append(&#8221; world!&#8221;);<br />
}</p>
<p>看起来没什么变化，但是这次mothed1中执行了change (x)后，x的值不再是&#8221;Hello&#8221;了，而是变成了&#8221;Hello world!&#8221;。这是因为x传递给change(i)的是x的引用。这是最经典的传引用。</p>
<p>似乎有些奇怪了，两段程序没有特别的不同，可是为什么一个传的是值而另一个传的是引用呢？&#8230;&#8230;</p>
<p><strong>2.非要搞清楚传值还是传引用的问题吗？</strong></p>
<p>搞清楚这自然是有必要的，不然我也不需要写这么多了，不过的确没有到&#8221;非要&#8221;的地步。</p>
<p>首先，如果我们不太关心什么是传值什么是传引用，我们一样能写出漂亮的代码，但是这些代码在运行过程中可能会存在着极大的隐患。</p>
<p>全局变量是让大家深恶痛绝（又难以割舍）的东西，原因就是使用全局变量要特别注意数据的保护。如果在多线程的程序里使用全局变量简直就等于跟自己过不去。不了解传值和传引用的问题，跟使用全局变量不考虑数据保护的罪过是不相上下下的，甚至有时候比它还要糟。你会莫名其妙，为什么我的返回参数没有起作用，为什么我传进去的参数变成了这样&#8230;&#8230;？</p>
<p>一个例子：</p>
<p>//例3<br />
<code><br />
void mothed1(){<br />
int x=0;<br />
int y=1;<br />
switchValue(x,y);<br />
System.out.println("x="+x);<br />
System.out.println("y="+y);<br />
}<br />
void switchValue(int a,int b){</code></p>
<p>int c=a;<br />
a=b;<br />
b=c;<br />
}</p>
<p>上面是一个交换a,b值的函数，看起来似乎蛮正确的，但是这个函数永远也不会完成你想要的工作。</p>
<p>还有一个例子：</p>
<p>//例4<br />
<code><br />
StringBuffer a=new StringBuffer("I am a ");<br />
StringBuffer b=a;<br />
a.append("after append");<br />
a=b;<br />
System.out.println("a="+a);<br />
</code></p>
<p>在编程过程中，经常会遇到这种情况，一个变量的值要被临时改变一下，等用完之后再恢复到开始的值。就好像上面的例子，a为了保持它的值，使用b=a做赋值，之后a被改变，再之后a把暂存在b里面的值取回来。这是我们一厢情愿的想法，而事实上，这段代码执行后，你会发现a的值已经改变了。</p>
<p>以上是两个最简单的例子，真正的程序开发过程中，比这要复杂的情况每天都会遇到。</p>
<p><strong>3.类型和类</strong></p>
<p>Java 提出的思想，在Java里面任何东西都是类。但是Java里面同时还有简单数据类型：int,byte,char,boolean，与这些数据类型相对应的类是Integer，Byte，Character，Boolean，这样做依然不会破坏Java关于任何东西都是类的提法。</p>
<p>这里提到数据类型和类似乎和我们要说的传值和传引用的问题无关，但这是我们分辨传值和传引用的基础。</p>
<p><strong>4.试图分辨传值还是传引用</strong></p>
<p>为什么是&#8221;试图分辨&#8221;呢？很简单，传值和传引用的问题无处不在，但是似乎还没有人能正统的给出标准，怎样的就是值拷贝调用，怎样的就是引用调用。面对这个问题，我们更多的应该是来自平时积累对Java的理解。</p>
<p>回过头来，我们分析一下上面的几个例子：</p>
<p>先看例1，即使你不明白为什么，但是你应该知道这样做肯定不会改变x的值。为了方便说明，我们给例子都加上行号。</p>
<p>//例1<br />
<code><br />
1 void method1(){<br />
2 int x=0;<br />
3 this.change(x);<br />
4 }<br />
5<br />
6 void int change(int i){<br />
7 i=7;<br />
8}<br />
</code></p>
<p>让我们从内存的存储方式看一下x和I之间到底是什么关系。</p>
<p>在执行到第2行的时候，变量x指向一个存放着int 0的内存地址。</p>
<p>变量x&#8212;-&gt;[存放值0]</p>
<p>执行第3行调用change(x)方法的时候，内存中是这样的情形：x把自己值在内存中复制一份，然后变量i指向这个被复制出来的0。</p>
<p>变量x&#8212;-&gt;[存放值0]</p>
<p>↓进行了一次值复制</p>
<p>变量x&#8212;-&gt;[存放值0]</p>
<p>这时候再执行到第7行的时候，变量i的被赋值为7，而这一步的操作已经跟x没有任何关系了。</p>
<p>变量x&#8212;-&gt;[存放值0]</p>
<p>变量x&#8212;-&gt;[存放值7]</p>
<p>说到这里应该已经理解为什么change(x)不能改变x的值了吧？因为这个例子是传值的。</p>
<p>那么，试着分析一下为什么例三中的switchValue()方法不能完成变量值交换的工作？</p>
<p>再看例2。</p>
<p>//例2<br />
<code><br />
1void method1(){<br />
2 StringBuffer x=new StringBuffer("Hello");<br />
3 this.change(x);<br />
4}<br />
5<br />
6void int change(StringBuffer i){<br />
7 i.append(" world!");<br />
8}<br />
</code></p>
<p>例2似乎和例1从代码上看不出什么差别，但是执行结果却是change(x)能改变x的值。依然才从内存的存储角度来看看例2的蹊跷在哪里。</p>
<p>在执行到第2行时候，同例1一样，x指向一个存放&#8221;Hello&#8221;的内存空间。</p>
<p>变量x&#8212;-&gt;[存放值"Hello"]</p>
<p>接下来执行第三行change(x)，注意，这里就与例1有了本质的不同：调用change(x)时，变量i也指向了x指向的内存空间，而不是指向x的一个拷贝。</p>
<p>变量x \</p>
<p>&#8211;&gt;[存放值"Hello"]</p>
<p>变量x /</p>
<p>于是，第7行对i调用append方法，改变i指向的内存空间的值，x的值也就随之改变了。</p>
<p>变量x \</p>
<p>&#8211;&gt;[追加为"Hello World!"]</p>
<p>变量x /</p>
<p>为什么x值能改变呢？因为这个例子是传引用的。</p>
<p>这几个例子是明白了，可是很多人会开始有另一个疑问了：这样看来，到底什么时候是传的值什么时候是传得引用呢？于是，我们前面讲到的类型和类在这里就派上了用场：对于参数传递，如果是简单数据类型，那么它传递的是值拷贝，对于类的实例它传递的是类的引用。需要注意的是，这条规则只适用于参数传递。为什么这么说呢？我们看看这样一个例子：</p>
<p>//例5<br />
<code><br />
String str="abcdefghijk";<br />
str.replaceAll("b","B");<br />
</code></p>
<p>这两句执行后，str的内容依然是&#8221;abcdefghijk&#8221;，但是我们明明是对str操作的，为什么是这样的呢？因为str的值究竟会不会被改变完全取决于replaceAll这个方法是怎么实现的。类似的，有这样一个例子：</p>
<p>//例6<br />
<code><br />
1 void method1() {<br />
2 StringBuffer x = new StringBuffer("Hello");<br />
3 change1(x);<br />
4 System.out.println(x);<br />
5 }<br />
6<br />
7 void method2() {<br />
8 StringBuffer x = new StringBuffer("Hello");<br />
9 change2(x);<br />
10 System.out.println(x);<br />
11 }<br />
12<br />
13 void change1(StringBuffer sb) {<br />
14 sb.append(" world!");<br />
15 }<br />
16<br />
17 void change2(StringBuffer sb) {<br />
18 sb = new StringBuffer("hi");<br />
19 sb.append(" world!");<br />
20 }<br />
</code></p>
<p>调用method1()，屏幕打印结果为：&#8221;Hello world!&#8221;</p>
<p>调用method2()，我们认为结果应该是&#8221;hi world&#8221;，因为sb传进来的是引用。可是实际执行的结果是&#8221;Hello&#8221;！</p>
<p>难道change2()又变成传值了？！其实change1()和change2()的确都是通过参数传入引用，但是在方法内部因为处理方法的不同而使结果大相径庭。我们还是从内存的角度分析：</p>
<p>执行method1()和change1()不用再多说了，上面的例子已经讲解过，这里我们分析一下method2()和change2()。</p>
<p>程序执行到第8行，x指向一个存放着&#8221;Hello&#8221;的内存空间。</p>
<p>变量x&#8212;-&gt;[存放值"Hello"]</p>
<p>第9行调用change2，将sb指向x指向的内存空间，也就是传入x的引用。</p>
<p>变量x \</p>
<p>&#8211;&gt;[存放值"Hello"]</p>
<p>变量x /</p>
<p>到这里为止还没有什么异样，接下来执行18行，这里就出现了类似传入值拷贝的变化：new 方法并没有改变sb指向内存的内容，而是在内从中开辟了一块新的空间存放串&#8221;hi&#8221;，同时sb指向了这块空间。</p>
<p>变量x&#8212;-&gt;[存放值"Hello"]</p>
<p>×原有的引用被切断</p>
<p>变量x&#8212;-&gt;[另一块存放"hi"的空间]</p>
<p>接下来再对sb进行append已经和x没有任何关系了。</p>
<p>所以，还有一条不成规则的规则：对于函数调用，最终效果是什么完全看函数内部的实现。比较标准的做法是如果会改变引用的内容，则使用void作为方法返回值，而不会改变引用内容的则在返回值中返回新的值。</p>
<p>虽然已经说了这么多，但是感觉传值还是传引用的问题依然没有完全说清楚。因为这个问题本身就是很难归纳总结的问题，所以更多的理解要靠平时的积累和形成。下面几个例子，给大家尝试进行分析。</p>
<p>//例7，打印结果是什么？<br />
<code>public static void main(String[] args) {<br />
int a;<br />
int b;<br />
StringBuffer c;<br />
StringBuffer d;<br />
a = 0;<br />
b = a;<br />
c = new StringBuffer(&#8221;This is c&#8221;);<br />
d = c;</p>
<p>a = 2;<br />
c.append(&#8221;!!&#8221;);</p>
<p>System.out.println(&#8221;a=&#8221; + a);<br />
System.out.println(&#8221;b=&#8221; + b);<br />
System.out.println(&#8221;c=&#8221; + c);<br />
System.out.println(&#8221;d=&#8221; + d);<br />
}<br />
</code></p>
<p>//例8，打印结果是什么？<br />
<code><br />
public class Test{</p>
<p>public static void main(String[] args) {</p>
<p>StringBuffer sb = new StringBuffer(&#8221;Hello &#8220;);</p>
<p>System.out.println(&#8221;Before change, sb = &#8221; + sb);</p>
<p>changeData(sb);</p>
<p>System.out.println(&#8221;After changeData(n), sb = &#8221; + sb);</p>
<p>}</p>
<p>public static void changeData(StringBuffer strBuf) {</p>
<p>StringBuffer sb2 = new StringBuffer(&#8221;Hi &#8220;);</p>
<p>strBuf = sb2;</p>
<p>sb2.append(&#8221;World!&#8221;);</p>
<p>}</p>
<p>}<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/20081021183.shtml/feed</wfw:commentRss>
		</item>
		<item>
		<title>如何编出健壮的代码 java编程30条规则</title>
		<link>http://column.ibeifeng.com/zhangweiliang/20081017176.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/20081017176.shtml#comments</comments>
		<pubDate>Fri, 17 Oct 2008 11:33:25 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[编程]]></category>

		<category><![CDATA[规则]]></category>

		<guid isPermaLink="false">http://column.ibeifeng.com/?p=176</guid>
		<description><![CDATA[这是一些相当不错的忠告！每个规则都很有分量！都是长期经验积累的总结，希望能对您有所帮助，使您编出高质量的JAVA代码。
(1)类名首字母应该大写。字段、方法以及对象（句柄）的首字母应小写。对于所有标识符，其中包含的所有单词都应紧靠在一起，而且大写中间单词的首字母。
例如： ThisIsAClassName
thisIsMethodOrFieldName
若在定义中出现了常数初始化字符，则大写staticfinal基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。
Java包（Package）属于一种特殊情况：它们全都是小写字母，即便中间的单词亦是如此。对于域名扩展名称，如com，org，net或者edu等，全部都应小写（这也是Java 1.1和Java 1.2的区别之一）。

(2) 为了常规用途而创建一个类时，请采取“经典形式”，并包含对下述元素的定义：

equals()
hashCode()
toString()
clone()（implement Cloneable）
implement Serializable

(3) 对于自己创建的每一个类，都考虑置入一个main()，其中包含了用于测试那个类的代码。为使用一个项目中的类，我们没必要删除测试代码。若进行了任何形式的改动，可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。
(4) 应将方法设计成简要的、功能性单元，用它描述和实现一个不连续的类接口部分。理想情况下，方法应简明扼要。若长度很大，可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用（有些时候，方法必须非常大，但它们仍应只做同样的一件事情）。
(5) 设计一个类时，请设身处地为客户程序员考虑一下（类的使用方法应该是非常明确的）。然后，再设身处地为管理代码的人考虑一下（预计有可能进行哪些形式的修改，想想用什么方法可把它们变得更简单）。
(6) 使类尽可能短小精悍，而且只解决一个特定的问题。下面是对类设计的一些建议：
■一个复杂的开关语句：考虑采用“多形”机制
■数量众多的方法涉及到类型差别极大的操作：考虑用几个类来分别实现
■许多成员变量在特征上有很大的差别：考虑使用几个类
(7) 让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”（一个方法、类或者一个字段等等），就永远不能把它拿出。若强行拿出，就可能破坏其他人现有的代码，使他们不得不重新编写和设计。若只公布自己必须公布的，就可放心大胆地改变其他任何东西。在多线程环境中，隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。
(8) 谨惕“巨大对象综合症”。对一些习惯于顺序编程思维、且初涉OOP领域的新手，往往喜欢先写一个顺序执行的程序，再把它嵌入一个或两个巨大的对象里。根据编程原理，对象表达的应该是应用程序的概念，而非应用程序本身。
(9) 若不得已进行一些不太雅观的编程，至少应该把那些代码置于一个类的内部。
(10) 任何时候只要发现类与类之间结合得非常紧密，就需要考虑是否采用内部类，从而改善编码及维护工作（参见第14章14.1.2小节的“用内部类改进代码”）。
(11) 尽可能细致地加上注释，并用javadoc注释文档语法生成自己的程序文档。
(12) 避免使用“魔术数字”，这些数字很难与代码很好地配合。如以后需要修改它，无疑会成为一场噩梦，因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以，我们应创建一个常数，并为其使用具有说服力的描述性名称，并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。
(13) 涉及构建器和异常的时候，通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来，调用者就不会以为那个对象已正确地创建，从而盲目地继续。
(14) 当客户程序员用完对象以后，若你的类要求进行任何清除工作，可考虑将清除代码置于一个良好定义的方法里，采用类似于cleanup()这样的名字，明确表明自己的用途。除此以外，可在类内放置一个boolean（布尔）标记，指出对象是否已被清除。在类的finalize()方法里，请确定对象已被清除，并已丢弃了从RuntimeException继承的一个类（如果还没有的话），从而指出一个编程错误。在采取象这样的方案之前，请确定finalize()能够在自己的系统中工作（可能需要调用System.runFinalizersOnExit(true)，从而确保这一行为）。
(15) 在一个特定的作用域内，若一个对象必须清除（非由垃圾收集机制处理），请采用下述方法：初始化对象；若成功，则立即进入一个含有finally从句的try块，开始清除工作。
(16) 若在初始化过程中需要覆盖（取消）finalize()，请记住调用super.finalize()（若Object属于我们的直接超类，则无此必要）。在对finalize()进行覆盖的过程中，对super.finalize()的调用应属于最后一个行动，而不应是第一个行动，这样可确保在需要基础类组件的时候它们依然有效。
(17) 创建大小固定的对象集合时，请将它们传输至一个数组（若准备从一个方法里返回这个集合，更应如此操作）。这样一来，我们就可享受到数组在编译期进行类型检查的好处。此外，为使用它们，数组的接收者也许并不需要将对象“造型”到数组里。
(18) 尽量使用interfaces，不要使用abstract类。若已知某样东西准备成为一个基础类，那么第一个选择应是将其变成一个interface（接口）。只有在不得不使用方法定义或者成员变量的时候，才需要将其变成一个abstract（抽象）类。接口主要描述了客户希望做什么事情，而一个类则致力于（或允许）具体的实施细节。
(19) 在构建器内部，只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法，因为那些方法可能被其他人覆盖或取消，从而在构建过程中产生不可预知的结果（参见第7章的详细说明）。
(20) 对象不应只是简单地容纳一些数据；它们的行为也应得到良好的定义。
(21) 在现成类的基础上创建新类时，请首先选择“新建”或“创作”。只有自己的设计要求必须继承时，才应考虑这方面的问题。若在本来允许新建的场合使用了继承，则整个设计会变得没有必要地复杂。
(22) 用继承及方法覆盖来表示行为间的差异，而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色，这是绝对应该避免的：应直接使用一个“颜色”字段。
(23) 为避免编程时遇到麻烦，请保证在自己类路径指到的任何地方，每个名字都仅对应一个类。否则，编译器可能先找到同名的另一个类，并报告出错消息。若怀疑自己碰到了类路径问题，请试试在类路径的每一个起点，搜索一下同名的.class文件。
(24) 在Java 1.1 AWT中使用事件“适配器”时，特别容易碰到一个陷阱。若覆盖了某个适配器方法，同时拼写方法没有特别讲究，最后的结果就是新添加一个方法，而不是覆盖现成方法。然而，由于这样做是完全合法的，所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。
(25) 用合理的设计方案消除“伪功能”。也就是说，假若只需要创建类的一个对象，就不要提前限制自己使用应用程序，并加上一条“只生成其中一个”注释。请考虑将其封装成一个“独生子”的形式。若在主程序里有大量散乱的代码，用于创建自己的对象，请考虑采纳一种创造性的方案，将些代码封装起来。
(26) 警惕“分析瘫痪”。请记住，无论如何都要提前了解整个项目的状况，再去考察其中的细节。由于把握了全局，可快速认识自己未知的一些因素，防止在考察细节的时候陷入“死逻辑”中。
(27) 警惕“过早优化”。首先让它运行起来，再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候，才应进行优化。除非用专门的工具分析瓶颈，否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解，而且难于维护。
(28) 请记住，阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序，但注释、细致的解释以及一些示例往往具有不可估量的价值。无论对你自己，还是对后来的人，它们都是相当重要的。如对此仍有怀疑，那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折，这样或许能将你说服。
(29) 如认为自己已进行了良好的分析、设计或者实施，那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家，但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作，看看是否能找出你一度熟视无睹的问题。采取这种方式，往往能在最适合修改的阶段找出一些关键性的问题，避免产品发行后再解决问题而造成的金钱及精力方面的损失。
(30) 良好的设计能带来最大的回报。简言之，对于一个特定的问题，通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法，以后的工作就轻松多了，再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报（甚至无可估量）。而且由于自己倾注了大量心血，最终获得一个出色的设计方案，成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失。
]]></description>
			<content:encoded><![CDATA[<p>这是一些相当不错的忠告！每个规则都很有分量！都是长期经验积累的总结，希望能对您有所帮助，使您编出高质量的JAVA代码。</p>
<p>(1)类名首字母应该大写。字段、方法以及对象（句柄）的首字母应小写。对于所有标识符，其中包含的所有单词都应紧靠在一起，而且大写中间单词的首字母。<br />
例如： ThisIsAClassName<br />
thisIsMethodOrFieldName<br />
若在定义中出现了常数初始化字符，则大写staticfinal基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。<br />
Java包（Package）属于一种特殊情况：它们全都是小写字母，即便中间的单词亦是如此。对于域名扩展名称，如com，org，net或者edu等，全部都应小写（这也是Java 1.1和Java 1.2的区别之一）。</p>
<p><span id="more-176"></span></p>
<p>(2) 为了常规用途而创建一个类时，请采取“经典形式”，并包含对下述元素的定义：<br />
<code><br />
equals()<br />
hashCode()<br />
toString()<br />
clone()（implement Cloneable）<br />
implement Serializable<br />
</code></p>
<p>(3) 对于自己创建的每一个类，都考虑置入一个main()，其中包含了用于测试那个类的代码。为使用一个项目中的类，我们没必要删除测试代码。若进行了任何形式的改动，可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。</p>
<p>(4) 应将方法设计成简要的、功能性单元，用它描述和实现一个不连续的类接口部分。理想情况下，方法应简明扼要。若长度很大，可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用（有些时候，方法必须非常大，但它们仍应只做同样的一件事情）。</p>
<p>(5) 设计一个类时，请设身处地为客户程序员考虑一下（类的使用方法应该是非常明确的）。然后，再设身处地为管理代码的人考虑一下（预计有可能进行哪些形式的修改，想想用什么方法可把它们变得更简单）。</p>
<p>(6) 使类尽可能短小精悍，而且只解决一个特定的问题。下面是对类设计的一些建议：</p>
<p>■一个复杂的开关语句：考虑采用“多形”机制<br />
■数量众多的方法涉及到类型差别极大的操作：考虑用几个类来分别实现<br />
■许多成员变量在特征上有很大的差别：考虑使用几个类</p>
<p>(7) 让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”（一个方法、类或者一个字段等等），就永远不能把它拿出。若强行拿出，就可能破坏其他人现有的代码，使他们不得不重新编写和设计。若只公布自己必须公布的，就可放心大胆地改变其他任何东西。在多线程环境中，隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。</p>
<p>(8) 谨惕“巨大对象综合症”。对一些习惯于顺序编程思维、且初涉OOP领域的新手，往往喜欢先写一个顺序执行的程序，再把它嵌入一个或两个巨大的对象里。根据编程原理，对象表达的应该是应用程序的概念，而非应用程序本身。</p>
<p>(9) 若不得已进行一些不太雅观的编程，至少应该把那些代码置于一个类的内部。</p>
<p>(10) 任何时候只要发现类与类之间结合得非常紧密，就需要考虑是否采用内部类，从而改善编码及维护工作（参见第14章14.1.2小节的“用内部类改进代码”）。</p>
<p>(11) 尽可能细致地加上注释，并用javadoc注释文档语法生成自己的程序文档。</p>
<p>(12) 避免使用“魔术数字”，这些数字很难与代码很好地配合。如以后需要修改它，无疑会成为一场噩梦，因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以，我们应创建一个常数，并为其使用具有说服力的描述性名称，并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。</p>
<p>(13) 涉及构建器和异常的时候，通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来，调用者就不会以为那个对象已正确地创建，从而盲目地继续。</p>
<p>(14) 当客户程序员用完对象以后，若你的类要求进行任何清除工作，可考虑将清除代码置于一个良好定义的方法里，采用类似于cleanup()这样的名字，明确表明自己的用途。除此以外，可在类内放置一个boolean（布尔）标记，指出对象是否已被清除。在类的finalize()方法里，请确定对象已被清除，并已丢弃了从RuntimeException继承的一个类（如果还没有的话），从而指出一个编程错误。在采取象这样的方案之前，请确定finalize()能够在自己的系统中工作（可能需要调用System.runFinalizersOnExit(true)，从而确保这一行为）。</p>
<p>(15) 在一个特定的作用域内，若一个对象必须清除（非由垃圾收集机制处理），请采用下述方法：初始化对象；若成功，则立即进入一个含有finally从句的try块，开始清除工作。</p>
<p>(16) 若在初始化过程中需要覆盖（取消）finalize()，请记住调用super.finalize()（若Object属于我们的直接超类，则无此必要）。在对finalize()进行覆盖的过程中，对super.finalize()的调用应属于最后一个行动，而不应是第一个行动，这样可确保在需要基础类组件的时候它们依然有效。</p>
<p>(17) 创建大小固定的对象集合时，请将它们传输至一个数组（若准备从一个方法里返回这个集合，更应如此操作）。这样一来，我们就可享受到数组在编译期进行类型检查的好处。此外，为使用它们，数组的接收者也许并不需要将对象“造型”到数组里。</p>
<p>(18) 尽量使用interfaces，不要使用abstract类。若已知某样东西准备成为一个基础类，那么第一个选择应是将其变成一个interface（接口）。只有在不得不使用方法定义或者成员变量的时候，才需要将其变成一个abstract（抽象）类。接口主要描述了客户希望做什么事情，而一个类则致力于（或允许）具体的实施细节。</p>
<p>(19) 在构建器内部，只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法，因为那些方法可能被其他人覆盖或取消，从而在构建过程中产生不可预知的结果（参见第7章的详细说明）。</p>
<p>(20) 对象不应只是简单地容纳一些数据；它们的行为也应得到良好的定义。</p>
<p>(21) 在现成类的基础上创建新类时，请首先选择“新建”或“创作”。只有自己的设计要求必须继承时，才应考虑这方面的问题。若在本来允许新建的场合使用了继承，则整个设计会变得没有必要地复杂。</p>
<p>(22) 用继承及方法覆盖来表示行为间的差异，而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色，这是绝对应该避免的：应直接使用一个“颜色”字段。</p>
<p>(23) 为避免编程时遇到麻烦，请保证在自己类路径指到的任何地方，每个名字都仅对应一个类。否则，编译器可能先找到同名的另一个类，并报告出错消息。若怀疑自己碰到了类路径问题，请试试在类路径的每一个起点，搜索一下同名的.class文件。</p>
<p>(24) 在Java 1.1 AWT中使用事件“适配器”时，特别容易碰到一个陷阱。若覆盖了某个适配器方法，同时拼写方法没有特别讲究，最后的结果就是新添加一个方法，而不是覆盖现成方法。然而，由于这样做是完全合法的，所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。</p>
<p>(25) 用合理的设计方案消除“伪功能”。也就是说，假若只需要创建类的一个对象，就不要提前限制自己使用应用程序，并加上一条“只生成其中一个”注释。请考虑将其封装成一个“独生子”的形式。若在主程序里有大量散乱的代码，用于创建自己的对象，请考虑采纳一种创造性的方案，将些代码封装起来。</p>
<p>(26) 警惕“分析瘫痪”。请记住，无论如何都要提前了解整个项目的状况，再去考察其中的细节。由于把握了全局，可快速认识自己未知的一些因素，防止在考察细节的时候陷入“死逻辑”中。</p>
<p>(27) 警惕“过早优化”。首先让它运行起来，再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候，才应进行优化。除非用专门的工具分析瓶颈，否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解，而且难于维护。</p>
<p>(28) 请记住，阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序，但注释、细致的解释以及一些示例往往具有不可估量的价值。无论对你自己，还是对后来的人，它们都是相当重要的。如对此仍有怀疑，那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折，这样或许能将你说服。</p>
<p>(29) 如认为自己已进行了良好的分析、设计或者实施，那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家，但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作，看看是否能找出你一度熟视无睹的问题。采取这种方式，往往能在最适合修改的阶段找出一些关键性的问题，避免产品发行后再解决问题而造成的金钱及精力方面的损失。</p>
<p>(30) 良好的设计能带来最大的回报。简言之，对于一个特定的问题，通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法，以后的工作就轻松多了，再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报（甚至无可估量）。而且由于自己倾注了大量心血，最终获得一个出色的设计方案，成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失。</p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/20081017176.shtml/feed</wfw:commentRss>
		</item>
		<item>
		<title>Hibernate+Struts的J2EE应用开发</title>
		<link>http://column.ibeifeng.com/zhangweiliang/20081014119.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/20081014119.shtml#comments</comments>
		<pubDate>Tue, 14 Oct 2008 05:55:47 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Hibernate]]></category>

		<category><![CDATA[j2ee]]></category>

		<category><![CDATA[Struts]]></category>

		<guid isPermaLink="false">http://column.ibeifeng.com/?p=119</guid>
		<description><![CDATA[随着Java技术的逐渐成熟与完善，作为建立企业级应用的标准平台，J2EE平台得到了长足的发展。借助于J2EE规范中包含的多项技术：Enterprise JavaBean(EJB)、Java Servlets(Servlet)、Java Server Pages(JSP)、Java Message Service(JMS)等，开发出了许多应用系统。但是，在传统J2EE应用的开发过程中也出现了一些问题：1）数据模型和逻辑模型之间的矛盾。目前使用的数据库基本上都是关系型数据库，而Java本质上是一种面向对象的语言，对象在存储和读取时使用SQL和JDBC进行数据库操作，降低了编程的效率以及系统的可维护性；2）传统的J2EE应用多采用基于EJB的重量级框架，这种框架适合于开发大型企业应用，但是使用EJB容器进行开发和调试需要耗费大量时间。为了降低代码的耦合性，提高系统的开发效率，本文提出了一种基于Struts框架和Hibernate框架的J2EE应用开发策略。

数据持久层及Hibernate
Hibernate是一个数据持久层框架，是一种实现对象和关系之间映射（O/R Mapping）的工具，它对JDBC进行了轻量级的对象封装，使程序员可以使用对象编程思想来操作数据库。它不仅提供了从Java类到数据表的映射，也提供了数据查询和恢复机制。相对于使用JDBC和SQL来操作数据库，使用Hibernate能大大的提高实现的效率。Hibernate框架用配置文件的形式来定义Java对象和数据表之间的映射关系，同时在更深的层面将数据表之间的关系解释为Java对象之间的继承及包含等关系。通过使用HQL语句将复杂的关系算法用对象的方式描述，在很大程度上简化了对数据的查询，加快了开发的效率。在Hibernate中有一个简单而直观的API，用于对数据库所表示的对象执行查询。要创建或修改这些对象，只需在程序中与它们进行交互，然后告诉Hibernate保存即可。这样，大量封装持久化操作的业务逻辑不再需要编写烦琐的JDBC语句，从而使数据持久层得到了极大的简化。

用Struts实现MVC架构
MVC（Model-View-Controller）由Trygve Reenskaug提出，首先被应用在SmallTalk-80环境中，是许多交互和界面系统的构成基础。根据界面设计可变性的需求，MVC把交互系统的组成分解成模型、视图、控制器三部分。
模型（Model）是软件所处理问题逻辑在独立于外在显示内容和形式情况下的内在抽象，封装了问题的核心数据、逻辑和功能的计算关系，独立于具体的界面表达和I/O操作。视图（View）把表示模型数据及逻辑关系和状态的信息及特定形式展示给用户。它从模型获得显示信息，对于相同的信息可以有多个不同的显示形式或视图。控制器（Controller）是处理用户与软件的交互操作的，其职责是控制提供模型中任何变化的传播，确保用户界面于模型间的对应联系；它接受用户的输入，将输入反馈给模型，进而实现对模型的计算控制，是使模型和视图协调工作的部件。通常一个视图对应一个控制器。模型、视图与控制器的分离，使得一个模型可以具有多个显示视图。如果用户通过某个视图的控制器改变了模型的数据，所有其它依赖于这些数据的视图都应反映到这些变化。因此，无论何时发生了何种数据变化，控制器都会将变化通知所有的视图，导致显示的更新。这实际上是一种模型的变化-传播机制。

Struts框架最早是作为Apache Jakarta项目的组成部分问世运做，它继承了MVC的各项特性，并根据J2EE的特点，做了相应的变化与扩展。Struts框架很好的结合了Jsp，Java Servlet，Java Bean，Taglib等技术。在Struts中，承担MVC中控制器角色的是ActionServlet。ActionServlet是一个通用的控制组件。这个控制组件提供了处理所有发送到Struts的HTTP请求的入口点。它截取和分发这些请求到相应的动作类（这些动作类都是Action类的子类）。另外控制组件也负责用相应的请求参数填充Action Form（FromBean），并传给动作类（ActionBean）。动作类访问核心商业逻辑，即访问Java Bean或调用EJB。最后动作类把控制权传给后续的JSP文件，由JSP文件生成视图。所有这些控制逻辑利用Struts-config.xml文件来配置。在Struts框架中，视图主要由JSP生成页面完成，Struts提供丰富的JSP标签库，这有利于分开表现逻辑和程序逻辑。模型以一个或多个Java Bean的形式存在。在Struts中，主要存在三种Bean，分别是：Action，ActionForm，EJB或者Java Bean。
Struts框架没有具体定义模型层的实现，在实际开发中，模型层通常是和业务逻辑紧密相连的，并且要对底层数据进行操作。下面介绍一种开发策略，将Hibernate引入到Struts框架的模型层中，使用它来进行数据封装和映射，提供持久化的支持。
运用Hibernate和Struts开发J2EE应用
1、体系结构
图3显示了基于Hibernate和Struts开发策略的体系结构图。

2、开发实践
下面结合开发实践，以在J2EE应用中非常普遍的用户登录过程为例，来说明上述体系结构是如何具体运用的。登录的流程非常清晰：用户从登录页面login.jsp输入登录信息，系统对登录信息进行验证，如果正确则成功登录，否则提示相应错误信息。
在开发过程中，使用Eclipse做为开发环境，同时加载了对Struts及Hibernate提供更好的控制和支持的第三方插件MyEclipse，Web服务器使用Tomcat，数据库选用了Mysql。
首先对Hibernate进行配置，只需要对系统自动生成的hibernate.cfg.xml进行修改，配置好数据库连接的各种参数以及定义数据映射文件。由于Hibernate所带的连接池主要用于测试，性能不是很好，可以通过JNDI将其修改为使用Tomcat的连接池。配置文件代码中关键的一部分如下：

＜hibernate-configuration＞＜session-factory＞
＜property name="connection.datasource"＞java:comp/env/jdbc/ sysdb ＜/property＞
＜property name="dialect"＞net.sf.hibernate.dialect.MySQLDialect ＜/property＞
＜mapping resource="User.hbm.xml"/＞
＜/session-factory＞＜/hibernate-configuration＞

其次是生成持久化类，实际上，由于使用了Hibernate，持久化类以及对持久化对象的映射定义均可以借助于系统中的向导自动生成。持久化对象的映射定义放到了User.hbm.xml中。部分代码如下：

＜class name=”User” table=”tUser”＞
＜id name=”UserID” column=”User_ID” type=”java.lang.Integer”＞
＜generator class=”native”＞＜/id＞
＜property name=”UserName” column=”User_Name” type=”java.lang.String”＞
…..＜/class＞

再次，使用Struts框架来实现逻辑控制，利用系统提供的向导可以生成视图及控制器。具体的配置在Struts-config.xml文件中。相关代码如下：

＜form-beans＞
＜form bean name=”LoginForm” type=”com.LoginForm”＞
＜/form-beans＞
＜action-mappings＞
＜action attribute=”LoginForm” input=”Login.jsp” path=”/DoLogin”
….
＜/action＞
＜/action-mappings＞

输入页面Login.jsp将用户填写的登录信息提交给控制器DoLogin进行处理，控制器调用由Hibernate定义好的持久化类User从数据库中读取相应信息进行验证。操作完成后将提示信息展示在表示层中。DoLogin中使用语句User u1=UserDAO.findbyname(username)得到User对象，然后就可以进行登录信息的验证。
从整个开发过程可以看出，程序员除了利用向导对有关的配置文件进行修改，只须编写少量代码即可完成登录模块的编码。开发完成的模块符合MVC框架的要求，质量比较高，易于修改和维护。
总结与展望
Hibernate是一个功能强大、高性能、非常流行的ORM工具，它推动了基于普通Java对象模型、用于映射底层数据结构的持久对象的开发，能够提供高级数据查询和检索服务。Struts是一个优秀的基于J2EE平台的MVC框架，它简化了WEB层的开发，实现了应用程序的显示逻辑和业务逻辑的分离。将这两种技术结合起来开发J2EE应用，能极大的提高开发效率，使系统具有更好的可维护性和可扩展性。这种开发策略正逐渐被众多程序员所接受，在实际开发中得到广泛的应用。
]]></description>
			<content:encoded><![CDATA[<p>随着Java技术的逐渐成熟与完善，作为建立企业级应用的标准平台，J2EE平台得到了长足的发展。借助于J2EE规范中包含的多项技术：Enterprise JavaBean(EJB)、Java Servlets(Servlet)、Java Server Pages(JSP)、Java Message Service(JMS)等，开发出了许多应用系统。但是，在传统J2EE应用的开发过程中也出现了一些问题：1）数据模型和逻辑模型之间的矛盾。目前使用的数据库基本上都是关系型数据库，而Java本质上是一种面向对象的语言，对象在存储和读取时使用SQL和JDBC进行数据库操作，降低了编程的效率以及系统的可维护性；2）传统的J2EE应用多采用基于EJB的重量级框架，这种框架适合于开发大型企业应用，但是使用EJB容器进行开发和调试需要耗费大量时间。为了降低代码的耦合性，提高系统的开发效率，本文提出了一种基于Struts框架和Hibernate框架的J2EE应用开发策略。</p>
<p><span id="more-119"></span></p>
<p><strong>数据持久层及Hibernate</strong></p>
<p>Hibernate是一个数据持久层框架，是一种实现对象和关系之间映射（O/R Mapping）的工具，它对JDBC进行了轻量级的对象封装，使程序员可以使用对象编程思想来操作数据库。它不仅提供了从Java类到数据表的映射，也提供了数据查询和恢复机制。相对于使用JDBC和SQL来操作数据库，使用Hibernate能大大的提高实现的效率。Hibernate框架用配置文件的形式来定义Java对象和数据表之间的映射关系，同时在更深的层面将数据表之间的关系解释为Java对象之间的继承及包含等关系。通过使用HQL语句将复杂的关系算法用对象的方式描述，在很大程度上简化了对数据的查询，加快了开发的效率。在Hibernate中有一个简单而直观的API，用于对数据库所表示的对象执行查询。要创建或修改这些对象，只需在程序中与它们进行交互，然后告诉Hibernate保存即可。这样，大量封装持久化操作的业务逻辑不再需要编写烦琐的JDBC语句，从而使数据持久层得到了极大的简化。</p>
<p><img src="http://column.ibeifeng.com/images/img/2008/10/hibemate.jpg" alt="hibemate" /></p>
<p><strong>用Struts实现MVC架构</strong></p>
<p>MVC（Model-View-Controller）由Trygve Reenskaug提出，首先被应用在SmallTalk-80环境中，是许多交互和界面系统的构成基础。根据界面设计可变性的需求，MVC把交互系统的组成分解成模型、视图、控制器三部分。</p>
<p>模型（Model）是软件所处理问题逻辑在独立于外在显示内容和形式情况下的内在抽象，封装了问题的核心数据、逻辑和功能的计算关系，独立于具体的界面表达和I/O操作。视图（View）把表示模型数据及逻辑关系和状态的信息及特定形式展示给用户。它从模型获得显示信息，对于相同的信息可以有多个不同的显示形式或视图。控制器（Controller）是处理用户与软件的交互操作的，其职责是控制提供模型中任何变化的传播，确保用户界面于模型间的对应联系；它接受用户的输入，将输入反馈给模型，进而实现对模型的计算控制，是使模型和视图协调工作的部件。通常一个视图对应一个控制器。模型、视图与控制器的分离，使得一个模型可以具有多个显示视图。如果用户通过某个视图的控制器改变了模型的数据，所有其它依赖于这些数据的视图都应反映到这些变化。因此，无论何时发生了何种数据变化，控制器都会将变化通知所有的视图，导致显示的更新。这实际上是一种模型的变化-传播机制。</p>
<p><img src="http://column.ibeifeng.com/images/img/2008/10/mvc.jpg" alt="mvc" /></p>
<p>Struts框架最早是作为Apache Jakarta项目的组成部分问世运做，它继承了MVC的各项特性，并根据J2EE的特点，做了相应的变化与扩展。Struts框架很好的结合了Jsp，Java Servlet，Java Bean，Taglib等技术。在Struts中，承担MVC中控制器角色的是ActionServlet。ActionServlet是一个通用的控制组件。这个控制组件提供了处理所有发送到Struts的HTTP请求的入口点。它截取和分发这些请求到相应的动作类（这些动作类都是Action类的子类）。另外控制组件也负责用相应的请求参数填充Action Form（FromBean），并传给动作类（ActionBean）。动作类访问核心商业逻辑，即访问Java Bean或调用EJB。最后动作类把控制权传给后续的JSP文件，由JSP文件生成视图。所有这些控制逻辑利用Struts-config.xml文件来配置。在Struts框架中，视图主要由JSP生成页面完成，Struts提供丰富的JSP标签库，这有利于分开表现逻辑和程序逻辑。模型以一个或多个Java Bean的形式存在。在Struts中，主要存在三种Bean，分别是：Action，ActionForm，EJB或者Java Bean。</p>
<p>Struts框架没有具体定义模型层的实现，在实际开发中，模型层通常是和业务逻辑紧密相连的，并且要对底层数据进行操作。下面介绍一种开发策略，将Hibernate引入到Struts框架的模型层中，使用它来进行数据封装和映射，提供持久化的支持。</p>
<p><strong>运用Hibernate和Struts开发J2EE应用</strong></p>
<p>1、体系结构</p>
<p>图3显示了基于Hibernate和Struts开发策略的体系结构图。</p>
<p><img src="http://column.ibeifeng.com/images/img/2008/10/hibernate-jdbc-jndi.jpg" alt="hibernate-jdbc-jndi" /></p>
<p>2、开发实践</p>
<p>下面结合开发实践，以在J2EE应用中非常普遍的用户登录过程为例，来说明上述体系结构是如何具体运用的。登录的流程非常清晰：用户从登录页面login.jsp输入登录信息，系统对登录信息进行验证，如果正确则成功登录，否则提示相应错误信息。</p>
<p>在开发过程中，使用Eclipse做为开发环境，同时加载了对Struts及Hibernate提供更好的控制和支持的第三方插件MyEclipse，Web服务器使用Tomcat，数据库选用了Mysql。</p>
<p>首先对Hibernate进行配置，只需要对系统自动生成的hibernate.cfg.xml进行修改，配置好数据库连接的各种参数以及定义数据映射文件。由于Hibernate所带的连接池主要用于测试，性能不是很好，可以通过JNDI将其修改为使用Tomcat的连接池。配置文件代码中关键的一部分如下：</p>
<p><code><br />
＜hibernate-configuration＞＜session-factory＞<br />
＜property name="connection.datasource"＞java:comp/env/jdbc/ sysdb ＜/property＞<br />
＜property name="dialect"＞net.sf.hibernate.dialect.MySQLDialect ＜/property＞<br />
＜mapping resource="User.hbm.xml"/＞<br />
＜/session-factory＞＜/hibernate-configuration＞<br />
</code></p>
<p>其次是生成持久化类，实际上，由于使用了Hibernate，持久化类以及对持久化对象的映射定义均可以借助于系统中的向导自动生成。持久化对象的映射定义放到了User.hbm.xml中。部分代码如下：</p>
<p><code><br />
＜class name=”User” table=”tUser”＞<br />
＜id name=”UserID” column=”User_ID” type=”java.lang.Integer”＞<br />
＜generator class=”native”＞＜/id＞<br />
＜property name=”UserName” column=”User_Name” type=”java.lang.String”＞<br />
…..＜/class＞<br />
</code></p>
<p>再次，使用Struts框架来实现逻辑控制，利用系统提供的向导可以生成视图及控制器。具体的配置在Struts-config.xml文件中。相关代码如下：</p>
<p><code><br />
＜form-beans＞<br />
＜form bean name=”LoginForm” type=”com.LoginForm”＞<br />
＜/form-beans＞<br />
＜action-mappings＞<br />
＜action attribute=”LoginForm” input=”Login.jsp” path=”/DoLogin”<br />
….<br />
＜/action＞<br />
＜/action-mappings＞<br />
</code></p>
<p>输入页面Login.jsp将用户填写的登录信息提交给控制器DoLogin进行处理，控制器调用由Hibernate定义好的持久化类User从数据库中读取相应信息进行验证。操作完成后将提示信息展示在表示层中。DoLogin中使用语句User u1=UserDAO.findbyname(username)得到User对象，然后就可以进行登录信息的验证。</p>
<p>从整个开发过程可以看出，程序员除了利用向导对有关的配置文件进行修改，只须编写少量代码即可完成登录模块的编码。开发完成的模块符合MVC框架的要求，质量比较高，易于修改和维护。</p>
<p><strong>总结与展望</strong></p>
<p>Hibernate是一个功能强大、高性能、非常流行的ORM工具，它推动了基于普通Java对象模型、用于映射底层数据结构的持久对象的开发，能够提供高级数据查询和检索服务。Struts是一个优秀的基于J2EE平台的MVC框架，它简化了WEB层的开发，实现了应用程序的显示逻辑和业务逻辑的分离。将这两种技术结合起来开发J2EE应用，能极大的提高开发效率，使系统具有更好的可维护性和可扩展性。这种开发策略正逐渐被众多程序员所接受，在实际开发中得到广泛的应用。</p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/20081014119.shtml/feed</wfw:commentRss>
		</item>
		<item>
		<title>Spring 2.X 中AOP的使用浅析</title>
		<link>http://column.ibeifeng.com/zhangweiliang/200809133.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/200809133.shtml#comments</comments>
		<pubDate>Sat, 13 Sep 2008 14:16:42 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[AOP]]></category>

		<category><![CDATA[spring]]></category>

		<guid isPermaLink="false">http://column.ibeifeng.com/?p=3</guid>
		<description><![CDATA[Spring 2.X和Spring 1.X相比，Spring 2.X使用AspectJ的语法来声明AOP，这使得它更“标准”，更灵活了。还是那句话，如果你不了解AspectJ并且打算使用Spring2.X的AspectJ式AOP，那就学学AspectJ吧]]></description>
			<content:encoded><![CDATA[<p>和Spring1.X相比，Spring2.X使用AspectJ的语法来声明AOP，这使得它更“标准”，更灵活了。还是那句话，如果你不了解AspectJ并且打算使用Spring2.X的AspectJ式AOP，那就学学AspectJ吧，这方面的书还是很多了。</p>
<p>Spring2.X下的切面有两种实现方式，一种是以Java文件定义切面（其只是普通的Java类），然后在配置文件中声明定义的切面；另一种是在Java类中引入和AOP相关的元数据（注释）。</p>
<p><span id="more-3"></span></p>
<p>先介绍第一种配置方式。需要指出的是，Spring2.X的beans名称空间和Spring1.X有所不同，它采用了Schema而不是DTD（也可采用DTD方式，具体的请参考文档）。还是引入毫无意义的日志切面，定义的切面类LogingAspect 如下：</p>
<p><code>public class LogingAspect {<br />
public void logMethod(JoinPoint jp){<br />
System.err.println(jp.getTarget().getClass());<br />
System.err.println(jp.getSignature().getName());<br />
}<br />
}<br />
</code></p>
<p>同时在配置文件中如下配置：</p>
<p><code><br />
＜ bean id = " logAspectTarget " class = " hibernatesample.service.util.LogingAspect " ＞<br />
＜/ bean ＞<br />
＜ aop:config ＞<br />
＜ aop:aspect id = " logAspect " ref = " logAspectTarget " ＞<br />
＜ aop:pointcut id = " businessService " expression = " execution(* hibernatesample.service.*.*(..)) " /＞<br />
＜ aop:after pointcut - ref = " businessService " method = " logMethod " /＞<br />
＜/ aop:aspect ＞<br />
＜/ aop:config ＞<br />
</code></p>
<p>对于上面的切面，切入点businessService是在配置文件中声明的，其表达式采用了 AspectJ的语法，LogingAspect 类中logMethod(JoinPoint jp)方法根据配置文件信息可知其是after通知，方法的JoinPoint参数不是必须的，它是来自于AspectJ的实用类，这里用它不过输出一些和连接点有关的信息。当然，在Spring2.X中，切入点和通知能更灵活的使用，我们可以如AspectJ一样传递参数给通知。如果需要在Service中引入事务功能，需要如下配置事务通知：</p>
<p><code><br />
＜ tx:advice id = " txAdvice " transaction - manager = " transactionManager " ＞<br />
＜ tx:attributes ＞<br />
＜ tx:method name = " get* " read - only = " true " /＞<br />
＜ tx:method name = " find* " read - only = " true " /＞<br />
＜ tx:method name = " * " /＞<br />
＜/ tx:attributes ＞<br />
＜/ tx:advice ＞<br />
＜ aop:config ＞<br />
＜ aop:pointcut id = " demoServiceMethods " expression = " execution(* hibernatesample.service.*.*(..)) " /＞<br />
＜ aop:advisor advice - ref = " txAdvice " pointcut - ref = " demoServiceMethods " /＞<br />
＜ aop:aspect id = " logAspect " ref = " logAspectTarget " ＞<br />
＜ aop:pointcut id = " businessService " expression = " execution(* hibernatesample.service.*.*(..)) " /＞<br />
＜ aop:after pointcut - ref = " businessService " method = " logMethod " /＞<br />
＜/ aop:aspect ＞<br />
＜/ aop:config ＞<br />
</code></p>
<p>完成上面的工作相当于完成了 Spring1.X 的 自动代理。 我们接下来需要定义的任何 Service Bean 都可以很纯粹很纯粹了：</p>
<p><code><br />
＜ bean id ="accountService" class ="hibernatesample.service.impl.AccountServiceImpl" ＞<br />
＜ property name ="accountDAO" ref ="accountDAO" ＞＜/ property ＞<br />
＜/ bean ＞<br />
</code></p>
<p>第二种实现 AOP 的方式和第一种相比，只是在 LogingAspect 中加入了注释，而省去了配置文件中和 LogingAspect 相关的配置。重新编写的 LogingAspect 如下：</p>
<p><code><br />
@Aspect</p>
<p>public class LogingAspect {<br />
@Pointcut( " execution(* hibernatesample.service.*.*(..)) " )<br />
public void businessService(){}<br />
@After( " businessService() " )<br />
public void logMethod(JoinPoint jp){<br />
System.err.println(jp.getTarget().getClass());<br />
System.err.println(jp.getSignature().getName());<br />
}<br />
}<br />
</code></p>
<p>而简化后的配置文件可以去除上面的如下和 logAspect 相关的配置信息：</p>
<p><code><br />
＜ aop:aspect id ="logAspect" ref ="logAspectTarget" ＞<br />
＜ aop:pointcut id ="businessService" expression ="execution(* hibernatesample.service.*.*(..))" /＞<br />
＜ aop:after pointcut-ref ="businessService" method ="logMethod" /＞<br />
＜/ aop:aspect ＞<br />
＜ bean id ="logAspectTarget" class ="hibernatesample.service.util.LogingAspect" ＞＜/ bean ＞<br />
</code></p>
<p>还没有完，为了使 Spring 应用 LogingAspect 的注释，需要在配置文件中添上</p>
<p><code><br />
＜aop:aspectj-autoproxy/＞<br />
</code></p>
<p>如果觉得事务的配置没有使用注释更简洁（我倒不会有这样的想法，毕竟在配置文件中声明的事务只是那么固定的几段，除非作用于类上的事务逻辑上很复杂），也可以使用Spring提供的事务注释作用于类文件上，这可是更细粒度的事务声明了。</p>
<p>坦率的说，由于时间有限，该文写的比较粗糙。对于Spring AOP有兴趣并有疑问的朋友，可以参考Spring的文档，它的文档做的还是不错的。</p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/200809133.shtml/feed</wfw:commentRss>
		</item>
		<item>
		<title>Spring总结实例之消息与事件</title>
		<link>http://column.ibeifeng.com/zhangweiliang/200809121.shtml</link>
		<comments>http://column.ibeifeng.com/zhangweiliang/200809121.shtml#comments</comments>
		<pubDate>Fri, 12 Sep 2008 08:13:50 +0000</pubDate>
		<dc:creator>张维亮</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[spring]]></category>

		<guid isPermaLink="false">http://www.ibeifeng.com/blog/?p=1</guid>
		<description><![CDATA[Spring的消息和事件实例]]></description>
			<content:encoded><![CDATA[<p>前几天看到网友总结的自学经验，觉得说得很好，引文：光看别人骑自行车很容易, 那么是不是看了几百遍别人怎么骑自行车你也就马上能骑着走了呢? 不摔跤是不可能学会的。</p>
<p>还有就是要经常总结：刚才说到会摔跤, 那么这时候就要总结遇到的问题, 这样下次再遇到就不会再去回忆了. 好记性不如烂笔头. 注释, 如果今天不写, 那么以后只会越来越忙, 以后再也没时间写注释了. If you doesn&#8217;t have time to do it today, then when do you have time to do it tomorrow?</p>
<p><span id="more-1"></span></p>
<p>所以今天就写个Spring的消息和事件实例。</p>
<p><code>1、JavaBean：User.java</p>
<p>package cn.xy.hw;</p>
<p>/** *//**<br />
* @author hanwei<br />
*<br />
*/<br />
public class User ...{<br />
private String name;<br />
private int age;</p>
<p>public int getAge() ...{<br />
return age;<br />
}<br />
public void setAge(int age) ...{<br />
this.age = age;<br />
}<br />
public String getName() ...{<br />
return name;<br />
}<br />
public void setName(String name) ...{<br />
this.name = name;<br />
}<br />
}<br />
</code></p>
<p>2、用于国际化的两个消息资源文件：<code>xiyou_en_US.properties和xiyou_zh_CN.properties</p>
<p>userlogin user ...{0} login at ...{1}</p>
<p>和</p>
<p>userlogin 使用者 ...{0} 于 ...{1}登入<br />
</code></p>
<p>自定义下雨的事件：<code>RainEvent.java</p>
<p>package cn.xy.hw;</p>
<p>import org.springframework.context.ApplicationEvent;</p>
<p>/** *//**<br />
* @author hanwei<br />
*<br />
*/<br />
public class RainEvent extends ApplicationEvent ...{</p>
<p>public RainEvent(Object arg0) ...{<br />
super(arg0);<br />
System.out.println("乌云密布、闪电、打雷，紧接着，下起了瓢泼大雨。");<br />
}<br />
}<br />
</code></p>
<p>下雨事件监听器：<code>RainListener.java</p>
<p>package cn.xy.hw;</p>
<p>import org.springframework.context.ApplicationEvent;<br />
import org.springframework.context.ApplicationListener;</p>
<p>/** *//**<br />
* @author hanwei<br />
*<br />
*/<br />
public class RainListener implements ApplicationListener ...{</p>
<p>/**//* (non-Javadoc)<br />
* @see org.springframework.context.ApplicationListener#onApplicationEvent(<br />
org.springframework.context.ApplicationEvent)<br />
*/<br />
public void onApplicationEvent(ApplicationEvent arg0) ...{</p>
<p>if(arg0 instanceof RainEvent)...{<br />
System.out.println("唐僧大喊："+arg0.getSource()+"赶快收衣服喽！");<br />
}<br />
}<br />
}<br />
</code></p>
<p>配置文件：<code>applicationContext.xml</p>
<p>＜!--sp--＞xml version="1.0" encoding="UTF-8"?＞<br />
＜beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]&#8220;＞</p>
<p>＜bean id=&#8221;user&#8221; class=&#8221;cn.xy.hw.User&#8221; abstract=&#8221;false&#8221;<br />
lazy-init=&#8221;default&#8221; autowire=&#8221;default&#8221; dependency-check=&#8221;default&#8221;＞<br />
＜property name=&#8221;name&#8221;＞<br />
＜value＞hanweivalue＞<br />
property＞<br />
＜property name=&#8221;age&#8221;＞<br />
＜value＞20value＞<br />
property＞<br />
bean＞</p>
<p>＜bean id=&#8221;messageSource&#8221;<br />
class=&#8221;org.springframework.context.support.ResourceBundleMessageSource&#8221;<br />
abstract=&#8221;false&#8221; lazy-init=&#8221;default&#8221; autowire=&#8221;default&#8221;<br />
dependency-check=&#8221;default&#8221;＞<br />
＜property name=&#8221;basename&#8221; value=&#8221;xiyou&#8221;＞property＞<br />
bean＞</p>
<p>＜bean id=&#8221;listener&#8221; class=&#8221;cn.xy.hw.RainListener&#8221; abstract=&#8221;false&#8221;<br />
lazy-init=&#8221;default&#8221; autowire=&#8221;default&#8221; dependency-check=&#8221;default&#8221;＞<br />
bean＞</p>
<p>beans＞<br />
</code></p>
<p>测试类：<code>MianTest.java</p>
<p>package cn.xy.hw;</p>
<p>import java.util.Calendar;<br />
import java.util.Locale;</p>
<p>import org.springframework.context.ApplicationContext;<br />
import org.springframework.context.support.ClassPathXmlApplicationContext;</p>
<p>/** *//**<br />
* @author hanwei<br />
*<br />
*/<br />
public class MianTest ...{</p>
<p>public static void main(String[] args) &#8230;{<br />
ApplicationContext context = new ClassPathXmlApplicationContext(&#8221;applicationContext.xml&#8221;);<br />
User user = (User)context.getBean(&#8221;user&#8221;);</p>
<p>Object[] obj=new Object[]&#8230;{user.getName(),Calendar.getInstance().getTime()};<br />
System.out.println(context.getMessage(&#8221;userlogin&#8221;,obj<br />
,&#8221;找不到指定模块！&#8221;,Locale.CHINA));<br />
System.out.println(context.getMessage(&#8221;userlogin&#8221;,obj<br />
,&#8221;找不到指定模块！&#8221;,Locale.US));</p>
<p>context.publishEvent(new RainEvent(&#8221;下雨了！&#8221;));<br />
}<br />
}<br />
</code></p>
<p>OK了，这是运行测试类的结果：</p>
<p>使用者 hanwei 于 07-8-26 下午6:14登入<br />
user hanwei login at 8/26/07 6:14 PM<br />
乌云密布、闪电、打雷，紧接着，下起了瓢泼大雨。<br />
唐僧大喊：下雨了！赶快收衣服喽！<br />
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).<br />
log4j:WARN Please initialize the log4j system properly.</p>
]]></content:encoded>
			<wfw:commentRss>http://column.ibeifeng.com/zhangweiliang/200809121.shtml/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
