﻿<?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-J++ = Java + ?-随笔分类-java</title><link>http://www.blogjava.net/killvin/category/8034.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 01 Mar 2007 18:28:06 GMT</lastBuildDate><pubDate>Thu, 01 Mar 2007 18:28:06 GMT</pubDate><ttl>60</ttl><item><title>IE浏览器的两大问题(续)</title><link>http://www.blogjava.net/killvin/archive/2006/07/02/56203.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Sun, 02 Jul 2006 13:53:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/07/02/56203.html</guid><description><![CDATA[
		<font face="Courier New">在上一篇的文章中，比较仔细的分析了IE对于Cookie处理过程中出现的问题，既然存在问题就要想办法解决，而问题的关键在于IE没有能够"老实"的报告用户对于Cookie的设置问题<br /><br />IE<br />GET /mail HTTP/1.1<br />Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*<br />Accept-Language: zh-cn<br />Accept-Encoding: gzip, deflate<br />User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon)<br />Host: mail.google.com<br />Connection: Keep-Alive<br /><strong>Cookie: </strong></font>
		<a href="mailto:gmailchat=killvin.liu@gmail.com/519974">
				<strong>
						<font face="Courier New">gmailchat=killvin.liu@gmail.com/519974</font>
				</strong>
		</a>
		<font face="Courier New">
				<strong>; PREF=ID=d68c481e542af276:NW=1:TM=1151742225:LM=1151742225:S=2qbdhg0_z3i-OAbW; SID=DQAAAG8AAACEdcjD2IZMNqZVatDbD62X8_U18oJuTVQc9XZUJi7MgCkM8sggJ8M5npZ35GXjdalT2o8QWPUve04tepy61MPv4v_EpILafg3JdIf8AFjD91aMT0tI5gb763FouV3e_2-C364HDO5Qzb4P4gjjgpHC</strong>
				<br />
				<br />Firefox<br />GET /mail HTTP/1.1<br />Host: mail.google.com<br />User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3<br />Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5<br />Accept-Language: en-us,en;q=0.5<br />Accept-Encoding: gzip,deflate<br />Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7<br />Keep-Alive: 300<br />Connection: keep-alive<br /><br />我们可以注意到：如果用户关闭了浏览器的Cookie选项，在Firefox中对于新的请求是包含Cookie的信息的，然而IE却恰恰相反！这也就直接用IE浏览一些网站所导致的Cookie校验失败问题（比如即使用户在使用过程中突然关闭Cookie，依然可以自由的访问网站的各项服务），进一步，如果某个网站是读取依靠Cookie中的信息来验证用户(尤其是那种保存客户信息的提示)，即使这个用户离开或者关闭窗口，任何一个后来的人都可以轻松的浏览这个用户的私有信息！这就是Cookie的最大的问题所在，当然这是另外的话题，不属于本次讨论的范畴，这一次要说的是</font>
		<font face="Courier New">
				<strong>如何避免IE浏览器下，用户关闭Cookie的问题。<br /></strong>
				<br />有一个思路，经过测试我们发现IE浏览器只是在首次页面请求的时候才会去读取Cookie的值，所以，为了避免IE浏览器关闭Cookie的问题，可以在用户请求某个链接的时候，先将用户的Cookie存放在某处，并清空此时所有的Cookie值，再次的请求页面，而这样做的目的就是强迫IE读取硬盘上的Cookie，如果此时用户关闭了Cookie，就不会在请求的头信息中看到Cookie信息；如果没有关闭Cookie,就可以放心的将原先的Cookie写回Reponse对象。<br /><br /><br />只是不知道Google的网站是否也是按照这个思路去解决此问题的？<br /><br /></font>
<img src ="http://www.blogjava.net/killvin/aggbug/56203.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-07-02 21:53 <a href="http://www.blogjava.net/killvin/archive/2006/07/02/56203.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IE浏览器的两大问题</title><link>http://www.blogjava.net/killvin/archive/2006/07/01/56096.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Sat, 01 Jul 2006 07:53:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/07/01/56096.html</guid><description><![CDATA[
		<p>
				<font face="Courier New">IE在处理COOKIE方面的问题<br /><br />1。即使你提高了浏览器的隐私登记，在第一次打开窗口的时候，你获取不到任何的Cookies对象（很显然的结果），然而当你再次刷新本页面的，Cookie此时会奇迹般的出现！而在Firefox浏览器中按照以上的步骤，是不会出现这样的情况的。<br /><br />2。不仅如此，你还可以透过Action的处理，多次的往Cookie中增加Cookie的数量（当然在Action中你依然可以自由的获取到Cookie这个数组，并且不为空），然而让人匪夷所思的是在Cookie的存放目录下你是找寻不到任何的蛛丝马迹的。而在Firefox没有出现以上的情况。<br /><br /></font>
				<font face="Courier New">
						<strong>－解决<br />1。在首次进入页面时查询客户端的Cookie，如果不存在则警告用户，并要求再次的登陆。<br />2。在用户登陆后，如果更改了浏览器的隐私级别，对于Firefox标准的浏览器，此时肯定不会再找到Cookie数组对象了，你需要做的仅仅只是将页面调转到登陆窗口；而在IE下就非常的麻烦了甚至无法解决，因为你依然可以访问到原来的Cookie数组值，（比如，用IE在CSDN登陆后提高隐私级别，你依然可以登陆到其他的服务区域）此时没有什么好的办法，不过Google解决了这样的问题，只是不知道如何解决的。</strong>
						<br />
						<br />
						<br />IE在处理Frame标记的问题<br />1。如果你在某个页面中嵌入了Frame标签，并且希望与这个嵌入的页面共享某些存放在Session中的数据，此时你需要将外部的sessionId传入到frame标记的页面中。然而在IE中你可能并不能完全的享受这样的逻辑，原因在于IE对于嵌入的页面不会自动的传递sessionId，也许你碰巧可以，也许不行，也就是说完全是在IE的"掌控"之下。而在Firefox没有出现这样的情况。<br /><br /><strong>－解决<br />为嵌入的页面中所有的链接增加sessionId参数。<br /></strong><br /><br /><strong>最好的办法就是：说服客户使用标准的浏览器Firefox!</strong><br /></font>
		</p>
