<?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-Sung in Blog</title><link>http://www.blogjava.net/qq13367612/</link><description>&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font class="subhead" size=3&gt;&lt;b&gt;一些技术文章 &amp; 一些生活杂碎&lt;/b&gt;&lt;/font&gt;</description><language>zh-cn</language><lastBuildDate>Sun, 23 Nov 2008 14:28:35 GMT</lastBuildDate><pubDate>Sun, 23 Nov 2008 14:28:35 GMT</pubDate><ttl>60</ttl><item><title>逝者如斯夫</title><link>http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sat, 18 Feb 2006 17:17:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/31433.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/31433.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/31433.html</trackback:ping><description><![CDATA[<P>一年多的Java学习，半年多的java兼职，再到如今实习彻彻底底脱离java，想到现在的路由器交换机，再想到外包公司的web项目，有的时候发现，工作不比兴趣，逝者如斯夫，再回blogjava，踩个脚印。</P><img src ="http://www.blogjava.net/qq13367612/aggbug/31433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2006-02-19 01:17 <a href="http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>值得回顾和体味：Java语言的9个关键特性 </title><link>http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18135.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18135.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18135.html</trackback:ping><description><![CDATA[<STRONG>Java的白皮书为我们提出了Java语言的9个关键特性<BR><BR></STRONG>(1)Easy:Java的语法比C++的相对简单，另一个方面就是Java能使软件在很小的机器上运行，基础解释其和类库的支持的大小约为40kb，增加基本的标准库和线程支持的内存需要增加125kb。<BR><BR>(2)分布式:Java带有很强大的TCP/IP协议族的例程库，Java应用程序能够通过URL来穿过网络来访问远程对象，由于servlet机制的出现，使Java编程非常的高效，现在许多的大的web server都支持servlet。<BR><BR>(3)OO:面向对象设计是把重点放在对象及对象的接口上的一个编程技术.其面向对象和C++有很多不同，在与多重继承的处理及Java的原类模型。<BR><BR>(4)健壮特性:Java采取了一个安全指针模型，能减小重写内存和数据崩溃的可能型。<BR><BR>(5)安全:Java用来设计网路和分布系统，这带来了新的安全问题，Java可以用来构建防病毒和防攻击的System.事实证明Java在防毒这一方面做的比较好。<BR><BR>(6)中立体系结构:Java编译其生成体系结构中立的目标文件格式可以在很多处理器上执行，编译器产生的指令字节码(Javabytecode)实现此特性，此字节码可以在任何机器上解释执行。<BR><BR>(7)可移植性:Java中对基本数据结构类型的大小和算法都有严格的规定所以可移植性很好。<BR><BR>(8)多线程:Java处理多线程的过程很简单，Java把多线程实现交给底下操作系统或线程程序完成.所以多线程是Java作为服务器端开发语言的流行原因之一。<BR><BR>(9)Applet和servlet:能够在网页上执行的程序叫Applet，需要支持Java的浏览器很多，而applet支持动态的网页，这是很多其他语言所不能做到的。<BR><BR><STRONG>值得回顾和体味的概念</STRONG><BR><BR>1.OOP中唯一关系的是对象的接口是什么，就像计算机的销售商她不管电源内部结构是怎样的，他只关系能否给你提供电就行了，也就是只要知道can or not而不是how and why.所有的程序是由一定的属性和行为对象组成的，不同的对象的访问通过函数调用来完成，对象间所有的交流都是通过方法调用，通过对封装对象数据，很大限度上提高复用率。<BR><BR>2.OOP中最重要的思想是类，类是模板是蓝图，从类中构造一个对象，即创建了这个类的一个实例(instance)。<BR><BR>3.封装:就是把数据和行为结合起在一个包中)并对对象使用者隐藏数据的实现过程，一个对象中的数据叫他的实例字段(instance field)。<BR><BR>4.通过扩展一个类来获得一个新类叫继承(inheritance)，而所有的类都是由Object根超类扩展而得，根超类下文会做介绍。<BR><BR>5.对象的3个主要特性<BR><BR>behavior---说明这个对象能做什么。<BR><BR>state---当对象施加方法时对象的反映。<BR><BR>dentity---与其他相似行为对象的区分标志。<BR><BR>每个对象有唯一的indentity 而这3者之间相互影响。<BR><BR>6.类之间的关系: <BR><BR>use-a :依赖关系<BR><BR>has-a :聚合关系<BR><BR>is-a:继承关系--例:A类继承了B类，此时A类不仅有了B类的方法，还有其自己的方法.(个性存在于共性中)<BR><BR>7.构造对象使用构造器:构造器的提出，构造器是一种特殊的方法，构造对象并对其初始化。<BR><BR>例:Data类的构造器叫Data<BR><BR>new Data()---构造一个新对象，且初始化当前时间。<BR><BR>Data happyday=new Data()---把一个对象赋值给一个变量happyday，从而使该对象能够多次使用，此处要声明的使变量与对象变量二者是不同的。new返回的值是一个引用。<BR><BR>构造器特点:构造器可以有0个，一个或多个参数，构造器和类有相同的名字。一个类可以有多个构造器，构造器没有返回值，构造器总是和new运算符一起使用。<BR><BR>8.重载:当多个方法具有相同的名字而含有不同的参数时，便发生重载.编译器必须挑选出调用哪个方法。<BR><BR>9.包(package)Java允许把一个或多个类收集在一起成为一组，称作包，以便于组织任务，标准Java库分为许多包.java.lang java.util java，net等，包是分层次的所有的java包都在java和javax包层次内。<BR><BR>10.继承思想:允许在已经存在的类的基础上构建新的类，当你继承一个已经存在的类时，那么你就复用了这个类的方法和字段，同时你可以在新类中添加新的方法和字段。<BR><BR>11.扩展类:扩展类充分体现了is-a的继承关系. 形式为:class (子类) extends (基类)。<BR><BR>12.多态:在java中，对象变量是多态的.而java中不支持多重继承。<BR><BR>13.动态绑定:调用对象方法的机制。<BR><BR>(1)编译器检查对象声明的类型和方法名。<BR><BR>(2)编译器检查方法调用的参数类型。<BR><BR>(3)静态绑定:若方法类型为priavte static final 编译器会准确知道该调用哪个方法。 (4)当程序运行并且使用动态绑定来调用一个方法时，那么虚拟机必须调用x所指向的对象的实际类型相匹配的方法版本。<BR><BR>(5)动态绑定:是很重要的特性，它能使程序变得可扩展而不需要重编译已存代码。<BR><BR>14.final类:为防止他人从你的类上派生新类，此类是不可扩展的。<BR><BR>15.动态调用比静态调用花费的时间要长。<BR><BR>16.抽象类:规定一个或多个抽象方法的类本身必须定义为abstract。<BR><BR>例: 
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public abstract string getDescripition</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>17.Java中的每一个类都是从Object类扩展而来的。 <BR><BR>18.object类中的equal和toString方法。 <BR><BR>equal用于测试一个对象是否同另一个对象相等。 <BR><BR>toString返回一个代表该对象的字符串，几乎每一个类都会重载该方法，以便返回当前状态的正确表示。(toString 方法是一个很重要的方法) <BR><BR>19.通用编程:任何类类型的所有值都可以同object类性的变量来代替。 <BR><BR>20.数组列表:ArrayList动态数组列表，是一个类库，定义在java.uitl包中，可自动调节数组的大小。 <BR><BR>21.class类object类中的getclass方法返回ckass类型的一个实例，程序启动时包含在main方法的类会被加载，虚拟机要加载他需要的所有类，每一个加载的类都要加载它需要的类。<BR><BR>22.class类为编写可动态操纵java代码的程序提供了强大的功能反射，这项功能为JavaBeans特别有用，使用反射Java能支持VB程序员习惯使用的工具。<BR><BR>能够分析类能力的程序叫反射器，Java中提供此功能的包叫Java.lang.reflect反射机制十分强大。<BR><BR>1).在运行时分析类的能力。<BR><BR>2).在运行时探察类的对象。<BR><BR>3).实现通用数组操纵代码。<BR><BR>4).提供方法对象。<BR><BR>而此机制主要针对是工具者而不是应用及程序。 反射机制中的最重要的部分是允许你检查类的结构.用到的API有:<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>　　java.lang.reflect.Field //返回字段.  

　　java.reflect.Method //返回方法.  

　　java.lang.reflect.Constructor //返回参数.</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>方法指针:java没有方法指针，把一个方法的地址传给另一个方法，可以在后面调用它，而接口是更好的解决方案。 <BR>23.接口(Interface)说明类该做什么而不指定如何去做，一个类可以实现一个或多个interface。 <BR><BR>24.接口不是一个类，而是对符合接口要求的类的一套规范。<BR><BR>若实现一个接口需要2个步骤:<BR><BR>1.声明类需要实现的指定接口。<BR><BR>2.提供接口中的所有方法的定义。<BR><BR>声明一个类实现一个接口需要使用implements 关键字 class actionB implements Comparable 其actionb需要提供CompareTo方法，接口不是类，不能用new实例化一个接口。<BR><BR>25.一个类只有一个超类，但一个类能实现多个接口。Java中的一个重要接口：Cloneable<BR><BR>26.接口和回调.编程一个常用的模式是回调模式，在这种模式中你可以指定当一个特定时间发生时回调对象上的方法。<BR><BR>例:ActionListener 接口监听. <BR><BR>类似的API有:<BR><BR>java.swing.JOptionPane<BR><BR>java.swing.Timer<BR><BR>java.awt.Tookit<BR><BR>27.对象clone:clone方法是object一个保护方法，这意味着你的代码不能简单的调用它。<BR><BR>28.内部类:一个内部类的定义是定义在另一个内部的类。<BR><BR>原因是: <BR><BR>1.一个内部类的对象能够访问创建它的对象的实现，包括私有数据。<BR><BR>2.对于同一个包中的其他类来说，内部类能够隐藏起来。<BR><BR>3.匿名内部类可以很方便的定义回调。<BR><BR>4.使用内部类可以非常方便的编写事件驱动程序。<BR><BR>29.代理类(proxy): <BR><BR>1.指定接口要求所有代码<BR><BR>2.object类定义的所有的方法(toString equals)<BR><BR>30.数据类型:Java是强调类型的语言，每个变量都必须先申明它都类型，java中总共有8个基本类型。4种是整型，2种是浮点型，一种是字符型，被用于Unicode编码中的字符，布尔型。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18135.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 14:16 <a href="http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的Web框架对比 </title><link>http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 01:54:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18086.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18086.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18086.html</trackback:ping><description><![CDATA[Java的Web框架对比<BR>第一项是优点，第二项是缺点。<BR><BR>以下是各种web框架比较: (by Matt Raible &amp; 个人观点)<BR><BR>Struts： <BR>轻量级表现层框架，在适当的时间适当的出现；<BR>网上拥有很多信息和示例；学习曲线较缓，容易上手；<BR>HTML标签库是一个非常好的东西。<BR>&nbsp;<BR>静态Forms难以让人接受；不能够做单元测试；<BR>大量的邮件列表令人无法忍受<BR>&nbsp;<BR>Spring MVC <BR>生命周期由重写绑定，效验等确定；能够无缝隙地与各种表现层程序结合在一起，如JSP，XSL等；IC使得很容易测试。 <BR><BR>实际使用较少；有脱离轻量级框架向包容万象发展的趋势；需要在JSP中写入大量代码；过于灵活，反而不能得到通用的控制。<BR>&nbsp;<BR>WebWork <BR>结构简单，容易被扩展；标签库容易被自定义，并获得Velocity支持；拦截机制成熟可靠。 <BR>文档资源不够充分，示例很少；客户端效验很不成熟。<BR>&nbsp;<BR>Tapestry <BR>效率较高；HTML模板很合适美工；有良好的社区支持。 <BR>文档过于理论，而缺少实践；学习曲线陡峭，示例非常少； <BR><BR><BR>JSF <BR>J2EE规范；能够快速和容易地开发；丰富的导航框架。已有不少公司开始尝试该方面技术的应用； <BR>标签依赖JSP；技术不够成熟；实现资源不单纯。 <BR><BR><BR>大家可以发表一下意见<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18086.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 09:54 <a href="http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[强烈推荐]IBM公司面试题（附答案）——病狗问题</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 31 Oct 2005 06:12:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17544.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html#Feedback</comments><slash:comments>19</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17544.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17544.html</trackback:ping><description><![CDATA[<FONT size=2><FONT style="BACKGROUND-COLOR: #ffffff" color=#000000 size=3><STRONG>村子中有50个人，每人有一条狗。在这50条狗中有病狗(这种病不会传染)。于是人们就要找出病狗。每个人可以观察其他的49条狗，以判断它们是否生病，只有自己的狗不能看。观察后得到的结果不得交流，也不能通知病狗的主人。主人一旦推算出自己家的是病狗就要枪毙自己的狗，而且每个人只有权利枪毙自己的狗，没有权利打死其他人的狗。第一天，第二天都没有枪响。到了第三天传来一阵枪声，问有几条病狗，如何推算得出？</STRONG></FONT> <BR></FONT><BR>推论 见本文结束（字体为白色）！<BR><BR>另附：IBM社会招聘笔试题<BR><BR>1．一个粗细均匀的长直管子，两端开口，里面有4个白球和4个黑球，球的直径、两端开口的直径等于管子的内径，现在白球和黑球的排列是wwwwbbbb，要求不取出任何一个球，使得排列变为bbwwwwbb。 <BR><BR>2．一只蜗牛从井底爬到井口，每天白天蜗牛要睡觉，晚上才出来活动，一个晚上蜗牛可以向上爬3尺，但是白天睡觉的时候会往下滑2尺，井深10尺，问蜗牛几天可以爬出来？ <BR><BR>3．在一个平面上画1999条直线最多能将这一平面划分成多少个部分？ <BR><BR>4．在太平洋的一个小岛上生活着土人，他们不愿意被外人打扰，一天，一个探险家到了岛上，被土人抓住，土人的祭司告诉他，你临死前还可以有一个机会留下一句话，如果这句话是真的，你将被烧死，是假的，你将被五马分尸，可怜的探险家如何才能活下来？ <BR><BR>5．怎样种四棵树使得任意两棵树的距离相等。 <BR><BR>6．27个小运动员在参加完比赛后，口渴难耐，去小店买饮料，饮料店搞促销，凭三个空瓶可以再换一瓶，他们最少买多少瓶饮料才能保证一人一瓶？ <BR><BR>7．有一座山，山上有座庙，只有一条路可以从山上的庙到山脚，每周一早上8点，有一个聪明的小和尚去山下化缘，周二早上8点从山脚回山上的庙里，小和尚的上下山的速度是任意的，在每个往返中，他总是能在周一和周二的同一钟点到达山路上的同一点。例如，有一次他发现星期一的8点30和星期二的8点30他都到了山路靠山脚的3/4的地方，问这是为什么？ <BR><BR>8．有两根不均匀分布的香，每根香烧完的时间是一个小时，你能用什么方法来确定一段15分钟的时间？ <BR><BR>IBM面试题目 <BR><BR>1. Describe your greatest achievement in the past 4-5 years? <BR><BR>2. What are your short &amp; long term career objectives? What do you think is the most ideal job for you? <BR><BR>3. Why do you want to join IBM? What do you think you can contribute to IBM? <BR><BR><BR><FONT color=#ffffff size=2>推论： <BR><BR>　　A、假设有1条病狗，病狗的主人会看到其他狗都没有病，那么就知道自己的狗有病，所以第一天晚上就会有枪响。因为没有枪响，说明病狗数大于1。 <BR><BR>　　B、假设有2条病狗，病狗的主人会看到有1条病狗，因为第一天没有听到枪响，是病狗数大于1，所以病狗的主人会知道自己的狗是病狗，因而第二天会有枪响。既然第二天也每有枪响，说明病狗数大于2。 <BR><BR>　　由此推理，如果第三天枪响，则有3条病狗。 </FONT><BR><img src ="http://www.blogjava.net/qq13367612/aggbug/17544.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-31 14:12 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用微软试题膨胀你的思维</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 31 Oct 2005 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17543.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17543.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17543.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 10pt">“微软”招聘考试，除了专业知识，往往更看重一个人的聪明程度。据参加招聘考试的学生说，“微软”笔试题“古怪刁钻”，有些简直“异想天开”，令人匪夷所思。不过，“微软”似乎不为所动，年年出一些“怪题目”。几年下来，这些题目不再被认为怪，更逐渐成为许多大企业拿来考大学生的“经典”。 <BR><BR>　　快速估算题：测试你的快速反应能力 <BR><BR>　　这类题目有不同的版本。比如说，问你如何在不使用台秤的情况下，称出一架飞机的重量？估算一下长江里的水的质量？ 这是大的、宏观方面的问题；小的方面试题，还会问你这一类题目：“估算一下一个行进在小雨中的人5分钟内身上淋到的雨的质量。” <BR><BR>　　再看另一道快速估算题。去年3月，“微软”在复旦大学举行了一场校园招聘，第一轮笔试题目中就有一道令应试者困惑的“东方明珠”题：“请估算一下东方明珠电视塔的重量”。当时真是难倒了众多学子。据上海微软软件有限公司负责招聘考试的软件研发部经理蔡先生说：“其实，有同学认为这个题目刁钻古怪也在情理之中，毕竟这种类型的试题在国内还不太多，所以大家都有些摸不着头脑。”他表示：“就东方明珠这道题来说，它和一般的谜语或智力题还是有区别的。这类题为快速估算题，主要考的是快速估算的能力，这是开发软件必备的能力之一。重要的是对考生得出这个结果的过程也就是方法的考察。” <BR><BR>　　蔡经理说了一种比较合理的答法。他首先在纸上画出东方明珠的草图，然后快速估算支架和各个支柱的高度，以及球的半径，算出各部分体积，然后进行各部分密度运算，最后相加得出一个结果。蔡经理认为：“像这样的题目，包括一些推理题，考的都是人解决问题的能力，不是哪道题你记住了答案就可以了的。” <BR><BR>　　开放性思维题：考验你的逻辑推理能力 <BR><BR>　　去年应聘“微软”的大学生，考试时遇到了一道“古怪”的智力题。题目是：一楼到十楼的每层电梯门口都放着一颗钻石，钻石大小不一。你乘坐电梯从一楼到十楼，每层楼电梯门都会打开一次，只能拿一次钻石，问：怎样才能拿到最大的一颗﹖应试者不知该怎么办。考试后主考官并没有明确公布答案，但他对其中一位女士的做法表示赞赏。那位女士的回答是：选择前五层楼都不拿，观察各层钻石的大小，做到心中有数。后面五个楼层再选择，选择大小接近前五层楼出现过最大钻石大小的钻石。这位女士后来在互联网上谈体会时说： “我至今也不知道这道题的准确答案，也许本来就没有准确答案，就是考一下你的思路。” <BR><BR>　　这种说法得到微软中国有限公司人力资源部招聘经理尹冬梅的肯定。尹冬梅表示，以上这些都是属于笔试的题目。“微软”希望招到更多开放型思维的人，因此很多题目其实都没有一个标准答案。比如说，你认为北京有多少公共汽车站﹖你可以随便给出答案，5家或者5000家，但你得有理由。 <BR><BR>　　基础数学题：考核你的数学基础是否扎实 <BR><BR>　　一般来说，“微软”招聘只收理工科的学生，要求有扎实的数学基础，因此基础数学题量是最大的。有些题目初看很复杂，思路一打开，其实相当简单，所以有的大学生戏称是“小学三年级的题目”。比如如下这道题：有8颗弹子球，其中1颗是“缺陷球”，也就是它比其他的球都重。你怎样使用天平只通过两次称量就能够找到这个球﹖ <BR><BR>　　主考官在解释这道题的答案时指出：要想解决这个问题，必须充分利用天平可以量出两边弹子球重量是否相等这一事实，即无论什么时候只要两边重量相等，就表明“缺陷球”不在这些弹子球中。第一次称重，在天平的两边各任意放3颗球。这时候会有两种可能的结果。如果天平两边的重量是平衡的，就可以确定所称量的6个球当中没有“缺陷球”。因此第二次称重时只要称量剩下的2颗球，较重的 1颗就是“缺陷球”。如果天平的一边比另一边重。那么可以确定 “缺陷球”肯定位于天平较重一边的3颗球当中。第二次称量时只要从这3个球当中任意拿出2个进行称量。如果两边平衡，则3颗球中剩下的没有参加称量的1颗球就是“缺陷球”，如果两边不平衡，则较重的一边就是“缺陷球”。 <BR><BR>　　从上面这道题和其他大量的数学题内容来看，“微软”招聘考试重视数学基础是由它工作性质决定的。“微软”招聘主管戴维·普里查得先生认为，软件开发需要极严格的数学模式。对数学没有兴趣的人难以胜任最起码的程序员工作。他表示，对于笔试题目，如果有大学生对他说：“这真是一个愚蠢的问题”，这并不是错误的回答。 <BR><BR>　　智力测试题：看你能否创造性地思考 <BR><BR>　　“请用一笔画出四根直线，将图上9个点全部联结。” 答案：画一根与水平成45度角的斜线到某一点，然后以此点作为直角三角形两个直角边的交点，向任何一边作直角三角形，就可以把9个点联结起来。 <BR><BR>　　这类智力题考的是在懂得数学原理基础上的创造性思考。比如下面这个问题就需要动动脑子了。题目是：一个正三角形的每个角上各有一只蚂蚁。每只蚂蚁开始朝另一只蚂蚁做直线运动，目标角是随机选择。蚂蚁互不相撞的概率是多少﹖ <BR><BR>　　答案应当是：只有两种方法可以让蚂蚁避免相撞：或者它们全部顺时针运动，或者它们全部逆时针运动。否则，肯定会撞到一起。选择一只蚂蚁，一旦它确定了自己是逆时针或者是顺时针运动，其他的蚂蚁就必须做相同方向的运动才能避免相撞。由于蚂蚁运动的方向是随机选择的，那么第二只蚂蚁有1／2的概率选择与第一只蚂蚁相同的运动方向。第三只蚂蚁也有1／2的概率选择与第一只相同的方向。因此，蚂蚁避免撞到一起的概率是1／4。 <BR><BR>　　还有一些问题虽然不需要数学知识，但要懂得基本的科学常识。比如问你：太阳总是从东边升起吗﹖答案应该是否定的。因为在北极点，根本就没有“东方”这个方向。每一个方向都是南。在6个月的“极昼”时间，太阳从南边升起从南边落下。另外在南极也一样，每一个方向都是北方。 <BR><BR>　　诸如此类的问题我们还可以举出一些，其中包括拿“微软”领袖比尔·盖茨“开涮”的题目。此题是“比尔·盖茨的办公桌下有五只带锁的抽屉，分别贴着财富、兴趣、幸福、荣誉和成功五个标签；盖茨总是只带一把钥匙，请问是哪一把？” 答案：兴趣。 <BR><BR>　　所有这些都出于这样的考虑： “微软”想找“聪明的、有开放思维的人。”负责“微软”招聘工作的戴维·普里查得先生强调：其实我们并不是想得到“正确”的答案，我们是想看看应聘者是否能找到最好的解题方案，看他们是否能够创造性地思考问题。 “我们的目的是选人，而不是难倒学生。”</SPAN><img src ="http://www.blogjava.net/qq13367612/aggbug/17543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-31 14:07 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一些腾讯笔试题目 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 31 Oct 2005 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17524.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html#Feedback</comments><slash:comments>17</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17524.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17524.html</trackback:ping><description><![CDATA[1、请定义一个宏，比较两个数a、b的大小，不能使用大于、小于、if语句<BR>2、如何输出源文件的标题和目前执行行的行数<BR>3、两个数相乘，小数点后位数没有限制，请写一个高精度算法<BR>4、写一个病毒<BR>5、有A、B、C、D四个人，要在夜里过一座桥。他们通过这座桥分别需要耗时1、2、5、10分钟，只有一支手电，并且同时最多只能两个人一起过桥。请问，如何安排，能够在17分钟内这四个人都过桥？<BR><BR>2005年腾讯招聘<BR>选择题(60) <BR>  c/c++ os linux 方面的基础知识 c的Sizeof函数有好几个! <BR>程序填空(40) <BR>1.(20) 4空x5 <BR>  不使用额外空间,将 A,B两链表的元素交叉归并 <BR>2.(20) 4空x5 <BR>MFC  将树序列化 转存在数组或 链表中!<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/17524.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-31 11:50 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VO、TO、PO、POJO、BO</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 03:39:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17149.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17149.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17149.html</trackback:ping><description><![CDATA[VO：实际上很模糊，通常指ValueObject和ViewObject <BR><BR>ViewObject：UI需要的对象，如Struts的FormBean <BR><BR>Value Object：ValueObject和Transfer Object的总称。 <BR><BR>TO： Transfer Object，数据传输物件，在应用程序不同tie之间传输的对象 <BR><BR>PO：Persistent Object（持久化物件），基本上就是Entity了 <BR><BR>POJO：plain ordinary java object ，POJO是这样一个对象，它是一个普通的Java对象，它不同于EJB这样的带有繁重的容器控制功能的对象，它也不是JDO那种被Enhanced过的对象，也不是类似Hibernate那样被动态的byte code generation过<BR><BR>BO：Business Object。可以参照<A HREF="/qq13367612/archive/2005/10/21/16250.aspx"><STRONG>这里</STRONG></A>的讨论。<img src ="http://www.blogjava.net/qq13367612/aggbug/17149.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 11:39 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于VO、PO的理解</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17147.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17147.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17147.html</trackback:ping><description><![CDATA[O/R Mapping 是 Object Relational Mapping（对象关系映射）的缩写。通俗点讲，就是将对象与关系数据库绑定，用对象来表示关系数据。在O/R Mapping的世界里，有两个基本的也是重要的东东需要了解，即VO，PO。<BR>　　VO，值对象(Value Object)，PO，持久对象(Persisent Object)，它们是由一组属性和属性的get和set方法组成。从结构上看，它们并没有什么不同的地方。但从其意义和本质上来看是完全不同的。<BR><BR>１．VO是用new关键字创建，由GC回收的。 <BR>　　PO则是向数据库中添加新数据时创建，删除数据库中数据时削除的。并且它只能存活在一个数据库连接中，断开连接即被销毁。 <BR><BR>２．VO是值对象，精确点讲它是业务对象，是存活在业务层的，是业务逻辑使用的，它存活的目的就是为数据提供一个生存的地方。 <BR>　　PO则是有状态的，每个属性代表其当前的状态。它是物理数据的对象表示。使用它，可以使我们的程序与物理数据解耦，并且可以简化对象数据与物理数据之间的转换。<BR><BR>３．VO的属性是根据当前业务的不同而不同的，也就是说，它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。 <BR>　　PO的属性是跟数据库表的字段一一对应的。<BR><BR>PO对象需要实现序列化接口。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/17147.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 11:30 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>热点招聘信息汇总!!</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 09:32:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16899.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16899.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16899.html</trackback:ping><description><![CDATA[一,投行,咨询,四大: <BR><BR>Morgan Stanley <BR>http://www.morganstanley.com/careers/recruiting/apply_now.html <BR><BR>UBS <BR>http://graduates.ubs.com/career/cms/index.html <BR><BR>瑞士信贷第一波士顿 <BR>http://www.csfb.com/standout <BR>Deadline: 24th.Oct <BR><BR>高盛 <BR>www.gs.com/careers/apply_online <BR><BR>美林 <BR>http://www.ml.com/index.asp?id=7695_8199_8205_14187_14215_16358 <BR><BR>中国国际金融有限公司公司(CICC) <BR>http://www.cicc.com.cn/CICCHRWEB/Resume_CPage.asp?op=Jobs_1&amp;typ=2 <BR><BR>麦肯锡 <BR>http://www.mckinsey.com/locations/ChinaSimplified/index.asp <BR>或http://campus.chinahr.com/pages/mckinsey/ <BR>截止日期：11.4下午5点 <BR><BR>BCG <BR>邮寄地址: <BR>Anna Zhang <BR>Boston Consulting (Shanghai) Co., Ltd Beijing Branch <BR>朝阳区建国路乙118号京汇大厦902室，邮编：100022 <BR><BR>贝恩 <BR>www.joinbainchina.com <BR>由于网络问题,请同学们于10月21号到11月9号期间提交简历 <BR><BR>Monitor <BR>Mailing Address: 北京市朝阳区霄云路38号现代汽车大厦808室 100027 <BR>Deadline for candidates in Beijing: November 3, 2005 <BR>Deadline for candidates in Shanghai: November 10, 2005 <BR><BR><BR>科尔尼 <BR>Application Methods (Choose ONE ONLY) <BR>Submitting your application on the campus presentation day; <BR>or Sending in your application electronically to BA-recruiting.bj@atkearney.com;or Mailing in your application to A.T. Kearney Beijing Office at the belowaddress: <BR>科尔尼企业咨询有限公司北京办事处 <BR>人力资源部 <BR>北京市建国门外大街1号 <BR>中国国际贸易中心西办公楼504室 <BR>邮政编码 100004 <BR>Application Deadline:November 9, 2005 <BR><BR>Booz Allen Hamilton <BR>具体申请方式：Download the application form from : www.bah.com.cn , <BR>"people and career" and go to "application" and then download the <BR>application form and send applicaton form, Chinese and English CV as well as cover letter to china_hr@bah.com <BR><BR>PWC <BR>http://www.pwccn.com/home/eng/graduate_opportunities.html <BR>Deadline: 10.28 <BR><BR>KPMG <BR>http:// www.kpmg.com.hk <BR><BR>德勤 <BR>http://career.deloitte.com.cn/RMS0102_Requisition.aspx?Loc=BJ# <BR>Deadline: 11.6 <BR><BR>安永 <BR>www.eygraduate.com <BR>Deadline: November 6, 2005 <BR>答疑邮箱: graduate.bj@cn.ey.com <BR><BR>二,IT,通信,电子电器类: <BR><BR>微软(MS China, MSN China, MSRA, MSATC) <BR>http://campus.chinahr.com/pages/microsoft/ <BR><BR>IBM <BR>http://campus.chinahr.com/pages/ibm/ <BR>截至日期:10.31 <BR><BR>英特尔(Intel) <BR>http://campus.chinahr.com/pages/intel/jobs.asp <BR>(宣讲会前一周截至) <BR><BR>百度 <BR>http://www.baidu.com/hr/index.html <BR><BR>英飞凌 <BR>http://campus.chinahr.com/pages/Infineon/Programs.asp <BR><BR>锐捷网络 <BR>http://campus.chinahr.com/pages/ruijie/index.asp <BR><BR>卓越网 <BR>http://joyo.com/campus <BR><BR>华为集团 <BR>http://career.huawei.com/career/zh/campus.do <BR>截至日期:11月5日 <BR><BR>中兴 <BR>http://job.zte.com.cn/zte.graduate.web/index.aspx <BR><BR>掌中万维科技有限公司 <BR>http://www1.zhaopin.com/Publish/Company/Newpalm/index.htm <BR><BR>威盛电子（VIA）电子股份有限公司 <BR>http://campus.chinahr.com/viatech/ <BR>截止日期：10.24 <BR><BR>中电赛龙 <BR>http://campus.chinahr.com/pages/cecw/ <BR><BR>Google中国 <BR>http://www.google.com/intl/zh-CN/jobs/ <BR><BR>网易互动娱乐2006高校巡回招聘会 <BR>http://hr2006.163.com/ 十月中旬上线 <BR><BR>松下电器（中国）有限公司 <BR>http://recruit.panasonic.cn/campusjob.asp <BR><BR>创维集团 <BR>http://skyworth.com/job/default.asp?action=1&amp;title <BR><BR>ericsson <BR>http://www.pincn.com/clientpages/2005/ericsson/ <BR>截至日期:11月28日 <BR><BR>联想集团 <BR>http://campus.chinahr.com/pages/lenovo <BR><BR>腾讯 <BR>http://hr.tencent.com/ <BR><BR>ABB电气校园招聘 <BR>http://companyads.51job.com/companyads/shanghai/bj/abb050824/index.htm <BR><BR>台积电 <BR>http://tsmcsh.51job.com/ <BR><BR>海信Hisense <BR>http://campus.chinahr.com/pages/hisense/ <BR><BR>TCL校园招聘 <BR>http://www.tcl.com/job/hr/school/ <BR><BR>中奥集团OTIS(奥的斯电梯)2006年校园招聘网申： <BR>http://otis.51job.com/page/6-4.htm <BR><BR>施奈德电气 <BR>http://career.schneider-electric.com.cn/hr0001.nsf/pwelcome?open <BR><BR>2006 GE China Campus <BR>http://hr.ge-china.com <BR><BR>美的集团 <BR>http://campus.chinahr.com/pages/midea/ <BR><BR>西门子 <BR>http://www.careers.siemens.com.cn/graduates <BR><BR>三,其他类: <BR><BR>玛氏中国Mars <BR>http://campus.chinahr.com/pages/mars <BR>截至日期:11.1 <BR><BR>强生(中国)有限公司 <BR>http://campus.chinahr.com/pages/jnj/ <BR>截至日期:11月15日 <BR><BR>高露洁 <BR>http://campus.chinahr.com/pages/colgate <BR><BR>BP <BR>http://www1.zhaopin.com/Publish/Market/bp/index.htm <BR><BR>联合利华 <BR>http://www.unilever.com.cn/ourcompany/careers/trainee/ <BR>截止日期10.31 <BR><BR>博世（Bosch） <BR>http://campus.chinahr.com/pages/bosch/jobs.asp <BR><BR>壳牌（Shell） <BR>http://student.zhaopin.com/extend/shell/grad_apply.htm <BR><BR>艾默生网络能源有限公司2006年校园招聘 <BR>登陆网站http://www.emersonnetwork.com.cn <BR><BR>金地（集团）股份有限公司2006年校园招聘 <BR>公司网址：http://www.gemdale.com <BR>简历投递地址：2006gemdale@gemdale.com <BR><BR>马士基2006校园招聘 <BR>http://campus.chinahr.com/pages/maersk/ <BR><BR>广州本田 <BR>http://campus.chinahr.com/pages/gzhonda/ <BR><BR>中国广东核电集团有限公司 <BR>http://campus.chinahr.com/pages/cgnpc/ <BR><BR>欧莱雅 <BR>http://campus.chinahr.com/pages/loreal/ <BR><BR>龙湖房地产 <BR>http://pages.chinahr.com/2005/cd/longhu_1009/ <BR><BR>世联房地产 <BR>http://companyadc.51job.com/companyads/shanghai/shzh/shilian051011/index.htm <BR><BR>ICI <BR>http://campus.chinahr.com/pages/ici/ <BR><BR>炬力 <BR>http://www.actions.com.cn/campus.aspx <BR><BR>招商证券 <BR>http://news.newone.com.cn/adv/zhaopin/zhaopin.html <BR>泰鼎多媒体技术（上海）有限公司 <BR>http://www.trident.com.cn/careers.asp <BR><BR>优识 <BR>http://www.ucamp.com.cn/About_us/usys_job.jsp <BR><BR>农夫山泉 <BR>http://www.51job.com/hot/show_list.php?id=4670965 <BR><BR>青岛啤酒 <BR>http://campus.chinahr.com/pages/tsingtao/index.asp <BR><BR>环球 <BR>http://career.globalmarket.com/jobs.aspx <BR>截至日期:10月26日 <BR><BR>=================================================<BR><BR>有借鉴价值，值得保留<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/16899.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 17:32 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2005各大公司薪水民间版本</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 02:57:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16848.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16848.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16848.html</trackback:ping><description><![CDATA[<FONT face=Georgia>我们在学校都混得差不多，可是因为去了不同的城市、进了不同的公司，薪水有了差异，似乎也有了“贵贱”之分。<BR><BR>　　下面是我的老同学们从各大公司传来的薪水民间版本。<BR><BR>　　日本SONY 索尼 &nbsp;1万／月，仅要研究生。<BR><BR>　　韩国三星电子中国总部&nbsp;25万／年<BR><BR>　　法国索姆软件&nbsp;20万／年，赴欧工作。<BR><BR>　　美国Cisco 思科 &nbsp;1.5万／月，仅要研究生。<BR><BR>　　美国INTEL 英特尔 &nbsp;1.3万／月<BR><BR>　　美国IBM&nbsp;5000左右／月<BR><BR>　　德国西门子&nbsp;8000／月<BR><BR>　　西藏联通&nbsp;8年100万，不过高原反应太艰苦。<BR><BR>　　青海联通&nbsp;5000-7000／月，可以只签一年。<BR><BR>　　深圳联通&nbsp;3000-4000／月&nbsp;<BR><BR>　　深圳记忆科技&nbsp;本科10-15万／年，硕士15-20万／年。<BR><BR>　　上海网道信息技术 除房食补外 7000-8000／月<BR><BR>　　中国电信&nbsp;7-10万／年，另有房交补。<BR><BR>　　AVANT 6-8万／年&nbsp;<BR><BR>　　东方通信&nbsp;6-8万／年，仅要研究生。<BR><BR>　　北京联想&nbsp;6-7.5万／年&nbsp;<BR><BR>　　深大通信&nbsp;6万／年<BR><BR>　　福州康顺光通讯&nbsp;5-10万／年<BR><BR>　　美国FIBERON公司&nbsp;本科6万／年，硕士10万／年。<BR><BR>　　深圳华为&nbsp;5480／月<BR><BR>　　青岛海尔&nbsp;5-6万／年<BR><BR>　　ut斯达康&nbsp;本科5000／月，研究生6000-8000／月。<BR><BR>　　MOTOROLA(苏州)&nbsp;5000／月<BR><BR>　　深圳中兴&nbsp;5000／月<BR><BR>　　北京华虹NEC&nbsp;4000-5000／月<BR><BR>　　深圳黎明网络&nbsp;本4000／月，硕6500／月，博8500／月。&nbsp;<BR><BR>　　广东北电&nbsp;4000-5000／月<BR><BR>　　上海贝尔&nbsp;4万／年&nbsp;＋&nbsp;1.5万(年终奖)<BR><BR>　　成都迈普&nbsp;5万／年(研发)<BR><BR>　　中国建设银行新疆分行&nbsp;4.5万／年<BR><BR>　　北大方正奥德&nbsp;3500-4000／月<BR><BR>　　青岛海信&nbsp;4万／年，两年后6-7万／年。<BR><BR>　　中华通信&nbsp;本3000元／月，硕4000元／月，博5000元／月&nbsp;。<BR><BR>　　烟台东方电子&nbsp;4000／月，年终奖1.5万，婚后分房。<BR><BR>　　广东金鹏&nbsp;3000-4000／月<BR><BR>　　深圳康佳&nbsp;3000-4000／月&nbsp;<BR><BR>　　TCL&nbsp;3800／月 深圳 ，2000-3000／月 惠州 。<BR><BR>　　深圳奥林巴斯 日 &nbsp;3000／月，另1200／月房补。<BR><BR>　　武汉邮科院&nbsp;本科 3000／月，研究生5000／月。<BR><BR>　　福建实达&nbsp;3万／年<BR><BR>　　上海计算机研究所&nbsp;2500-4000／月<BR><BR>　　深圳飞通&nbsp;本科3000／月，研究生5000／月。<BR><BR>　　中讯&nbsp;3000／月<BR><BR>　　北京网通&nbsp;3000／月<BR><BR>　　宁波雅戈尔&nbsp;3000／月<BR><BR>　　广州华南资讯&nbsp;本科2500／月，硕士6万／年。<BR><BR>　　宁波波导&nbsp;2.5-5万／年<BR><BR>　　青岛朗讯 合资 &nbsp;2.5-3万／年&nbsp;（研究生 大連朗訊 1万/月）<BR><BR>　　上海电真空研究所&nbsp;2000／月，转正3000／月。<BR><BR>　　和记黄埔&nbsp;本2000-4000／月，硕4000-6000／月。<BR><BR>　　中国电信广州研究中心&nbsp;2-10万／年<BR><BR>　　西安电子工程研究所&nbsp;2000-3000／月<BR><BR>　　南京自动化研究所&nbsp;2000／月<BR><BR>　　AMD&nbsp;本科2000／月<BR><BR>　　厦门厦新&nbsp;1800-1900／月<BR><BR>　　广东美的&nbsp;1800-2400／月<BR><BR>　　苏州明基电脑 台资 1600-2000／月<BR><BR>　　四川长虹&nbsp;1500-2000／月<BR><BR>　　深圳天马微电子&nbsp;1500／月，转正3-4万／年。<BR><BR>　　合肥英图微电子 合资 &nbsp;1500／月，2000／月(转正)。<BR><BR>　　长城电脑&nbsp;1500／月<BR><BR>　　成都国腾通信&nbsp;1200／月，研发人员2000-4000／月。<BR><BR>　　成都TOP&nbsp;1500／月<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;三菱，三星研发中心 5000/月<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 西门子制造部 3200/月<BR></FONT><img src ="http://www.blogjava.net/qq13367612/aggbug/16848.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 10:57 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正确优雅地解决用户退出问题——JSP及Struts解决方案</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Tue, 25 Oct 2005 02:00:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16710.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16710.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16710.html</trackback:ping><description><![CDATA[大部分Web应用不会包含象银行账户或信用卡资料那样机密的信息，但一旦涉及到敏感数据，我们就需要提供一类密码保护机制。举例来说，一个工厂中工人通过Web访问他们的时间安排、进入他们的训练课程以及查看他们的薪金等等。此时应用SSL(Secure Socket Layer)有点杀鸡用牛刀的感觉，但不可否认，我们又必须为这些应用提供密码保护，否则，工人（也就是Web应用的使用者）可以窥探到工厂中其他雇员的私人机密信息。<BR>&nbsp;&nbsp;&nbsp;&nbsp;与上述情形相似的还有位处图书馆、医院等公共场所的计算机。在这些地方，许多用户共同使用几台计算机，此时保护用户的个人数据就显得至关重要。设计良好编写优秀的应用对用户专业知识的要求少之又少。<BR>&nbsp;&nbsp;&nbsp;&nbsp;我们来看一下现实世界中一个完美的Web应用是如何表现的：一个用户通过浏览器访问一个页面。Web应用展现一个登陆页面要求用户输入有效的验证信息。用户输入了用户名和密码。此时我们假设用户提供的身份验证信息是正确的，经过了验证过程，Web应用允许用户浏览他有权访问的区域。用户想退出时，点击退出按钮，Web应用要求用户确认他是否则真的需要退出，如果用户确定退出，Session结束，Web应用重新定位到登陆页面。用户可以放心的离开而不用担心他的信息会泄露。另一个用户坐到了同一台电脑前，他点击后退按钮，Web应用不应该出现上一个用户访问过的任何一个页面。事实上，Web应用在第二个用户提供正确的验证信息之前应当一直停留在登陆页面上。<BR>&nbsp;&nbsp;&nbsp;&nbsp;通过示例程序，文章向您阐述了如何在一个Web应用中实现这一功能。<BR><BR><BR><BR><B>JSP示例</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了更为有效地阐述实现方案，本文将从展示一个示例应用logoutSampleJSP1中碰到的问题开始。这个示例代表了许多没有正确解决退出过程的Web应用。logoutSampleJSP1包含了下述jsp页面：login.jsp, home.jsp, secure1.jsp, secure2.jsp, logout.jsp, loginAction.jsp, and logoutAction.jsp。其中页面home.jsp, secure1.jsp, secure2.jsp, 和logout.jsp是不允许未经认证的用户访问的，也就是说，这些页面包含了重要信息，在用户登陆之前或者退出之后都不应该出现在浏览器中。login.jsp包含了用于用户输入用户名和密码的form。logout.jsp页包含了要求用户确认是否退出的form。loginAction.jsp和logoutAction.jsp作为控制器分别包含了登陆和退出代码。<BR>&nbsp;&nbsp;&nbsp;&nbsp;第二个示例应用logoutSampleJSP2展示了如何解决示例logoutSampleJSP1中的问题。然而，第二个应用自身也是有疑问的。在特定的情况下，退出问题还是会出现。<BR>&nbsp;&nbsp;&nbsp;&nbsp;第三个示例应用logoutSampleJSP3在第二个示例上进行了改进，比较完善地解决了退出问题。<BR>&nbsp;&nbsp;&nbsp;&nbsp;最后一个示例logoutSampleStruts展示了Struts如何优美地解决登陆问题。<BR>&nbsp;&nbsp;&nbsp;&nbsp;注意：本文所附示例在最新版本的Microsoft Internet Explorer (IE), Netscape Navigator, Mozilla, FireFox和Avant浏览器上测试通过。<BR><BR><BR><B>Login action</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Brian Pontarelli的经典文章<A href="http://www.javaworld.com/javaworld/jw-07-2004/jw-0726-security.html" target=_new>《J2EE Security: Container Versus Custom》</A>讨论了不同的J2EE认证途径。文章同时指出，HTTP协议和基于form的认证并未提供处理用户退出的机制。因此，解决途径便是引入自定义的安全实现机制。<BR>&nbsp;&nbsp;&nbsp;&nbsp;自定义的安全认证机制普遍采用的方法是从form中获得用户输入的认证信息，然后到诸如LDAP (lightweight directory access protocol)或关系数据库的安全域中进行认证。如果用户提供的认证信息是有效的，登陆动作往HttpSession对象中注入某个对象。HttpSession存在着注入的对象则表示用户已经登陆。为了方便读者理解，本文所附的示例只往HttpSession中写入一个用户名以表明用户已经登陆。清单1是从loginAction.jsp页面中节选的一段代码以此阐述登陆动作：<BR><BR><PRE class=overflow title="pre code"><BR>Listing 1 <BR>//...<BR>//initialize RequestDispatcher object; set forward to home page by default<BR>RequestDispatcher rd = request.getRequestDispatcher("home.jsp");<BR><BR>//Prepare connection and statement<BR>rs = stmt.executeQuery("select password from USER where userName = &amp;#39;" + userName + "&amp;#39;");<BR>if (rs.next()) { //Query only returns 1 record in the result set; only 1 <BR>&nbsp;&nbsp;password per userName which is also the primary key<BR>&nbsp;&nbsp; if (rs.getString("password").equals(password)) { //If valid password<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.setAttribute("User", userName); //Saves username string in the session object<BR>&nbsp;&nbsp; }<BR>&nbsp;&nbsp; else { //Password does not match, i.e., invalid user password<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setAttribute("Error", "Invalid password."); <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; }<BR>} //No record in the result set, i.e., invalid username<BR>&nbsp;&nbsp; else {<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setAttribute("Error", "Invalid user name.");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; }<BR>}<BR><BR>//As a controller, loginAction.jsp finally either forwards to "login.jsp" or "home.jsp"<BR>rd.forward(request, response);<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;本文所附示例均以关系型数据库作为安全域，但本文所阐述的观点对任何类型的安全域都是适用的。<BR><BR><B>Logout action </B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;退出动作就包含了简单的删除用户名以及对用户的HttpSession对象调用invalidate()方法。清单2是从loginoutAction.jsp页面中节选的一段代码以此阐述退出动作：<BR><BR><PRE class=overflow title="pre code"><BR>Listing 2 <BR>//...<BR>session.removeAttribute("User");<BR>session.invalidate();<BR>//...<BR><BR></PRE><BR><BR><B>阻止未经认证访问受保护的JSP页面</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从form中获取用户提交的认证信息并经过验证后，登陆动作简单地往 HttpSession对象中写入一个用户名，退出动作则做相反的工作，它从用户的HttpSession对象中删除用户名并调用invalidate()方法销毁HttpSession。为了使登陆和退出动作真正发挥作用，所有受保护的JSP页面都应该首先验证HttpSession中是否包含了用户名以确认当前用户是否已经登陆。如果HttpSession中包含了用户名，也就是说用户已经登陆，Web应用则将剩余的JSP页发送给浏览器，否则，JSP页将跳转到登陆页login.jsp。页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp均包含清单3中的代码段：<BR><BR><PRE class=overflow title="pre code"><BR>Listing 3 <BR>//...<BR>String userName = (String) session.getAttribute("User");<BR>if (null == userName) {<BR>&nbsp;&nbsp; request.setAttribute("Error", "Session has ended.&nbsp;&nbsp;Please login.");<BR>&nbsp;&nbsp; RequestDispatcher rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; rd.forward(request, response);<BR>}<BR>//...<BR>//Allow the rest of the dynamic content in this JSP to be served to the browser<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这个代码段中，程序从HttpSession中减缩username字符串。如果字符串为空，Web应用则自动中止执行当前页面并跳转到登陆页，同时给出Session has ended. Please log in.的提示；如果不为空，Web应用则继续执行，也就是把剩余的页面提供给用户。<BR><BR><B>运行logoutSampleJSP1</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行logoutSampleJSP1将会出现如下几种情形：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;如果用户没有登陆，Web应用将会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行，也就是说，假如用户在浏览器地址栏中直接敲入受保护JSP页的地址试图访问，Web应用将自动跳转到登陆页并提示Session has ended.Please log in.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;同样的，当一个用户已经退出，Web应用也会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;用户退出后，如果点击浏览器上的后退按钮，Web应用将不能正确保护受保护的页面——在Session销毁后（用户退出）受保护的JSP页重新在浏览器中显示出来。然而，如果用户点击返回页面上的任何链接，Web应用将会跳转到登陆页面并提示Session has ended.Please log in.<BR><BR><B>阻止浏览器缓存</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上述问题的根源在于大部分浏览器都有一个后退按钮。当点击后退按钮时，默认情况下浏览器不是从Web服务器上重新获取页面，而是从浏览器缓存中载入页面。基于Java的Web应用并未限制这一功能，在基于PHP、ASP和.NET的Web应用中也同样存在这一问题。<BR>&nbsp;&nbsp;&nbsp;&nbsp;在用户点击后退按钮后，浏览器到服务器再从服务器到浏览器这样通常意思上的HTTP回路并没有建立，仅仅只是用户，浏览器和缓存进行了交互。所以，即使包含了清单3上的代码来保护JSP页面，当点击后退按钮时，这些代码是不会执行的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;缓存的好坏，真是仁者见仁智者见智。缓存的确提供了一些便利，但通常只在使用静态的HTML页面或基于图形或影响的页面你才能感受到。而另一方面，Web应用通常是基于数据的，数据通常是频繁更改的。与从缓存中读取并显示过期的数据相比，提供最新的数据才是更重要的！<BR>&nbsp;&nbsp;&nbsp;&nbsp;幸运的是，HTTP头信息“Expires”和“Cache-Control”为应用程序服务器提供了一个控制浏览器和代理服务器上缓存的机制。HTTP头信息Expires告诉代理服务器它的缓存页面何时将过期。HTTP1.1规范中新定义的头信息Cache-Control可以通知浏览器不缓存任何页面。当点击后退按钮时，浏览器重新访问服务器已获取页面。如下是使用Cache-Control的基本方法：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;no-cache:强制缓存从服务器上获取新的页面<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;no-store: 在任何环境下缓存不保存任何页面<BR>&nbsp;&nbsp;&nbsp;&nbsp;HTTP1.0规范中的Pragma:no-cache等同于HTTP1.1规范中的Cache-Control:no-cache，同样可以包含在头信息中。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;通过使用HTTP头信息的cache控制，第二个示例应用logoutSampleJSP2解决了logoutSampleJSP1的问题。logoutSampleJSP2与logoutSampleJSP1不同表现在如下代码段中，这一代码段加入进所有受保护的页面中:<BR><BR><PRE class=overflow title="pre code"><BR>//...<BR>response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server<BR>response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance<BR>response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"<BR>response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility<BR>String userName = (String) session.getAttribute("User");<BR>if (null == userName) {<BR>&nbsp;&nbsp; request.setAttribute("Error", "Session has ended.&nbsp;&nbsp;Please login.");<BR>&nbsp;&nbsp; RequestDispatcher rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; rd.forward(request, response);<BR>}<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过设置头信息和检查HttpSession中的用户名确保了浏览器不缓存页面，同时，如果用户未登陆，受保护的JSP页面将不会发送到浏览器，取而代之的将是登陆页面login.jsp。<BR><BR><B>运行logoutSampleJSP2</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行logoutSampleJSP2后将回看到如下结果：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;当用户退出后试图点击后退按钮，浏览器并不会显示受保护的页面，它只会现实登陆页login.jsp同时给出提示信息Session has ended. Please log in.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;然而，当按了后退按钮返回的页是处理用户提交数据的页面时，IE和Avant浏览器将弹出如下信息提示：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 警告：页面已过期……（你肯定见过）<BR>&nbsp;&nbsp;&nbsp;&nbsp;选择刷新后前一个JSP页面将重新显示在浏览器中。很显然，这不是我们所想看到的因为它违背了logout动作的目的。发生这一现象时，很可能是一个恶意用户在尝试获取其他用户的数据。然而，这个问题仅仅出现在后退按钮对应的是一个处理POST请求的页面。<BR><BR><B>记录最后登陆时间 </B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上述问题之所以出现是因为浏览器将其缓存中的数据重新提交了。这本文的例子中，数据包含了用户名和密码。无论是否给出安全警告信息，浏览器此时起到了负面作用。<BR>&nbsp;&nbsp;&nbsp;&nbsp;为了解决logoutSampleJSP2中出现的问题，logoutSampleJSP3的login.jsp在包含username和password的基础上还包含了一个称作lastLogon的隐藏表单域，此表单域动态的用一个long型值初始化。这个long型值是调用System.currentTimeMillis()获取到的自1970年1月1日以来的毫秒数。当login.jsp中的form提交时，loginAction.jsp首先将隐藏域中的值与用户数据库中的值进行比较。只有当lastLogon表单域中的值大于数据库中的值时Web应用才认为这是个有效的登陆。<BR>&nbsp;&nbsp;&nbsp;&nbsp;为了验证登陆，数据库中lastLogon字段必须以表单中的lastLogon值进行更新。上例中，当浏览器重复提交数据时，表单中的lastLogon值不比数据库中的lastLogon值大，因此，loginAction转到login.jsp页面，并提示Session has ended.Please log in.清单5是loginAction中节选的代码段：<BR><BR><PRE class=overflow title="pre code"><BR>清单5<BR>//...<BR>RequestDispatcher rd = request.getRequestDispatcher("home.jsp"); //Forward to homepage by default<BR>//...<BR>if (rs.getString("password").equals(password)) { //If valid password<BR>&nbsp;&nbsp; long lastLogonDB = rs.getLong("lastLogon");<BR>&nbsp;&nbsp; if (lastLogonForm &gt; lastLogonDB) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.setAttribute("User", userName); //Saves username string in the session object<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stmt.executeUpdate("update USER set lastLogon= " + lastLogonForm + " where userName = &amp;#39;" + userName + "&amp;#39;");<BR>&nbsp;&nbsp; }<BR>&nbsp;&nbsp; else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setAttribute("Error", "Session has ended.&nbsp;&nbsp;Please login.");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rd = request.getRequestDispatcher("login.jsp");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR>else&nbsp;&nbsp; { //Password does not match, i.e., invalid user password<BR>&nbsp;&nbsp; request.setAttribute("Error", "Invalid password.");<BR>&nbsp;&nbsp; rd = request.getRequestDispatcher("login.jsp");&nbsp;&nbsp; <BR>}<BR>//...<BR>rd.forward(request, response);<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了实现上述方法，你必须记录每个用户的最后登陆时间。对于采用关系型数据库安全域来说，这点可以可以通过在某个表中加上lastLogin字段轻松实现。LDAP以及其他的安全域需要稍微动下脑筋，但很显然是可以实现的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;表示最后登陆时间的方法有很多。示例logoutSampleJSP3利用了自1970年1月1日以来的毫秒数。这个方法在许多人在不同浏览器中用一个用户账号登陆时也是可行的。<BR><BR><B>运行logoutSampleJSP3</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行示例logoutSampleJSP3将展示如何正确处理退出问题。一旦用户退出，点击浏览器上的后退按钮在任何情况下都不会是受保护的页面在浏览器上显示出来。这个示例展示了如何正确处理退出问题而不需要额外的培训。<BR>&nbsp;&nbsp;&nbsp;&nbsp;为了使代码更简练有效，一些冗余的代码可以剔除掉。一种途径就是把清单4中的代码写到一个单独的JSP页中，通过标签&lt;jsp:include&gt;其他页面也可以引用。<BR><BR><B>Struts框架下的退出实现</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;与直接使用JSP或JSP/servlets相比，另一个可选的方案是使用Struts。为一个基于Struts的Web应用添加一个处理退出问题的框架可以优雅地不费气力的实现。这部分归功于Struts是采用MVC设计模式的因此将模型和视图清晰的分开。另外，Java是一个面向对象的语言，其支持继承，可以比JSP中的脚本更为容易地实现代码重用。在Struts中，清单4中的代码可以从JSP页面中移植到Action类的execute()方法中。<BR>&nbsp;&nbsp;&nbsp;&nbsp;此外，我们还可以定义一个继承Struts Action类的基本类，其execute()方法中包含了清单4中的代码。通过使用类继承机制，其他类可以继承基本类中的通用逻辑来设置HTTP头信息以及检索HttpSession对象中的username字符串。这个基本类是一个抽象类并定义了一个抽象方法executeAction()。所有继承自基类的子类都应实现exectuteAction()方法而不是覆盖它。清单6是基类的部分代码：<BR><BR><PRE class=overflow title="pre code"><BR>清单6<BR>&nbsp;&nbsp;public abstract class BaseAction extends Action {<BR>&nbsp;&nbsp; public ActionForward execute(ActionMapping mapping, ActionForm form,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest request, HttpServletResponse response) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws IOException, ServletException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!this.userIsLoggedIn(request)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActionErrors errors = new ActionErrors();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errors.add("error", new ActionError("logon.sessionEnded"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.saveErrors(request, errors);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mapping.findForward("sessionEnded");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return executeAction(mapping, form, request, response);<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; protected abstract ActionForward executeAction(ActionMapping mapping,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActionForm form, HttpServletRequest request, HttpServletResponse response) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws IOException, ServletException;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp; private boolean userIsLoggedIn(HttpServletRequest request) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (request.getSession().getAttribute("User") == null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;<BR>&nbsp;&nbsp; }<BR>}<BR><BR></PRE><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;清单6中的代码与清单4中的很相像，仅仅只是用ActionMapping findForward替代了RequestDispatcher forward。清单6中，如果在HttpSession中未找到username字符串，ActionMapping对象将找到名为sessionEnded的forward元素并跳转到对应的path。如果找到了，子类将执行其实现了executeAction()方法的业务逻辑。因此，在配置文件struts-web.xml中为所有子类声明个一名为sessionEnded的forward元素是必须的。清单7以secure1 action阐明了这样一个声明：<BR><BR><PRE class=overflow title="pre code">清单7<BR>&lt;action path="/secure1" <BR>&nbsp;&nbsp; type="com.kevinhle.logoutSampleStruts.Secure1Action"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp; scope="request"&gt;<BR>&nbsp;&nbsp; &lt;forward name="success" path="/WEB-INF/jsps/secure1.jsp"/&gt;<BR>&nbsp;&nbsp; &lt;forward name="sessionEnded" path="/login.jsp"/&gt;<BR>&lt;/action&gt;<BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;继承自BaseAction类的子类Secure1Action实现了executeAction()方法而不是覆盖它。Secure1Action类不执行任何退出代码，如清单8：<BR><BR><PRE class=overflow title="pre code"><BR>public class Secure1Action extends BaseAction {<BR>&nbsp;&nbsp; public ActionForward executeAction(ActionMapping mapping, ActionForm form,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest request, HttpServletResponse response)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws IOException, ServletException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession session = request.getSession();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (mapping.findForward("success"));<BR>&nbsp;&nbsp; }<BR>}<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;只需要定义一个基类而不需要额外的代码工作，上述解决方案是优雅而有效的。不管怎样，将通用的行为方法写成一个继承StrutsAction的基类是许多Struts项目的共同经验，值得推荐。<BR><BR><B>局限性</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;上述解决方案对JSP或基于Struts的Web应用都是非常简单而实用的，但它还是有某些局限。在我看来，这些局限并不是至关紧要的。<I>（局限性未翻译，请参见原文）</I><BR><BR><B>结论</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;本文阐述了解决退出问题的方案，尽管方案简单的令人惊讶，但却在所有情况下都能有效地工作。无论是对JSP还是Struts，所要做的不过是写一段不超过50行的代码以及一个记录用户最后登陆时间的方法。在Web应用中混合使用这些方案能够使拥护的私人数据不致泄露，同时，也能增加用户的经验。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/16710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-25 10:00 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 性能优化技巧集锦 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 14:16:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16659.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16659.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16659.html</trackback:ping><description><![CDATA[<P>1.用new关键词创建类的实例时，构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口，我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。 <BR><BR>在使用设计模式（Design Pattern）的场合，如果用Factory模式创建对象，则改用clone()方法创建新的对象实例非常简单。例如，下面是Factory模式的一个典型实现： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public static Credit getNewCredit() {
return new Credit();
} 
 