<img src ="http://www.blogjava.net/killvin/aggbug/56096.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-07-01 15:53 <a href="http://www.blogjava.net/killvin/archive/2006/07/01/56096.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从ActionErrors过渡到ActionMessages</title><link>http://www.blogjava.net/killvin/archive/2006/06/27/55429.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Tue, 27 Jun 2006 15:02:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/06/27/55429.html</guid><description><![CDATA[
		<p>
				<font face="Courier New" size="2">在Struts中我们经常这样循环的打印Message<br />&lt;logic:messagesPresent message="true"&gt;<br />  &lt;html:messages id="msg" message="true"&gt;<br />    &lt;div class="success"&gt;<br />      &lt;bean:write name="msg"/&gt;<br />    &lt;/div&gt;&lt;br/&gt;<br />  &lt;/html:messages&gt;<br />&lt;/logic:messagesPresent&gt;<br /><br />查阅struts tag的文档我们看到了关于messagesPresent的message属性注释如下<br /></font>
		</p>
		<p>
		</p>
		<table class="tag-attributes">
				<tbody>
						<tr class="evenRow">
								<td align="middle">
										<font face="Courier New" size="2">message</font>
								</td>
								<td>
										<p>
												<font face="Courier New" size="2">By default the tag will retrieve the request scope bean it will iterate over from the <code>Globals.ERROR_KEY</code> constant string, but if this attribute is set to 'true' the request scope bean will be retrieved from the <code>Globals.MESSAGE_KEY</code> constant string. Also if this is set to 'true', any value assigned to the name attribute will be ignored<br /></font>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<br />
				<font face="Courier New" size="2">也就是说在将message设置为true时，会去request中寻找Globals.MESSAGE_KEY所代表的bean，然而我们在具体的Action使用ActionMessages类的时候往往是这样的<br />ActionMessages messages = getErrors(request);<br />messages.add(ActionMessages.GLOBAL_MESSAGE , new ActionMessage(key , value0));<br />saveMessages(request,messages);<br /><br /><strong>而往往困扰人的就在于为什么要给messages中放入名称为"ActionMessages.GLOBAL_MESSAGE"的ActionMessage对象，而且还需要再次的调用saveErrors(request,messages)方法？</strong><br /><br />首先要说明的是你为ActionMessage起任何的名称都没有关系，因为ActionMessages本身维持着一个HashMap，而参数property就是这个HashMap中的key值，如果不存在则会建立相应的key,并将需要保存的ActionMessage对象存入到这个key所对应的List中。<br />    public void add(String property, ActionMessage message) {</font>
		</p>
		<p>
				<font face="Courier New" size="2">        ActionMessageItem item = (ActionMessageItem) messages.get(property);<br />        List list = null;</font>
		</p>
		<p>
				<font face="Courier New" size="2">        if (item == null) {<br />            list = new ArrayList();<br />            item = new ActionMessageItem(list, iCount++, property);</font>
		</p>
		<p>
				<font face="Courier New" size="2">            messages.put(property, item);<br />        } else {<br />            list = item.getList();<br />        }</font>
		</p>
		<p>
				<font face="Courier New" size="2">        list.add(message);</font>
		</p>
		<p>
				<font face="Courier New" size="2">    }<br /><br />至于为什么一定要调用saveMessages(request,messages)？看看它具体的实现逻辑就清楚了<br />    protected void saveMessages(<br />        HttpServletRequest request,<br />        ActionMessages messages) {</font>
		</p>
		<p>
				<font face="Courier New" size="2">        // Remove any messages attribute if none are required<br />        if ((messages == null) || messages.isEmpty()) {<br />            request.removeAttribute(<strong>Globals.MESSAGE_KEY</strong>);<br />            return;<br />        }</font>
		</p>
		<p>
				<font face="Courier New" size="2">        // Save the messages we need<br />        request.setAttribute(<strong>Globals.MESSAGE_KEY</strong>, messages);<br />    }</font>
		</p>
		<p>
				<font face="Courier New" size="2">再对比前面介绍的messagesPresent标签的使用，是不是就清楚了呢？原来它是将ActionMessages对象保存在request中，并且名称是Globals.ERROR_KEY！从而为tag的顺利解析铺平了道路。当然按理你可以选择将这样的对象放置在任何的scope中，但Action只是提供了request , session两种Scope（不过page , application不经常使用，可以理解，但不提供相应的结构就不太好了）<br /><br />至于messagesPresent标签是如何在scope中寻找ActionMessages对象<br /><br /><strong>org.apache.struts.taglib.logic.MessagesPresentTag</strong><br />    protected boolean condition(boolean desired) throws JspException {<br />        ActionMessages am = null;</font>
		</p>
		<p>
				<font face="Courier New" size="2">        String key = name;<br />        if (message != null &amp;&amp; "true".equalsIgnoreCase(message)){<br />           key = <strong>Globals.MESSAGE_KEY</strong>;<br />        }</font>
		</p>
		<p>
				<font face="Courier New" size="2">        try {<br />            <strong>am = TagUtils.getInstance().getActionMessages(pageContext, key);<br /></strong>            <br />        } catch (JspException e) {<br />            TagUtils.getInstance().saveException(pageContext, e);<br />            throw e;<br />        }</font>
		</p>
		<p>
				<font face="Courier New" size="2">        Iterator iterator = (property == null) ? am.get() : am.get(property);</font>
		</p>
		<p>
				<font face="Courier New" size="2">        return (iterator.hasNext() == desired);</font>
		</p>
		<p>
				<font face="Courier New" size="2">    }<br /><br /><strong>org.apache.struts.taglib.TagUtils</strong><br />   public ActionErrors getActionErrors(PageContext pageContext, String paramName)<br />            throws JspException {</font>
		</p>
		<p>
				<font face="Courier New" size="2">        ActionErrors errors = new ActionErrors();</font>
		</p>
		<p>
				<font face="Courier New" size="2">        Object value = <strong>pageContext.findAttribute</strong>(paramName);<br />        if (value != null) {<br />            try {<br />                if (value instanceof String) {<br />                    errors.add(<br />                            ActionMessages.GLOBAL_MESSAGE,<br />                            new ActionMessage((String) value));</font>
		</p>
		<p>
				<font face="Courier New" size="2">                } else if (value instanceof String[]) {<br />                    String keys[] = (String[]) value;<br />                    for (int i = 0; i &lt; keys.length; i++) {<br />                        errors.add(<br />                                ActionMessages.GLOBAL_MESSAGE,<br />                                new ActionMessage(keys[i]));<br />                    }</font>
		</p>
		<p>
				<font face="Courier New" size="2">                } else if (value instanceof ActionErrors) {<br />                    errors = (ActionErrors) value;</font>
		</p>
		<p>
				<font face="Courier New" size="2">                } else {<br />                    throw new JspException(<br />                            messages.getMessage(<br />                                    "actionErrors.errors",<br />                                    value.getClass().getName()));<br />                }</font>
		</p>
		<p>
				<font face="Courier New" size="2">            } catch (JspException e) {<br />                throw e;</font>
		</p>
		<p>
				<font face="Courier New" size="2">            } catch (Exception e) {<br />                log.debug(e, e);<br />            }<br />        }<br />        return errors;<br />    }<br /><br />PageContext中的findAttribute会帮你在scope中寻找名称为<strong>Globals.MESSAGE_KEY的ActionMessage对象。<br /><br /><br />注意<br /></strong>虽然Struts已经声明：不推荐使用ActionErrors &amp; ActionError对象，但在一些遗留的系统中，依然还是可以看到其影子，所以如果你的系统不幸属于这样的两种混合系统，有以下的几种方法可以参考<br /></font>
				<font face="Courier New" size="2">1。两次调用messagesPresent，如下<br />&lt;!-- Print ActionErrors Object --&gt;<br />&lt;logic:messagesPresent&gt;<br />  &lt;html:messages id="msg" message="true"&gt;<br />    &lt;div class="success"&gt;<br />      &lt;bean:write name="msg"/&gt;<br />    &lt;/div&gt;&lt;br/&gt;<br />  &lt;/html:messages&gt;<br />&lt;/logic:messagesPresent&gt;<br /><br />&lt;!-- Print ActionMessages Object --&gt;<br />&lt;logic:messagesPresent message="true"&gt;<br />  &lt;html:messages id="msg" message="true"&gt;<br />    &lt;div class="success"&gt;<br />      &lt;bean:write name="msg"/&gt;<br />    &lt;/div&gt;&lt;br/&gt;<br />  &lt;/html:messages&gt;<br />&lt;/logic:messagesPresent&gt;<br /><br />2.分别使用&lt;html:messages&gt; &lt;html:errors&gt;标签，当然在老系统中需要调用Action的saveErrors方法，而在新的应用中要调用saveMessages方法。</font>
		</p>
		<p>
				<font face="Courier New" size="2">3.更换所有的ActionErrors为ActionMessages,并将所有调用saveErrors的地方更换成saveMessages，并将&lt;html:errors&gt;标签相应的更换成&lt;html:messages message="true"&gt; － 推荐！</font>
		</p>
		<p>
				<font face="Courier New" size="2">
				</font> </p>
<img src ="http://www.blogjava.net/killvin/aggbug/55429.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-06-27 23:02 <a href="http://www.blogjava.net/killvin/archive/2006/06/27/55429.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java到底能干什么？</title><link>http://www.blogjava.net/killvin/archive/2006/03/29/38034.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Wed, 29 Mar 2006 05:49:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/29/38034.html</guid><description><![CDATA[
		<p>
				<br />[深入的思考] 为什么要采用java这个平台？</p>
		<p>
				<br />
				<strong>从开发项目的类别角度看java平台</strong>
		</p>
		<p>基于B/S结构的系统，在这个方向上的竞争是激烈的，有专注于此的LAMP（Linux + Apache + Mysql + Php）;也有刚刚兴起的Rails（Ruby Frameworks）甚至是号称快速开发的ASP.NET;当然了java在这个领域里的MVC框架数都数不完，比如Struts . Webwork等，然而即便是如此，选择java作为开发的理由也是不充分的，因为在这个梯队里java顶多排名最后。</p>
		<p>基于C/S结构的系统，在这个方面java显然没有考虑周到，面对VB 、DELPHI、vc这些个如狼似虎的快速开发IDE，JAVA实在是显得异常的淡薄，即使你找到了一个可以匹敌这些个ide的工具，面对第三方的组件又会成为一大障碍，所以java在这个方面又一次的输了。</p>
		<p>
				<br />
				<strong>从java所强调的特性角度看java平台</strong>
		</p>
		<p>java的重点是业务逻辑！（我以前也是如此坚信不移）可是谁有能够说别的语言不注重业务逻辑呢，业务逻辑只是一个抽象的概念，java只是依靠ejb提出了业务组件而已，其他的语言在实现业务逻辑的时候也可以包装成POJO的形式，看来这个观点也是失败的。</p>
		<p>java强调的是跨平台的优势！这可以理解为初级的、商业的、忽悠人的词汇，面对众多动态语言如Python，在若干平台上的表现，java又如何来强调自己这方面的优势呢？失败</p>
		<p>java支持分布式应用的项目！可笑的言论，分布式根本不是值得炫耀的资本，在java之前的c/s项目中何尝不是分布式的应用呢？失败</p>
		<p>
				<br />既然没有了这些个优势，我们看看java到底还剩下些什么？对了其实就是应用服务器！然而看过J2EE WITHOUT EJB的读者肯定知道Spring所希望达到的目的，也就是脱离应用服务器概念上的J2EE体系实现，既然在作者的眼里APPLICATION SERVER只不过是一个忽悠人的词汇，那么任何项目都选择java作为开发的依据显然就是自找苦吃，<br /></p>
		<p>
				<strong>那么什么情况下改选择java作为开发的平台呢？<br /></strong>&lt;1&gt; 如果你真的遇到了大型的系统开发任务，恭喜你，你终于可以看到分布式对象、集群的优势了。<br />&lt;2&gt; 客户是一个java的忠实fans或者是sun、ibm的金牌合作伙伴之类的，选择java是不得已的，但记住并不能证明java是最好的实现方式<br />&lt;3&gt; 如果你只想关心业务逻辑的实现，对于事务、缓存、查找等服务的实现没有兴趣的话，倒是不妨考虑采用ejb的形式，当然前提是你不愿意在寻找合适的替代品的情况下。<br />&lt;4&gt; 如果项目迫切的寻找某种框架的支持，选择java就是对的，你有众多优秀的、免费的、可扩展的、天才的框架可以选择，更多的时候你是出于尴尬的境地，因为任何一个都让你心动、而这样的选择往往是最痛苦、和快乐的。</p>
		<p>
				<br />
				<strong>正确的选择</strong>
				<br />&lt;1&gt; <br />条件: 如果项目仅仅只是一个小型的网站系统<br />选择: LAMP、Rails</p>
		<p>&lt;2&gt; <br />条件: 项目规模中等<br />并且项目的时间比较紧，<br />项目可以架构在windows的系统之上，<br />选择: .Net  / Delphi </p>
		<p>&lt;3&gt;<br />条件: 大型的系统，有支持分布式对象、集群的要求；或者SUN / IBM的金牌合作伙伴 ; 想要寻找某种优秀的框架来解决问题<br />选择: java是不二的选择，可是我想问一下，在现实中你能遇到这样的项目吗？</p>
		<p>所以，从实际的角度出发，我们面对的99％可能都是一些符合条件1，2的系统，而选择java实在是得不偿失的。最后以一段Code Complete中的话来作为结束语</p>
		<p>
				<strong>每个程序员都有很多的工具，但并不存在任何一个能够适用于所有工作的工具，因地制宜的选择正确工具是成为能有效编程的程序员的关键。</strong>
		</p>
<img src ="http://www.blogjava.net/killvin/aggbug/38034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-29 13:49 <a href="http://www.blogjava.net/killvin/archive/2006/03/29/38034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit 4 抢先看</title><link>http://www.blogjava.net/killvin/archive/2006/03/05/33687.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Sun, 05 Mar 2006 05:10:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/05/33687.html</guid><description><![CDATA[不错的文章<A href="http://www-128.ibm.com/developerworks/cn/java/j-junit4.html"><BR>http://www-128.ibm.com/developerworks/cn/java/j-junit4.html<BR></A><BR>
<BLOCKQUOTE>JUnit 是 Java™ 语言<I>事实上的</I> 标准单元测试库。JUnit 4 是该库三年以来最具里程碑意义的一次发布。它的新特性主要是通过采用 Java 5 中的标记（annotation）而不是利用子类、反射或命名机制来识别测试，从而简化测试。在本文中，执着的代码测试人员 Elliotte Harold 以 JUnit 4 为例，详细介绍了如何在自己的工作中使用这个新框架。注意，本文假设读者具有 JUnit 的使用经验。</BLOCKQUOTE>
<P>JUnit 由 Kent Beck 和 Erich Gamma 开发，几乎毫无疑问是迄今所开发的最重要的第三方 Java 库。正如 Martin Fowler 所说，“在软件开发领域，从来就没有如此少的代码起到了如此重要的作用”。JUnit 引导并促进了测试的盛行。由于 JUnit，Java 代码变得更健壮，更可靠，bug 也比以前更少。JUnit（它本身的灵感来自 Smalltalk 的 SUnit）衍生了许多 xUnit 工具，将单元测试的优势应用于各种语言。nUnit (.NET)、pyUnit (Python)、CppUnit (C++)、dUnit (Delphi) 以及其他工具，影响了各种平台和语言上的程序员的测试工作。</P>
<P>然而，JUnit 仅仅是一个工具而已。真正的优势来自于 JUnit 所采用的思想和技术，而不是框架本身。单元测试、测试先行的编程和测试驱动的开发并非都要在 JUnit 中实现，任何比较 GUI 的编程都必须用 Swing 来完成。JUnit 本身的最后一次更新差不多是三年以前了。尽管它被证明比大多数框架更健壮、更持久，但是也发现了 bug；而更重要的是，Java 不断在发展。Java 语言现在支持泛型、枚举、可变长度参数列表和注释，这些特性为可重用的框架设计带来了新的可能。</P>
<P>JUnit 的停滞不前并没有被那些想要废弃它的程序员所打败。挑战者包括 Bill Venners 的 Artima SuiteRunner 以及 Cedric Beust 的 TestNG 等。这些库有一些可圈可点的特性，但是都没有达到 JUnit 的知名度和市场占有份额。它们都没有在诸如 Ant、Maven 或 Eclipse 之类的产品中具有广泛的开箱即用支持。所以 Beck 和 Gamma 着手开发了一个新版本的 JUnit，它利用 Java 5 的新特性（尤其是注释）的优势，使得单元测试比起用最初的 JUnit 来说更加简单。用 Beck 的话来说，“JUnit 4 的主题是通过进一步简化 JUnit，鼓励更多的开发人员编写更多的测试。”JUnit 4 尽管保持了与现有 JUnit 3.8 测试套件的向后兼容，但是它仍然承诺是自 JUnit 1.0 以来 Java 单元测试方面最重大的改进。</P>
<P><B>注意：</B>该框架的改进是相当前沿的。尽管 JUnit 4 的大轮廓很清晰，但是其细节仍然可以改变。这意味着本文是对 JUnit 4 抢先看，而不是它的最终效果。</P><img src ="http://www.blogjava.net/killvin/aggbug/33687.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-05 13:10 <a href="http://www.blogjava.net/killvin/archive/2006/03/05/33687.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE究竟是什么？ </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33308.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:59:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33308.html</guid><description><![CDATA[J2EE（即Java 2 平台企业版）是由Sun公司主持推出的一项中间件技术。从CORBA、IDL到面向消息的系统，中间件技术已经走过了很长的一段路程，如今J2EE作为中间件技术史上的一块具有决定意义的里程碑，正受到业界越来越广泛的重视和采纳。 <BR><BR>J2EE，一方面有着一套相当庞大的标准体系和数个不同版本，另一方面，由于市场上应用服务器品种多样，各家开发商使用的术语又不尽相同，因此，围绕着J2EE，常常有不少被人误解的地方。本文将深入探讨J2EE究竟是什么，它到底能做什么。 <BR>什么是J2EE？ <BR>在试图给J2EE 下一个明确的定义之前，我们首先要了解J2EE 并不简单地只是一门语言、一种工具或一套服务。 <BR><BR>· J2EE——Java 2 平台企业版 <BR>简单地说，J2EE是一个标准中间件体系结构，旨在简化和规范多层分布式企业应用系统的开发和部署。J2EE方案的实施可显著地提高系统的可移植性、安全性、可伸缩性、负载平衡和可重用性。 <BR><BR>J2EE技术出现之前，几家主要的中间件开发商的产品各自为阵，彼此之间缺乏兼容性，可移植性差，难以实现互操作，没有一个被普遍认可的行业标准。J2EE的出现标志着中间件技术在经历了多年的不断摸索和经验总结后，正逐步走向成熟。 <BR><BR>J2EE的核心是一组规范和指南，定义了一个使用Java语言开发多层分布式企业应用系统的标准平台。开发人员在这些规范和指南的基础上开发企业级应用，同时由J2EE供应商确保不同的J2EE平台之间的兼容性。由于基于规范的各J2EE平台之间具有良好的兼容性， 因此J2EE应用系统可以部署在不同的应用服务器上，无需或只需进行少量的代码修改。 <BR><BR><BR>· J2EE视点 <BR>下面我们将从几个不同的侧面来考察J2EE，以期读者能对J2EE有个更全面清晰的印象。 <BR><BR>（1）J2EE：多层、分布式中间件语法 <BR>采用多层分布式应用模型，J2EE将应用开发划分为多个不同的层，并在每一个层上定义组件。各个应用组件根据他们所在的层分布在同一个或不同的服务器上，共同组成基于组件的多层分布式系统。典型的J2EE四层结构包括客户层、表示逻辑层（Web层）、商业逻辑层和企业信息系统层。 <BR><BR>有了J2EE，分布式系统的开发变得简单了，部署的速度也可以加快。J2EE组件的分布与服务器环境无关，所有的资源都可通过分布式目录进行访问。这意味着开发人员不再需要为组件和资源的分布问题耗费精力，从而可以有更多的时间专注于业务逻辑的实现，提高开发效率。 <BR><BR>（2）J2EE：企业级应用系统开发平台 <BR>J2EE本身是一个标准，一个为企业分布式应用的开发提供的标准平台。而J2EE的实施，则具体表现为诸如BEA Web logic或IBM Web sphere之类的特定Web服务器产品。利用J2EE应用-编程模型开发的企业应用系统，可以部署在不同厂商生产的、但相互兼容的J2EE 应用服务器上。 <BR><BR>目前，市场上基于J2EE的Web服务器品种繁多，性能特点各有千秋，每家厂商的产品都有精心设计的独到之处。但与产品个性无关的是，所有的J2EE应用服务器都为企业级应用系统的开发和部署提供了一个共同的基础。 <BR><BR>（3）J2EE：电子化应用开发模型 <BR>J2EE应用很容易发布到Web、掌上电脑或移动电话等手持设备上。换言之，应用组件可以很轻松地实现电子化。J2EE的应用-编程模型保证组件在向不同类型的客户端移植过程中，商业逻辑和后端系统保持不变。 <BR><BR>此外，J2EE平台的其他主要优点还有：自动负载平衡、可伸缩、容错和具有故障排除等功能。部署在J2EE环境中的组件将自动获得上述特性，而不必增加额外的代码开销。 <BR><BR>J2EE所有这些特性对于需要构建全天候网络门户的企业来说显得尤为重要。 <BR><BR>（4）J2EE：Web应用服务器上广泛采用的标准 <BR>可以说，J2EE是首个获得业界广泛认可和采纳的中间件标准。目前几乎所有的一流Web应用服务器，如BEA的Web logic、IBM的Web sphere、HP的应用服务器、Sun的iPlanet和Macromedia的Jrun等，都是基于J2EE的。迄今为止，还没有哪个其他标准能获得如此众多的中间件供应商的一致支持。 <BR><BR>而且，有了J2EE，企业的应用开发对于某个特定的开发商或应用服务供应商的依赖性更小。应用组件只要符合J2EE规范，完全可以部署在不同的应用服务器上。为了确保不同厂商的J2EE应用服务器的兼容性和一致性，Sun公司发布了J2EE兼容性测试包。 <BR><BR>· J2EE究竟是什么 <BR>至此，我们可以试着用一句话来概括J2EE，那就是：J2EE是一个中间件基础架构，有了它，开发者只需要集中精力编写代码来表达企业应用的商业逻辑和表示逻辑，至于其他系统问题，如内存管理，多线程，资源分布和垃圾收集等，都将由J2EE自动完成。 <BR><BR>J2EE如何应对挑战？ <BR>在这一部分里，我们将探讨J2EE是如何应对企业开发过程中所面临的问题，以及如何为企业未来发展之需要提供空间。 <BR><BR>· 独立于硬件配置和操作系统 <BR>J2EE运行在Java虚拟机（JVM）上，利用Java本身的跨平台特性，独立于硬件配置和操作系统。Java运行环境（JRE）——JVM的可安装版本加上其他一些重要组件——几乎可以运行于所有的硬件/OS组合。因此，通过采用Java，J2EE使企业免于高昂的硬件设备和操作系统的再投资，保护已有的IT资源。在很多情况下，J2EE还可以直接运行在EIS服务器环境中，从而节约网络带宽，提高性能。 <BR><BR><BR>· 坚持面向对象的设计原则 <BR>作为一门完全面向对象的语言，Java几乎支持所有的面向对象的程序设计特征。面向对象和基于组件的设计原则构成了J2EE应用编程模型的基础。 <BR><BR>J2EE多层结构的每一层都有多种组件模型。因此，开发人员所要做的就是为应用项目选择适当的组件模型组合，灵活地开发和装配组件，这样不仅有助于提高应用系统的可扩展性，还能有效地提高开发速度，缩短开发周期。此外，基于J2EE的应用还具有结构良好，模块化，灵活和高度可重用性等优点。 <BR><BR>· 灵活性、可移植性和互操作性 <BR>利用Java的跨平台特性，J2EE组件可以很方便地移植到不同的应用服务器环境中。这意味着企业不必再拘泥于单一的开发平台。 <BR><BR>J2EE的应用系统可以部署在不同的应用服务器上，在全异构环境下，J2EE组件仍可彼此协同工作。这一特征使得装配应用组件首次获得空前的互操作性。例如，安装在IBM Websphere环境下的EJB，一方面可以直接与Websphere环境下的CICS直接交互，另一方面也可以通过安装在别处的BEA Weblogic 服务器上的EJB进行访问。 <BR><BR>· 轻松的企业信息系统集成 <BR>J2EE技术出台后不久，很快就将JDBC、 JMS和 JCA等一批标准纳归自身体系之下，这大大简化了企业信息系统整合的工作量，方便企业将诸如legacy system（早期投资系统），ERP和数据库等多个不同的信息系统进行无缝集成。 <BR><BR>由于几乎所有的关系型数据库系统都支持JDBC，因此只需借助必要的JDBC驱动程序，J2EE应用就可以和所有主流数据库系统进行通信。类似的，目前业界正冒出一批基于Java连接器体系标准的EI适配器，也用于提供各类legacy system和ERP/CRM的无缝集成。 <BR><BR>· 引进面向服务的体系结构 <BR>随着Web服务以及SOAP等开放标准的出现，企业异构系统之间的互操作性成为可能。J2EE，作为一个可扩展平台，很自然需要加入Web服务特性。为此，Sun公司发布了一整套称为“JAX包”的API，支持从XML语法分析、XML绑定、SOAP消息发送、注册表查寻、XML RPC到XML消息传递等所有各种Web服务需求。 <BR><BR>虽然J2EE平台的出现早于Web服务技术，但它的可扩展能力使它能很好地适应技术的最新发展。我们有理由相信，在未来，J2EE将引入更多的技术进步而不会动摇它的核心框架和应用-编程模型。 <BR><BR>结束语 <BR>作为一个被业界广泛采用的中间件标准，J2EE是开发可伸缩的、具有负载平衡能力的多层分布式跨平台企业应用的理想平台。J2EE的首要任务在于提供一个标准中间件基础架构，由该基础架构负责处理企业开发中所涉及的所有系统级问题，从而使得开发人员可以集中精力重视商业逻辑的设计和应用的表示，提高开发工作的效率。 <BR><BR>J2EE有效地满足了行业需求，提供独立于操作系统的开发环境。基于J2EE的应用系统灵活且易于移植和重用，可运行在不同厂家的Web服务器上。更为重要的是，J2EE是一个开放体系，完全有能力适应未来技术的进步和发展。 <BR><img src ="http://www.blogjava.net/killvin/aggbug/33308.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:59 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33308.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Entity Bean vs Hibernate Pojo </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33307.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:58:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33307.html</guid><description><![CDATA[<P><SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">该文转自guty</SPAN> <BR><BR>O-R Mapping <BR><BR>J2EE的标准是CMP Entity Bean，而实际应用中受到诟病最多的也是它。我们化了整整半年时间研究CMP2.0的开发方法，目前总算能够将代码量减少到70%，并且有希望减少到 90%。我曾经很满足现有的成绩，但是当我真正地阅读了hibernate后，对CMP2.0的信心彻底动摇了。 <BR><BR>hibernate至少比CMP2.0有以下优点： <BR>1. 兼容性。 规范一模一样，实现各有不同，这是CMP的现状。用第三方O-R Mapping工具可以解决这个问题。 <BR>2. 保护智力投资。在了解了Orion, Weblogic, JBoss的CMP实现后，我不愿意再去学习Websphere 或者Resin的实现了。 <BR>3. 性能。 <BR>a. local v.s. remote， hibernate、JDO、Castor都是本地调用，CMP2.0虽然也有Local接口，但是Web层还是需要通过Remote接口访问<SPAN style="COLOR: #ffa34f"><B>EJB</B></SPAN>层的数据,序列化、网络调用、创建大量的对象，都是性能降低的原因。 <BR>b. transaction，J2EE提出了一个全新的事务模型（method-based descriptor)，对程序员的开发确实是个“简化”，记得一本教程建议所有的<SPAN style="COLOR: #ffa34f"><B>EJB</B></SPAN>方法都用Required。但这样的结果是什么？性能极度降低！互锁！没有办法，我们只有再去调节各个方法的Transaction属性，然后又出现 新的互锁... <BR>新的事务模型是不成功的。它试图简化问题，却引入了更为严重的问题。各家厂商的Transaction实现也不尽相同，有的支持Optimistic Lock，有的在VM中同步Entity对象，又是兼容性的一大敌。 <BR>hibernate没有试图创造一个更新的模式，相反，它沿用了传统数据库的Transaction编程模式，在对J2EE的Transaction伤透脑筋后看到它，真是十分亲切，感觉自己确实在编程，而不是碰运气填代码了。 <BR>4. 动态Query。 <BR>Entity Bean很难实现动态Query，这是因为它基于代码自动生成技术，即最终的执行代码是在部署编译时生成的。hibernate则有根本的改变，它基于 reflection机制，运行时动态Query是很自然的事。另外，hibernate几乎支持所有的SQL语法，传统数据库可以做的它就可以做。 <BR>5. 发展速度。 <BR>I have a dream, 有一天Entity Bean会变得很好。但至少目前来看，Entity Bean是一个不完善的产品，它是大公司政治斗争和妥协的产品，而且习惯性将一些问题“无限期搁置”，典型的例子就是Query（之所以不提其他问题，是因为其他都是Entity Bean的致命伤：）） <BR>形成强烈反差的是，hibernate的核心程序员只有一人，但它改进的速度确是Entity Bean无法企及的。 <BR>6. 继承和多态。 <BR>OO语言的精华在Entity Bean这里是行不通的，我曾经自我安慰将Entity Bean看做一个“内存中的数据表”，才找到了一点平衡。 <BR>但当我看到hibernate时，又开始不平衡了。 <BR><BR>另外，CMP2.0也有一些缺点是可以弥补的。 <BR>1. 代码维护。 <BR>大量的接口文件和配置文件，开发和维护的工作量很大。 <BR>解决途径：采用xdoclet,可以自动产生众多的接口和配置文件，甚至facade, delegate等高级模式。 <BR><BR>至少目前来看，hibernate的缺点有： <BR>1. 代码维护 <BR>hibernate提供了自动生成mapping文件“框架”的工具，但还需要手工调节。而这类开发，能想到的最佳模式就是xdoclet的（代码+注释）的模式了。幸好，hibernate的程序员已经向xdoclet项目增加了hibernate的模块。现在需要的是等待xdoclet的下一个 release。 <BR><BR>结论： <BR>hibernate至少从文档上超越了Entity Bean很多，我要学习hibernate。 <BR></SPAN><SPAN class=postbody></SPAN></P>
<P><SPAN class=postbody><STRONG>以下是robbin的观点</STRONG></SPAN></P>
<P><SPAN class=postbody><SPAN class=postbody>如果说不使用Session Facade模式的话，我认为EB还是一个很有意义的的东西，因为EB是唯一直接支持跨RMI的持久化方案。但是由于EB的效率和减少跨RMI的网络调用的原因，EB已经完全被封装到SB的后面，EB的分布式调用的功能，EB的安全验证功能，EB的容器事务功能完全被前面的SB给做了，结果EB就只剩下了唯一的ORM功能了，单就ORM这一点来说EB实在是一个非常非常糟糕的东西。那么EB还有什么功能值得我非去用它不可呢？ <BR><BR>用 Session Bean + DAO + Hibernate 来取代 Session Bean + Entity Bean，不但能够极大降低软件设计难度，软件开发难度，软件调试难度和软件部署难度，而且还可以提高允许效率，降低硬件要求。 <BR><BR>不要把EB直接拿来和Hibernate做比较，两者不是一个范畴的东西，而应该整体比较两种方案： <BR>Session Bean + DAO + Hibernate <BR>Session Bean + Entity Bean <BR>我找不出来第二方案有哪怕一点方面能够比第一方案好的。 <BR><BR>CMP可以使用CMR来表示多表之间通过外键关联的关系。但是你仍然会遇到即使没有键关联的表仍然需要连接查询的情况，这是一个非常普遍的现象。 <BR><BR>如果是Hibernate，可以在HSQL里面定义outer join，BMP也可以写JDBC，而CMP没有任何办法来解决该问题，除非你把需要的连接查询都定义为CMR，但那样的话，凡是有需要连接查询，或者有键关联的表都必须打在一个包里面。你如果不打在一个jar包里面，如果能够建立CMR？不是我想放在一个jar里面，而是不得不放在一个jar里面。基本上CMP还是非常笨拙的。 <BR><BR>CMP的另一大缺点是不能动态SQL，guty已经提到了，一个SQL就要定义一个EJBFinder方法，在编译的时候就确定死了。在实际应用中，经常会遇到不确定查询条件的查询，比如说用户在页面上用下拉列表来选择查询的条件，用户既有可能什么限制条件都不选，也有可能选择某几个条件。这时候你怎么办？假设有n个查询条件，你要写 C1n + C2n + C3n +...+ Cnn(C是组合公式的符合，n是下标，1...n是上标)个EJBFinder方法才行，很恐怖吧。 <BR><BR>其实JDBC的PrepareStatement也不能很好的解决这个问题，因为它是按照1，2这样的次序来set参数的。用Statement是肯定不行的，会严重影响数据库，甚至会导致数据库down掉(我的实际经验)。但是Hibernate就解决的不错，因为它可以按照 :name 这样的形式来设定SQL中的Placeholder，这样set参数就可以按照参数名称传递，因为次序不是死的，在程序里面就很容易根据用户选择的查询条件，动态的产生SQL，动态的set参数了。 <BR><BR>CMP2.0还有一个大问题是不支持order by，当然你可以在Java里面对取出来的集合排序，但是速度和数据库里面就排好序速度不在一个数量级了。Hibernate不但可以order by，还可以group by，having，子查询，真是没有办法比下去了。 <BR><BR>其实对于动态SQL和排序问题，特定的App Server也可以做，但那不是CMP2.0的规范罢了，所以为了可移植性，也不敢随便去用。 <BR><BR>在项目开发时， 开发和运行效率以及灵活性是非常重要的指标。由于Entity Bean天生是一种粗粒度的使用方式，这就必定使它在装载的时候有较长的响应时间，也不能自如的支持懒装入的方式，使用成细粒度会使程序变得复杂，以及远程调用细粒度的entity bean是一种非常可怕的行为, 太慢了. <BR><BR>Hibernate正好满足开发和运行效率以及灵活性，说来说去，它可以称做一个OO化的JDBC, 这样大家就不会对Hibernate产生误解及恐惧心理。它支持粗细两种粒度方式，运用起来灵活自如，前提是你必知道如何使用，一个entity bean 实现要N种重复的方法, such as ejbRemove,ejbstore,<SPAN style="COLOR: #ffa34f"><B>ejb</B></SPAN>...., 光类也有一大堆，象Home Interface, Romote Interface..., Primary class if necessary. Hibernate只需要一个就行了。 <BR><BR>CMP在进行O/R Mapping方面只是做了最基础的工作而已，完全用CMP做数据层，会发现你在把数据库应该做的工作全部都搬到App Server里面来重新实现一遍，有这必要吗？ <BR><BR>CMP是把EJBQL写死在<SPAN style="COLOR: #ffa34f"><B>ejb</B></SPAN>-jar.xml里面的，所以n个条件就需要(c0n+c1n+...cnn )2的n次方个EJBFinder方法，简直没有办法说。 <BR><BR>JDBC实现PrepareStatement的动态SQL构造不是不能够，而是非常麻烦，需要写一个非常非常大的if elseif else嵌套的判断。 <BR><BR>Hibernate实现起来特别简单，（其实OJB也实现了PrepareStatement的动态SQL构造）这本身并不复杂，但是需要你多写些代码而已，由于CMP把EJBQL写死在配置文件里面了，你连选择的余地都没有。</SPAN></SPAN></P><img src ="http://www.blogjava.net/killvin/aggbug/33307.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:58 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33307.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Aaron Johnson对Class.forName()的解释！ </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33305.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:57:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33305.html</guid><description><![CDATA[<P>A reader asked a question via a comment a couple months ago that I didn't really have an answer for (and had always kind of wondered the same thing). In <A href="http://cephas.net/blog/2004/05/10/coldfusion_mx_and_javasql.html"><FONT color=#000080>the original post</FONT></A> (which showed how to use JDBC with ColdFusion), I used the following snippet of code:<BR><CODE><BR>Class.forName("jdbc.DriverXYZ");<BR>Connection con = DriverManager.getConnection(url,<BR>&nbsp;&nbsp;"myLogin", "myPassword");<BR></CODE><BR>and the reader wanted to know what the <CODE>Class.forName(..)</CODE> method did. The most common answer you'll hear is that it loads the database driver, which, while technically true, is shallow. Where does it get loaded? How does it happen? And why?</P>
<P>To answer the question I started with the <A href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#forName(java.lang.String,%20boolean,%20java.lang.ClassLoader)"><FONT color=#000080>JavaDoc for the Class.forName() method</FONT></A>. According to the documentation, the method: </P>
<BLOCKQUOTE>... attempts to locate, load, and link the class or interface</BLOCKQUOTE>I wasn't perfectly clear on what "locate, load, and link" meant, so I did a little digging through the <A href="http://java.sun.com/docs/books/jls/"><FONT color=#000080>Java Language Specification</FONT></A>. According to <A href="http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44411"><FONT color=#000080>chapter 12 of the JLS</FONT></A>: 
<BLOCKQUOTE>Loading refers to the process of finding the binary form of a class or interface type with a particular name, perhaps by computing it on the fly, but more typically by retrieving a binary representation previously computed from source code by a compiler, and constructing, from that binary form, a Class object to represent the class or interface.</BLOCKQUOTE>Next, again according to the JLS, it must be transformed from it's binary representation to something the Java virtual machine can use, this process is called linking. Finally, the class is initialized, which is the process that executes the static initializer and the initializers for static fields declared in the class. 
<P></P>
<P>So then back to the original problem, when Class.forName() is called with an argument like this:<BR><CODE><BR>Class.forName("org.gjt.mm.mysql.Driver");<BR></CODE><BR>the classloader attempts to load and link the <CODE>Driver</CODE> class in the "org.gjt.mm.mysql" package and if successful, the static initializer is run. The MySQL <CODE>Driver</CODE> (download the <A href="http://dev.mysql.com/downloads/connector/j/3.1.html"><FONT color=#000080>source code</FONT></A>) static initializer looks like this:<BR><CODE><BR>static {<BR>&nbsp;&nbsp;try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;java.sql.DriverManager.registerDriver(new Driver());<BR>&nbsp;&nbsp;} catch (SQLException E) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException("Can't register driver!");<BR>&nbsp;&nbsp;}<BR>}<BR></CODE><BR>So it calls a static method in the <A href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DriverManager.html"><FONT color=#000080>java.sql.DriverManager</FONT></A> class which apparently registers a copy of itself when it loads.</P>
<P>So now I understand the where and the how, what about why? To understand the why you have to look at the next line in the initial code example:<BR><CODE><BR>Connection con = DriverManager.getConnection(url,<BR>&nbsp;&nbsp;"myLogin", "myPassword");<BR></CODE><BR>The <CODE>DriverManager</CODE> class (view DriverManager source <A href="http://developer.classpath.org/doc/java/sql/DriverManager-source.html"><FONT color=#000080>here</FONT></A>) returns a database connection given a JDBC URL string, a username and a password. In order to create that connection, the DriverManager class has to know which database driver you want to use. It does that by iterating over the array (internally a Vector) of drivers that have registered with it (ie: the <CODE>registerDriver(Driver driver)</CODE> method illustrated above) and calls the <CODE>acceptsURL(url))</CODE> method on each driver in the array, effectively asking the driver to tell it whether or not it can handle the JDBC URL. </P>
<P>So there you have it. Class.forName explained.</P><img src ="http://www.blogjava.net/killvin/aggbug/33305.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:57 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33305.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java基础问题：java.lang.String - intern() </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33304.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:56:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33304.html</guid><description><![CDATA[<P>原文: <A href="http://forum.javaeye.com/viewtopic.php?t=17912" target=_blank><FONT color=#002c99>http://forum.javaeye.com/viewtopic.php?t=17912</FONT></A><BR><BR><BR>搂主的问题问的含含糊糊:flyjie给出了非常详细的解释，<BR>不过就是没有解释String实例化的特殊方面以及Intern()方法的含义<BR><BR>-------<BR>---------------------------------------------------------------------------------<BR>---------------------------------------------------------------------------------<BR>java代码: <BR>String str ; <BR><BR>这样声明str它只支是一个对象的reference，不会产生实际的对象。如果没有初始化str，编译时便会发生错误。 <BR>java代码: <BR>String str1=new String("test"); <BR>String str2 = "test"; <BR><BR>str1是一个新的对象。new关键字的意思就是创建某个新的对象。而str2是一个对象的引用。　它们的内容相同，但内存地址是不一样的。 java中对象的引用存在Stack(栈)中，而对象由Heap(堆)分配空间。 <BR><BR>3、引用==变量? 　不一定 <BR>java代码: <BR><BR>　 public class TestString { <BR>public static void main(String[] args) { <BR>String s1 = "test"; <BR>String s2 = new String("test"); <BR>if (s1 == s2) <BR>System.out.println("s1 == s2"); <BR>else <BR>System.out.println("s1 != s2"); <BR>if (s1.equals(s2)) <BR>System.out.println("s1 equals s2"); <BR>else System.out.println("s1 not equals s2"); <BR>} <BR>} <BR><BR>我们将 s2 用 new 操作符创建程序输出：s1 != s2 s1 equals s2. <BR>java代码: <BR><BR>s2 = s2.intern(); <BR><BR>在你加上这句话后，上面的程序输入：s1 == s2　s1 equals s2 <BR><BR>而String a = "test" ; String b = "test" ; a == b 会返回true; 这里a="test"时创建一个在栈中的reference, b=test时jvm发现栈中已存在名为"test"的字符串，直接引用。结论：String 是个对象，要对比两个不同的String对象的值是否相同明显的要用到 equals() 这个方法. 而== 比较的是内存地址的值。 <BR><BR>4、private final String a = "test", 这个a属于常量，存放在常量存储空间(CS)中。 <BR><BR>5、建议你看看&lt;&lt;深入浅出java虚拟机&gt;&gt;一书。 <BR><BR>-------<BR>---------------------------------------------------------------------------------<BR>---------------------------------------------------------------------------------<BR><BR><BR><BR>总结<BR><BR>1. 在学习JAVA的时候就知道＝＝比较的是内存地址.而equals比较的是内存地址对应的值！（可是还是有很多的人问来问去的，真不知道他们JAVA的基础课程是怎么学的？！）<BR><BR>2. JAVA所有的对象都是存放在堆中的!你获取的"对象"仅仅只是对象的引用而已<BR><BR>3. String是比较特殊的对象，特殊在<BR>3.1 &gt; String a = new String("test") －此时你是在堆中实例化了一个字符串对象<BR>3.2 &gt; String b = "test"－此时JVM会先去堆中寻找这样的对象；如果有就返回此对象的引用；如果没有就重新实例化一个这样的对象！基于这样的一个过程所以JAVA要求String不可以更改值的。<BR><BR>3.3 &gt;intern()方法就是试图完成这样的一个寻找过程<BR>When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.</P>
<P>这里有一份详细的参考资料：<BR><BR>关于Java栈与堆的思考 <A href="http://www.javafan.net/article/20051123115654293.html" target=_blank><FONT color=#000080>http://www.javafan.net/article/20051123115654293.html</FONT></A></P>
<P></P><BR><img src ="http://www.blogjava.net/killvin/aggbug/33304.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:56 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33304.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Nested class </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33303.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33303.html</guid><description><![CDATA[<DIV class=content>URL : <A href="http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html" target=_blank><FONT color=#000080>http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html</FONT></A><BR><BR>网上的一些关于内部类的概念是不完整的，还是看看SUN的文档上的标准答案。<BR>...<BR><BR>Like other members, a nested class can be declared static (or not). A static nested class is called just that: a static nested class. A nonstatic nested class is called an inner class. <BR><BR>－ Nested class分为静态Static nested class 的和非静态的 inner class, 在SUN的眼里只有Nested Class！！<BR><BR><BR>As with static methods and variables, which we call class methods and variables, a static nested class is associated with its enclosing class. And like class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class — it can use them only through an object reference. <BR><BR>－ 静态的Static nested class是不可以直接调用它的外部类enclosing class的，但是可以通过外部类的引用来调用，就像你在一个类中写了main方法一样。<BR>...<BR><BR>As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's instance variables and methods. Also, because an inner class is associated with an instance, it cannot define any static members itself. <BR><BR>－非静态类inner class 可以自由的引用外部类的属性和方法，但是它与一个实例绑定在了以其，不可以定义静态的属性、方法（这点不是很理解，可能需要看JVM的类实现）<BR><BR>...<BR>class EnclosingClass {<BR>...<BR>class InnerClass {<BR>...<BR>}<BR>}<BR><BR><BR>The interesting feature about the relationship between these two classes is not that InnerClass is syntactically defined within EnclosingClass. Rather, it's that an instance of InnerClass can exist only within an instance of EnclosingClass and that it has direct access to the instance variables and methods of its enclosing instance. The next figure illustrates this idea. <BR><SPAN class=img><IMG alt="" src="http://my.opera.com/Killvin/homes/blog/classes-inner.gif"></SPAN><BR><BR>－图形化的嵌入类与外部类的关系</DIV><img src ="http://www.blogjava.net/killvin/aggbug/33303.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:55 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33303.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Difference requestDispatcher.forward and response.sendRedirect. </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33301.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:54:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33301.html</guid><description><![CDATA[<DIV class=content>引言<BR>在使用response的过程中经常会遇到跳转页面的事情，这个时候有两种情况供你选择<BR>1。就是调用<BR>ServletContext.getRequestDispatcher(java.lang.String).forward(request ,<BR>response) ;<BR>2。就是调用response.setRedirect(),可是这两个方法有什么不同呢？<BR><BR><BR>看看TSS上关于这个问题的解释：<BR>Difference request.forward() and response.sendRedirect .<BR>Posted by: Kapil Israni on August 24, 2000 in response to Message #2253 1<BR>replies in this thread<BR>i suppose u r talking bout requestDispatcher.forward() here.<BR><BR>well basically both method calls redirect u to new resource/page/servlet.<BR><BR>the difference between the two is that sendRedirect always sends a header<BR>back to the client/browser. this header then contains the<BR>resource(page/servlet) which u wanted to be redirected. the browser uses<BR>this header to make another fresh request. thus sendRedirect has a overhead<BR>as to the extra remort trip being incurred. its like any other Http request<BR>being generated by ur browser. the advantage is that u can point to any<BR>resource(whether on the same domain or some other domain). for eg if<BR>sendRedirect was called at www.mydomain.com then it can also be used to<BR>redirect a call to a resource on www.theserverside.com.<BR><BR>where as in case of forward() call, the above is not true. resources from<BR>the server, where the fwd. call was made, can only be requested for. but<BR>the major diff between the two is that forward just routes the request to<BR>the new resources which u specify in ur forward call. that means this route<BR>is made by the servlet engine at the server level only. no headers r sent<BR>to the browser which makes this very eficient. also the request and<BR>response objects remain the same both from where the forward call was made<BR>and the resource which was called.<BR><BR>i hope i have hit ur question right.<BR></DIV><img src ="http://www.blogjava.net/killvin/aggbug/33301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:54 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>初识Java内部类 </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33302.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:54:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33302.html</guid><description><![CDATA[初识Java内部类<BR>&nbsp;&nbsp;&nbsp; 提起Java内部类（Inner Class）可能很多人不太熟悉，实际上类似的概念在C++里也有，那就是嵌套类（Nested Class），关于这两者的区别与联系，在下文中会有对比。内部类从表面上看，就是在类中又定义了一个类（下文会看到，内部类可以在很多地方定义），而实际上并没有那么简单，乍看上去内部类似乎有些多余，它的用处对于初学者来说可能并不是那么显著，但是随着对它的深入了解，你会发现Java的设计者在内部类身上的确是用心良苦。学会使用内部类，是掌握Java高级编程的一部分，它可以让你更优雅地设计你的程序结构。下面从以下几个方面来介绍：<BR>&nbsp;&nbsp;&nbsp; ·第一次见面<BR>public interface Contents {<BR>&nbsp;&nbsp;&nbsp; int value();<BR>}<BR>public interface Destination {<BR>&nbsp;&nbsp;&nbsp; String readLabel();<BR>}<BR>public class Goods {<BR>&nbsp;&nbsp;&nbsp; private class Content implements Contents {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int i = 11;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int value() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return i; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; protected class GDestination implements Destination {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String label;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private GDestination(String whereTo) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label = whereTo;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String readLabel() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return label; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public Destination dest(String s) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new GDestination(s);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public Contents cont() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Content();<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR>class TestGoods {<BR>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Goods p = new Goods();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Contents c = p.cont();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Destination d = p.dest("Beijing");<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR>&nbsp;&nbsp;&nbsp; 在这个例子里类Content和GDestination被定义在了类Goods内部，并且分别有着protected和private修饰符来控制访问级别。Content代表着Goods的内容，而GDestination代表着Goods的目的地。它们分别实现了两个接口Content和Destination。在后面的main方法里，直接用 Contents c和Destination d进行操作，你甚至连这两个内部类的名字都没有看见！这样，内部类的第一个好处就体现出来了——隐藏你不想让别人知道的操作，也即封装性。 <BR>&nbsp;&nbsp;&nbsp; 同时，我们也发现了在外部类作用范围之外得到内部类对象的第一个方法，那就是利用其外部类的方法创建并返回。上例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢？当然有，其语法格式如下：<BR>&nbsp;&nbsp;&nbsp; outerObject=new outerClass(Constructor Parameters);<BR>&nbsp;&nbsp;&nbsp; outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);<BR>&nbsp;&nbsp;&nbsp; 注意在创建非静态内部类对象时，一定要先创建起相应的外部类对象。至于原因，也就引出了我们下一个话题——<BR>&nbsp;&nbsp;&nbsp; ·非静态内部类对象有着指向其外部类对象的引用<BR>&nbsp;&nbsp;&nbsp; 对刚才的例子稍作修改：<BR>public class Goods {<BR>&nbsp;&nbsp;&nbsp; private valueRate=2;<BR>&nbsp;&nbsp;&nbsp; private class Content implements Contents {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int i = 11*valueRate;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int value() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return i; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; protected class GDestination implements Destination {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String label;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private GDestination(String whereTo) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label = whereTo;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String readLabel() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return label; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public Destination dest(String s) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new GDestination(s);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public Contents cont() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Content();<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR>&nbsp;&nbsp;&nbsp; 修改的部分用蓝色显示了。在这里我们给Goods类增加了一个private成员变量valueRate，意义是货物的价值系数，在内部类Content的方法value()计算价值时把它乘上。我们发现，value()可以访问valueRate，这也是内部类的第二个好处——一个内部类对象可以访问创建它的外部类对象的内容，甚至包括私有变量！这是一个非常有用的特性，为我们在设计时提供了更多的思路和捷径。要想实现这个功能，内部类对象就必须有指向外部类对象的引用。Java编译器在创建内部类对象时，隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象，同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。<BR>&nbsp;&nbsp;&nbsp; 有人会问，如果内部类里的一个成员变量与外部类的一个成员变量同名，也即外部类的同名成员变量被屏蔽了，怎么办？没事，Java里用如下格式表达外部类的引用：<BR>outerClass.this<BR>&nbsp;&nbsp;&nbsp; 有了它，我们就不怕这种屏蔽的情况了。<BR>&nbsp;&nbsp;&nbsp; ·静态内部类<BR>&nbsp;&nbsp;&nbsp; 和普通的类一样，内部类也可以有静态的。不过和非静态内部类相比，区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套类很相像了，Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上，当然从设计的角度以及以它一些细节来讲还有区别。<BR>&nbsp;&nbsp;&nbsp; 除此之外，在任何非静态内部类中，都不能有静态数据，静态方法或者又一个静态内部类（内部类的嵌套可以不止一层）。不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。<BR>&nbsp;&nbsp;&nbsp; ·局部内部类 <BR>&nbsp;&nbsp;&nbsp; 是的，Java内部类也可以是局部的，它可以定义在一个方法甚至一个代码块之内。<BR>public class Goods1 {<BR>&nbsp;&nbsp;&nbsp;&nbsp; public Destination dest(String s) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class GDestination implements Destination {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String label;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private GDestination(String whereTo) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label = whereTo;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String readLabel() { return label; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new GDestination(s);<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Goods1 g= new Goods1();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Destination d = g.dest("Beijing");<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>}<BR>&nbsp;&nbsp;&nbsp; 上面就是这样一个例子。在方法dest中我们定义了一个内部类，最后由这个方法返回这个内部类的对象。如果我们在用一个内部类的时候仅需要创建它的一个对象并创给外部，就可以这样做。当然，定义在方法中的内部类可以使设计多样化，用途绝不仅仅在这一点。<BR>&nbsp;&nbsp;&nbsp; 下面有一个更怪的例子：<BR>public class Goods2{<BR>&nbsp;&nbsp;&nbsp;&nbsp; private void internalTracking(boolean b) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(b) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class TrackingSlip {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String id;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TrackingSlip(String s) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id = s;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getSlip() { return id; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TrackingSlip ts = new TrackingSlip("slip");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String s = ts.getSlip();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; public void track() { internalTracking(true); }<BR>&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Goods2 g= new Goods2();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.track();<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>}<BR>&nbsp;&nbsp;&nbsp; 你不能在if之外创建这个内部类的对象，因为这已经超出了它的作用域。不过在编译的时候，内部类TrackingSlip和其他类一样同时被编译，只不过它由它自己的作用域，超出了这个范围就无效，除此之外它和其他内部类并没有区别。<BR>&nbsp;&nbsp;&nbsp; ·匿名内部类 <BR>&nbsp;&nbsp;&nbsp; java的匿名内部类的语法规则看上去有些古怪，不过如同匿名数组一样，当你只需要创建一个类的对象而且用不上它的名字时，使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的：<BR>&nbsp;&nbsp;&nbsp; new interfacename(){......}; 或 new superclassname(){......};<BR>&nbsp;&nbsp;&nbsp; 下面接着前面继续举例子：<BR>public class Goods3 {<BR>&nbsp;&nbsp;&nbsp;&nbsp; public Contents cont(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Contents(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int i = 11;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int value() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return i; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>}<BR>&nbsp;&nbsp;&nbsp; 这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象，看上去的确十分简洁。<BR>&nbsp;&nbsp;&nbsp; 在java的事件处理的匿名适配器中，匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码：<BR>frame.addWindowListener(new WindowAdapter(){<BR>&nbsp;&nbsp;&nbsp;&nbsp; public void windowClosing(WindowEvent e){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.exit(0); <BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>});<BR>&nbsp;&nbsp;&nbsp; 有一点需要注意的是，匿名内部类由于没有名字，所以它没有构造函数（但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类，创建它的时候必须带上这些参数，并在实现的过程中使用super关键字调用相应的内容）。如果你想要初始化它的成员变量，有下面几种方法：<BR>&nbsp;&nbsp;&nbsp; 1.如果是在一个方法的匿名内部类，可以利用这个方法传进你想要的参数，不过记住，这些参数必须被声明为final。 <BR>&nbsp;&nbsp;&nbsp; 2.将匿名内部类改造成有名字的局部内部类，这样它就可以拥有构造函数了。 <BR>&nbsp;&nbsp;&nbsp; 3.在这个匿名内部类中使用初始化代码块。 <BR>&nbsp;&nbsp;&nbsp; ·为什么需要内部类？ <BR>&nbsp;&nbsp;&nbsp; java内部类有什么好处？为什么需要内部类？<BR>&nbsp;&nbsp;&nbsp; 首先举一个简单的例子，如果你想实现一个接口，但是这个接口中的一个方法和你构想的这个类中的一个方法的名称，参数相同，你应该怎么办？这时候，你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的，所以这样做可以完成所有你直接实现这个接口的功能。<BR>&nbsp;&nbsp;&nbsp; 不过你可能要质疑，更改一下方法的不就行了吗？<BR>&nbsp;&nbsp;&nbsp; 的确，以此作为设计内部类的理由，实在没有说服力。<BR>&nbsp;&nbsp;&nbsp; 真正的原因是这样的，java中的内部类和接口加在一起，可以的解决常被C++程序员抱怨java中存在的一个问题——没有多继承。实际上，C++的多继承设计起来很复杂，而java通过内部类加上接口，可以很好的实现多继承的效果。<BR><img src ="http://www.blogjava.net/killvin/aggbug/33302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:54 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java关键字: transient | strictfp | volatile </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33300.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:53:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33300.html</guid><description><![CDATA[<H2>Java Language Keywords </H2>
<BLOCKQUOTE>
<P>Here's a list of keywords in the Java language. These words are reserved — you cannot use any of these words as names in your programs. <CODE>true</CODE>, <CODE>false</CODE>, and <CODE>null</CODE> are not keywords but they are reserved words, so you cannot use them as names in your programs either. </P>
<P>abstract&nbsp; &nbsp; |&nbsp; &nbsp;continue&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;for&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;switch <BR>assert***&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;default&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;goto*&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;package&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;synchronized <BR>boolean&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;do&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;if&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;private&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;this <BR>break&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;double&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;implements&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;protected&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;throw <BR>byte&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;else&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;import&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;public&nbsp;&nbsp;throws <BR>case&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;enum****&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;instanceof&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;return&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;transient&nbsp; <BR>catch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;extends&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;short&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;try <BR>char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;final&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;interface&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;static&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;void <BR>class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;finally&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;long&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;strictfp**&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;volatile <BR>const*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;float&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;native&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;super&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;while </P>
<P><BR>*&nbsp;&nbsp; not used <BR>**&nbsp;&nbsp; added in 1.2 <BR>***&nbsp;&nbsp; added in 1.4&nbsp; <BR>****&nbsp;&nbsp; added in 5.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P></BLOCKQUOTE>
<P>&nbsp;</P>
<P>Key: <STRONG><FONT face="Courier New">strictfp</FONT></STRONG><SUP>**</SUP></P>
<P>使用对象：类、方法</P>
<P>自Java2以来，Java语言增加了一个关键字strictfp，虽然这个关键字在大多数场合比较少用，但是还是有必要了解一下。</P>
<P>strictfp的意思是FP-strict，也就是说精确浮点的意思。在Java虚拟机进行浮点运算时，如果没有指定strictfp关键字时，Java的编译器以及运行环境在对浮点运算的表达式是采取一种近似于我行我素的行为来完成这些操作，以致于得到的结果往往无法令你满意。而一旦使用了strictfp来声明一个类、接口或者方法时，那么所声明的范围内Java的编译器以及运行环境会完全依照浮点规范IEEE-754来执行。因此如果你想让你的浮点运算更加精确，而且不会因为不同的硬件平台所执行的结果不一致的话，那就请用关键字strictfp。</P>
<P>你可以将一个类、接口以及方法声明为strictfp，但是不允许对接口中的方法以及构造函数声明strictfp关键字，例如下面的代码：</P>
<P>1. 合法的使用关键字strictfp</P>
<P><FONT style="BACKGROUND-COLOR: #000000" color=#ffff00 size=1><FONT color=#000000><FONT style="BACKGROUND-COLOR: #ffffff"><STRONG>strictfp</STRONG> <STRONG>interface</STRONG> A {}<BR><BR>public <STRONG>strictfp</STRONG> class FpDemo1 {<BR>&nbsp;&nbsp;&nbsp; <STRONG>strictfp</STRONG> void f() {}<BR></FONT></FONT><FONT style="BACKGROUND-COLOR: #ffffff" color=#000000>}</FONT></FONT></P>
<P>2. 错误的使用方法</P>
<P><FONT style="BACKGROUND-COLOR: #ffffff" color=#000000 size=1>interface A {<BR>&nbsp;&nbsp;&nbsp; <STRONG>strictfp</STRONG> void f();<BR>}<BR><BR>public class FpDemo2 {<BR>&nbsp;&nbsp;&nbsp; <STRONG>strictfp</STRONG> FpDemo2() {}<BR>}</FONT></P>
<P>一旦使用了关键字strictfp来声明某个类、接口或者方法时，那么在这个关键字所声明的范围内所有浮点运算都是精确的，符合IEEE-754规范的。例如一个类被声明为strictfp，那么该类中所有的方法都是strictfp的。</P>
<P>&nbsp;</P>
<P>Keys: <STRONG>volatile</STRONG></P>
<P>使用对象：字段<BR><BR>介绍：因为异步线程可以访问字段，所以有些优化操作是一定不能作用在字段上的。volatile有时<BR><BR>可以代替synchronized。<BR></P>
<P>&nbsp;</P>
<P>Keys：<FONT face="Courier New"><STRONG>transient</STRONG> </FONT></P>
<P>　　使用对象：字段</P>
<P>　　介绍：字段不是对象持久状态的一部分，不应该把字段和对象一起串起。</P><img src ="http://www.blogjava.net/killvin/aggbug/33300.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:53 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33300.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java异常框架设计一 </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33299.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:50:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33299.html</guid><description><![CDATA[<P>引言 </P>
<P>&nbsp;记得当初参与某公司的ERP项目中，接触过异常框架这个概念，可是似乎并没有感觉到当时技术经理提出这个概念的意义，而且他也对这个概念似乎很"保守"，虽然按照他的思路去执行，但没有理解的概念再实施起来的时候总是觉得很"别扭"，而如今面对自己要设计咚咚了，不得不重新审视异常这个概念，JAVA异常的介绍文章在网络上非常的少，而对于如何构件J2EE的异常处理框架更显的稀少，于是就促使自己写下了这样的文章。</P>
<P>&nbsp;本文只是自己的一些愚见，希望和大家相互学习。Email:Killvin@hotmail.com</P>
<P>概念</P>
<P>什么是异常？</P>
<P>异常（exception）应该是异常事件（exceptional event）的缩写。<BR>异常定义：异常是一个在程序执行期间发生的事件，它中断正在执行的程序的正常的指令流。<BR>当在一个方法中发生错误的时候，这个方法创建一个对象，并且把它传递给运行时系统。这个对象被叫做异常对象，它包含了有关错误的信息，这些信息包括错误的类型和在程序发生错误时的状态。创建一个错误对象并把它传递给运行时系统被叫做抛出异常。<BR>一个方法抛出异常后，运行时系统就会试着查找一些方法来处理它。这些处理异常的可能的方法的集合是被整理在一起的方法列表，这些方法能够被发生错误的方法调用。这个方法列表被叫做堆栈调用（call　stack）</P>
<P>运行时系统搜寻包含能够处理异常的代码块的方法所请求的堆栈。这个代码块叫做异常处理器，搜寻首先从发生的方法开始，然后依次按着调用方法的倒序检索调用堆栈。当找到一个相应的处理器时，运行时系统就把异常传递给这个处理器。一个异常处理器要适当地考滤抛出的异常对象的类型和异常处理器所处理的异常的类型是否匹配。异常被捕获以后，异常处理器关闭。如果运行时系统搜寻了这个方法的所有的调用堆栈，而没有找到相应的异常处理器。</P>
<P>&nbsp;</P>
<P>怎么设计异常框架</P>
<P>任何的异常都是Throwable类（为何不是接口？？），并且在它之下包含两个字类Error / Exception，而Error仅在当在Java虚拟机中发生动态连接失败或其它的定位失败的时候，Java虚拟机抛出一个Error对象。典型的简易程序不捕获或抛出Errors对象，你可能永远不会遇到需要实例化Error的应用，那就让我们关心一下Exception</P>
<P>Exception中比较重要的就是RuntimeException－运行时异常（当然这个名字是存在争议的，因为任何的异常都只会发生在运行时），为什么说这个类时很重要的呢？因为它直接关系到你的异常框架的设计，仔细看RuntimeException</P>
<P>A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method but not caught.</P>
<P>－可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在 throws 子句中进行声明。 </P>
<P>也就是说你的应用应该不去“关心”（说不关心是不服责任的，但只是你不应该试图实例化它的字类）RuntimeException，就如同你不应该关心Error的产生与处理一样！RuntimeException描述的是程序的错误引起来的，因该由程序负担这个责任！（&lt;B&gt;从责任这个角度看Error属于JVM需要负担的责任;RuntimeException是程序应该负担的责任;checked exception 是具体应用负担的责任&lt;/B&gt;）</P>
<P>那就有人会问，那我该关心什么！答案就是除了Error与RuntimeException，其他剩下的异常都是你需要关心的，而这些异常类统称为Checked Exception,至于Error与RuntimeException则被统称为Unchecked Exception.</P>
<P><BR>异常的概念就这些了，即使你在网络上搜索也就不过如此，是不是感觉到有点清晰又有点模糊？那么怎么该如何在这样单薄而模糊的概念下设计J2EE的异常框架呢？</P>
<P><BR>解决方案：J2EE异常框架</P>
<P>我们拿一个模拟的例子来说明异常框架的设计过程，比如我们要对外提供doBusiness()这个业务方法</P>
<P>public void doBusiness() throws xxxBusinessException</P>
<P>当客户端调用这样的方法的时候应该这样处理异常（包括处理RuntimeException , checked exception）<BR>&lt;B&gt;记住，无论如何我们都不希望或者确切的说是不应该将RuntimeException这样的异常暴露给客户的，因为他们没有解决这个问题的责任！&lt;/B&gt;<BR>我们暂时将Struts中的某个Action看作时客户端，其中doExecute(....)要调用doBusiness()这个方法</P>
<P>public void doAction(......)<BR>{<BR>&nbsp;try<BR>&nbsp;{</P>
<P>&nbsp;&nbsp;xxx.doBusiness();<BR>&nbsp;}<BR>&nbsp;catch(Exception e)<BR>&nbsp;{<BR>&nbsp;&nbsp;&nbsp;if(e instanceof RuntimeException)&nbsp; <BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;// catch runtime exception<BR>&nbsp;&nbsp;&nbsp;&nbsp;// 你可以在这里将捕获到的RuntimeException<BR>&nbsp;&nbsp;&nbsp;&nbsp;// 将异常通知给某个负责此程序的程序员，让他知道他<BR>&nbsp;&nbsp;&nbsp;&nbsp;// 自己犯了多么低级的错误！</P>
<P><BR>&nbsp;&nbsp;&nbsp;}else<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;//checked exception such as xxxBusinessException<BR>&nbsp;&nbsp;&nbsp;&nbsp;//将这样的异常暴露给客户显示&nbsp;&nbsp;&nbsp;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;}</P>
<P>&nbsp;}<BR>}</P>
<P>我们可以这样设计xxxBusinessException</P>
<P>public class xxxBusinessException extends ApplicationException <BR>{<BR>&nbsp;&nbsp;&nbsp; public xxxBusinessException(String s){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(s);</P>
<P>};</P>
<P>import java.io.PrintStream;<BR>import java.io.PrintWriter;<BR>public class ApplicationException extends Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /** A wrapped Throwable */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected Throwable cause;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ApplicationException() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super("Error occurred in application.");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ApplicationException(String message)&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(message);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ApplicationException(String message, Throwable cause)&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(message);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.cause = cause;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Created to match the JDK 1.4 Throwable method.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Throwable initCause(Throwable cause)&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.cause = cause;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return cause;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String getMessage() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get this exception's message.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String msg = super.getMessage();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable parent = this;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Look for nested exceptions.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while((child = getNestedException(parent)) != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get the child's message.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String msg2 = child.getMessage();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // If we found a message for the child exception, <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // we append it.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (msg2 != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (msg != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg += ": " + msg2;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg = msg2;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Any nested ApplicationException will append its own<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // children, so we need to break out of here.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (child instanceof ApplicationException) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent = child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Return the completed message.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return msg;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void printStackTrace() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print the stack trace for this exception.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable parent = this;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print the stack trace for each nested exception.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while((child = getNestedException(parent)) != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (child != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.print("Caused by: ");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; child.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (child instanceof ApplicationException) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent = child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void printStackTrace(PrintStream s) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print the stack trace for this exception.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.printStackTrace(s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable parent = this;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print the stack trace for each nested exception.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while((child = getNestedException(parent)) != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (child != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.print("Caused by: ");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; child.printStackTrace(s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (child instanceof ApplicationException) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent = child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void printStackTrace(PrintWriter w) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print the stack trace for this exception.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.printStackTrace(w);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable parent = this;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print the stack trace for each nested exception.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while((child = getNestedException(parent)) != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (child != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w.print("Caused by: ");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; child.printStackTrace(w);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (child instanceof ApplicationException) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent = child;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Throwable getCause()&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return cause;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>而"聪明"的读者肯定要问我那doBusiness()这个业务方法该如何包装异常呢？</P>
<P>&nbsp;public void doBusiness() throw xxxBusinessException<BR>&nbsp;{<BR>&nbsp;&nbsp;&nbsp;try<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;execute1(); // if it throw exception1</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exexute2(); // if it throw exception 2</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.... ..... </P>
<P>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;catch (exception1 e1)<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;throw new xxxBusinessException(e1);<BR>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;catch(exception2 e2)<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;throw new xxxBusinessException(e2);<BR>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;........<BR>&nbsp;}</P>
<P>&nbsp;也可以这样</P>
<P>&nbsp;public void doBusiness() throw xxxBusinessException<BR>&nbsp;{<BR>&nbsp;&nbsp;&nbsp;try<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;execute1(); // if it throw exception1</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exexute2(); // if it throw exception 2</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.... ..... </P>
<P>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;catch (Exception e)<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;// 注意很多应用在这里根本不判断异常的类型而一股脑的采用<BR>&nbsp;&nbsp;&nbsp;&nbsp;// throw new xxxBusinessException(e);<BR>&nbsp;&nbsp;&nbsp;&nbsp;// 而这样带来的问题就是xxxBusinessException"吞掉了"RuntimeException<BR>&nbsp;&nbsp;&nbsp;&nbsp;// 从而将checked excption 与unchecked exception混在了一起！</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;// 其实xxxBusinessException属于checked excpetion ，它根本不应该也不能够理睬RuntimeException<BR>&nbsp;&nbsp;&nbsp;&nbsp;if(! e instanceof RuntimeException) throw new xxxBusinessException(e);<BR>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;}</P>
<P><BR>总结<BR>&nbsp;1。JAVA的异常分为两类: checked exception &amp; unchecked excpetion<BR>&nbsp;2。应用开发中产生的异常都应该集成自Exception 但都属于checked excpetion类型<BR>&nbsp;3。应用中的每一层在包装并传递异常的时候要过滤掉RuntimeException！<BR>&nbsp;4。从责任这个角度看Error属于JVM需要负担的责任;RuntimeException是程序应该负担的责任;checked exception 是具体应用负担的责任<BR>&nbsp;5。无论如何我们都不希望或者确切的说是不应该将RuntimeException这样的异常暴露给客户的，因为他们没有解决这个问题的责任！</P><img src ="http://www.blogjava.net/killvin/aggbug/33299.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:50 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33299.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java内部类:如何在内部类中返回外部类对象 </title><link>http://www.blogjava.net/killvin/archive/2006/03/02/33298.html</link><dc:creator>killvin</dc:creator><author>killvin</author><pubDate>Thu, 02 Mar 2006 12:49:00 GMT</pubDate><guid>http://www.blogjava.net/killvin/archive/2006/03/02/33298.html</guid><description><![CDATA[<P>Nasted Class 的介绍，请详见参考</P>
<P>今天讨论的不是不是内部类的概念，而是具体使用的一个场景－如何在内部类中返回外部对象</P>
<P>看一段代码</P>
<P>import java.util.LinkedList;<BR>import java.util.List;</P>
<P><BR>public class OuterClass<BR>{</P>
<P>&nbsp;private List listeners = new LinkedList();</P>
<P>&nbsp;public void addListeners(IListener listener)<BR>&nbsp;{<BR>&nbsp;&nbsp;this.listeners.add(listener);<BR>&nbsp;}</P>
<P><BR>&nbsp;<STRONG>private OuterClass outer = this;&nbsp;&nbsp;（1）<BR></STRONG>&nbsp;private class InnterClass<BR>&nbsp;{<BR>&nbsp;&nbsp;public void publish()<BR>&nbsp;&nbsp;{<BR><STRONG>&nbsp;&nbsp;&nbsp;//将事件发布出去 （2）<BR>&nbsp;&nbsp;&nbsp;for(int i=0;i &lt; listeners.size();i++)<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;IListener listener = (IListener) listeners.get(i);<BR>&nbsp;&nbsp;&nbsp;&nbsp;listener.receiveEvent(outer); <BR>&nbsp;&nbsp;&nbsp;}</STRONG><BR>&nbsp;&nbsp;}<BR>&nbsp;}</P>
<P><BR>&nbsp;public void execute()<BR>&nbsp;{<BR><STRONG>&nbsp;&nbsp;InnterClass in = new InnterClass(); （3）<BR>&nbsp;&nbsp;in.publish();</STRONG><BR>&nbsp;}<BR>}</P>
<P>public interface IListener<BR>{</P>
<P>public void receiveEvent(OuterClass obj);<BR>}</P>
<P>你可能觉得这个例子很别扭，在哪里让你觉得难受呢？其实问题的关键就在于接口IListener的定义，这里需要给receiveEvent方法传递的参数是外部对象！（别激动，下面我会说明需要传递的一个场景）</P>
<P>场景</P>
<P>在一个GUI系统中，我们要在画板<STRONG>WorkSpace</STRONG>（WorkSpace实现了IListener接口）上产生一颗树，但树中的每个节点的产生（绘图）是我们不知道的算法，系统只为我们提供了一些绘图的接口，并返回元素的句柄！看来我们需要"包装"一下这个绘图的句柄<STRONG>Brush</STRONG>（其实我把它叫做笔刷，因为它只知道如何"刷"出图像来，就这点本事！）并对外提供节点<STRONG>Node</STRONG>这样一个通用的类。</P>
<P>此时Node与Brush的关系就很微妙了，不过我们可以抛开这些外表，看到Node与Brush其实就是外部类与内部类的关系！－<STRONG>第一步完成了：确定了两者的关系</STRONG></P>
<P>然而，事情没有这么简单，Node类必须处理一些事件，而这些事件理所当然只有Brush能够看懂，而Node根本不知道这样的事件处理过程，现在有两个办法：办法一，让Node实现Brush所有的事件；办法二，把Brush返回回去，让它来处理自己的事件，看来办法二是个好主意，因为我可以不关心事件的种类！－<STRONG>第二步完成了：确定了事件处理的责任</STRONG></P>
<P>还没完呢，你肯定不希望<STRONG>画板</STRONG>WorkSpace面对的是<STRONG>绘图的句柄</STRONG>Brush这样的对象，相反你只希望WokSpace只知道Node的存在！IListener接口中receiveEvent方法的参数定义为OuterClass 就由此而来！－<STRONG>第三步完成：接口的定义</STRONG></P>
<P>public interface IListener<BR>{</P>
<P>public void receiveEvent(OuterClass obj);<BR>}</P>
<P>&nbsp;既然说清楚了这个问题（应该比较清楚了吧？）那改如何实现这样一个蹩脚而有无可奈何的设计呢？让我们回忆一下内部类，内部类拥有访问外部类的方法与属性的权限</P>
<P>&nbsp;private OuterClass outer = this;&nbsp;&nbsp;－ <STRONG>这个对外部类的引用就是为内部类的访问准备的</STRONG></P>
<P>&nbsp;private class InnterClass<BR>&nbsp;{<BR>&nbsp;&nbsp;public void publish()<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;//将事件发布出去&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;for(int i=0;i &lt; listeners.size();i++)<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;IListener listener = (IListener) listeners.get(i);<BR>&nbsp;&nbsp;&nbsp;&nbsp;listener.receiveEvent(<STRONG>outer</STRONG>);&nbsp; －&nbsp;<STRONG>这里不可以返回this，因为this代表的是内部类自己<BR></STRONG>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}</P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P>参考</P>
<P>Java Nested class
<SCRIPT language=javascript type=text/javascript>document.title="Java Nested class - "+document.title</SCRIPT>
 <FONT color=#000000>&nbsp; <A href="http://blog.csdn.net/Killvin/archive/2006/01/10/574983.aspx">http://blog.csdn.net/Killvin/archive/2006/01/10/574983.aspx</A></FONT></P>
<P>初识Java内部类
<SCRIPT language=javascript type=text/javascript>document.title="初识Java内部类 - "+document.title</SCRIPT>
 &nbsp;&nbsp; <A href="http://blog.csdn.net/killvin/archive/2006/01/10/574991.aspx">http://blog.csdn.net/killvin/archive/2006/01/10/574991.aspx</A></P><img src ="http://www.blogjava.net/killvin/aggbug/33298.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killvin/" target="_blank">killvin</a> 2006-03-02 20:49 <a href="http://www.blogjava.net/killvin/archive/2006/03/02/33298.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>