改进后的代码使用clone()方法，如下所示：

private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() {
return (Credit) BaseCredit.clone();
}</PRE></TD></TR></TBODY></TABLE></CENTER><BR><BR>面的思路对于数组处理同样很有用。 <BR><BR>2. 使用非阻塞I/O <BR><BR>版本较低的JDK不支持非阻塞I/O API。为避免I/O阻塞，一些应用采用了创建大量线程的办法（在较好的情况下，会使用一个缓冲池）。这种技术可以在许多必须支持并发I/O流的应用中见到，如Web服务器、报价和拍卖应用等。然而，创建Java线程需要相当可观的开销。 <BR><BR>3. 慎用异常 <BR><BR>异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地（Native）方法，fillInStackTrace()方法检查堆栈，收集调用跟踪信息。只要有异常被抛出，VM就必须调整调用堆栈，因为在处理过程中创建了一个新的对象。异常只能用于错误处理，不应该用来控制程序流程。 <BR><BR>4. 不要重复初始化变量 <BR><BR>默认情况下，调用类的构造函数时， Java会把变量初始化成确定的值：所有的对象被设置成null，整数变量（byte、short、int、long）设置成0，float和double变量设置成0.0，逻辑值设置成false。当一个类从另一个类派生时，这一点尤其应该注意，因为用new关键词创建一个对象时，构造函数链中的所有构造函数都会被自动调用。 <BR><BR>5. 尽量指定类的final修饰符 <BR><BR>带有final修饰符的类是不可派生的。在Java核心API中，有许多应用final的例子，例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外，如果指定一个类为final，则该类所有的方法都是final。Java编译器会寻找机会内联（inline）所有的final方法（这和具体的编译器实现有关）。此举能够使性能平均提高50%。 <BR><BR>6. 尽量使用局部变量 <BR><BR>调用方法时传递的参数以及在调用中创建的临时变量都保存在栈（Stack）中，速度较快。其他变量，如静态变量、实例变量等，都在堆（Heap）中创建，速度较慢。另外，依赖于具体的编译器/JVM，局部变量还可能得到进一步优化。 <BR><BR>7. 乘法和除法 <BR><BR>考虑下面的代码： <BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>for (val = 0; val &lt; 100000; val +=5) { alterX = val * 8; myResult = val * 2; } 
 
用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码：

for (val = 0; val &lt; 100000; val += 5) { alterX = val &lt;&lt; 3; myResult = val &lt;&lt; 1; }</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>修改后的代码不再做乘以8的操作，而是改用等价的左移3位操作，每左移1位相当于乘以2。相应地，右移1位操作相当于除以2。值得一提的是，虽然移位操作速度快，但可能使代码比较难于理解，所以最好加上一些注释。 </P>
<P></P>
<P>前面介绍的改善性能技巧适合于大多数Java应用，接下来要讨论的问题适合于使用JSP、EJB或JDBC的应用。 <BR><BR>1. 使用缓冲标记 <BR><BR>一些应用服务器加入了面向JSP的缓冲标记功能。例如，BEA的WebLogic Server从6.0版本开始支持这个功能，Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断，也能够缓冲整个页面。当JSP页面执行时，如果目标片断已经在缓冲之中，则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求，并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说，这个功能极其有用。对于这类应用，页面级缓冲能够保存页面执行的结果，供后继请求使用。 <BR><BR>对于代码逻辑复杂的页面，利用缓冲标记提高性能的效果比较明显；反之，效果可能略逊一筹。 <BR><BR>2. 始终通过会话Bean访问实体Bean <BR><BR>直接访问实体Bean不利于性能。当客户程序远程访问实体Bean时，每一个get方法都是一个远程调用。访问实体Bean的会话Bean是本地的，能够把所有数据组织成一个结构，然后返回它的值。 <BR><BR>用会话Bean封装对实体Bean的访问能够改进事务管理，因为会话Bean只有在到达事务边界时才会提交。每一个对get方法的直接调用产生一个事务，容器将在每一个实体Bean的事务之后执行一个“装入-读取”操作。一些时候，使用实体Bean会导致程序性能不佳。如果实体Bean的唯一用途就是提取和更新数据，改成在会话Bean之内利用JDBC访问数据库可以得到更好的性能。 <BR><BR>3. 选择合适的引用机制 <BR><BR>在典型的JSP应用系统中，页头、页脚部分往往被抽取出来，然后根据需要引入页头、页脚。当前，在JSP页面中引入外部资源的方法主要有两种：include指令，以及include动作。 <BR><BR>include指令：例如 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;%@ include file="copyright.html" %&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>该指令在编译时引入指定的资源。在编译之前，带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定，比运行时才确定资源更高效。 <BR><BR>include动作：例如 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;jsp:include page="copyright.jsp" /&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>该动作引入指定页面执行后生成的结果。由于它在运行时完成，因此对输出结果的控制更加灵活。但时，只有当被引用的内容频繁地改变时，或者在对主页面的请求没有出现之前，被引用的页面无法确定时，使用include动作才合算。 <BR><BR>4. 在部署描述器中设置只读属性 <BR><BR>实体Bean的部署描述器允许把所有get方法设置成“只读”。当某个事务单元的工作只包含执行读取操作的方法时，设置只读属性有利于提高性能，因为容器不必再执行存储操作。 <BR><BR><BR><BR>5. 缓冲对EJB Home的访问 <BR><BR>EJB Home接口通过JNDI名称查找获得。这个操作需要相当可观的开销。JNDI查找最好放入Servlet的init()方法里面。如果应用中多处频繁地出现EJB访问，最好创建一个EJBHomeCache类。EJBHomeCache类一般应该作为singleton实现。 <BR><BR>6. 为EJB实现本地接口 <BR><BR>本地接口是EJB 2.0规范新增的内容，它使得Bean能够避免远程调用的开销。请考虑下面的代码。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>PayBeanHome home = (PayBeanHome)
javax.rmi.PortableRemoteObject.narrow
(ctx.lookup ("PayBeanHome"), PayBeanHome.class); 
PayBean bean = (PayBean)
javax.rmi.PortableRemoteObject.narrow 
(home.create(), PayBean.class);</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>第一个语句表示我们要寻找Bean的Home接口。这个查找通过JNDI进行，它是一个RMI调用。然后，我们定位远程对象，返回代理引用，这也是一个RMI调用。第二个语句示范了如何创建一个实例，涉及了创建IIOP请求并在网络上传输请求的stub程序，它也是一个RMI调用。要实现本地接口，我们必须作如下修改： <BR><BR>方法不能再抛出java.rmi.RemoteException异常，包括从RemoteException派生的异常，比如TransactionRequiredException、TransactionRolledBackException和NoSuchObjectException。EJB提供了等价的本地异常，如TransactionRequiredLocalException、TransactionRolledBackLocalException和NoSuchObjectLocalException。 <BR><BR>所有数据和返回值都通过引用的方式传递，而不是传递值。本地接口必须在EJB部署的机器上使用。简而言之，客户程序和提供服务的组件必须在同一个JVM上运行。如果Bean实现了本地接口，则其引用不可串行化。 </P>
<P></P>
<P></P>
<P>7. 生成主键 <BR><BR>在EJB之内生成主键有许多途径，下面分析了几种常见的办法以及它们的特点。利用数据库内建的标识机制（SQL Server的IDENTITY或Oracle的SEQUENCE）。这种方法的缺点是EJB可移植性差。由实体Bean自己计算主键值（比如做增量操作）。它的缺点是要求事务可串行化，而且速度也较慢。 <BR><BR>利用NTP之类的时钟服务。这要求有面向特定平台的本地代码，从而把Bean固定到了特定的OS之上。另外，它还导致了这样一种可能，即在多CPU的服务器上，同一个毫秒之内生成了两个主键。借鉴Microsoft的思路，在Bean中创建一个GUID。然而，如果不求助于JNI，Java不能确定网卡的MAC地址；如果使用JNI，则程序就要依赖于特定的OS。 <BR><BR>还有其他几种办法，但这些办法同样都有各自的局限。似乎只有一个答案比较理想：结合运用RMI和JNDI。先通过RMI注册把RMI远程对象绑定到JNDI树。客户程序通过JNDI进行查找。下面是一个例子： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public class keyGenerator
extends UnicastRemoteObject implements
Remote { private static long KeyValue = System.currentTimeMillis();
public static synchronized long getKey()
throws RemoteException { return KeyValue++; }</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>8. 及时清除不再需要的会话 <BR><BR>为了清除不再活动的会话，许多应用服务器都有默认的会话超时时间，一般为30分钟。当应用服务器需要保存更多会话时，如果内存容量不足，操作系统会把部分内存数据转移到磁盘，应用服务器也可能根据“最近最频繁使用”（Most Recently Used）算法把部分不活跃的会话转储到磁盘，甚至可能抛出“内存不足”异常。在大规模系统中，串行化会话的代价是很昂贵的。当会话不再需要时，应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。 <BR><BR>9. 在JSP页面中关闭无用的会话 <BR><BR>对于那些无需跟踪会话状态的页面，关闭自动创建的会话可以节省一些资源。使用如下page指令： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;%@ page session="false"%&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>10. Servlet与内存使用 <BR><BR>许多开发者随意地把大量信息保存到用户会话之中。一些时候，保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看，典型的症状是用户感到系统周期性地变慢，却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间，它的表现是内存占用不正常地大起大落。解决这类内存问题主要有二种办法。第一种办法是，在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样，只要实现valueUnbound()方法，就可以显式地释放Bean使用的资源。 <BR><BR>另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外，也可以用编程的方式调用会话的setMaxInactiveInterval()方法，该方法用来设定在作废会话之前，Servlet容器允许的客户请求的最大间隔时间，以秒计算。 <BR><BR>11. HTTP Keep-Alive <BR><BR>Keep-Alive功能使客户端到服务器端的连接持续有效，当出现对服务器的后继请求时，Keep-Alive功能避免了建立或者重新建立连接。市场上的大部分Web服务器，包括iPlanet、IIS和Apache，都支持HTTP Keep-Alive。对于提供静态内容的网站来说，这个功能通常很有用。但是，对于负担较重的网站来说，这里存在另外一个问题：虽然为客户保留打开的连接有一定的好处，但它同样影响了性能，因为在处理暂停期间，本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时，Keep-Alive功能对资源利用的影响尤其突出。 <BR><BR>12. JDBC与Unicode <BR><BR>想必你已经了解一些使用JDBC时提高性能的措施，比如利用连接池、正确地选择存储过程和直接执行的SQL、从结果集删除多余的列、预先编译SQL语句，等等。除了这些显而易见的选择之外，另一个提高性能的好选择可能就是把所有的字符数据都保存为Unicode（代码页13488）。Java以Unicode形式处理所有数据，因此，数据库驱动程序不必再执行转换过程。但应该记住：如果采用这种方式，数据库会变得更大，因为每个Unicode字符需要2个字节存储空间。另外，如果有其他非Unicode的程序访问数据库，性能问题仍旧会出现，因为这时数据库驱动程序仍旧必须执行转换过程。 <BR></P>
<P>13. JDBC与I/O <BR><BR>如果应用程序需要访问一个规模很大的数据集，则应当考虑使用块提取方式。默认情况下，JDBC每次提取32行数据。举例来说，假设我们要遍历一个5000行的记录集，JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512，则调用数据库的次数将减少到10次。在一些情形下这种技术无效。例如，如果使用可滚动的记录集，或者在查询中指定了FOR UPDATE，则块操作方式不再有效。 <BR><BR>14. 内存数据库 <BR><BR>许多应用需要以用户为单位在会话对象中保存相当数量的数据，典型的应用如购物篮和目录等。由于这类数据可以按照行/列的形式组织，因此，许多应用创建了庞大的Vector或HashMap。在会话中保存这类数据极大地限制了应用的可伸缩性，因为服务器拥有的内存至少必须达到每个会话占用的内存数量乘以并发用户最大数量，它不仅使服务器价格昂贵，而且垃圾收集的时间间隔也可能延长到难以忍受的程度。 <BR><BR>一些人把购物篮/目录功能转移到数据库层，在一定程度上提高了可伸缩性。然而，把这部分功能放到数据库层也存在问题，且问题的根源与大多数关系数据库系统的体系结构有关。对于关系数据库来说，运行时的重要原则之一是确保所有的写入操作稳定、可靠，因而，所有的性能问题都与物理上把数据写入磁盘的能力有关。关系数据库力图减少I/O操作，特别是对于读操作，但实现该目标的主要途径只是执行一套实现缓冲机制的复杂算法，而这正是数据库层第一号性能瓶颈通常总是CPU的主要原因。 <BR><BR>一种替代传统关系数据库的方案是，使用在内存中运行的数据库（In-memory Database），例如TimesTen。内存数据库的出发点是允许数据临时地写入，但这些数据不必永久地保存到磁盘上，所有的操作都在内存中进行。这样，内存数据库不需要复杂的算法来减少I/O操作，而且可以采用比较简单的加锁机制，因而速度很快。 <BR></P>
<P></P>
<P>这一篇中介绍的内容适合于图形用户界面的应用（Applet和普通应用），要用到AWT或Swing。 1. 用JAR压缩类文件 <BR><BR>Java档案文件（JAR文件）是根据JavaBean标准压缩的文件，是发布JavaBean组件的主要方式和推荐方式。JAR档案有助于减少文件体积，缩短下载时间。例如，它有助于Applet提高启动速度。一个JAR文件可以包含一个或者多个相关的Bean以及支持文件，比如图形、声音、HTML和其他资源。要在HTML/JSP文件中指定JAR文件，只需在Applet标记中加入ARCHIVE = "name.jar"声明。 <BR><BR>2. 提示Applet装入进程 <BR><BR>你是否看到过使用Applet的网站，注意到在应该运行Applet的地方出现了一个占位符？当Applet的下载时间较长时，会发生什么事情？最大的可能就是用户掉头离去。在这种情况下，显示一个Applet正在下载的信息无疑有助于鼓励用户继续等待。下面我们来看看一种具体的实现方法。首先创建一个很小的Applet，该Applet负责在后台下载正式的Applet： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>import java.applet.Applet;
import java.applet.AppletStub;
import java.awt.Label;
import java.awt.Graphics;
import java.awt.GridLayout;
public class PreLoader extends Applet implements Runnable, AppletStub {
String largeAppletName;
Label label;
public void init() {
// 要求装载的正式Applet
largeAppletName = getParameter("applet");// “请稍等”提示信息
label = new Label("请稍等..." + largeAppletName);
add(label);
}
public void run(){
try 
{
// 获得待装载Applet的类
Class largeAppletClass = Class.forName(largeAppletName);
// 创建待装载Applet的实例
Applet largeApplet = (Applet)largeAppletClass.newInstance();
// 设置该Applet的Stub程序
largeApplet.setStub(this);
// 取消“请稍等”信息
remove(label);
// 设置布局
setLayout(new GridLayout(1, 0));
add(largeApplet);
// 显示正式的Applet
largeApplet.init();
largeApplet.start();
}
catch (Exception ex)
{
// 显示错误信息
label.setText("不能装入指定的Applet");
}
// 刷新屏幕
validate();
}
public void appletResize(int width, int height)
{
// 把appletResize调用从stub程序传递到Applet
resize(width, height);
}
}</PRE></TD></TR></TBODY></TABLE><BR><BR></CENTER>
<P><BR><BR>编译后的代码小于2K，下载速度很快。代码中有几个地方值得注意。首先，PreLoader实现了AppletStub接口。一般地，Applet从调用者判断自己的codebase。在本例中，我们必须调用setStub()告诉Applet到哪里提取这个信息。另一个值得注意的地方是，AppletStub接口包含许多和Applet类一样的方法，但appletResize()方法除外。这里我们把对appletResize()方法的调用传递给了resize()方法。 <BR><BR>3. 在画出图形之前预先装入它 <BR><BR>ImageObserver接口可用来接收图形装入的提示信息。ImageObserver接口只有一个方法imageUpdate()，能够用一次repaint()操作在屏幕上画出图形。下面提供了一个例子。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
if ((flags &amp; ALLBITS) !=0 {
repaint();
}
else if (flags &amp; (ERROR |ABORT )) != 0) {
error = true;
// 文件没有找到，考虑显示一个占位符
repaint();
}
return (flags &amp; (ALLBITS | ERROR| ABORT)) == 0;
}</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>当图形信息可用时，imageUpdate()方法被调用。如果需要进一步更新，该方法返回true；如果所需信息已经得到，该方法返回false。 <BR><BR>4. 覆盖update方法 <BR><BR>update()方法的默认动作是清除屏幕，然后调用paint()方法。如果使用默认的update()方法，频繁使用图形的应用可能出现显示闪烁现象。要避免在paint()调用之前的屏幕清除操作，只需按照如下方式覆盖update()方法： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public void update(Graphics g) {
paint(g);
}</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>更理想的方案是：覆盖update()，只重画屏幕上发生变化的区域，如下所示: <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public void update(Graphics g) {
g.clipRect(x, y, w, h);
paint(g);
}</PRE></TD></TR></TBODY></TABLE><BR></CENTER>
<P><BR></P>
<P></P>
<P>5. 延迟重画操作 <BR><BR>对于图形用户界面的应用来说，性能低下的主要原因往往可以归结为重画屏幕的效率低下。当用户改变窗口大小或者滚动一个窗口时，这一点通常可以很明显地观察到。改变窗口大小或者滚动屏幕之类的操作导致重画屏幕事件大量地、快速地生成，甚至超过了相关代码的执行速度。对付这个问题最好的办法是忽略所有“迟到”的事件。 <BR><BR>建议在这里引入一个数毫秒的时差，即如果我们立即接收到了另一个重画事件，可以停止处理当前事件转而处理最后一个收到的重画事件；否则，我们继续进行当前的重画过程。 <BR><BR>如果事件要启动一项耗时的工作，分离出一个工作线程是一种较好的处理方式；否则，一些部件可能被“冻结”，因为每次只能处理一个事件。下面提供了一个事件处理的简单例子，但经过扩展后它可以用来控制工作线程。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public static void runOnce(String id, final long milliseconds) {
synchronized(e_queue) {
// e_queue: 所有事件的集合
if (!e_queue.containsKey(id)) {
e_queue.put(token, new LastOne());
}
}
final LastOne lastOne = (LastOne) e_queue.get(token);
final long time = System.currentTimeMillis(); // 获得当前时间
lastOne.time = time;
(new Thread() {public void run() {
if (milliseconds &gt; 0) {
try {Thread.sleep(milliseconds);} // 暂停线程
catch (Exception ex) {}
}
synchronized(lastOne.running) { // 等待上一事件结束
if (lastOne.time != time) // 只处理最后一个事件
return;
}
}}).start();
}
private static Hashtable e_queue = new Hashtable(); private static class LastOne {
public long time=0;
public Object running = new Object();
}</PRE></TD></TR></TBODY></TABLE><BR><BR></CENTER>
<P><BR><BR>6. 使用双缓冲区 <BR><BR>在屏幕之外的缓冲区绘图，完成后立即把整个图形显示出来。由于有两个缓冲区，所以程序可以来回切换。这样，我们可以用一个低优先级的线程负责画图，使得程序能够利用空闲的CPU时间执行其他任务。下面的伪代码片断示范了这种技术。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>Graphics myGraphics;
Image myOffscreenImage = createImage(size().width, size().height);
Graphics offscreenGraphics = myOffscreenImage.getGraphics();
offscreenGraphics.drawImage(img, 50, 50, this);
myGraphics.drawImage(myOffscreenImage, 0, 0, this);</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>7. 使用BufferedImage <BR><BR>Java JDK 1.2使用了一个软显示设备，使得文本在不同的平台上看起来相似。为实现这个功能，Java必须直接处理构成文字的像素。由于这种技术要在内存中大量地进行位复制操作，早期的JDK在使用这种技术时性能不佳。为解决这个问题而提出的Java标准实现了一种新的图形类型，即BufferedImage。BufferedImage子类描述的图形带有一个可访问的图形数据缓冲区。一个BufferedImage包含一个ColorModel和一组光栅图形数据。这个类一般使用RGB（红、绿、蓝）颜色模型，但也可以处理灰度级图形。它的构造函数很简单，如下所示： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public BufferedImage (int width, int height, int imageType)</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>ImageType允许我们指定要缓冲的是什么类型的图形，比如5-位RGB、8-位RGB、灰度级等。 <BR><BR><BR><BR>8. 使用VolatileImage <BR><BR>许多硬件平台和它们的操作系统都提供基本的硬件加速支持。例如，硬件加速一般提供矩形填充功能，和利用CPU完成同一任务相比，硬件加速的效率更高。由于硬件加速分离了一部分工作，允许多个工作流并发进行，从而缓解了对CPU和系统总线的压力，使得应用能够运行得更快。利用VolatileImage可以创建硬件加速的图形以及管理图形的内容。由于它直接利用低层平台的能力，性能的改善程度主要取决于系统使用的图形适配器。VolatileImage的内容随时可能丢失，也即它是“不稳定的（volatile）”。因此，在使用图形之前，最好检查一下它的内容是否丢失。VolatileImage有两个能够检查内容是否丢失的方法： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public abstract int validate(GraphicsConfiguration gc);public abstract Boolean contentsLost();</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>每次从VolatileImage对象复制内容或者写入VolatileImage时，应该调用validate()方法。contentsLost()方法告诉我们，自从最后一次validate()调用之后，图形的内容是否丢失。虽然VolatileImage是一个抽象类，但不要从它这里派生子类。VolatileImage应该通过Component.createVolatileImage()或者GraphicsConfiguration.createCompatibleVolatileImage()方法创建。 <BR><BR>9. 使用Window Blitting <BR><BR>进行滚动操作时，所有可见的内容一般都要重画，从而导致大量不必要的重画工作。许多操作系统的图形子系统，包括WIN32 GDI、MacOS和X/Windows，都支持Window Blitting技术。Window Blitting技术直接在屏幕缓冲区中把图形移到新的位置，只重画新出现的区域。要在Swing应用中使用Window Blitting技术，设置方法如下： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>setScrollMode(int mode);</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>在大多数应用中，使用这种技术能够提高滚动速度。只有在一种情形下，Window Blitting会导致性能降低，即应用在后台进行滚动操作。如果是用户在滚动一个应用，那么它总是在前台，无需担心任何负面影响</P><img src ="http://www.blogjava.net/qq13367612/aggbug/16659.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 22:16 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse使用秘技 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 14:05:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16645.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16645.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16645.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 前言： 本来我都是使用JBuilderX当主力IDE、但使用了Eclipse后 发现...Eclipse原来也这么好用...渐渐的就爱上了它...... Eclipse优点：免费、程序代码排版功能、有中文化包、可增 设许多功能强大的外挂、支持多种操作系统(Windows、 Linux、Solaris、Mac OSX)..等等。 开此篇讨论串的目的，是希望能将Eclipse的一些使用技巧集 合起来....&nbsp;&nbsp;<a href='http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html'>阅读全文</a><img src ="http://www.blogjava.net/qq13367612/aggbug/16645.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 22:05 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Web Framework综述</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 06:04:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16565.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16565.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16565.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 0.简介本文介绍Java Web Framework的基本工作原理，和一些常用的开源Web MVC Framework(Struts, Web Work, Tapestry, Echo, JSF, Maverick, Spring MVC, Turbine, Cocoon, Barracuda)。Web开发的最重要的基本功是HTTP；Java Web开发的最重要的基本功是Servlet S...&nbsp;&nbsp;<a href='http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html'>阅读全文</a><img src ="http://www.blogjava.net/qq13367612/aggbug/16565.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 14:04 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>推荐一篇好文章——Java Web开发构想</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16549.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 03:24:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16549.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16549.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16549.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16549.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16549.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 width="100%" border=0>
<TBODY>
<TR>
<TD><SPAN>Java Web开发构想</SPAN></TD></TR>
<TR>
<TD></TD></TR>
<TR>
<TD>
<P>1．背景、形势 <BR>能够进行Web开发的编程语言和技术很多 <BR>(1) 动态解释语言 <BR>PHP; Perl; Python (Zope, Plone); Ruby (Ruby on Rails); <BR>(2) 编译语言 <BR>Java; .net <BR><BR>Java Web开发远非一枝独秀： <BR>除了受到来自.net 这个重量级对手的最大挑战之外，更受到Zope, Ruby on Rail 等新式轻骑兵的冲击（当然，也继续受到老式轻步兵PHP, Perl的冲击）。 <BR><BR>官方Java走的是复杂路线，Servlet -&gt; JSP -&gt; Taglib。.net走的也是复杂路线，依靠成熟友好的集成化开发环境取胜。Java阵营好容易应对过来，从纷纭复杂的各种开发框架基础上，发展出了重量级Web开发框架JSF，以及相应的集成化开发环境；渴望以此应对.net的攻势。胜负未分，前途未卜。这时，另一个方向又杀来了新式轻骑Zope, Ruby on Rail。 <BR>Python, Ruby等动态解释语言，面向对象特性更好，先天支持 动态绑定、AOP、函数式编程、“编程即配置”等时髦概念。开发速度更快，代码量更小，达到killer级别。 <BR><BR>传统的HTML Web开发领域里面，Java已经是腹背受敌。领域外也展开了征战，Rich Client Architecture的兴起：AJAX(XMLHttp), Flash RIA, XUL, XAML, Smart Client（以及从前的ActiveX, Applet, Web Start）。 <BR><BR>Web的发展趋势是 语义Web，最终目的是让整个Web成为一个巨大的数据库。 <BR>这意味着，未来的Web应用将更加的面向文本内容数据，更加搜索引擎友好 – Search Engine Friendly. <BR>二进制的客户端插件，如Flash RIA, ActiveX, Applet, Web Start等，虽然交互性能最好，但不是以文本内容数据为中心，搜索引擎不友好。所以，我只是保持适当关注。我更关注基于文本的UI表现，如HTML, XUL, XAML等。XUL, XAML还没有广泛流行，只是保持一种有兴趣的关注。 <BR>当下关注的重点，还是 XHTML + CSS + Javascript少量的 AJAX(XMLHttp)增加更好的交互性。 <BR><BR>我一直认为：轻量、简洁、高效 才是硬道理。后面阐述我对Java Web开发的理解和构想。 </P>
<P>2. Web开发框架层次概述 <BR>从上到下，Web开发框架的层次如下： <BR>(1) HTML, JavaScript, CSS等页面资源。 <BR>(2) 页面模板层。 <BR>如JSP, Freemarker, Velocity, XSL，fastm等。用来生成HTML, JavaScript, CSS等页面资源。 <BR>(3) Web框架。把HTTP Request调度分派到对应的Service Entry。 <BR>(4) Business Logic. <BR>(5) O/R Mapping. <BR>(6) JDBC <BR>(7) DB <BR><BR>根据我的经验，一个典型的Web应用中的代码比例如下： <BR>页面逻辑约占 50%，商业逻辑约占30%, O/R 约占20%。 <BR><BR>但事实上，页面却是最不受重视的部分，从来都被认为是脏活，累活，杂活。典型的开发过程通常是这样： <BR>页面设计人员迅速的用Dreamweaver等生成一堆文本杂乱无章的页面，然后交给JSP程序员加入更加杂乱无章的Java代码和Taglib。 <BR>当页面布局风格需要改变的时候，页面设计人员用Dreamweaver等生成一堆新的页面。JSP程序员再重新加入更加杂乱无章的Java代码Taglib。 <BR>至于页面中的脚本逻辑调试，更是一门精深的工夫了。 <BR><BR>根据社会规则，通常来说，工作内容越轻松，收入越高；工作内容越脏月累，收入越低；Web开发也是如此：做着最脏最累的活的页面程序员，工资一般比不上后台业务逻辑程序员。 <BR><BR>开发框架通常会带来这样的结果：让简单的东西，变得更简单；让复杂的东西，变得更复杂。 <BR>这其中的原因在于： <BR>一般来说，一个应用中简单重复的东西占80%，复杂特殊的东西占20%。 <BR>简单重复的东西很容易摸清规律，进行包装，通用化。但是，在包装的同时，经常就阻挡住了底层的一些灵活强大的控制能力。在复杂特殊的需求中，确实又需要这些底层控制能力，那么为了绕开框架的限制，付出的努力要比不用框架 大得多。 <BR>打个比方，一个比较极端的例子。编译语言比汇编语言的开发效率高很多，但是却无法直接操作寄存器。当需要在编译语言中操作寄存器的时候，就非常的痛苦。比如Java，也许需要JNI，写C代码，还要在C代码里面嵌入汇编。编译、连接都很麻烦。 <BR>所以，一个框架的开发效率，就在于这个80%简单 与 20%复杂之间的平衡。 <BR>假如，不用框架来开发，简单的80%要消耗 80个资源数，复杂的20%要消耗20个资源数，总资源数是100；使用了某个框架，简单的80%只要消耗10个资源数，复杂的20%要消耗40个资源数，总资源数是50。那么，我们说，这个开发框架是有效率的。 <BR><BR>我的思路是，同时应对复杂和简单。当然，为了应对复杂，简单的东西可能就应对得不那么好。比如，做这样一个开发框架，简单的80%要消耗20个资源数，复杂的20%要消耗10个资源数，总资源数是30。 <BR>这种开发框架是有可能实现的。而且是很有意义的。尤其是在复杂部分的比例提高的时候。越复杂的系统，这种开发框架就越有意义。 <BR><BR>后面的关于Web各层开发的论述，主要就按照这个“应对复杂、让复杂更简单”的思路展开。</P>
<P>3．页面资源 <BR>也许有人会说，页面资源，不就是HTML吗？太简单，太低极了，没劲。Dreamweaver、Frontpage多简单阿。随便找个人来用就可以了。文本内容乱糟糟不要紧，浏览器里面显示出来的效果好看就行。要增加炫的、酷的动画效果，那就写JavaScript呗。写在HTML里面，看看在IE里面能不能运行就可以了呗。 <BR>这也正是大多数公司开发页面资源的方式。因为页面的需求变化是最多、最快的，而页面的制作成本很低，人们不愿意在上面投入更多的资源。 <BR><BR>我的看法是，万丈高楼平地起。应用程序的每一个部分都应该完善管理，结构优美。越是需求变化多的地方，越是脏乱差的地方，越应该加大力度处理好。 <BR><BR>页面结构方面，Javaeye论坛的Dlee做了很多工作。 <BR><BR>(1) 在 2005 年我们如何写 JavaScript <BR><A href="http://forum.javaeye.com/viewtopic.php?t=12973" target=_blank><FONT color=#004377>http://forum.javaeye.com/viewtopic.php?t=12973</FONT></A> <BR>(2)使用 Unordered Lists 制作的下拉菜单和树 <BR><A href="http://forum.javaeye.com/viewtopic.php?t=12995" target=_blank><FONT color=#004377>http://forum.javaeye.com/viewtopic.php?t=12995</FONT></A> <BR>从上面的Dlee的论述和给出的资料。可以看出，页面资源分为三部分： <BR>(1) XHTML。结构，Structure。 <BR>XHTML里面的Tag部分只应该包括 &lt;ul&gt; &lt;table&gt; &lt;p&gt; &lt;div&gt;&lt;span&gt;等结构布局Tag，或者&lt;strong&gt;&lt;emphasis&gt;表示语义的Tag。 <BR>XHTML里面不应该包括风格信息，比如字体、颜色、大小、粗细等，也不应该包括&lt;font&gt; &lt;b&gt; &lt;i&gt; &lt;h&gt; 等字体信息。 <BR>XHTML里面不应该包括Javascript的定义和调用。 <BR><BR>(2) JavaScript。行为，behavior。 <BR>JavaScritp应该存在于一个独立于XHTML文件的独立文件中。这样可以做自动化单元测试。JavaScript应该只改变HTML DOM的结构和内容，而不应该改变它的风格。 <BR><BR>(3) CSS。Style，风格。或者说，Presentation，表现。 <BR>前面说了，XHTML里面不应该包括JavaScript的调用。那么，XHTML的元素是如何JavaScript事件绑定起来？就是在CSS里面指定的。 <BR>当然，众所周知，CSS的本职工作是处理页面风格。 <BR><BR>页面资源方面，我完全认同Dlee的观点。从技术和资源积累的长远目标看来，这方面的初期投入的回报将是非常丰厚的。 <BR>即使将来HTML消亡了，进入了XAML, XUL, RSS时代，这些结构清晰的各部分，重用的可能性都非常巨大。JavaScript + CSS + XML UI的这种经典设计思路，将留存很久。混杂成一团的HTML的命运只能是全盘被抛弃。 </P>
<P>4．页面模板层 <BR>页面模板层是指Server端运行的用来生成HTML（或JavaScript，CSS）的Server Side Template Engine。 <BR>这一层也是著名的脏乱差楼层。著名的HTML的Java代码污染事件，就发生在这个楼层。不仅JSP有这个问题，其他的template, 如freemarker, velocity, tapestry等含有逻辑的脚本，都不同程度上有HTML的Script Logic污染问题。 <BR><BR>Dlee的做法很美。直接就不要页面模板层，不用Server Side Template Engine。直接用JavaScript更改HTML DOM的结构、内容、数据。同时，会用到少量的浏览器端XSL。 <BR>这样带来的结果，Template就是很干净纯粹的HTML，不含有任何Server Side Script。这个效果，和Servier Side Template 的 Jivan，XMLC达到的一样。只是一个是在浏览器端执行，一个是在Server端执行。 <BR><BR>我研究比较了几乎所有的Server Side Template Engine，力图采众家之长，避众家之短，写了一个Server Side Template Engine -- fastm, 能够最优雅方便的实现页面模板层。关于fastm，我的Blog上有不少文章论述。 <BR>我的Blog，里面专门有个fastm 分类。 <BR><A href="http://blog.csdn.net/buaawhl" target=_blank><FONT color=#004377>http://blog.csdn.net/buaawhl</FONT></A> <BR><A href="http://buaawhl.blogdriver.com/" target=_blank><FONT color=#004377>http://buaawhl.blogdriver.com</FONT></A> <BR><BR>Fastm发布在java.net上。 <BR><A href="https://fastm.dev.java.net/" target=_blank><FONT color=#004377>https://fastm.dev.java.net</FONT></A> <BR><BR><BR>我仍然对Server Side Template Engine持肯定态度。基于如下原因： <BR>(1) JavaScript代码量大、文件多的时候，不容易管理，不容易进行语法检查，不容易跟踪调试。 <BR>这里有人会争辩，Server Side Template Engine也用到了很多脚本阿，比如Freemarker, Velocity, 而且嵌在HTML中，怎么管理，怎么调试？即使是JSP，也是Java Code嵌在HTML里面，怎么管理，怎么调试？ <BR>这里我要说，Jivan, XMLC, fastm，Wicket等Template Engine的逻辑都是在Java Code里面。 <BR><BR>(2) 用JavaScript生成文本内容，搜索引擎不友好。 <BR>一般的网络蜘蛛程序，只根据URL获取HTML文本，搜索里面的文本内容，而不会执行里面的JavaScript脚本。 <BR><BR>(3) JavaScript代码重用还是有些局限 <BR>比如，有两个HTML文件，一个是Table布局，一个是List布局。 <BR>我有同样的一批数据，要在这两种布局中显示。 <BR>这时候，就要给这两个HTML分别写两套JavaScript。这里面的DOM层次，元素，属性都不同，再怎么定义ID，Class，也无法用完全相同的一套JavaScript处理。 <BR>这里有人会争辩，Server Side Template Engine也无法做到。别说JSP, Velocity, Freemarker等要在两套HTML里面嵌入相同的代码，就是Jivan, XMLC, Wicket也要分别写不同的两套Java Code，因为它们的XML DOM Node / Model View (Table, List) 都是不同的。 <BR>这里我要说。fastm可以做到只用一套代码逻辑。而且只有fastm可以。fastm的代码重用率是最高的。 <BR><BR>关于Ajax(XMLHttp)，我的意见是必要时才用，而且最好采用粗粒度的用法 -- JavaScript发出一个URL请求，返回一整段HTML，直接替换到页面的某一块，而不是用JavaScript来做这样的把数据填充到HTML DOM中。如果你直接在浏览器里面输入那个URL，也可以获取那整段的HTML内容。 <BR>典型的应用场合是Portal。Portal页面的每个Portlet都含有这样的 Ajax(XMLHttp) javascript代码 -- 发出一个Portlet URL请求，返回一整段Portlet的内容，直接替换当前的Portlet块。 <BR>这样做的好处是： <BR>(1) 减少JavaScript代码的量和复杂度。 <BR>(2) 搜索引擎友好。网络蜘蛛程序可以辨别JavaScript中的URL，并根据这个URL，获取整段处理好的HTML文本，进行内容搜索。 <BR>有人可能会争辩：如果URL请求返回的是XML数据，不是整段处理好的HTML，搜索引擎也可以进行内容搜索。 <BR>这点我同意。前提是XML数据的内容是足够连贯的，而不是散落的。比如，你返回的XML数据是“中国”。这个“中国”要放在HTML中的一个{country}位置，{country}足球。这个时候，结果HTML的内容含有“中国足球”。而XML数据中只含有“中国”。如果用户用“中国足球”作为关键字来搜索，就找不到这个URL。 <BR><BR>从前面给出的fastm资料的连接中，可以得知。如同Jivan, XMLC, Wicket一样，fastm的template里面不含有逻辑，所有的逻辑都写在Java里面。 <BR>有人会争辩说：页面逻辑写在Java里面，我改变了页面逻辑，还需要重新编译。这也太不方便了。Velocity, Freemarker, JSP就不用重新编译。 <BR>这里我的看法是：业务逻辑代码改变了，不也需要重新编译吗？页面逻辑就不是逻辑了吗？HTML里面的脚本怎么语法检查、跟踪调试？业务逻辑需要语法检查、跟踪调试，页面逻辑就不需要语法检查、跟踪调试了吗？ <BR>对方可能会说：在我的应用中，页面逻辑的改动需求非常频繁，而且这些页面逻辑非常简单，不需要语法检查、跟踪调试。 <BR>这里我的意见是： <BR>(1) 那就使用JSP, Velocity, Freemarker等脚本。 <BR>(2) fastm, Jivan, XMLC, Wicket的Java代码部分也可以写在脚本里面，比如，Server Side JavaScript, Jython(Python), Groovy, Bean Shell 等脚本语言都可以很方便的和Java相互调用。 <BR><BR>fastm的生命周期将很长。 <BR>HTML, XUL, XAML都是，或将是可以在浏览器或可视化编辑工具里面显示的XML UI定义语言。Microsoft Office的Word, Excel, Powerpoint等格式都提供了相应的XML格式。这些XML文件都可以在Office里面显示，并编辑。 <BR>Adobe公司也提供了PDF的XML格式 -- XDP。可以在Adobe Designer里面显示并编辑。 <BR>由于fastm是Designer Friendly的XML UI所见即所得的模板技术。这方面具有很大的潜力。 <BR>根本不需要第三方花大力气专门做个IDE，来显示自定义的Tag。目标文件格式提供商自己的阅读编辑工具就可以直接用了，而且效果就是运行后产生的结果文件的效果。 <BR><BR>即使没有可视化要求的场合。比如，Web Service需要的XML数据。fastm同样有用武之地。比如， <BR>&lt;!-- BEGIN DYNAMIC: users --&gt; <BR>&lt;user&gt; <BR>&lt;name&gt;{name}&lt;/name&gt; <BR>&lt;address&gt;{name}&lt;/address&gt; <BR>&lt;/user&gt; <BR>&lt;!-- END DYNAMIC: users --&gt; <BR><BR>可以很容易的把一个Java Object List转化为XML数据。 <BR><BR>另外，我不得不承认。浏览器端的JavaScript的页面逻辑，可移植性要高于Server Side Template Engine。因为Server Side Template Engine通常是特定语言相关的。 <BR>目前fastm是用Java实现的。由于实现很简单，移植到其它的语言，也很简单。如果是移植到Python, Ruby等动态解释语言，那就更简单了。我是有这个考虑，因为Zope, Ruby on Rails 的模板还是Logic 和 HTML混杂的，fastm这个思路有很大的用武之地。 <BR><BR>前面讲了这么多。清理了两层有名的脏乱差的老大难的楼层 -- 页面资源层和页面模板层。让这两层变得和下面的楼层同样的优雅、清洁。 <BR><BR>下面该讲到Web框架层了。在向下讲之前，由于前面提到了脚本，我想先插入一段关于“可配置”、“可编程”、“可热部署”、“脚本逻辑 vs XML Tag逻辑”的话题。把这个人们比较关心、讨论比较多的话题，先讲清楚。 </P>
<P>5．可配置、可编程、可热部署、脚本逻辑 vs XML Tag逻辑 <BR>由于Java是编译语言，人们通常把变化的参数部分抽取出来，放到配置文件中。 <BR>这些配置文件通常是XML文件。这很好，没什么问题。XML很适合用来表达数据结构。 <BR>但是，对于某一种技术的狂热，通常引起对这种技术的过度使用，或者误用。 <BR>人们开始觉得，XML能够表达一切东西，包括for, if, else等逻辑。这方面的典型例子有 Workflow XML Definition，Logic TagLib, XSL Logic Tag等。 <BR>这点我不敢苟同。我的看法是，XML不适合表达逻辑，XML表达逻辑非常蹩脚。XML表达逻辑相当于自定义一门XML格式的脚本语言。 <BR><BR>比如，Logic Tablib，很难自然的支持 if else, switch。只能蹩脚地支持一堆 &lt;logic:if&gt; &lt;logic:ifNot&gt; &lt;logic:exists&gt; &lt;logic:notExists&gt; &lt;logic:ifNull&gt; &lt;logic:notNull&gt;。 <BR>（注，好久没有接触过Taglib了。这些Tag Name都是凭以前的使用印象写的，也许名字不对，但表达这些意思的TagLib都还是有的） <BR>如果要表达if () else if() else 就更蹩脚了。要进行非常麻烦的嵌套。 <BR><BR>再比如，XSL 支持if, else 也非常蹩脚。非要多出来一个层次才行。 <BR>&lt;xsl:choose&gt; <BR>&lt;xsl:when test="…"&gt; <BR>…. If …. <BR>&lt;/xsl:when&gt; <BR>&lt;xsl:otherwise&gt; <BR>… else … <BR>&lt;/xsl:otherwise&gt; <BR>&lt;/xsl:choose&gt; <BR><BR>同样，如果要表达if () else if() else 就更蹩脚了。 <BR>&lt;xsl:choose&gt; <BR>&lt;xsl:when test="…"&gt; <BR>…. If …. <BR>&lt;/xsl:when&gt; <BR>&lt;xsl:otherwise&gt; <BR>&lt;xsl:choose&gt; <BR>&lt;xsl:when test="…"&gt; <BR>…. If …. <BR>&lt;/xsl:when&gt; <BR>&lt;xsl:otherwise&gt; <BR>… else … <BR>&lt;/xsl:otherwise&gt; <BR>&lt;/xsl:choose&gt; <BR>&lt;/xsl:otherwise&gt; <BR>&lt;/xsl:choose&gt; <BR><BR>可以看到，XML Tag 表达逻辑，非常麻烦，可读性很差，完全是一种误用，没有半点优势。当然，逻辑简单的情况下，还是可以接受的。 <BR>有人会说：XML表达逻辑，可以免编译阿。 <BR>那么我说：语法检查呢，跟踪调试呢？ <BR>对方说：只是一些简单的逻辑，不需要语法检查、跟踪调试。 <BR>我说：如果只是为了免编译，前面列出的那么多的解释执行的脚本语言更适合。XML表达的逻辑，比Java等编译语言还要麻烦很多，而脚本语言比Java等编译语言简洁多了，可读性非常好，而且脚本语言和Java语言有很好的交互性，可以相互调用。重用、结构方面都具有优势。 <BR><BR>有人会举出<B>Spring</B> IoC为例子，说：你看，<B>Spring</B> IoC的配置文件不都是XML格式吗？ <BR>我说： <BR>(1) <B>Spring</B> IoC的配置文件基本都是属性设置，Bean ID声明。没有逻辑。 <BR>(2) 我也不是很赞同<B>Spring</B> IoC在XML配置文件里面引用Java类的做法。这方面，其它的容器如 Pico, Nano都支持多种配置方式，其中包括了不少脚本方式。我觉得，在脚本里面定义生成Java Object，比在XML中要好。当然，Web.xml里面也引用了Java Class名字。但那是非常简单的情况。没有嵌套引用、属性赋值、构造参数等复杂的定义方式。XML适合描述一些通用的资源、数据、结构。比如，HTML, XUL, XAML，RSS就是XML用的恰当的例子。 <BR><BR>所以，我的基本观点是这样。 <BR>(1) 纯数据，不用说，应该定义在XML中。 <BR>(2) 如果是系统中一些Java Object要用到的基本属性。比如，连接池大小等。定义在properties, XML, Script中都可以。如果定义中没有出现具体的Java Class名，倾向于定义在properties, XML文件中。如果出现了具体的Java Class名，倾向于定义在Script中。这个界限不那么明显，两者皆可。 <BR>(3) 复杂结构的Java Bean的构造生成，那是肯定会出现具体的Java Class名，应该定义在Script中。 <BR><BR>关于“可配置 vs 可编程”，有一点要明确：只要是可编程的，一定是可配置的。但如果是可配置的，却不一定是可编程的。 <BR>这里的可编程，是指框架给程序员提供了API；可配置，是指框架给程序员提供了配置文件的格式写法。 <BR>“可编程”一定是“可配置”的。 <BR>(1) 用户至少可以自己定义配置文件，读取参数，调用API。 <BR>(2) 有那么多的解释脚本可以直接和Java互操作，完全可以直接用来当作配置文件，定义参数。 <BR>“可配置” 却不一定“可编程”的。 <BR>如果框架只给你提供了配置方式，而没有API，那意味着，你只能进行参数的静态配置。很难在动态期间改变这些参数了。你总不能尝试着用代码去改变配置文件的内容吧？即使你改动了，如果框架不进行文件的时间戳检查，就是一开始装载进来，就不再检查更改了，你不就一点办法都没有了吗？ <BR>比如，Struts Tiles的XML定义，你只能静态配置，你想在运行期间改变布局，没有办法。Site Mesh也是如此。而我们可以在运行期间任意操作XML DOM Node，别说布局了，任何东西都可以改变。 <BR>所以，一个框架首要注重的是提供API，而不是提供配置方式。这是一个重要的原则。 <BR><BR>讨论完了“可编程”、“可配置”问题，我们来看“热部署”问题。 <BR>XML配置文件、脚本文件支持“热部署”当然要比编译语言程序的热部署容易得多。只要解释执行前，检查一下时间戳就可以了。要注意的问题，只是做好测试，因为没有编译期的语法检查。 <BR>不过，Java程序也是可以“热部署”的。只是稍微麻烦一点。典型的例子是JSP, EJB Jar等。JSP修改之后，会自动编译执行；EJB Jar丢到EJB Container里面，会被检测到并装载到JNDI命名空间。 <BR>编译语言Java程序的热部署的一个可能的