﻿<?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-Jungleford's Home BlogJava分舵-随笔分类-咖啡屋 - Java 技术研究 </title><link>http://www.blogjava.net/jungleford/category/933.html</link><description>Java技术研究，兼探讨历史话题</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 12:33:39 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 12:33:39 GMT</pubDate><ttl>60</ttl><item><title>SmartCalendar新增JNLP发布！</title><link>http://www.blogjava.net/jungleford/archive/2006/04/05/39487.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Wed, 05 Apr 2006 14:18:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2006/04/05/39487.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/39487.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2006/04/05/39487.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/39487.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/39487.html</trackback:ping><description><![CDATA[
		<font size="2">
		</font>
		<div>需要你的机器至少有JRE 5.0以及JWS（即Java Web Start，通常JRE和JDK 1.4以后都会带），程序启动地址：</div>
		<div>
				<a href="http://osdn.dl.sourceforge.net/sourceforge/smartcalendar/smartcalendar.jnlp">
						<font color="#618b2c">http://osdn.dl.sourceforge.net/sourceforge/smartcalendar/smartcalendar.jnlp</font>
				</a>
		</div>
		<div>在弹出的“安全警告”对话框中点“是”。</div>
		<div> </div>
		<div>在我的机器上试验远程启动成功，你愿不愿意试一下？<img src="http://spaces.msn.com/rte/emoticons/smile_regular.gif" /></div>
		<div>
				<table cellspacing="0" border="0">
						<tbody>
								<tr>
										<td valign="top">
												<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcfZQk6DSb0CuT6VOouyKe4gumHFaH63HG6uJSZ_N8cbaeAUzGhJjm488PRjvNsyVRpG7cBLBEw6YSFdjl06tH-1ayrmr5kz59y5VcPgcVfoZrgxAjN3wlmc" target="_blank">
														<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcfZQk6DSb0CuvdsdYfJ6TpPH8R2Y6MmIZwl5RIsuQtmgV2CF6CPt6SdRSj0angTi4LaG4z5lsNvyci70xW-Gvmw" border="0" />
												</a>
										</td>
										<td width="15">
										</td>
										<td valign="top">
												<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcT37l24oY1BFn8f1W2TDOG08h-1J82eRvKpwuRSoHVmJogfVqT6bsbYfYtgMjR8MIejsnzwlhzr5LZIG0mfmWb-aswccLC408POLzZ10xumCHPYFd8OlLic" target="_blank">
														<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcT37l24oY1BFExpQyeGWxCH3fYaO3TbNKRsFIRd2HNAXWn8Q7zHGdObyOFXyvRUHO-Kx6fitNg0P68Z9GE8FnZE" border="0" />
												</a>
										</td>
								</tr>
								<tr height="8">
										<td>
										</td>
								</tr>
								<tr>
										<td valign="top">
												<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcdIUFBPdOHhXbSda_QnGNPrHGRE_VcdHJjY1at6SCmVaDp45_NWI44iCXEnOrPHFFpn2xE7FRuA40NltNk80xWOqx1AaqMX9zSqVfGA9yPNd" target="_blank">
														<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcdIUFBPdOHhXokmE7Fm4VAZ1WcZSVtLhAn4S_3XHvRA0Mg_6Bnf9uP6z_etmKtVfgQo18bkL7k2vf7TZawx8cLM" border="0" />
												</a>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.blogjava.net/jungleford/aggbug/39487.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2006-04-05 22:18 <a href="http://www.blogjava.net/jungleford/archive/2006/04/05/39487.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SmartCalendar 0.1 beta released!</title><link>http://www.blogjava.net/jungleford/archive/2006/04/05/39483.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Wed, 05 Apr 2006 13:59:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2006/04/05/39483.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/39483.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2006/04/05/39483.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/39483.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/39483.html</trackback:ping><description><![CDATA[
		<font size="2">
		</font>
		<div>经过jungleford几个月业余时间的设计、编码、加工和文档化之后，时钟和日历的可视化组件库SmartCalendar终于发布到了<a href="http://sourceforge.net/"><font color="#618b2c">sourceforge</font></a>上，这是本人做的第一个（估计也可能是最后一个）比较正规化的软件，虽然目前规模很小，但个人觉得还是比较有意义的：某种程度上为JDK没有GUI日历组件的问题提供了一种可选的解决方案——当你在用<a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JFileChooser.html"><font color="#618b2c">JFileChooser</font></a>或<a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JColorChooser.html"><font color="#618b2c">JColorChooser</font></a>的时候，或许会纳闷怎么没有日期选择框呢？当然，这个工作早已有人做了，我的想法只是提高它的可重用性和规范化一些；锻炼了一些建模能力，理解了一些设计模式的思想；熟悉了一些软件设计规范化和文档化的过程；当然，嘿嘿，也锻炼了一把英语作文。</div>
		<div> </div>
		<div>您可以从<a href="https://sourceforge.net/projects/smartcalendar/"><font color="#618b2c">https://sourceforge.net/projects/smartcalendar/</font></a>访问到这个项目的主页。下面是这一版本的intro：</div>
		<div> </div>
		<div>============================================================</div>
		<div> </div>
		<div>-------------------------------------<br /><br />          SMART CALENDAR<br /><br />     for Java(TM) Applications<br /><br />-------------------------------------<br /><br />************<br />* CONTENTS *<br />************<br /><br />0. History<br /><br />1. Introduction<br /><br />2. Features<br /><br />3. Documentation and API Specification<br /><br />4. License and Announcement<br /><br />5. Acknowledgements<br /><br />6. Contact Me<br /><br /><br /><br /><br />0. HISTORY<br />----------<br /><br />The latest version of availability is SmartCalendar 0.1 beta.<br /><br />- SmartCalendar 0.1 beta [Feb 21th, 2006]<br /><br /><br /><br />1. INTRODUCTION<br />---------------<br /><br />1.1 Why do?<br /><br />As we know, class "Date" and "Calendar" in package "java.util" have provided plenty of functions to deal with the requirements related with time and calendar computation. However, we still feel a bean of GUI form in current JDK library is missing to help us fulfil the same kind of tasks, which is exactly my motivation to develop SmartCalendar.<br /><br />1.2 What is it?<br /><br />SmartCalendar is a library of graphical calendar beans, including calculagraph and calendar tools. It is purely Java based，and develop environments are:<br /><br />- SUN Java(TM) 2 Platform Standard Edition 5.0 Development Kit (JDK 5.0)<br /><br />- Eclipse Software Development Kit (SDK) 3.1 with Visual Editor<br /><br />1.3 Pre-Requisites<br /><br />JRE or JDK 5.0 or above and compatible environments. You can get them from<br /><br /><a href="http://java.sun.com/j2se/1.5.0/download.jsp" target="_blank"><font color="#006699">http://java.sun.com/j2se/1.5.0/download.jsp</font></a><br /><br />1.4 Installation<br /><br />- Download the latest version from <a href="http://sourceforge.net/." target="_blank"><font color="#006699">http://SourceForge.net/.</font></a> Current archive is<br /><br />  <em>smartcalendar-0.1beta-bin.zip</em> .............. only binary code and documents<br /><br />  <em>smartcalendar-0.1beta-src.zip</em> .............. only source code and documents<br /><br />  <em>smartcalendar-0.1beta-doc.zip</em> .............. only documents<br /><br />  <em>smartcalendar-0.1beta-all.zip</em> .............. full codes, including binary code, source code, and documents<br /><br />- Decompress the archive into an arbitrary directory, e.g. "SmartCalendar":<br /><br />  For Windows OS, use Winzip or WinRAR or similar tools.<br /><br />  For UNIX OS, use the command "unzip":<br /><br />      <em>unzip smartcalendar-0.1beta-all.zip -d /SmartCalendar</em><br /><br />  Of course you can use OS independent command "jar" as:<br /><br />      <em>jar xvf smartcalendar-0.1beta-all.zip -C SmartCalendar/</em><br /><br />  then the structure is:<br /><br />  SmartCalendar/<br />    |<br />    |_smartcalendar-0.1beta/<br />        |<br />        |_bin/<br />        |   |_   ........................... the binary codes<br />        |<br />        |_doc/<br />        |   |_   ........................... the documents<br />        |<br />        |_example/<br />        |   |_   ........................... some sample codes<br />        |<br />        |_src/<br />        |   |_   ........................... the source codes<br />        |<br />        |_build.xml   ...................... ANT build file<br />        |<br />        |_smartcalendar.jar   .............. the JAR archive of this library<br />        |<br />        |_ChangeLog.txt   .................. the log file of changes<br />        |<br />        |_LICENSE.txt   .................... the LGPL file<br />        |<br />        |_README.txt   ..................... this file<br />        |<br />        |_README-zh_cn.txt   ............... a Chinese version for this file<br />        |<br />        |_ReleaseNote.txt   ................ the release note<br /><br />  In <em>smartcalendar-0.1beta-bin.zip</em>, <em>smartcalendar-0.1beta-src.zip</em>, or <em>smartcalendar-0.1beta-doc.zip</em>, the directory structure is a little different. For example, in <em>smartcalendar-0.1beta-src.zip</em>, you can use the command<br /><br />      <em>ant</em><br /><br />to rebuild the project, of course, you must add a parameter in your environment variable list to indicate where the apache ANT program is. For convenience, I wrote two scripts, and execute the batch file<br /><br />      <em>build.bat</em><br /><br />on Windows, or<br /><br />      <em>chmod +x build.sh</em><br />      <em>./build.sh</em><br /><br />on UNIX-like OS.<br /><br />- Setup your environment variables (may be optional, depending on your JRE installation way):<br /><br />  JAVA_HOME ----------&gt; &lt;your JRE or JDK dir&gt;<br /><br />  ClassPath ----------&gt; .;%JAVA_HOME%\lib<br />                                 or<br />                                 .;%JAVA_HOME%\jre\lib<br /><br />  Path ---------------&gt; %JAVA_HOME%\bin<br /><br />- Launch the sample codes:<br /><br />  For Windows OS, open a command console, enter:<br /><br />      <em>cd SmartCalendar\smartcalendar-0.1beta\example</em><br />      <em>run.bat</em><br /><br />  For UNIX OS:<br /><br />      <em>cd SmartCalendar/smartcalendar-0.1beta/example</em><br />      <em>chmod +x run.sh</em><br />      <em>./run.sh</em><br /><br />If you want to use this library in your own program, just copy smartcalendar.jar to your workspace, and add it to your classpath.<br /><br /><br /><br />2. FEATURES<br />-----------<br /><br />Till now, the development of SmartCalendar is still ongoing, but it already has some basic functionalities including:<br /><br />- A suite of graphical clock interfaces:<br /><br />  * Digital-type clock bean<br /><br />  * Analog-type clock bean and entire extendable APIs<br /><br />- Calendar interfaces:<br /><br />  * Month calendar interface and default implementation<br /><br />  * Year calendar bean, actually it is reusage of month calendar bean<br /><br />For more details, please refer to "doc/manual.html".<br /><br /><br /><br />3. DOCUMENTATION AND API SPECIFICATION<br />--------------------------------------<br /><br />Manuals and APIs in JavaDoc format are archived in directory "<em>doc/</em>". All of them are HTML pages.<br /><br /><br /><br />4. LICENSE AND ANNOUNCEMENT<br />---------------------------<br /><br />This project is licensed under the terms defined in LICENSE.txt, it is a copy of GNU Lesser General Public License (LGPL).<br /><br />SmartCalendar is an open source project. The source code of the software is available upon requests, but must be marked with "Powered by Samuel Lee" whenever it is redistributed, or "With contributions from Samuel Lee" whenever it is modified. The author will NOT be responsible for legal liabilities caused by any redistribution with or without any modification.<br /><br />CAUTION: THIS SOFTWARE MUST NOT BE USED FOR ANY COMMERCIAL PURPOSE WITHOUT THE AUTHOR'S PERMISSION! ANY VIOLATION MAY BE FACED WITH LITIGATION!<br /><br /><br /><br />5. ACKNOWLEDGEMENTS<br />-------------------<br /><br />The codes written by Mitch Goldstein give me lots of ideas and hints, so that I used and modified some of them in my own work. If you are interested in his works, please follow this link:<br /><br /><a href="http://www-128.ibm.com/developerworks/edu/j-dw-java2d-i.html" target="_blank"><font color="#006699">http://www-128.ibm.com/developerworks/edu/j-dw-java2d-i.html</font></a><br /><br />And thanks to my friend Mr. Rong, he revised this document and gave me many advices.<br /><br /><br /><br />6. CONTACT ME<br />-------------<br /><br />If you find any bugs or have any suggestions, please feel free to inform me, and I will appreciate your valuable and selfless help. You can get in touch with me by the following e-mail address:<br /><br /><a href="mailto:smartcalendar@gmail.com">smartcalendar@gmail.com</a><br /><br />Thanks for all, and all the luck in the world! Sincerely and truly,<br /><br />                                              SAMUEL LEE<br /><br />Copyright 2006 The SmartCalendar Project, Samuel Lee</div>
		<div>
				<table class="fixedTable blogpost" cellspacing="0" width="100%" border="0">
						<tbody>
								<tr>
										<td id="msgcns!E733CCEEE4BE0FB2!759">
										</td>
								</tr>
								<tr>
										<td>
												<table cellspacing="0" border="0">
														<tbody>
																<tr height="8">
																		<td>
																		</td>
																</tr>
																<tr>
																		<td valign="top">
																				<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcTqo5koQVroRHcj0bWbipgOOWArVHNFJU_InVuJ6VLUP3d7BDqL51aWFNUkOG3DjbuUhI8ub1IeMC2I17tp0rpW0PLaty6fVt_WVPSSz7OXBPMdYL8oHm3k" target="_blank">
																						<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcTqo5koQVroR8mpnDJEtt5ZuqZKm3amkvCh7jIIsvMJVxEYV_SXubsLUnj8OMBiB_4qN9kzEcdsAbim2m_quXK8" border="0" />
																				</a>
																		</td>
																		<td width="15">
																		</td>
																		<td valign="top">
																				<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCccgZQinWmG7XdZL2KnFlIAAQi_OYFW7vFg943QTvtzXQypsuGjsPD90tPndKD-ucNrp_OEuX6z1hZlH82cCAktYVjBAZqtnnlb1PK3fm4DcZs2LGmVDjoVE" target="_blank">
																						<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCccgZQinWmG7Xr3DseL8wxUTb6flQGhrBw-LEKOlo-Uv2uSxQJ0Di7n8m0Vh3cQbSlBjspvM-IZZB9Ji9D6Pwbjw" border="0" />
																				</a>
																		</td>
																</tr>
																<tr height="8">
																		<td>
																		</td>
																</tr>
																<tr>
																		<td valign="top">
																				<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCccjTv6azgdBFZ_dFJV6XLwAdyfQsXSN7R5AEVPPqF48i5rZXbICh3wrUZpQJqumcZVvGKVQ3h_zrhKljc9j0Ik-Zs4Scw_TcsZPgeWZowFPpP_8AKr1Wwnc" target="_blank">
																						<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCccjTv6azgdBFJesdH0abNKmnCkM0r-jkCWNl4eDdPtiLjespNanZunOFa5DjfSMtztA2zhY4OgHqCl3kX501bhg" border="0" />
																				</a>
																		</td>
																		<td width="15">
																		</td>
																		<td valign="top">
																				<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcWnSLHobTEQQLd91U6KPaVznr0SwCXAKPDYz9bok_em6s4YD5p_48GR2V6Sc-WzJpEIT68OHrgGgEnSxASr0gpcPcLmGBNTBwnxoxuCHnH2vmzra7-3kbSk" target="_blank">
																						<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcWnSLHobTEQQBae6xHtnDod9lt6KjVPhgHNMVEx6hu3X6jfektarhgCQqodx3DMDO7llNcetMhmkao3J55jJTSI" border="0" />
																				</a>
																		</td>
																</tr>
																<tr height="8">
																		<td>
																		</td>
																</tr>
																<tr>
																		<td valign="top">
																				<a href="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcdR-y6KeCWdcdEFgzcEg3LNI_wnRUCOeoEii94CInzMAl4r4z1KNeBVCWJ_RTEipf5BnZaIod4MEsZskJbv6DkBXB9Q1VetHAHooPa4AZyYw24qi5gPVYHU" target="_blank">
																						<img src="http://tk.files.storage.msn.com/x1pmAkndzHuOffRRIuJbaqCcdR-y6KeCWdcP2ILbv5Hw9eFGrX1zb9V4oT2d5GafUJ519fDZ6CstzCn4LFaNSv9XARohD-gKboS5cSK1YfRyJw" border="0" />
																				</a>
																		</td>
																</tr>
														</tbody>
												</table>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.blogjava.net/jungleford/aggbug/39483.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2006-04-05 21:59 <a href="http://www.blogjava.net/jungleford/archive/2006/04/05/39483.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MSNHC居然被人继续开发下去了！</title><link>http://www.blogjava.net/jungleford/archive/2006/04/05/39481.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Wed, 05 Apr 2006 13:47:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2006/04/05/39481.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/39481.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2006/04/05/39481.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/39481.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/39481.html</trackback:ping><description><![CDATA[
		<font size="2">
		</font>
		<div>jungleford大大惊讶了一把！啥？什么叫MSNHC？就是<a href="http://spaces.msn.com/jungleford/Blog/cns!1pSE8tZ3x3hxOv2CxSnIJQbw!407.entry"><font color="#618b2c">MSNHistoryCombiner</font></a>，去年做的一个小玩意，只是一个为了解决问题的东东，并没有正儿八经做成软件的意思。后来一位网友有意完成MSN历史记录和gaim聊天记录之间的合并工作，但我对gaim不了解，今天chow chou网友告诉我他的工作做的差不多了，看到这个小东西居然对别人有所帮助，真的很高兴<img src="http://spaces.msn.com/rte/emoticons/smile_regular.gif" /></div>
		<div> </div>
		<div>chow chou网友的工作在<a href="http://svn.redv.com/websvn/log.php?repname=hc&amp;path=/&amp;rev=0&amp;sc=0&amp;isdir=1"><font color="#618b2c">这里</font></a></div>
<img src ="http://www.blogjava.net/jungleford/aggbug/39481.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2006-04-05 21:47 <a href="http://www.blogjava.net/jungleford/archive/2006/04/05/39481.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【翻译】仿射变换</title><link>http://www.blogjava.net/jungleford/archive/2005/11/29/21829.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Tue, 29 Nov 2005 07:43:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/11/29/21829.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/21829.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/11/29/21829.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/21829.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/21829.html</trackback:ping><description><![CDATA[<H3><U><B><FONT color=#ff0000>jungleford如是说</FONT></B></U></H3>开始做模拟时钟的bean了，看<A class=l2 href="http://www-128.ibm.com/developerworks/cn/" target=_blank>dW</A>上Java&nbsp;2D的那个例子很有意思，比JDK自带的applet要简单，因为用到了仿射变换（Affine&nbsp;Transformation），只用简单得多的运算就可以绘制较高质量的动画。乘SUN的<A class=l2 href="http://gceclub.sun.com.cn/chinese_java_docs.html" target=_blank>JavaDoc中文化</A>之东风，先把java.awt.geom.<A class=l2 href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/geom/AffineTransform.html" target=_blank>AffineTransform</A>的API&nbsp;doc之一部分简单翻译一下，只涉及到一点几何和线性代数的小常识：<BR>===============================================================================<BR><BR>&nbsp;AffineTransform类描述了一种二维仿射变换的功能，它是一种二维坐标到二维坐标之间的线性变换，保持二维图形的“平直性”（译注：straightness，即变换后直线还是直线不会打弯，圆弧还是圆弧）和“平行性”（译注：parallelness，其实是指保二维图形间的相对位置关系不变，平行线还是平行线，相交直线的交角不变。大二学过的复变，“保形变换/保角变换”都还记得吧，数学就是王道啊！）。仿射变换可以通过一系列的原子变换的复合来实现，包括：平移（Translation）、缩放（Scale）、翻转（Flip）、旋转（Rotation）和剪切（Shear）。<BR>&nbsp;<BR>此类变换可以用一个3×3的矩阵来表示，其最后一行为(0,&nbsp;0,&nbsp;1)。该变换矩阵将原坐标(x,&nbsp;y)变换为新坐标(x',&nbsp;y')，这里原坐标和新坐标皆视为最末一行为(1)的三维列向量，原列向量左乘变换矩阵得到新的列向量：<BR>&nbsp;<BR>[x']&nbsp;&nbsp;&nbsp;&nbsp;[m00&nbsp;m01&nbsp;m02]&nbsp;[x]&nbsp;&nbsp;&nbsp;&nbsp;[m00*x+m01*y+m02]<BR>[y']&nbsp;=&nbsp;[m10&nbsp;m11&nbsp;m12]&nbsp;[y]&nbsp;=&nbsp;[m10*x+m11*y+m12]<BR>[1&nbsp;]&nbsp;&nbsp;&nbsp;[&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;]&nbsp;[1]&nbsp;&nbsp;&nbsp;&nbsp;[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]<BR><BR>&nbsp;<BR>几种典型的仿射变换：<BR><BR>
<P class=codeStyle><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<FONT color=#ff0000>AffineTransform</FONT>&nbsp;<A href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/geom/AffineTransform.html#getTranslateInstance(double, double)">getTranslateInstance</A>(<B><FONT color=#0000ff>double</FONT></B>&nbsp;tx,&nbsp;<B><FONT color=#0000ff>double</FONT></B>&nbsp;ty) </P>平移变换，将每一点移动到(x+tx,&nbsp;y+ty)，变换矩阵为：<BR>[&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;tx&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;ty&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;]<BR>（译注：平移变换是一种“刚体变换”，rigid-body transformation，中学学过的物理，都知道啥叫“刚体”吧，就是不会产生形变的理想物体，平移当然不会改变二维图形的形状。同理，下面的“旋转变换”也是刚体变换，而“缩放”、“错切”都是会改变图形形状的。）&nbsp;<BR>
<P class=codeStyle><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<FONT color=#ff0000>AffineTransform</FONT>&nbsp;<A href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/geom/AffineTransform.html#getScaleInstance(double, double)">getScaleInstance</A>(<B><FONT color=#0000ff>double</FONT></B>&nbsp;sx,&nbsp;<B><FONT color=#0000ff>double</FONT></B>&nbsp;sy) </P>缩放变换，将每一点的横坐标放大（缩小）至sx倍，纵坐标放大（缩小）至sy倍，变换矩阵为：<BR>[&nbsp;&nbsp;&nbsp;sx&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;sy&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;]<BR><BR>
<P class=codeStyle><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<FONT color=#ff0000>AffineTransform</FONT>&nbsp;<A href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/geom/AffineTransform.html#getShearInstance(double, double)">getShearInstance</A>(<B><FONT color=#0000ff>double</FONT></B>&nbsp;shx,&nbsp;<B><FONT color=#0000ff>double</FONT></B>&nbsp;shy) </P>剪切变换，变换矩阵为：<BR>[&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;shx&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;shy&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;]<BR>相当于一个横向剪切与一个纵向剪切的复合<BR>[&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;][&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;shx&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;shy&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;][&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;][&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;]<BR>（译注：“剪切变换”又称“错切变换”，指的是类似于四边形不稳定性那种性质，街边小商店那种铁拉门都见过吧？想象一下上面铁条构成的菱形拉动的过程，那就是“错切”的过程。）&nbsp;<BR>
<P class=codeStyle><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<FONT color=#ff0000>AffineTransform</FONT>&nbsp;<A href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/geom/AffineTransform.html#getRotateInstance(double)">getRotateInstance</A>(<B><FONT color=#0000ff>double</FONT></B>&nbsp;theta) </P>旋转变换，目标图形围绕原点顺时针旋转theta弧度，变换矩阵为：<BR>[&nbsp;&nbsp;&nbsp;cos(theta)&nbsp;&nbsp;&nbsp;&nbsp;-sin(theta)&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;sin(theta)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cos(theta)&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;]<BR><BR>
<P class=codeStyle><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<FONT color=#ff0000>AffineTransform</FONT>&nbsp;<A href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/geom/AffineTransform.html#getRotateInstance(double, double, double)">getRotateInstance</A>(<B><FONT color=#0000ff>double</FONT></B>&nbsp;theta,&nbsp;<B><FONT color=#0000ff>double</FONT></B>&nbsp;x,&nbsp;<B><FONT color=#0000ff>double</FONT></B>&nbsp;y) </P>旋转变换，目标图形以(x,&nbsp;y)为轴心顺时针旋转theta弧度，变换矩阵为：<BR>[&nbsp;&nbsp;&nbsp;cos(theta)&nbsp;&nbsp;&nbsp;&nbsp;-sin(theta)&nbsp;&nbsp;&nbsp;&nbsp;x-x*cos+y*sin]<BR>[&nbsp;&nbsp;&nbsp;sin(theta)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cos(theta)&nbsp;&nbsp;&nbsp;&nbsp;y-x*sin-y*cos&nbsp;]<BR>[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]<BR>相当于两次平移变换与一次原点旋转变换的复合：<BR>[1&nbsp;&nbsp;0&nbsp;&nbsp;-x][cos(theta)&nbsp;&nbsp;-sin(theta)&nbsp;&nbsp;0][1&nbsp;&nbsp;0&nbsp;&nbsp;x]<BR>[0&nbsp;&nbsp;1&nbsp;&nbsp;-y][sin(theta)&nbsp;&nbsp;&nbsp;cos(theta)&nbsp;&nbsp;0][0&nbsp;&nbsp;1&nbsp;&nbsp;y]<BR>[0&nbsp;&nbsp;0&nbsp;&nbsp;1&nbsp;][&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;][0&nbsp;&nbsp;0&nbsp;&nbsp;1] <BR><img src ="http://www.blogjava.net/jungleford/aggbug/21829.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-11-29 15:43 <a href="http://www.blogjava.net/jungleford/archive/2005/11/29/21829.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从Jar包获取资源的方法</title><link>http://www.blogjava.net/jungleford/archive/2005/06/11/5907.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 11 Jun 2005 04:41:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/06/11/5907.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/5907.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/06/11/5907.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/5907.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/5907.html</trackback:ping><description><![CDATA[<P><FONT face=Verdana color=#ff0000><STRONG><U>jungleford如是说</U></STRONG></FONT></P>
<P><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp; 可能有不少初学者会有这样的困惑（以前我也有过）：在你的代码里调用了一些资源文件，如图片，音乐等，在调试环境或单独运行的时候可以正常显示或播放，而一旦打包到jar文件中，这些东东就再也出不来了，除非把这个jar放到原来未打包以前的目录下，但通常jar是单独发布的。这里介绍一个解决这类问题的方法。</FONT></P>
<P><FONT face=Verdana color=#ff0000><STRONG><U>getResource和getResourceAsStream</U></STRONG></FONT></P>
<P><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp; 问题的根源还是在于老生常谈的所谓class path，不信的话你在系统环境变量里的ClassPath加上你的jar文件，这下你就看得到你的图片了！但单独发布jar的话不可能指望每次都让用户为你的jar而专门修改classpath。那么有没有什么办法一劳永逸地搞定它呢？我们需要从类的装载入手。先扯远一点，在开发JSP之类的Web应用程序的时候要用到第三方的库怎么办？通常的做法是把这些库（可以是class，也可以是jar）统统放到WEB-INF/lib/目录下面，为什么这样系统就认了呢？因为Web容器（譬如Tomcat）在装载类的时候有自己的组织方式（可以参考<A href="http://jakarta.apache.org/tomcat/tomcat-4.1-doc/class-loader-howto.html">Tomcat手册</A>）。特别地，jar也是类装载器的一个可访问媒介，ClassLoader提供了两个方法用于从装载的类路径中取得资源：<BR></FONT><FONT face=Verdana size=2></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;URL&nbsp;getResource(String&nbsp;name);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;InputStream&nbsp;getResourceAsStream(String&nbsp;name);</SPAN></DIV></DIV>
<P><BR>这里name是资源的类路径，它是相对与“/”根路径下的位置。getResource得到的是一个URL对象来定位资源，而getResourceAsStream取得该资源输入流的引用保证程序可以从正确的位置抽取数据。<BR>&nbsp;&nbsp;&nbsp; 真正使用的不是ClassLoader的这两个方法，而是Class的getResource和getResourceAsStream方法，因为Class对象可以从你的类得到（如YourClass.class或YourClass.getClass()），而ClassLoader则需要再调用一次YourClass.getClassLoader()方法，但根据JDK文档的说法，Class对象的这两个方法其实是“委托”（delegate）给装载它的ClassLoader来做的，所以只需要使用Class对象的这两个方法就可以了。<BR>&nbsp;&nbsp;&nbsp; 在参考资料中有一篇老外写的文章比较深入介绍了从jar中装载资源的方法。</FONT></P>
<P><FONT face=Verdana color=#ff0000><STRONG><U>一个应用的例子</U></STRONG></FONT></P>
<P><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp; 以下是在我写的一个小工具MSNHistoryCombiner中用到的一段代码，可以从jar中装载图片和文本信息。譬如，你的jar中根目录下有个img目录，里面放有一些图片，如img1.jpg，你可以这样调用<BR></FONT><FONT face=Verdana size=2></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">Utilities.getImageFromJar(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">/img/img1.jpg</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;YourClass.</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">);</SPAN></DIV></DIV>
<P><BR>注意必须这里是“/img/img1.jpg”而非“img/img1.jpg”。从jar中读文本资源也是类似方法调用getTextFromJar。<BR>&nbsp;&nbsp;&nbsp; 需要说明的是，这段代码也不是我原创的，是从一段别的代码中经过修改得到的，但原代码的来源忘记了，在这里向原作者表示感谢和歉意。</FONT><FONT face=Verdana size=2></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">import&nbsp;java.io.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>import&nbsp;java.awt.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;Utilities<BR><IMG id=Codehighlighter1_61_2162_Open_Image onclick="this.style.display='none'; Codehighlighter1_61_2162_Open_Text.style.display='none'; Codehighlighter1_61_2162_Closed_Image.style.display='inline'; Codehighlighter1_61_2162_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_61_2162_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_61_2162_Closed_Text.style.display='none'; Codehighlighter1_61_2162_Open_Image.style.display='inline'; Codehighlighter1_61_2162_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align=top></SPAN><SPAN id=Codehighlighter1_61_2162_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_61_2162_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG id=Codehighlighter1_65_263_Open_Image onclick="this.style.display='none'; Codehighlighter1_65_263_Open_Text.style.display='none'; Codehighlighter1_65_263_Closed_Image.style.display='inline'; Codehighlighter1_65_263_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_65_263_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_65_263_Closed_Text.style.display='none'; Codehighlighter1_65_263_Open_Image.style.display='inline'; Codehighlighter1_65_263_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_65_263_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</SPAN><SPAN id=Codehighlighter1_65_263_Open_Text><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">*<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;*&nbsp;&lt;p&gt;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;*&nbsp;Description:&nbsp;Return&nbsp;an&nbsp;Image&nbsp;based&nbsp;on&nbsp;the&nbsp;supplied&nbsp;image&nbsp;identifier.&nbsp;The<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;*&nbsp;image&nbsp;is&nbsp;assumed&nbsp;to&nbsp;reside&nbsp;at&nbsp;the&nbsp;defined&nbsp;location&nbsp;within&nbsp;the&nbsp;same<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;*&nbsp;repository&nbsp;as&nbsp;this&nbsp;class.<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">*/</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">static</SPAN><SPAN style="COLOR: #000000">&nbsp;Image&nbsp;getImageFromJar(final&nbsp;String&nbsp;imageId,&nbsp;Class&nbsp;c)<BR><IMG id=Codehighlighter1_336_1519_Open_Image onclick="this.style.display='none'; Codehighlighter1_336_1519_Open_Text.style.display='none'; Codehighlighter1_336_1519_Closed_Image.style.display='inline'; Codehighlighter1_336_1519_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_336_1519_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_336_1519_Closed_Text.style.display='none'; Codehighlighter1_336_1519_Open_Image.style.display='inline'; Codehighlighter1_336_1519_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_336_1519_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_336_1519_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;Image&nbsp;reference&nbsp;initialised&nbsp;to&nbsp;null&nbsp;(the&nbsp;image&nbsp;may&nbsp;not&nbsp;be&nbsp;found).</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;Image&nbsp;image&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;Open&nbsp;a&nbsp;resource&nbsp;stream&nbsp;on&nbsp;the&nbsp;supplied&nbsp;image&nbsp;identifier.</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;InputStream&nbsp;inputStream&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;c.getResourceAsStream(imageId);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;If&nbsp;the&nbsp;image&nbsp;data&nbsp;is&nbsp;found<IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(inputStream&nbsp;</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">)<BR><IMG id=Codehighlighter1_637_1497_Open_Image onclick="this.style.display='none'; Codehighlighter1_637_1497_Open_Text.style.display='none'; Codehighlighter1_637_1497_Closed_Image.style.display='inline'; Codehighlighter1_637_1497_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_637_1497_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_637_1497_Closed_Text.style.display='none'; Codehighlighter1_637_1497_Open_Image.style.display='inline'; Codehighlighter1_637_1497_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_637_1497_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_637_1497_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;Open&nbsp;a&nbsp;byte&nbsp;array&nbsp;output&nbsp;stream&nbsp;so&nbsp;that&nbsp;we&nbsp;can&nbsp;create&nbsp;a&nbsp;byte<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;array&nbsp;with&nbsp;which&nbsp;to&nbsp;create&nbsp;the&nbsp;image.</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;ByteArrayOutputStream&nbsp;byteArrayOutputStream&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ByteArrayOutputStream();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;Attempt&nbsp;to&nbsp;copy&nbsp;the&nbsp;source&nbsp;image&nbsp;data&nbsp;into&nbsp;the&nbsp;byte&nbsp;array<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;stream,&nbsp;and&nbsp;then&nbsp;create&nbsp;an&nbsp;image&nbsp;from&nbsp;the&nbsp;result.</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000"><BR><IMG id=Codehighlighter1_985_1402_Open_Image onclick="this.style.display='none'; Codehighlighter1_985_1402_Open_Text.style.display='none'; Codehighlighter1_985_1402_Closed_Image.style.display='inline'; Codehighlighter1_985_1402_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_985_1402_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_985_1402_Closed_Text.style.display='none'; Codehighlighter1_985_1402_Open_Image.style.display='inline'; Codehighlighter1_985_1402_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_985_1402_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_985_1402_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;Read/write&nbsp;image&nbsp;data&nbsp;in&nbsp;1k&nbsp;chunks.</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;</SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[]&nbsp;bytes&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[</SPAN><SPAN style="COLOR: #000000">1024</SPAN><SPAN style="COLOR: #000000">];<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;read&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">while</SPAN><SPAN style="COLOR: #000000">&nbsp;((read&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;inputStream.read(bytes))&nbsp;</SPAN><SPAN style="COLOR: #000000">&gt;=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">)<BR><IMG id=Codehighlighter1_1163_1228_Open_Image onclick="this.style.display='none'; Codehighlighter1_1163_1228_Open_Text.style.display='none'; Codehighlighter1_1163_1228_Closed_Image.style.display='inline'; Codehighlighter1_1163_1228_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_1163_1228_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1163_1228_Closed_Text.style.display='none'; Codehighlighter1_1163_1228_Open_Image.style.display='inline'; Codehighlighter1_1163_1228_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_1163_1228_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_1163_1228_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byteArrayOutputStream.write(bytes,&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,&nbsp;read);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;Create&nbsp;an&nbsp;image&nbsp;from&nbsp;the&nbsp;resulting&nbsp;byte&nbsp;array.</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Toolkit.getDefaultToolkit().createImage(<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byteArrayOutputStream.toByteArray());<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(IOException&nbsp;exception)<BR><IMG id=Codehighlighter1_1446_1491_Open_Image onclick="this.style.display='none'; Codehighlighter1_1446_1491_Open_Text.style.display='none'; Codehighlighter1_1446_1491_Closed_Image.style.display='inline'; Codehighlighter1_1446_1491_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_1446_1491_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1446_1491_Closed_Text.style.display='none'; Codehighlighter1_1446_1491_Open_Image.style.display='inline'; Codehighlighter1_1446_1491_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_1446_1491_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_1446_1491_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exception.printStackTrace();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;image;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">static</SPAN><SPAN style="COLOR: #000000">&nbsp;String&nbsp;getTextFromJar(final&nbsp;String&nbsp;filename,&nbsp;Class&nbsp;c)<BR><IMG id=Codehighlighter1_1594_2160_Open_Image onclick="this.style.display='none'; Codehighlighter1_1594_2160_Open_Text.style.display='none'; Codehighlighter1_1594_2160_Closed_Image.style.display='inline'; Codehighlighter1_1594_2160_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_1594_2160_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1594_2160_Closed_Text.style.display='none'; Codehighlighter1_1594_2160_Open_Image.style.display='inline'; Codehighlighter1_1594_2160_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_1594_2160_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_1594_2160_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;text&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;Open&nbsp;a&nbsp;resource&nbsp;stream&nbsp;on&nbsp;the&nbsp;supplied&nbsp;file&nbsp;name.</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;InputStream&nbsp;inputStream&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;c.getResourceAsStream(filename);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;If&nbsp;the&nbsp;file&nbsp;is&nbsp;found<IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(inputStream&nbsp;</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">)<BR><IMG id=Codehighlighter1_1808_2139_Open_Image onclick="this.style.display='none'; Codehighlighter1_1808_2139_Open_Text.style.display='none'; Codehighlighter1_1808_2139_Closed_Image.style.display='inline'; Codehighlighter1_1808_2139_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_1808_2139_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1808_2139_Closed_Text.style.display='none'; Codehighlighter1_1808_2139_Open_Image.style.display='inline'; Codehighlighter1_1808_2139_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_1808_2139_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_1808_2139_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;BufferedReader&nbsp;</SPAN><SPAN style="COLOR: #0000ff">in</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;BufferedReader(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;InputStreamReader(<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inputStream));<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000"><BR><IMG id=Codehighlighter1_1925_2044_Open_Image onclick="this.style.display='none'; Codehighlighter1_1925_2044_Open_Text.style.display='none'; Codehighlighter1_1925_2044_Closed_Image.style.display='inline'; Codehighlighter1_1925_2044_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_1925_2044_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1925_2044_Closed_Text.style.display='none'; Codehighlighter1_1925_2044_Open_Image.style.display='inline'; Codehighlighter1_1925_2044_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_1925_2044_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_1925_2044_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;s;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">while</SPAN><SPAN style="COLOR: #000000">&nbsp;((s&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">in</SPAN><SPAN style="COLOR: #000000">.readLine())&nbsp;</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">)<BR><IMG id=Codehighlighter1_1997_2036_Open_Image onclick="this.style.display='none'; Codehighlighter1_1997_2036_Open_Text.style.display='none'; Codehighlighter1_1997_2036_Closed_Image.style.display='inline'; Codehighlighter1_1997_2036_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_1997_2036_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1997_2036_Closed_Text.style.display='none'; Codehighlighter1_1997_2036_Open_Image.style.display='inline'; Codehighlighter1_1997_2036_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_1997_2036_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_1997_2036_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;</SPAN><SPAN style="COLOR: #000000">+=</SPAN><SPAN style="COLOR: #000000">&nbsp;s&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">\n</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(IOException&nbsp;exception)<BR><IMG id=Codehighlighter1_2088_2133_Open_Image onclick="this.style.display='none'; Codehighlighter1_2088_2133_Open_Text.style.display='none'; Codehighlighter1_2088_2133_Closed_Image.style.display='inline'; Codehighlighter1_2088_2133_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_2088_2133_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2088_2133_Closed_Text.style.display='none'; Codehighlighter1_2088_2133_Open_Image.style.display='inline'; Codehighlighter1_2088_2133_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN id=Codehighlighter1_2088_2133_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_2088_2133_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exception.printStackTrace();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;text;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV></DIV>
<P><BR><FONT color=#ff0000 size=3><STRONG><U>参考资料</U></STRONG></FONT></P>
<P><A href="http://java.sun.com/j2se/1.5.0/docs/api/index.html">J2SE API Documentation</A></P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-onejar/">用 One-JAR 简化应用程序交付</A><BR></FONT></P><img src ="http://www.blogjava.net/jungleford/aggbug/5907.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-06-11 12:41 <a href="http://www.blogjava.net/jungleford/archive/2005/06/11/5907.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MSN历史记录合并工具MSNHistoryCombiner</title><link>http://www.blogjava.net/jungleford/archive/2005/06/11/5906.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 11 Jun 2005 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/06/11/5906.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/5906.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/06/11/5906.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/5906.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/5906.html</trackback:ping><description><![CDATA[<P><FONT size=2></FONT><FONT color=#ff0000><STRONG><U>jungleford如是说</U></STRONG></FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你是一个MSN的多年用户，是否会有这样的烦恼：可能你在好几个不同的地方上网（家中、学校、公司、使用笔记本移动等等），在每个地方的机器上都保留一套MSN聊天记录，一段时间后也许你就要离开某个地方了（譬如毕业），但又想保存那台机器上的聊天记录，怎么办？一般是把“C:\Documents and Settings\&lt;你的机器帐号&gt;\My Documents\我接收到的文件\&lt;你的MSN帐号再加上一串数字&gt;\历史记录”文件夹拷贝一份备份着，这样时间一长，你可能就有好几个这样的“历史记录”，在MSN Messenger里查看得到的只是你当前机器上的那部分记录，如果想要看其它记录，只好手动打开那些拷贝过来的XML文件了。当然，QQ可能也有类似的问题，但我早就不用它了，所以只能说“可能”<IMG height=19 src="http://www.blogjava.net/Emoticons/regular_smile.gif" width=19 border=0><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 懒汉jungleford于是就想了，能不能把这好几套聊天记录合并成一套，同时又保持记录原有的格式使MSN能够识别呢？于是<A href="http://www.google.com/">google</A>之，找了好久都没有找到，哼，于是自己动手了，趁毕业前的空闲花几天时间写了这么个小工具——MSN历史记录合并器。由于只是玩玩，可能还有一些不尽人意的地方，但大抵可以满足要求了。</FONT></P>
<P><FONT color=#ff0000><STRONG><U>使用方法</U></STRONG></FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 该程序是纯Java编写的，需要你的机器上安装JRE或JDK，如果没有，可以到以下地址下载：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A href="http://java.sun.com/j2se/1.5.0/download.jsp">http://java.sun.com/j2se/1.5.0/download.jsp</A></FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 安装完后一般直接双击我这个Combiner.jar文件就可以运行了。如果有什么问题，就在“我的电脑→属性→高级→环境变量”里添加一个名为“JAVA_HOME”的环境变量，其值为你安装的JRE或JDK的路径，然后修改“path”环境变量，在其值尾部加一个“%JAVA_HOME%\bin”。然后打开一个命令行方式的控制台，转到Combiner.jar所在的目录，键入<BR></FONT><FONT size=2><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;javaw -jar Combiner.jar<BR><BR>或<BR></FONT><FONT size=2><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java -jar Combiner.jar<BR></FONT><FONT size=2><BR>运行即可。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本工具可以合并两个历史记录目录（比如一个是你当前机器上的“<FONT style="BACKGROUND-COLOR: #a9a9a9">C:\Documents and Settings\&lt;你的机器帐号&gt;\My Documents\我接收到的文件\&lt;你的MSN帐号再加上一串数字&gt;\历史记录</FONT>”，一个是你从其它机器上拷过来的另一套MSN聊天记录，当然，这两套记录必须都属于你自己的同一个MSN帐号，同时要保证这两个目录里都有<FONT style="BACKGROUND-COLOR: #a9a9a9">MessageLog.xsl</FONT>配置文件），也可以合并两个单独的历史记录XML文件。合并完后把这些新生成的记录拷回到“C:\Documents and Settings\&lt;你的机器帐号&gt;\My Documents\我接收到的文件\&lt;你的MSN帐号再加上一串数字&gt;\历史记录”里去，以后运行MSN就可以看到所有的聊天记录啦~~~如果担心有什么问题，在拷回去之前最好先备份一下你当前的历史记录。工具虽小，但如果能给您帮一点小忙的话，jungleford也很高兴<IMG height=19 src="http://www.blogjava.net/Emoticons/regular_smile.gif" width=19 border=0></FONT></P>
<P><FONT color=#ff0000><STRONG><U>开发工具</U></STRONG></FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A href="http://java.sun.com/j2se/1.5.0/index.jsp">J2SE 5.0 SDK</A>，<A href="http://www.eclipse.org/downloads/index.php">Eclipse 3.01</A></FONT></P>
<P><FONT color=#ff0000><STRONG><U>声明</U></STRONG></FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本工具主要算法和代码是我写的，但有部分是修改自<A href="http://www.javaalmanac.com/">Java Almanac</A>的例子代码。本工具是完全自由和免费的，在Combiner.jar中已经包含了所有源代码（用jar xf命令或zip解压缩工具就看得到），您尽可以修改这些代码，也欢迎继续传播，但请注名“Powered by JUNGLEFORD”之类的说明来源的字眼。对于您的修改后传播可能产生的任何法律问题，本人概不负责。</FONT></P>
<P><FONT size=2>运行时截图<BR></FONT><FONT size=2><IMG height=419 alt=o_Combiner.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_Combiner.jpg" width=338 border=0><BR><BR>下载<BR>点<A href="http://www.blogjava.net/Files/jungleford/Combiner.rar">这里</A><BR></FONT></P><img src ="http://www.blogjava.net/jungleford/aggbug/5906.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-06-11 12:34 <a href="http://www.blogjava.net/jungleford/archive/2005/06/11/5906.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我的JavaMail客户端 - JExp</title><link>http://www.blogjava.net/jungleford/archive/2005/05/05/4022.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Wed, 04 May 2005 16:41:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/05/05/4022.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/4022.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/05/05/4022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/4022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/4022.html</trackback:ping><description><![CDATA[<FONT face=Verdana color=#ff0000><U><STRONG>jungleford如是说</STRONG></U></FONT>
<P><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;近来实在太忙了，一个邮件客户端写写停停，直到现在还是一个没怎么经过测试的命令行方式的版本，但聊胜于无，通过这个小项目学习了一下JavaMail和POP协议，又实践了一两个设计模式，感觉还是比较有意义的，以后有空会慢慢做好。ICEMail是个相当不错的客户端系统，但我试过以后还是发现它每次接受POP邮件的时候还是从头开始收起，POP协议虽然简单——的确是简单——但方便了服务器的同时却让客户端的开发人员皱眉头：不维护状态的协议需要在客户端考虑太多的东西。JavaMail虽然封装得非常好，但要写一个好的客户端并不是一件easy的事情。我只用了JavaMail本身的API，没有用SUN的provider，中间碰到了不少细节上的问题，有的地方解决方法还是老土，但觉得比啥都copy来得有意思，呵呵……现在这个版本可能有的中文邮件显示成乱码，那是因为根据RFC 822，邮件在发送时可能没有按标准编码，如果指定用中文解码可以得到解决，但在别的字符集下仍然不是一个好的方案，利用Locale可能是一个办法，等我忙完手头的先～～～</FONT></P>
<P><FONT size=2><FONT face=Verdana>JExp 0.1 命令行版本：</FONT><A href="http://www.blogjava.net/Files/jungleford/JExp.rar"><FONT face=Verdana>JExp.rar</FONT></A></FONT></P>
<P><FONT size=2><FONT face=Verdana>环境：JDK 1.4，</FONT><A href="http://java.sun.com/products/javabeans/glasgow/jaf.html"><FONT face=Verdana>JAF</FONT></A><FONT face=Verdana>，</FONT><A href="http://java.sun.com/products/javamail/downloads/index.html"><FONT face=Verdana>JavaMail</FONT></A><FONT face=Verdana>；将activation.jar和mail.jar加入classpath</FONT></FONT></P>
<P><FONT face=Verdana size=2>运行：先解开rar文件，再用 jar -x 解开jar包，在目录下运行 java com.jungleford.jexp.JExp，进入命令行用help命令显示各种命令（仿UNIX的形式）</FONT></P>
<P><FONT face=Verdana size=2>源代码：已经包含在jar包中，是完全公开的，你可以随意修改，如果你能给我一些意见建议的话，我将会非常感激&nbsp; :)</FONT></P>
<P><FONT face=Verdana size=2>截图：<BR><IMG height=431 alt=o_jexp.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_jexp.jpg" width=668 border=0><BR></FONT></P>
<P><FONT face=Verdana size=2>资料：</FONT></P>
<UL>
<LI><FONT face=Verdana size=2><A href="http://java.sun.com/products/javamail/javadocs/index.html" target=_blank>JavaMail API 文档</A></FONT>
<LI><FONT face=Verdana size=2><A href="http://java.sun.com/products/javamail/FAQ.html">JavaMail FAQ</A></FONT>
<LI><FONT size=2><A href="http://www.icemail.org/" target=_blank><FONT face=Verdana>ICEMail</FONT></A><FONT face=Verdana>&nbsp;project</FONT></FONT></LI></UL><img src ="http://www.blogjava.net/jungleford/aggbug/4022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-05-05 00:41 <a href="http://www.blogjava.net/jungleford/archive/2005/05/05/4022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaMail API FAQ</title><link>http://www.blogjava.net/jungleford/archive/2005/05/05/4021.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Wed, 04 May 2005 16:26:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/05/05/4021.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/4021.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/05/05/4021.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/4021.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/4021.html</trackback:ping><description><![CDATA[<P><U><FONT face=Verdana color=#cc0000 size=3><STRONG>jungleford如是说</STRONG></FONT></U></P>
<P><FONT color=#000000><FONT size=2>&nbsp;&nbsp;&nbsp; 近来在完善一个简单的邮件客户端程序，还好SUN J2EE SDK提供了JavaMail API，我们可以方便地调用它已经封装好的接口而不用理会协议本身的实现。但现在很多mail服务器都提供的是POP协议接收邮件，由于POP3是“无状态”的协议，服务器端不保存诸如标记这样的任何状态，你把一封邮件移到“垃圾箱”里在服务器端看来和其它邮件并没有什么区别，所以所有邮件状态的解析工作全部留给客户端处理，这就是为什么利用JavaMail编写接收邮件的程序要比编写发送邮件的程序要困难不少的原因。尽管JavaMail封装得相当不错，但写一个好的邮件客户端仍需要对基本协议有一定的了解，初次接触JavaMail的朋友可能会有一些困惑，譬如：为什么Message对象不是可序列化的对象，否则我的客户端解析起来可能方便得多；为什么对中文内容的邮件支持得不好，我应当如何实现自己的中文邮件处理方案；……这些问题的答案可以在</FONT><A href="http://java.sun.com/"><FONT color=#618b2c size=2>SUN官方网站</FONT></A><FONT size=2>上提供的</FONT><A href="http://java.sun.com/products/javamail/FAQ.html"><FONT color=#618b2c size=2>JavaMail API FAQ</FONT></A><FONT size=2>找到，它会对你的疑问有一个初步的解释，还有它的</FONT><A href="http://www.huihoo.com/java/javamail/javamail_faq.html"><FONT color=#618b2c size=2>中文译文</FONT></A><FONT size=2>转自</FONT><A href="http://gceclub.sun.com.cn/"><FONT color=#618b2c size=2>SUN中国网站</FONT></A><FONT size=2>。不过清晰的概念还是了解协议和RFC比较好，比如RFC 822等。</FONT></FONT></P><img src ="http://www.blogjava.net/jungleford/aggbug/4021.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-05-05 00:26 <a href="http://www.blogjava.net/jungleford/archive/2005/05/05/4021.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>“给你第二次机会”——小议PushbackInputStream</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2764.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 14:03:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2764.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2764.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2764.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2764.html</trackback:ping><description><![CDATA[<SPAN class=style2><STRONG><U><FONT face=Verdana color=#ff0000 size=4>jungleford如是说</FONT></U></STRONG></SPAN><FONT size=-1><FONT face=Verdana> </FONT>
<P><FONT face=Verdana>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PushbackInputStream.html" target=_blank><FONT face=Verdana color=#009933>PushbackInputStream</FONT></A><FONT face=Verdana>和</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PushbackReader.html" target=_blank><FONT face=Verdana color=#009933>PushbackReader</FONT></A><FONT face=Verdana>是</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/io/index.html" target=_blank><FONT face=Verdana color=#009933>Java I/O系统</FONT></A><FONT face=Verdana>里两个比较让人迷惑的类，我以前对它（们）就不太了解，直到某一天看了以前水母Java版的牛人zms的评论和一些资料以后才有所获益。这是几个月以前的事情了，这几天写有关序列化的总结时才想到这也不失为一个好的话题。</FONT></P>
<P class=style2><STRONG><U><FONT face=Verdana color=#ff0000 size=4>一个允许你反悔的hook</FONT></U></STRONG></P>
<P><FONT face=Verdana>&nbsp;&nbsp;&nbsp;&nbsp;Java I/O系统是一个典型的Decorator模式的实现，它以</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/InputStream.html" target=_blank><FONT face=Verdana color=#009933>InputStream</FONT></A><FONT face=Verdana>/</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/OutputStream.html" target=_blank><FONT face=Verdana color=#009933>OutputStream</FONT></A><FONT face=Verdana>为基本核心，通过继承关系，不断为该核心添加新的功能，如文件流、缓冲、加解密等。对I/O系统设计模式感兴趣的话，可以参考</FONT><A href="http://www-900.ibm.com/developerWorks/cn/" target=_blank><FONT face=Verdana color=#009933>developerWorks</FONT></A><FONT face=Verdana>上的一篇文章：</FONT><A href="http://www-900.ibm.com/developerWorks/cn/java/l-jdkdp/part3/index.shtml#2" target=_blank><FONT face=Verdana color=#009933>从Java类库看设计模式</FONT></A><FONT face=Verdana>。Java I/O默认是不缓冲流的，所谓“缓冲”就是先把从流中得到的一块字节序列暂存在一个被称为buffer的内部字节数组里，然后你可以一下子取到这一整块的字节数据，没有缓冲的流只能一个字节一个字节读，效率孰高孰低一目了然。有两个特殊的输入流实现了缓冲功能，一个是我们常用的</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/BufferedInputStream.html" target=_blank><FONT face=Verdana color=#009933>BufferedInputStream</FONT></A><FONT face=Verdana>，像读文件我们常用</FONT></P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><FONT face=Verdana><SPAN class=style3><STRONG><FONT color=#990000>BufferedInputStream</FONT></STRONG></SPAN> in = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style3><STRONG><FONT color=#990000>BufferedInputStream</FONT></STRONG></SPAN>(<SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileInputStream.html" target=_blank><STRONG><FONT face=Verdana color=#009933>FileInputStream</FONT></STRONG></A><FONT face=Verdana>(<SPAN class=style5><FONT color=#ff00ff>"datafile"</FONT></SPAN>));<BR><SPAN class=style4><STRONG><FONT color=#ff0000>while</FONT></STRONG></SPAN> ((b = in.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/BufferedInputStream.html#read()" target=_blank><FONT face=Verdana color=#009933>read</FONT></A><FONT face=Verdana>()) != <SPAN class=style6><FONT color=#0000ff>-1</FONT></SPAN>)<BR>{<BR>&nbsp;&nbsp;...<BR>}<BR>in.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/BufferedInputStream.html#close()" target=_blank><FONT face=Verdana color=#009933>close</FONT></A><FONT face=Verdana>();</FONT></FONT></TD></TR></TBODY></TABLE><FONT face=Verdana><FONT size=-1>&nbsp;</FONT> </FONT>
<P><FONT size=-1><FONT face=Verdana>&nbsp;&nbsp;&nbsp;&nbsp;这是我们几乎不用查什么</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/index.html" target=_blank><FONT face=Verdana color=#009933>JDK文档</FONT></A><FONT face=Verdana>就能信手拈来的代码段，写的时候也应该思考一下套一个BufferedInputStream的意义何在。另一个就是我们不怎么看到的PushbackInputStream（其对应的字符流模式为PushbackReader）。<BR>&nbsp;&nbsp;&nbsp;&nbsp;在通常状态下，“流”意味着“<STRONG>一次性</STRONG>”，就是说你进行了一次操作后它的状态就变了，譬如读，无论是文件还是socket，你读的过程中一个潜在的“读指针”一样的东东就在移动，你无法在读以后再重新定位（当然RandomAccessFile是另一种情况），如果你以前奇怪为什么数据库操作中ResultSet里get某个字段以后就不能再第二次get它了，这里或许是个解释。但好在PushbackInputStream给了我们<STRONG>第二次读</STRONG>的机会。我们先来区别一下“监听”和“截获”的概念，“<STRONG>监听</STRONG>”就是把得到的消息copy一份，原始消息并不作任何改变地传递到目的地；而“<STRONG>截获</STRONG>”则是先把消息“扣押”下来，不让其自动转给目标，而是先进行一些处理以后在转发给目标（如果是网络安全专业的背景知识，大概知道“监听”是对“机密性”的攻击，而“截获”不仅是对“机密性”还是对“完整性”的攻击）。有的朋友大概对<STRONG>hook</STRONG>这个名词有些了解，它是一种Windows的一种消息处理机制，似乎就是一种消息截获手段，但我对Windows编程几乎一窍不通<IMG height=19 src="http://www.blogjava.net/Emoticons/red_smile.gif" width=19 border=0>；此外，如果你熟悉Servlet的话，也能找到像Filter这样的处理机制，在对每个HTTP请求/应答进行转发之前，先在里头耍一点花招，确定哪些予以转发，哪些屏蔽掉，这也算是“截获”吧。通过上面的介绍，我们不妨把PushbackInputStream看成是对输入流的一种“截获”手段，其中最重要的方法是unread：</FONT></FONT></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><SPAN class=style4><FONT face=Verdana color=#ff0000 size=-1><STRONG>public void</STRONG></FONT></SPAN><FONT size=-1><FONT face=Verdana> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PushbackInputStream.html#unread(int)" target=_blank><FONT face=Verdana color=#009933>unread</FONT></A><FONT face=Verdana>(<SPAN class=style4><STRONG><FONT color=#ff0000>int</FONT></STRONG></SPAN> b) <SPAN class=style4><STRONG><FONT color=#ff0000>throws</FONT></STRONG></SPAN> </FONT><FONT face=Verdana><STRONG><FONT color=#990000><SPAN class=style3>IOException</SPAN><BR></FONT><SPAN class=style4><FONT color=#ff0000>public void</FONT></SPAN></STRONG> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PushbackInputStream.html#unread(byte%5B%5D)" target=_blank><FONT face=Verdana color=#009933>unread</FONT></A><FONT face=Verdana>(<SPAN class=style4><STRONG><FONT color=#ff0000>byte</FONT></STRONG></SPAN>[] b) <SPAN class=style4><STRONG><FONT color=#ff0000>throws</FONT></STRONG></SPAN> </FONT><FONT face=Verdana><STRONG><FONT color=#990000><SPAN class=style3>IOException</SPAN><BR></FONT><SPAN class=style4><FONT color=#ff0000>public void</FONT></SPAN></STRONG> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PushbackInputStream.html#unread(byte%5B%5D,%20int,%20int)" target=_blank><FONT face=Verdana color=#009933>unread</FONT></A><FONT face=Verdana>(<SPAN class=style4><STRONG><FONT color=#ff0000>byte</FONT></STRONG></SPAN>[] b, <SPAN class=style4><STRONG><FONT color=#ff0000>int</FONT></STRONG></SPAN> off, <SPAN class=style4><STRONG><FONT color=#ff0000>int</FONT></STRONG></SPAN> len) <SPAN class=style4><STRONG><FONT color=#ff0000>throws</FONT></STRONG></SPAN> <SPAN class=style3><STRONG><FONT color=#990000>IOException</FONT></STRONG></SPAN></FONT></FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P><FONT face=Verdana>&nbsp;&nbsp;&nbsp;&nbsp;我们可以想象一下，PushbackInputStream内置一个缓冲区（事实上，你可以从它的源代码里找到这个protected的字节数组），当低层流进来时先流进这个buffer，在你把流“物归原主”之前还有机会对它耍花招，然后再用unread方法“反悔”一下，把缓冲区里已经读过的内容（一般是没有被改动的，当然你也可以改动它，那就失去“归赵”的意义了，因为已经不是“完璧”了）再插入到流的头部，下次读的时候是流剩余的部分再加上从缓冲区“归还”的部分。上面三个unread方法分别代表从缓冲区“归还”一个字节、一个字节数组以及一个字节数组中指定的部分。<BR>&nbsp;&nbsp;&nbsp;&nbsp;PushbackInputStream是对二进制流的处理，字符流下相对应的就是PushbackReader。</FONT></P>
<P class=style2><STRONG><U><FONT face=Verdana color=#ff0000 size=4>有什么用？</FONT></U></STRONG></P>
<P><FONT face=Verdana>&nbsp;&nbsp;&nbsp;&nbsp;学过编译的话就容易理解了，比如从左向右扫描字符流“for(int i=0;i&lt;10;i++)”，扫描到“for”是不是就可以说是个关键字了呢？不行，说不定后面是“for1”，那就是个变量而不是关键字了，知道看到“(”才恍然大悟，哦，我可以安全地说“看到for关键字”了，但“(”还得归还给输入流，因为需要后面继续扫描。在上下文相关语言里，就更需要这种补偿机制。又如，在解析HTML文档的时候，我需要根据它的“meta”标签的“charset”属性来决定使用哪种字符集进行解析，但HTML可不是“charset”而是“&lt;html&gt;”开头的哦！所以需要通过PushbackInputStream缓冲前面一段内容，等取到字符集名称后在把读到的流全部归还，再用指定的字符集进行解析。</FONT></P>
<P class=style2><STRONG><U><FONT face=Verdana color=#ff0000 size=4>参考资料</FONT></U></STRONG></P>
<UL>
<LI><A href="http://www.china-pub.com/computers/common/info.asp?id=3345" target=_blank><FONT face=Verdana color=#009933>Java Network Programming</FONT></A><FONT face=Verdana>. by<EM> Elliotte R. Harold</EM> </FONT>
<LI><FONT face=Verdana>zms兄在水母的帖子. by <EM>zms</EM>（无奈的是，水木清华已经不能对校外开放了） </FONT>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/api/index.html" target=_blank><FONT face=Verdana color=#009933>JDK 1.4.2 Documentation</FONT></A><FONT face=Verdana>. by </FONT><A href="http://java.sun.com/" target=_blank><EM><FONT face=Verdana color=#009933>java.sun.com</FONT></EM></A></LI></UL></FONT><img src ="http://www.blogjava.net/jungleford/aggbug/2764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 22:03 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>存取程序状态的几种方法——Java I/O应用杂谈</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2763.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 14:01:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2763.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2763.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2763.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2763.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2763.html</trackback:ping><description><![CDATA[<FONT face=Verdana size=2>
<P class=style2><U><FONT color=#ff0000 size=4><STRONG>jungleford如是说</STRONG></FONT></U></P><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;已经有一个多月没有搭理blog了，原因很多，譬如实验室的项目正在收工，巨忙；譬如找工作及其相关的事情；而且二月份大部分时间是陪老爹老妈，家里拨号的速度可想而知……但主要还是没有找到一个合适的topic，或者说这段时间懒了（临毕业前期综合症），净在看《汉武大帝》和历史方面的书，还有其它乱七八糟的闲书，就是没有认真地玩Java，哈哈！现在工作差不多落实了，好在不算太烂，小资青年jungleford的生活又开始步入正轨了！以上是新年里的一些废话。<BR>&nbsp;&nbsp;&nbsp;&nbsp;今天稍微聊一点关于“程序状态保存”方面的问题，我们很容易就会想到“<STRONG>序列化</STRONG>”（<A href="http://java.sun.com/j2se/1.4.2/docs/guide/serialization/index.html" target=_blank><FONT color=#009933>Serialization</FONT></A>，有的书上又翻译为“顺序化”或者“串行化”，但“串行”一词总是让我联想到通信和硬件接口，所以我更习惯于“序列化”的叫法，何况这种叫法是有来头的，后面我会谈到<A href="#1"><FONT color=#009933>这个名称的由来</FONT></A>），当然，序列化是一种方便有效的数据存取方式，但它还有更加广泛的应用。广义上讲，就是讨论一下I/O的一些应用。 </P></FONT>
<P class=style2><U><STRONG><FONT color=#ff0000 size=4>文件I/O：文件流→序列化</FONT></STRONG></U></P><FONT size=-1>
<P>★<STRONG>文件流</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;文件操作是最简单最直接也是最容易想到的一种方式，我们说的文件操作不仅仅是通过<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileInputStream.html" target=_blank><FONT color=#009933>FileInputStream</FONT></A>/<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileOutputStream.html" target=_blank><FONT color=#009933>FileOutputStream</FONT></A>这么“裸”的方式直接把数据写入到本地文件（像我以前写的一个扫雷的小游戏JavaMine就是这样保存一局的状态的），这样就比较“底层”了。 </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH align=middle><FONT size=-1>主要类与方法</FONT></TH>
<TH align=middle><FONT size=-1>描述</FONT></TH></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>FileInputStream</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileInputStream.html#read()" target=_blank><FONT color=#009933>read</FONT></A>()</FONT></TD>
<TD><FONT size=-1>从本地文件读取<STRONG>二进制格式</STRONG>的数据</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileReader.html" target=_blank><FONT color=#009933>FileReader</FONT></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/InputStreamReader.html#read()" target=_blank><FONT color=#009933>read</FONT></A>()</FONT></TD>
<TD><FONT size=-1>从本地文件读取<STRONG>字符</STRONG>（文本）数据</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>FileOutputStream</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileOutputStream.html#write(int)" target=_blank><FONT color=#009933>write</FONT></A>()</FONT></TD>
<TD><FONT size=-1>保存二进制数据到本地文件</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileWriter.html" target=_blank><FONT color=#009933>FileWriter</FONT></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/OutputStreamWriter.html#write(int)" target=_blank><FONT color=#009933>write</FONT></A>()</FONT></TD>
<TD><FONT size=-1>保存字符数据到本地文件</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>★<STRONG>XML</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;和上面的单纯的I/O方式相比，XML就显得“高档”得多，以至于成为一种数据交换的标准。以<STRONG>DOM方式</STRONG>为例，它关心的是首先<STRONG>在内存中构造文档树</STRONG>，数据保存在某个结点上（可以是叶子结点，也可以是标签结点的属性），构造好了以后一次性的写入到外部文件，但我们只需要知道文件的位置，并不知道I/O是怎么操作的，XML操作方式可能多数人也实践过，所以这里也只列出相关的方法，供初学者预先了解一下。主要的包是<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/parsers/package-summary.html" target=_blank><FONT color=#009933>javax.xml.parsers</FONT></A>，<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/package-summary.html" target=_blank><FONT color=#009933>org.w3c.dom</FONT></A>，<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/package-summary.html" target=_blank><FONT color=#009933>javax.xml.transform</FONT></A>。 </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH align=middle><FONT size=-1>主要类与方法</FONT></TH>
<TH align=middle><FONT size=-1>描述</FONT></TH></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/parsers/DocumentBuilderFactory.html" target=_blank><FONT color=#009933>DocumentBuilderFactory</FONT></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#newDocumentBuilder()" target=_blank><FONT color=#009933>newDocumentBuilder</FONT></A>().<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/parsers/DocumentBuilder.html#parse(java.io.File)" target=_blank><FONT color=#009933>parse</FONT></A>()</FONT></TD>
<TD><FONT size=-1>解析一个外部的XML文件，得到一个Document对象的DOM树</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>DocumentBuilderFactory</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#newInstance()" target=_blank><FONT color=#009933>newInstance</FONT></A>().newDocumentBuilder().<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/parsers/DocumentBuilder.html#newDocument()" target=_blank><FONT color=#009933>newDocument</FONT></A>()</FONT></TD>
<TD><FONT size=-1>初始化一棵DOM树</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Document.html" target=_blank><FONT color=#009933>Document</FONT></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Document.html#getDocumentElement()" target=_blank><FONT color=#009933>getDocumentElement</FONT></A>(). <A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Node.html#appendChild(org.w3c.dom.Node)" target=_blank><FONT color=#009933>appendChild</FONT></A>()</FONT></TD>
<TD><FONT size=-1>为一个标签结点添加一个子结点</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>Document</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Document.html#createTextNode(java.lang.String)" target=_blank><FONT color=#009933>createTextNode</FONT></A>()</FONT></TD>
<TD><FONT size=-1>生成一个字符串结点</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Node.html" target=_blank><FONT color=#009933>Node</FONT></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Node.html#getChildNodes()" target=_blank><FONT color=#009933>getChildNodes</FONT></A>()</FONT></TD>
<TD><FONT size=-1>取得某个结点的所有下一层子结点</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>Node</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Node.html#removeChild(org.w3c.dom.Node)" target=_blank><FONT color=#009933>removeChild</FONT></A>() </FONT></TD>
<TD><FONT size=-1>删除某个结点的子结点</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>Document</STRONG></FONT></SPAN><FONT size=-1>. <A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Document.html#getElementsByTagName(java.lang.String)" target=_blank><FONT color=#009933>getElementsByTagName</FONT></A>()</FONT></TD>
<TD><FONT size=-1>查找所有指定名称的标签结点</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>Document</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Document.html#getElementById(java.lang.String)" target=_blank><FONT color=#009933>getElementById</FONT></A>()</FONT></TD>
<TD><FONT size=-1>查找指定名称的一个标签结点，如果有多个符合，则返回某一个，通常是第一个</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Element.html" target=_blank><STRONG><FONT color=#009933>Element</FONT></STRONG></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Element.html#getAttribute(java.lang.String)" target=_blank><FONT color=#009933>getAttribute</FONT></A>()</FONT></TD>
<TD><FONT size=-1>取得一个标签的某个属性的的值</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>Element</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Element.html#setAttribute(java.lang.String,%20java.lang.String)" target=_blank><FONT color=#009933>setAttribute</FONT></A>()</FONT></TD>
<TD><FONT size=-1>设置一个标签的某个属性的的值</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>Element</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/Element.html#removeAttribute(java.lang.String)" target=_blank><FONT color=#009933>removeAttribute</FONT></A>()</FONT></TD>
<TD><FONT size=-1>删除一个标签的某个属性</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/TransformerFactory.html" target=_blank><STRONG><FONT color=#009933>TransformerFactory</FONT></STRONG></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/TransformerFactory.html#newInstance()" target=_blank><FONT color=#009933>newInstance</FONT></A>().<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/TransformerFactory.html#newTransformer()" target=_blank><FONT color=#009933>newTransformer</FONT></A>().<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/Transformer.html#transform(javax.xml.transform.Source,%20javax.xml.transform.Result)" target=_blank><FONT color=#009933>transform</FONT></A>()</FONT></TD>
<TD><FONT size=-1>将一棵DOM树写入到外部XML文件</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>★<STRONG>序列化</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;使用基本的文件读写方式存取数据，如果我们仅仅保存<STRONG>相同类型</STRONG>的数据，则可以用同一种格式保存，譬如在我的JavaMine中保存一个盘局时，需要保存每一个方格的坐标、是否有地雷，是否被翻开等，这些信息组合成一个“复合类型”；相反，如果有多种<STRONG>不同类型</STRONG>的数据，那我们要么把它分解成若干部分，以相同类型（譬如String）保存，要么我们需要在程序中添加解析不同类型数据格式的逻辑，这就很不方便。于是我们期望用一种比较“高”的层次上处理数据，程序员应该<STRONG>花尽可能少的时间和代码对数据进行解析</STRONG>，事实上，序列化操作为我们提供了这样一条途径。<BR>&nbsp;&nbsp;&nbsp;&nbsp;序列化（Serialization）大家可能都有所接触，它可以把对象以某种特定的编码格式写入或从外部字节流（即<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInputStream.html" target=_blank><FONT color=#009933>ObjectInputStream</FONT></A>/<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html" target=_blank><FONT color=#009933>ObjectOutputStream</FONT></A>）中读取。序列化一个对象非常之简单，仅仅实现一下<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html" target=_blank><FONT color=#009933>Serializable</FONT></A>接口即可，甚至都不用为它专门添加任何方法： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><SPAN class=style3><STRONG><FONT color=#ff0000>public class</FONT></STRONG></SPAN> MySerial <SPAN class=style3><STRONG><FONT color=#ff0000>implements</FONT></STRONG></SPAN> java.io.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html" target=_blank><STRONG><FONT color=#009933>Serializable</FONT></STRONG></A><BR>{<BR>&nbsp;&nbsp;...<BR>}</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>但有一个条件：即<STRONG><A name=2></A>你要序列化的类当中，它的每个属性都必须是是“可序列化”的</STRONG>。这句话说起来有点拗口，其实所有<STRONG>基本类型</STRONG>（就是int，char，boolean之类的）<STRONG>都是“可序列化”的</STRONG>，而你可以看看<A href="http://java.sun.com/j2se/1.4.2/docs/api/index.html" target=_blank><FONT color=#009933>JDK文档</FONT></A>，会发现很多类其实已经实现了Serializable（即已经是“可序列化”的了），于是这些类的对象以及基本数据类型都可以直接作为你需要序列化的那个类的内部属性。如果碰到了不是“可序列化”的属性怎么办？对不起，那这个属性的类还需要事先实现Serializable接口，如此递归，<STRONG>直到所有属性都是“可序列化”的</STRONG>。 </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH align=middle><FONT size=-1>主要类与方法</FONT></TH>
<TH align=middle><FONT size=-1>描述</FONT></TH></TR>
<TR>
<TD><STRONG><FONT color=#009933 size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html" target=_blank>ObjectOutputStream</A></FONT></STRONG><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html#writeObject(java.lang.Object)" target=_blank><FONT color=#009933>writeObject</FONT></A>()</FONT></TD>
<TD><FONT size=-1>将一个对象序列化到外部字节流</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInputStream.html" target=_blank><STRONG><FONT color=#009933>ObjectInputStream</FONT></STRONG></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInputStream.html#readObject()" target=_blank><FONT color=#009933>readObject</FONT></A>()</FONT></TD>
<TD><FONT size=-1>从外部字节流读取并重新构造对象</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;从实际应用上看来，“Serializable”这个接口并没有定义任何方法，仿佛它只是一个<STRONG>标记</STRONG>（或者说像是Java的关键字）而已，一旦虚拟机看到这个“标记”，就会尝试调用自身<STRONG>预定义的序列化机制</STRONG>，除非你在实现Serializable接口的同时还定义了私有的readObject()或writeObject()方法。这一点很奇怪。不过你要是不愿意让系统使用缺省的方式进行序列化，那就必须定义上面提到的两个方法： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><SPAN class=style3><STRONG><FONT color=#ff0000>public class</FONT></STRONG></SPAN> MySerial implements java.io.<STRONG><FONT color=#990000><SPAN class=style4>Serializable</SPAN><BR></FONT></STRONG>{<BR>&nbsp;&nbsp;<SPAN class=style3><STRONG><FONT color=#ff0000>private void</FONT></STRONG></SPAN> writeObject(java.io.<SPAN class=style4><STRONG><FONT color=#990000>ObjectOutputStream</FONT></STRONG></SPAN> out) <SPAN class=style3><STRONG><FONT color=#ff0000>throws</FONT></STRONG></SPAN> <A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/IOException.html" target=_blank><STRONG><FONT color=#009933>IOException</FONT></STRONG></A><BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;...<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;<SPAN class=style3><STRONG><FONT color=#ff0000>private void</FONT></STRONG></SPAN> readObject(java.io.<SPAN class=style4><STRONG><FONT color=#990000>ObjectInputStream</FONT></STRONG></SPAN> in) <SPAN class=style3><STRONG><FONT color=#ff0000>throws</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#990000>IOException</FONT></STRONG></SPAN>, <A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassNotFoundException.html" target=_blank><STRONG><FONT color=#009933>ClassNotFoundException</FONT></STRONG></A><BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;...<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;...<BR>}</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;譬如你可以在上面的writeObject()里调用默认的序列化方法ObjectOutputStream.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html#defaultWriteObject()" target=_blank><FONT color=#009933>defaultWriteObject</FONT></A>();譬如你不愿意将某些敏感的属性和信息序列化，你也可以调用ObjectOutputStream.writeObject()方法明确指定需要序列化那些属性。关于用户<A href="#4"><FONT color=#009933>可定制的序列化方法</FONT></A>，我们将在后面提到。 </P></FONT><FONT size=-1>
<P>★<STRONG>Bean</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;上面的序列化只是一种基本应用，你把一个对象序列化到外部文件以后，用notepad打开那个文件，只能从为数不多的一些可读字符中猜到这是有关这个类的信息文件，这需要你熟悉序列化文件的字节编码方式，那将是比较痛苦的（在<A href="http://www.china-pub.com/computers/common/info.asp?id=14354" target=_blank><FONT color=#009933>《Core Java 2》第一卷</FONT></A>里提到了相关编码方式，有兴趣的话可以查看<A href="#3"><FONT color=#009933>参考资料</FONT></A>），某些情况下我们可能需要被序列化的文件具有更好的可读性。另一方面，作为Java组件的核心概念“<A href="http://java.sun.com/j2se/1.4.2/docs/guide/beans/index.html" target=_blank><FONT color=#009933>JavaBeans</FONT></A>”，从JDK 1.4开始，其规范里也要求支持文本方式的“<STRONG>长期的持久化</STRONG>”（long-term persistence）。<BR>&nbsp;&nbsp;&nbsp;&nbsp;打开<A href="http://java.sun.com/j2se/1.4.2/docs/api/index.html" target=_blank><FONT color=#009933>JDK文档</FONT></A>，<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/package-summary.html" target=_blank><FONT color=#009933>java.beans包</FONT></A>里的有一个名为“<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/package-summary.html" target=_blank><FONT color=#009933>Encoder</FONT></A>”的类，这就是一个可以序列化bean的实用类。和它相关的两个主要类有<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/XMLEncoder.html" target=_blank><FONT color=#009933>XMLEcoder</FONT></A>和<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/XMLDecoder.html" target=_blank><FONT color=#009933>XMLDecoder</FONT></A>，显然，这是以XML文件的格式保存和读取bean的工具。他们的用法也很简单，和上面ObjectOutputStream/ObjectInputStream比较类似。 </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH align=middle><FONT size=-1>主要类与方法</FONT></TH>
<TH align=middle><FONT size=-1>描述</FONT></TH></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>XMLEncoder</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/XMLEncoder.html#writeObject(java.lang.Object)" target=_blank><FONT color=#009933>writeObject</FONT></A>()</FONT></TD>
<TD><FONT size=-1>将一个对象序列化到外部字节流</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>XMLDecoder</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/XMLDecoder.html#readObject()" target=_blank><FONT color=#009933>readObject</FONT></A>()</FONT></TD>
<TD><FONT size=-1>从外部字节流读取并重新构造对象</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;如果一个bean是如下格式： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><SPAN class=style3><FONT color=#ff0000 size=-1><STRONG>public class</STRONG></FONT></SPAN><FONT size=-1> MyBean<BR>{<BR>&nbsp;&nbsp;<SPAN class=style3><STRONG><FONT color=#ff0000>int</FONT></STRONG></SPAN> i;<BR>&nbsp;&nbsp;<SPAN class=style3><STRONG><FONT color=#ff0000>char</FONT></STRONG></SPAN>[] c;<BR>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#990000>String</FONT></STRONG></SPAN> s;<BR>&nbsp;&nbsp;...(get和set操作省略)...<BR>}</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>那么通过XMLEcoder序列化出来的XML文件具有这样的形式： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<BR>&lt;java version="1.4.0" class="java.beans.XMLDecoder"&gt;<BR>&nbsp;&nbsp;&lt;object class="MyBean"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="i"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;1&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="c"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;array class="char" length="3"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;void index="0"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;a&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;void index="1"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;b&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;void index="2"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;c&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/array&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="s"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;string&gt;fox jump!&lt;/string&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&lt;/object&gt;<BR>&lt;/java&gt;</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;像<A href="http://java.sun.com/j2se/1.4.2/docs/guide/awt/index.html" target=_blank><FONT color=#009933>AWT</FONT></A>和<A href="http://java.sun.com/j2se/1.4.2/docs/guide/swing/index.html" target=_blank><FONT color=#009933>Swing</FONT></A>中很多可视化组件都是bean，当然也是可以用这种方式序列化的，下面就是从JDK文档中摘录的一个<A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFrame.html" target=_blank><FONT color=#009933>JFrame</FONT></A>序列化以后的XML文件： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<BR>&lt;java version="1.0" class="java.beans.XMLDecoder"&gt;<BR>&nbsp;&nbsp;&lt;object class="javax.swing.JFrame"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="name"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;string&gt;frame1&lt;/string&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="bounds"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;object class="java.awt.Rectangle"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;0&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;0&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;200&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;int&gt;200&lt;/int&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/object&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="contentPane"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;void method="add"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;object class="javax.swing.JButton"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="label"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;string&gt;Hello&lt;/string&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/object&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;void property="visible"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;boolean&gt;true&lt;/boolean&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/void&gt;<BR>&nbsp;&nbsp;&lt;/object&gt;<BR>&lt;/java&gt;</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;因此但你想要保存的数据是一些不是太复杂的类型的话，把它做成bean再序列化也不失为一种方便的选择。 </P>
<P>★<STRONG>Properties</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;在以前我总结的一篇关于集合框架的小文章里提到过，<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html" target=_blank><FONT color=#009933>Properties</FONT></A>是历史集合类的一个典型的例子，这里主要不是介绍它的集合特性。大家可能都经常接触一些<STRONG>配置文件</STRONG>，如Windows的ini文件，Apache的conf文件，还有Java里的properties文件等，这些文件当中的数据以<STRONG>“关键字-值”对</STRONG>的方式保存。“<STRONG>环境变量</STRONG>”这个概念都知道吧，它也是一种“key-value”对，以前也常常看到版上问“如何取得系统某某信息”之类的问题，其实很多都保存在环境变量里，只要用一条 </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html" target=_blank><STRONG><FONT color=#009933>System</FONT></STRONG></A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#getProperties()" target=_blank><FONT color=#009933>getProperties</FONT></A>().<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#list(java.io.PrintStream)" target=_blank><FONT color=#009933>list</FONT></A>(<SPAN class=style4><STRONG><FONT color=#990000>System</FONT></STRONG></SPAN>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#out" target=_blank><FONT color=#009933>out</FONT></A>);</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>就能获得全部环境变量的列表： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>
<P>-- listing properties --<BR>java.runtime.name=Java(TM) 2 Runtime Environment, Stand...<BR>sun.boot.library.path=C:\Program Files\Java\j2re1.4.2_05\bin<BR>java.vm.version=1.4.2_05-b04<BR>java.vm.vendor=Sun Microsystems Inc.<BR>java.vendor.url=http://java.sun.com/<BR>path.separator=;<BR>java.vm.name=Java HotSpot(TM) Client VM<BR>file.encoding.pkg=sun.io<BR>user.country=CN<BR>sun.os.patch.level=Service Pack 1<BR>java.vm.specification.name=Java Virtual Machine Specification<BR>user.dir=d:\my documents\项目\eclipse\SWTDemo<BR>java.runtime.version=1.4.2_05-b04<BR>java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment<BR>java.endorsed.dirs=C:\Program Files\Java\j2re1.4.2_05\li...<BR>os.arch=x86<BR>java.io.tmpdir=C:\DOCUME~1\cn2lx0q0\LOCALS~1\Temp\<BR>line.separator=</P>
<P>java.vm.specification.vendor=Sun Microsystems Inc.<BR>user.variant=<BR>os.name=Windows XP<BR>sun.java2d.fontpath=<BR>java.library.path=C:\Program Files\Java\j2re1.4.2_05\bi...<BR>java.specification.name=Java Platform API Specification<BR>java.class.version=48.0<BR>java.util.prefs.PreferencesFactory=java.util.prefs.WindowsPreferencesFac...<BR>os.version=5.1<BR>user.home=D:\Users\cn2lx0q0<BR>user.timezone=<BR>java.awt.printerjob=sun.awt.windows.WPrinterJob<BR>file.encoding=GBK<BR>java.specification.version=1.4<BR>user.name=cn2lx0q0<BR>java.class.path=d:\my documents\项目\eclipse\SWTDemo\bi...<BR>java.vm.specification.version=1.0<BR>sun.arch.data.model=32<BR>java.home=C:\Program Files\Java\j2re1.4.2_05<BR>java.specification.vendor=Sun Microsystems Inc.<BR>user.language=zh<BR>awt.toolkit=sun.awt.windows.WToolkit<BR>java.vm.info=mixed mode<BR>java.version=1.4.2_05<BR>java.ext.dirs=C:\Program Files\Java\j2re1.4.2_05\li...<BR>sun.boot.class.path=C:\Program Files\Java\j2re1.4.2_05\li...<BR>java.vendor=Sun Microsystems Inc.<BR>file.separator=\<BR>java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...<BR>sun.cpu.endian=little<BR>sun.io.unicode.encoding=UnicodeLittle<BR>sun.cpu.isalist=pentium i486 i386</P></FONT></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH align=middle><FONT size=-1>主要类与方法</FONT></TH>
<TH align=middle><FONT size=-1>描述</FONT></TH></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream)" target=_blank><FONT color=#009933>load</FONT></A>()</FONT></TD>
<TD><FONT size=-1>从一个外部流读取属性</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#store(java.io.OutputStream,%20java.lang.String)" target=_blank><FONT color=#009933>store</FONT></A>()</FONT></TD>
<TD><FONT size=-1>将属性保存到外部流（特别是文件）</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#getProperty(java.lang.String)" target=_blank><FONT color=#009933>getProperty</FONT></A>()</FONT></TD>
<TD><FONT size=-1>取得一个指定的属性</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#setProperty(java.lang.String,%20java.lang.String)" target=_blank><FONT color=#009933>setProperty</FONT></A>()</FONT></TD>
<TD><FONT size=-1>设置一个指定的属性</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#list(java.io.PrintStream)" target=_blank><FONT color=#009933>list</FONT></A>()</FONT></TD>
<TD><FONT size=-1>列出这个Properties对象包含的全部“key-value”对</FONT></TD></TR>
<TR>
<TD><SPAN class=style4><FONT color=#990000 size=-1><STRONG>System</STRONG></FONT></SPAN><FONT size=-1>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#getProperties()" target=_blank><FONT color=#009933>getProperties</FONT></A>()</FONT></TD>
<TD><FONT size=-1>取得系统当前的环境变量</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;你可以这样保存一个properties文件： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html" target=_blank><STRONG><FONT color=#009933>Properties</FONT></STRONG></A> prop = <SPAN class=style3><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#990000>Properties</FONT></STRONG></SPAN>();<BR>prop.setProperty(<SPAN class=style5><FONT color=#ff00ff>"key1"</FONT></SPAN>, <SPAN class=style5><FONT color=#ff00ff>"value1"</FONT></SPAN>);<BR>...<BR><SPAN class=style4><STRONG><FONT color=#990000>FileOutputStream</FONT></STRONG></SPAN> out = <SPAN class=style3><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#990000>FileOutputStream</FONT></STRONG></SPAN>(<SPAN class=style5><FONT color=#ff00ff>"config.properties"</FONT></SPAN>);<BR>prop.store(out, <SPAN class=style5><FONT color=#ff00ff>"--这里是文件头，可以加入注释--"</FONT></SPAN>);</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>★<STRONG>Preferences</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;如果我说Java里面可以不使用JNI的手段操作Windows的注册表你信不信？很多软件的菜单里都有“Setting”或“Preferences”这样的选项用来设定或修改软件的配置，这些配置信息可以保存到一个像上面所述的配置文件当中，如果是Windows平台下，也可能会保存到系统注册表中。从JDK 1.4开始，Java在<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/package-summary.html" target=_blank><FONT color=#009933>java.util</FONT></A>下加入了一个专门处理用户和系统配置信息的<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/package-summary.html" target=_blank><FONT color=#009933>java.util.prefs</FONT></A>包，其中一个类<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html" target=_blank><FONT color=#009933>Preferences</FONT></A>是一种比较“高级”的玩意。从本质上讲，Preferences本身是一个与平台无关的东西，但不同的OS对它的SPI（Service Provider Interface）的实现却是与平台相关的，因此，在不同的系统中你可能看到首选项保存为本地文件、LDAP目录项、数据库条目等，像在Windows平台下，它就保存到了系统注册表中。不仅如此，你还可以把首选项导出为XML文件或从XML文件导入。 </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH align=middle><FONT size=-1>主要类与方法</FONT></TH>
<TH align=middle><FONT size=-1>描述</FONT></TH></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html#systemNodeForPackage(java.lang.Class)" target=_blank><FONT color=#009933>systemNodeForPackage</FONT></A>()</FONT></TD>
<TD><FONT size=-1>根据指定的Class对象得到一个Preferences对象，这个对象的注册表路径是从“HKEY_LOCAL_MACHINE\”开始的</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html#systemRoot()" target=_blank><FONT color=#009933>systemRoot</FONT></A>()</FONT></TD>
<TD><FONT size=-1>得到以注册表路径HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs 为根结点的Preferences对象</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html#userNodeForPackage(java.lang.Class)" target=_blank><FONT color=#009933>userNodeForPackage</FONT></A>()</FONT></TD>
<TD><FONT size=-1>根据指定的Class对象得到一个Preferences对象，这个对象的注册表路径是从“HKEY_CURRENT_USER\”开始的</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html#userRoot()" target=_blank><FONT color=#009933>userRoot</FONT></A>()</FONT></TD>
<TD><FONT size=-1>得到以注册表路径HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs 为根结点的Preferences对象</FONT></TD></TR>
<TR>
<TD><FONT size=-1>putXXX()</FONT></TD>
<TD><FONT size=-1>设置一个属性的值，这里XXX可以为基本数值型类型，如int、long等，但首字母大写，表示参数为相应的类型，也可以不写而直接用put，参数则为字符串</FONT></TD></TR>
<TR>
<TD><FONT size=-1>getXXX()</FONT></TD>
<TD><FONT size=-1>得到一个属性的值</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html#exportNode(java.io.OutputStream)" target=_blank><FONT color=#009933>exportNode</FONT></A>()</FONT></TD>
<TD><FONT size=-1>将全部首选项导出为一个XML文件</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html#exportSubtree(java.io.OutputStream)" target=_blank><FONT color=#009933>exportSubtree</FONT></A>()</FONT></TD>
<TD><FONT size=-1>将部分首选项导出为一个XML文件</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html#importPreferences(java.io.InputStream)" target=_blank><FONT color=#009933>importPreferences</FONT></A>()</FONT></TD>
<TD><FONT size=-1>从XML文件导入首选项</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;你可以按如下步骤保存数据： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD>
<P><SPAN class=style4><FONT color=#990000 size=-1><STRONG>Preferences</STRONG></FONT></SPAN><FONT size=-1> myPrefs1 = <SPAN class=style4><STRONG><FONT color=#990000>Preferences</FONT></STRONG></SPAN>.userNodeForPackage(<SPAN class=style3><STRONG><FONT color=#ff0000>this</FONT></STRONG></SPAN>);<FONT color=#006600><SPAN class=style7>// 这种方法是在“HKEY_CURRENT_USER\”下按当前类的路径建立一个注册表项</SPAN><BR></FONT><SPAN class=style4><STRONG><FONT color=#990000>Preferences</FONT></STRONG></SPAN> myPrefs2 = <SPAN class=style4><STRONG><FONT color=#990000>Preferences</FONT></STRONG></SPAN>.systemNodeForPackage(<SPAN class=style3><STRONG><FONT color=#ff0000>this</FONT></STRONG></SPAN>);<FONT color=#006600><SPAN class=style7>// 这种方法是在“HKEY_LOCAL_MACHINE\”下按当前类的路径建立一个注册表项</SPAN><BR></FONT><SPAN class=style4><STRONG><FONT color=#990000>Preferences</FONT></STRONG></SPAN> myPrefs3 = <SPAN class=style4><STRONG><FONT color=#990000>Preferences</FONT></STRONG></SPAN>.userRoot().node(<SPAN class=style5><FONT color=#ff00ff>"com.jungleford.demo"</FONT></SPAN>);<FONT color=#006600><SPAN class=style7>// 这种方法是在“HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表项</SPAN><BR></FONT><SPAN class=style4><STRONG><FONT color=#990000>Preferences</FONT></STRONG></SPAN> myPrefs4 = <SPAN class=style4><STRONG><FONT color=#990000>Preferences</FONT></STRONG></SPAN>.systemRoot().node(<SPAN class=style5><FONT color=#ff00ff>"com.jungleford.demo"</FONT></SPAN>);<FONT color=#006600><SPAN class=style7>// 这种方法是在“HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表项</SPAN><BR></FONT>myPrefs1.putInt(<SPAN class=style5><FONT color=#ff00ff>"key1"</FONT></SPAN>, <SPAN class=style6><FONT color=#0000ff>10</FONT></SPAN>);<BR>myPrefs1.putDouble(<SPAN class=style5><FONT color=#ff00ff>"key2"</FONT></SPAN>, <SPAN class=style6><FONT color=#0000ff>-7.15</FONT></SPAN>);<BR>myPrefs1.put(<SPAN class=style5><FONT color=#ff00ff>"key3"</FONT></SPAN>, <SPAN class=style5><FONT color=#ff00ff>"value3"</FONT></SPAN>);<BR><SPAN class=style4><STRONG><FONT color=#990000>FileOutputStream</FONT></STRONG></SPAN> out = <SPAN class=style3><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#990000>FileOutputStream</FONT></STRONG></SPAN>(<SPAN class=style5><FONT color=#ff00ff>"prefs.xml"</FONT></SPAN>);<BR>myPrefs1.exportNode(out);</FONT></P></TD></TR></TBODY></TABLE>
<P class=style2><STRONG><U><FONT color=#ff0000 size=4>网络I/O：Socket→RMI</FONT></U></STRONG></P><FONT size=-1>
<P>★<STRONG>Socket</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;Socket编程可能大家都很熟，所以就不多讨论了，只是说通过socket把数据保存到远端服务器或从网络socket读取数据也不失为一种值得考虑的方式。</P>
<P>★<STRONG>RMI</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;RMI机制其实就是RPC（远程过程调用）的Java版本，它使用socket作为基本传输手段，同时也是序列化最重要的一个应用。现在网络传输从编程的角度来看基本上都是<STRONG>以流的方式操作</STRONG>，socket就是一个例子，将对象转换成字节流的一个重要目标就是为了方便网络传输。<BR>&nbsp;&nbsp;&nbsp;&nbsp;想象一下传统的<STRONG>单机环境</STRONG>下的程序设计，对于Java语言的函数（方法）调用（注意与C语言函数调用的区别）的参数传递，会有两种情况：如果是<STRONG>基本数据类型</STRONG>，这种情况下和C语言是一样的，采用<STRONG>值传递</STRONG>方式；如果是<STRONG>对象</STRONG>，则传递的是对象的<STRONG>引用</STRONG>，包括返回值也是引用，而不是一个完整的对象拷贝！试想一下在<STRONG>不同的虚拟机</STRONG>之间进行方法调用，即使是两个完全同名同类型的对象他们也很可能是不同的引用！此外对于方法调用过程，由于被调用过程的压栈，内存“现场”完全被被调用者占有，当被调用方法返回时，才将调用者的地址写回到程序计数器（PC），恢复调用者的状态，如果是两个虚拟机，根本不可能用简单压栈的方式来保存调用者的状态。因为种种原因，我们才需要建立RMI通信实体之间的“代理”对象，譬如“存根”就相当于远程服务器对象在客户机上的代理，stub就是这么来的，当然这是后话了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;本地对象与远程对象（未必是物理位置上的不同机器，只要不是在同一个虚拟机内皆为“远程”）之间传递参数和返回值，可能有这么几种情形：<BR></P>
<UL>
<LI>值传递：这又包括两种子情形：如果是基本数据类型，那么都是“可序列化”的，统统序列化成可传输的字节流；如果是对象，而且不是“远程对象”（所谓“远程对象”是实现了java.rmi.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/rmi/Remote.html" target=_blank><FONT color=#009933>Remote</FONT></A>接口的对象），本来对象传递的应该是引用，但由于上述原因，引用是不足以证明对象身份的，所以传递的仍然是<STRONG>一个序列化的拷贝</STRONG>（当然这个对象也必须满足上述“可序列化”的条件）。 
<LI>引用传递：可以引用传递的只能是“远程对象”。这里所谓的“引用”不要理解成了真的只是一个符号，它其实是一个留在（客户机）本地stub中的，和远端服务器上那个真实的对象张得一模一样的<STRONG>镜像</STRONG>而已！只是因为它有点“特权”（不需要经过序列化），在本地内存里已经有了一个实例，真正引用的其实是这个“孪生子”。 </LI></UL>&nbsp;&nbsp;&nbsp;&nbsp;由此可见，序列化在RMI当中占有多么重要的地位。 
<P></P></FONT>
<P class=style2><FONT color=#ff0000 size=4><STRONG><U>数据库I/O：CMP、Hibernate</U></STRONG></FONT></P><FONT size=-1>
<P>★<STRONG>什么是“Persistence”</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;用过VMWare的朋友大概都知道当一个guest OS正在运行的时候点击“Suspend”将虚拟OS挂起，它会把整个虚拟内存的内容保存到磁盘上，譬如你为虚拟OS分配了128M的运行内存，那挂起以后你会在虚拟OS所在的目录下找到一个同样是128M的文件，这就是虚拟OS内存的完整镜像！这种内存的镜像手段其实就是“Persistence”（持久化）概念的由来。</P>
<P>★<STRONG>CMP和Hibernate</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;因为我对J2EE的东西不是太熟悉，随便找了点材料看看，所以担心说的不到位，这次就不作具体总结了，人要学习……真是一件痛苦的事情&nbsp;<IMG height=19 src="http://www.blogjava.net/Emoticons/77_77.gif" width=19 border=0> </P></FONT>
<P class=style2><STRONG><U><FONT color=#ff0000 size=4>序列化再探讨</FONT></U></STRONG></P><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;从以上技术的讨论中我们不难体会到，序列化是Java之所以能够出色地实现其鼓吹的两大卖点——分布式（distributed）和跨平台（OS independent）的一个重要基础。TIJ（即“<A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT color=#009933>Thinking in Java</FONT></A>”）谈到I/O系统时，把序列化称为“lightweight persistence”——“轻量级的持久化”，这确实很有意思。</P>
<P><A name=1></A>★<STRONG>为什么叫做“序列”化？</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;开场白里我说更习惯于把“Serialization”称为“序列化”而不是“串行化”，这是有原因的。介绍这个原因之前先回顾一些计算机基本的知识，我们知道现代计算机的内存空间都是<STRONG>线性编址</STRONG>的（什么是“线性”知道吧，就是一个元素只有一个唯一的“前驱”和唯一的“后继”，当然头尾元素是个例外；对于地址来说，它的下一个地址当然不可能有两个，否则就乱套了），“地址”这个概念推广到数据结构，就相当于“指针”，这个在本科低年级大概就知道了。注意了，既然是线性的，那“地址”就可以看作是内存空间的“<STRONG>序号</STRONG>”，说明它的组织是有顺序的，“序号”或者说“序列号”正是“Serialization”机制的一种体现。为什么这么说呢？譬如我们有两个对象a和b,分别是类A和B的实例，它们都是可序列化的，而A和B都有一个类型为C的属性，根据前面我们说过的<A href="#2"><FONT color=#009933>原则</FONT></A>，C当然也必须是可序列化的。 </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD>
<P><SPAN class=style3><FONT color=#ff0000 size=-1><STRONG>import</STRONG></FONT></SPAN><FONT size=-1> java.io.*;<BR>...<BR><SPAN class=style3><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> A <SPAN class=style3><STRONG><FONT color=#ff0000>implements</FONT></STRONG></SPAN> <STRONG><FONT color=#990000><SPAN class=style4>Serializable</SPAN><BR></FONT></STRONG>{<BR>&nbsp;&nbsp;C c;<BR>&nbsp;&nbsp;...<BR>}</FONT></P>
<P><SPAN class=style3><FONT color=#ff0000 size=-1><STRONG>class</STRONG></FONT></SPAN><FONT size=-1> B <SPAN class=style3><STRONG><FONT color=#ff0000>implements</FONT></STRONG></SPAN> <STRONG><FONT color=#990000><SPAN class=style4>Serializable</SPAN><BR></FONT></STRONG>{<BR>&nbsp;&nbsp;C c;<BR>&nbsp;&nbsp;...<BR>}</FONT></P>
<P><SPAN class=style3><FONT color=#ff0000 size=-1><STRONG>class</STRONG></FONT></SPAN><FONT size=-1> C <SPAN class=style3><STRONG><FONT color=#ff0000>implements</FONT></STRONG></SPAN> <STRONG><FONT color=#990000><SPAN class=style4>Serializable</SPAN><BR></FONT></STRONG>{<BR>&nbsp;&nbsp;...<BR>}</FONT></P><FONT size=-1>
<P>A a;<BR>B b;<BR>C c1;<BR>...</P></FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;注意，这里我们在实例化a和b的时候，有意让他们的c属性使用同一个C类型对象的引用，譬如c1，那么请试想一下，但我们序列化a和b的时候，它们的c属性在外部字节流（当然可以不仅仅是文件）里保存的是一份拷贝还是两份拷贝呢？序列化在这里使用的是一种<STRONG>类似于“指针”的方案</STRONG>：它为每个被序列化的对象标上一个“<STRONG>序列号</STRONG>”（serial number），但序列化一个对象的时候，如果其某个属性对象是已经被序列化的，那么这里只向输出流写入该属性的序列号；从字节流恢复被序列化的对象时，也根据序列号找到对应的流来恢复。这就是“序列化”名称的由来！这里我们看到“序列化”和“指针”是极相似的，只不过“指针”是内存空间的地址链，而序列化用的是<STRONG>外部流中的“序列号链”</STRONG>。<BR>&nbsp;&nbsp;&nbsp;&nbsp;使用“序列号”而不是内存地址来标识一个被序列化的对象，是因为从流中恢复对象到内存，其地址可能就未必是原来的地址了——我们需要的只是这些对象之间的引用关系，而不是死板的原始位置，这在RMI中就更是必要，在两台不同的机器之间传递对象（流），根本就不可能指望它们在两台机器上都具有相同的内存地址。 </P></FONT><FONT size=-1>
<P><A name=4></A>★<STRONG>更灵活的“序列化”：transient属性和Externalizable</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;Serializable确实很方便，方便到你几乎不需要做任何额外的工作就可以轻松将内存中的对象保存到外部。但有两个问题使得Serializable的威力收到束缚：<BR>&nbsp;&nbsp;&nbsp;&nbsp;一个是效率问题，《Core Java 2》中指出，Serializable使用系统默认的序列化机制会影响软件的运行速度，因为需要为每个属性的引用编号和查号，再加上I/O操作的时间（I/O和内存读写差的可是一个数量级的大小），其代价当然是可观的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;另一个困扰是“裸”的Serializable<STRONG>不可定制</STRONG>，傻乎乎地什么都给你序列化了，不管你是不是想这么做。其实你可以有至少三种定制序列化的选择。其中一种前面已经提到了，就是在implements Serializable的类里面添加私有的writeObject()和readObject()方法（这种Serializable就不裸了，<IMG height=19 src="http://www.blogjava.net/Emoticons/teeth_smile.gif" width=19 border=0>），在这两个方法里，该序列化什么，不该序列化什么，那就由你说了算了，你当然可以在这两个方法体里面分别调用ObjectOutputStream.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html#defaultWriteObject()" target=_blank><FONT color=#009933>defaultWriteObject</FONT></A>()和ObjectInputStream.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInputStream.html#defaultReadObject()" target=_blank><FONT color=#009933>defaultReadObject</FONT></A>()仍然执行<STRONG>默认的序列化动作</STRONG>（那你在代码上不就做无用功了？呵呵），也可以用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()方法对你中意的属性进行序列化。但虚拟机一看到你定义了这两个方法，它就不再用默认的机制了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;如果仅仅为了跳过某些属性不让它序列化，上面的动作似乎显得麻烦，更简单的方法是对不想序列化的属性加上<SPAN class=style3><STRONG><FONT color=#ff0000>transient</FONT></STRONG></SPAN>关键字，说明它是个“暂态变量”，默认序列化的时候就不会把这些属性也塞到外部流里了。当然，你如果定义writeObject()和readObject()方法的化，仍然可以把暂态变量进行序列化。题外话，像<SPAN class=style3><STRONG><FONT color=#ff0000>transient</FONT></STRONG></SPAN>、<SPAN class=style3><STRONG><FONT color=#ff0000>violate</FONT></STRONG></SPAN>、<SPAN class=style3><FONT color=#ff0000><STRONG>finally</STRONG><FONT color=#000000>、</FONT><STRONG>assert</STRONG></FONT></SPAN>这样的关键字初学者可能会不太重视，而现在有的公司招聘就偏偏喜欢问这样的问题 <IMG height=19 src="http://www.blogjava.net/Emoticons/sad_smile.gif" width=19 border=0><BR>&nbsp;&nbsp;&nbsp;&nbsp;再一个方案就是不实现Serializable而改成实现<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/Externalizable.html" target=_blank><FONT color=#009933>Externalizable</FONT></A>接口。我们研究一下这两个接口的源代码，发现它们很类似，甚至容易混淆。我们要记住的是：Externalizable默认并<STRONG>不保存任何对象相关信息</STRONG>！任何保存和恢复对象的动作都是你自己定义的。Externalizable包含两个public的方法： </P></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><SPAN class=style3><FONT color=#ff0000 size=-1><STRONG>public void</STRONG></FONT></SPAN><FONT size=-1> <A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/Externalizable.html#writeExternal(java.io.ObjectOutput)" target=_blank><FONT color=#009933>writeExternal</FONT></A>(<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutput.html" target=_blank><STRONG><FONT color=#009933>ObjectOutput</FONT></STRONG></A> out) <SPAN class=style3><STRONG><FONT color=#ff0000>throws</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#990000>IOException</FONT></STRONG></SPAN>;<BR><SPAN class=style3><STRONG><FONT color=#ff0000>public void</FONT></STRONG></SPAN> <A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/Externalizable.html#readExternal(java.io.ObjectInput)" target=_blank><FONT color=#009933>readExternal</FONT></A>(<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInput.html" target=_blank><STRONG><FONT color=#009933>ObjectInput</FONT></STRONG></A> in) <SPAN class=style3><STRONG><FONT color=#ff0000>throws</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#990000>IOException</FONT></STRONG></SPAN>, <SPAN class=style4><STRONG><FONT color=#990000>ClassNotFoundException</FONT></STRONG></SPAN>;</FONT></TD></TR></TBODY></TABLE><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;乍一看这和上面的writeObject()和readObject()几乎差不多，但Serializable和Externalizable走的是两个不同的流程：Serializable在对象不存在的情况下，就可以仅凭外部的字节序列把整个对象重建出来；但Externalizable在重建对象时，先是调用该类的默认构造函数（即不含参数的那个构造函数）使得内存中先有这么一个实例，然后再调用readExternal方法对实例中的属性进行恢复，因此，如果默认构造函数中和readExternal方法中都没有赋值的那些属性，特别他们是非基本类型的话，将会是空（null）。在这里需要注意的是，<STRONG>transient只能用在对Serializable而不是Externalizable的实现里面</STRONG>。 </P></FONT><FONT size=-1>
<P>★<STRONG>序列化与克隆</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;从“可序列化”的递归定义来看，一个序列化的对象貌似对象内存映象的外部克隆，如果没有共享引用的属性的化，那么应该是一个<STRONG>深度克隆</STRONG>。关于克隆的话题有可以谈很多，这里就不细说了，有兴趣的话可以参考<A href="http://www-900.ibm.com/developerWorks/cn/" target=_blank><FONT color=#009933>IBM developerWorks</FONT></A>上的一篇文章：<A href="http://www-900.ibm.com/developerWorks/cn/java/l-jpointer/index.shtml" target=_blank><FONT color=#009933>JAVA中的指针,引用及对象的clone</FONT></A> </P></FONT>
<P class=style2><STRONG><U><FONT color=#ff0000 size=4>一点启示</FONT></U></STRONG></P><FONT size=-1>
<P>&nbsp;&nbsp;&nbsp;&nbsp;作为一个实际的应用，我在写那个简易的邮件客户端JExp的时候曾经对比过好几种保存Message对象（主要是几个关键属性和邮件的内容）到本地的方法，譬如XML、Properties等，最后还是选择了用序列化的方式，因为这种方法最简单， 大约可算是“学以致用”罢。这里“存取程序状态”其实只是一个引子话题罢了，我想说的是——就如同前面我们讨论的关于logging的话题一样——在Java面前对同一个问题你可以有很多种solution：熟悉文件操作的，你可能会觉得Properties、XML或Bean比较方便，然后又发现了还有Preferences这么一个东东，大概又会感慨“天外有天”了，等到你接触了很多种新方法以后，结果又会“殊途同归”，重新反省Serialization机制本身。这不仅是Java，科学也是同样的道理。 </P></FONT>
<P class=style2><A name=3></A><STRONG><U><FONT color=#ff0000 size=4>参考资料</FONT></U></STRONG></P>
<UL><FONT size=-1>
<LI><A href="http://www.horstmann.com/corejava.html" target=_blank><FONT color=#009933>Core Java 2</FONT></A>. by <EM><A href="http://www.horstmann.com/" target=_blank><FONT color=#009933>Cay S. Horstmann</FONT></A>, Gary Cornell</EM> 
<LI>J2SE进阶. by <A href="http://www.javaresearch.org/" target=_blank><EM><FONT color=#009933>JavaResearch.org</FONT></EM></A> 
<LI><A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT color=#009933>Thinking in Java</FONT></A>. by <A href="http://www.bruceeckel.com/" target=_blank><EM><FONT color=#009933>Bruce Eckel</FONT></EM></A> 
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/api/index.html" target=_blank><FONT color=#009933>J2SE 1.4.2 Documentation</FONT></A>. by <A href="http://java.sun.com/" target=_blank><EM><FONT color=#009933>java.sun.com</FONT></EM></A> 
<LI><A href="http://www.china-pub.com/computers/common/info.asp?id=3345" target=_blank><FONT color=#009933>Java Network Programming</FONT></A>. by <EM>Elliotte R. Harold</EM> 
<LI><A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/java-onlinecourse-bytitle/40A4A92C2F1C8A95C8256CD40035BAA1?OpenDocument" target=_blank><FONT color=#009933>Java分布式对象：RMI和CORBA</FONT></A>. by <A href="http://www-900.ibm.com/developerWorks/cn/" target=_blank><EM><FONT color=#009933>IBM developerWorks</FONT></EM></A></FONT></LI></UL></FONT><img src ="http://www.blogjava.net/jungleford/aggbug/2763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 22:01 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java调试的变迁：从System.out.println到log4j</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2760.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 13:55:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2760.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2760.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2760.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2760.html</trackback:ping><description><![CDATA[<FONT face=Verdana size=2>&nbsp;<STRONG><U><FONT color=#cc0000><SPAN class=style2><FONT size=3>jungleford如是说 <IMG title=log4j的logo height=55 alt=o_log4j.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_log4j.jpg" width=82 border=0></FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;用惯了<A href="http://msdn.microsoft.com/visualc/" target=_blank>VC</A>的人刚接触<A href="http://java.sun.com/" target=_blank>Java</A>大概很不习惯代码的调试，的确，在<A href="http://www.microsoft.com/" target=_blank>M$</A>的大部分IDE都做得相当出色，包括像VJ++这样一直被Java程序员称为是“垃圾”的类库（记得以前在<A href="http://bbs.ustc.edu.cn/" target=_blank>瀚海星云</A>的<A href="http://bbs.ustc.edu.cn/cgi-bin/bbsdoc?board=Java" target=_blank>Java版</A>提有关VJ问题的人是有可能被封的，^_^），它的开发工具在调试上都相当容易。Java也有命令行方式的调试和IDE的调试，但现在的像<A href="http://www.borland.com/jbuilder/" target=_blank>JB</A>这样的玩意又是个庞然大物，低配置的机器可能就是个奢望，不像VC那样。怎么办呢，高手们说，“我的jdb用得贼熟练”，那我会报以景仰的目光，像我这样的菜鸟基本上就没使过jdb，还是老老实实在代码里面System.out.println(...)。直到1996年一个叫做“欧洲安全电子市场”（E.U. <A href="http://www.semper.org/" target=_blank>SEMPER</A>）的项目启动，“调试”不再是一件“体力活”，而是一种软件设计的艺术，这个项目组开发的日志管理接口后来成为<A href="http://www.apache.org/" target=_blank>Apache</A> <A href="http://jakarta.apache.org/" target=_blank>Jakarta</A>项目中的一员，它就是现在我们所熟悉的<A href="http://logging.apache.org/" target=_blank>log4j</A>。下面的文字将概要介绍与Java日志记录相关的一些技术，目的不是让您放弃老土的System.out.println(...)，而是说，在Java的世界里可以有许多种选择，你今天觉得掌握了一件高级武器，明天可能就是“过时”的了，呵呵。 
<P><STRONG><U><FONT color=#cc0000><SPAN class=style2><FONT size=3>始祖：System.out.println(...)</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;为什么还是要一再提到它？毕竟我们的习惯不是那么容易改变的，而且<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html" target=_blank>System</A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#out" target=_blank>out</A>（别忘了还有System.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#err" target=_blank>err</A>）是一个直接和控制台打交道的<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PrintStream.html" target=_blank>PrintStream</A>对象，是终端显示的基础，高级的Logger要在终端显示日志内容，就必然会用到这个。一个小规模的程序调试，恰当地使用System.out.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PrintStream.html#println(java.lang.Object)" target=_blank>println</A>(...)我认为仍然是一种最方便最有效的方法，所以我们仍把它放在最开始，以示不能“数典忘祖” :)</P>
<P><STRONG><U><FONT color=#cc0000><SPAN class=style2><FONT size=3>不常用的关键字：assert</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;assert对多数人来讲可能还比较陌生，它也是一个调试工具，好像是<A href="http://java.sun.com/j2se/1.4.2/" target=_blank>J2SE 1.4</A>才加进来的东东，一种常见的用法是： 
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><SPAN class=style3><STRONG><FONT color=#ff0000>assert</FONT></STRONG></SPAN> (布尔表达式);</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;当表达式为true时没有任何反映，如果为false系统将会抛出一个<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/AssertionError.html" target=_blank>AssertionError</A>。如果你要使用assert，在编译时必须加上“<STRONG>-source 1.4</STRONG>”的选项，在运行时则要加上“<STRONG>-ea</STRONG>”选项。<BR></FONT></P>
<P><STRONG><U><FONT color=#cc0000><SPAN class=style2><FONT size=3>后生可畏：Java Logging API一瞥</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(...)对于较高要求的用户是远远不够的，它还不是一个日志系统，一个比较完善的日志系统应当有输出媒介、优先级、格式化、日志过滤、日志管理、参数配置等功能。伴随J2SE 1.4一起发布的Java日志包<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/package-summary.html" target=_blank>java.util.logging</A>适时地满足了我们的初步需求，在程序中按一定格式显示和记录丰富的调试信息已经是一件相当easy的事情。</P>
<P><STRONG>1. 日志记录器：Logger</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html" target=_blank>Logger</A>是一个直接面向用户的日志功能调用接口，从用户的角度上看，它完成大部分日志记录工作，通常你得到一个Logger对象，只需要使用一些简单方法，譬如info，warning，log，logp，logrb等就能完成任务，简单到和System.out.println(...)一样只用一条语句，但后台可能在向控制台，向文件，向数据库，甚至向网络同时输出该信息，而这个过程对用户是完全透明的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;在使用Logger之前，首先需要通过<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#getLogger(java.lang.String)" target=_blank>getLogger</A>()或<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#getAnonymousLogger()" target=_blank>getAnonymousLogger</A>()静态方法得到一个Logger对象（想想看，这里是不是设计模式当中的“工厂方法”的一个实实在在的应用？可以参考一下Logger的源代码，你就明白<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/LogManager.html" target=_blank>LogManager</A>是“工厂类”而Logger是“产品类”，凡事都要学以致用嘛，呵呵）。这里我们需要了解的是Logger的“名字空间”（<STRONG>namespace</STRONG>）的概念：通常我们调试时需要清楚地知道某个变量是出现在什么位置，精确到哪个类的哪个方法，namespace就是这么个用处。我们用getLogger()得到Logger时需要指定这个Logger的名字空间，通常是一个包名，譬如“com.jungleford.test”等，如果是指定了namespace，那么将在一个全局对象LogManager中注册这个namespace，Logger会基于namespace形成层次关系，譬如namespace为“com.jungleford”的Logger就是namespace为“com.jungleford.test”的Logger的父，后者调用<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#getParent()" target=_blank>getParent</A>()方法将返回前者，如果当前没有namespace为“com.jungleford”的Logger，则查找namespace为“com”的Logger，要是按照这个链找不到就返回根Logger，其namespace为""，根Logger的父是null。从理论上说，这个namespace可以是任意的，通常我们是按所调试的对象来定，但如果你是使用getAnonymousLogger()方法产生的Logger，那它就没有namespace，这个“匿名Logger”的父是根Logger。<BR>&nbsp;&nbsp;&nbsp;&nbsp;得到一个Logger对象后就可以记录日志了，下面是一些常用的方法： <BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#finest(java.lang.String)" target=_blank>finest</A>、<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#finer(java.lang.String)" target=_blank>finer</A>、<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#fine(java.lang.String)" target=_blank>fine</A>、<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#info(java.lang.String)" target=_blank>info</A>、<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#config(java.lang.String)" target=_blank>config</A>、<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#warning(java.lang.String)" target=_blank>warning</A>、<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#severe(java.lang.String)" target=_blank>severe</A>：简洁的方法，输出的日志为指定的级别。关于日志级别我们在后面将会详细谈到。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#log(java.util.logging.Level,%20java.lang.String)" target=_blank>log</A>：不仅可以指定消息和级别，还可以带一些参数，甚至可以直接是一个LogRecord对象（这些参数是LogRecord对象的重要组成部分）。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#logp(java.util.logging.Level,%20java.lang.String,%20java.lang.String,%20java.lang.String)" target=_blank>logp</A>：更加精细了，不但具有log方法的功能，还可以不使用当前的namespace，定义新的类名和方法名。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#entering(java.lang.String,%20java.lang.String)" target=_blank>entering</A>、<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#exiting(java.lang.String,%20java.lang.String)" target=_blank>exiting</A>：这两个方法在调试的时候特别管用，用来观察一个变量变化的情况，就如同我们在VC的调试状态下watch一个变量，然后按F10，呵呵。</FONT></TD></TR></TBODY></TABLE><BR></P>
<P><STRONG>2. 输出媒介控制：Handler</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;日志的意义在于它可以以多种形式输出，尤其是像文件这样可以长久保存的媒介，这是System.out.println(...)所无法办到的。Logging API的<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Handler.html" target=_blank>Handler</A>类提供了一个处理日志记录（<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/LogRecord.html" target=_blank>LogRecord</A>，它是对一条日志消息的封装对象）的接口，包括几个已实现的API： <BR><FONT size=-1></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/ConsoleHandler.html" target=_blank>ConsoleHandler</A>：向控制台输出。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/FileHandler.html" target=_blank>FileHandler</A>：向文件输出。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/SocketHandler.html" target=_blank>SocketHandler</A>：向网络输出。</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;这三个输出控制器都是<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/StreamHandler.html" target=_blank>StreamHandler</A>的子类，另外Handler还有一个MemoryHandler的子类，它有特殊的用处，我们在后面将会看到。在程序启动时默认的Handler是ConsoleHandler，不过这个是可以配置的，下面会谈到logging配置文件的问题。<BR>&nbsp;&nbsp;&nbsp;&nbsp;此外用户还可以定制自己输出控制器，继承Handler即可，通常只需要实现Handler中三个未定义的抽象方法： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Handler.html#publish(java.util.logging.LogRecord)" target=_blank>publish</A>：主要方法，把日志记录写入你需要的媒介。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Handler.html#flush()" target=_blank>flush</A>：清除缓冲区并保存数据。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Handler.html#close()" target=_blank>close</A>：关闭控制器。</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;通过重写以上三个方法我们可以很容易就实现一个把日志写入数据库的控制器。<BR></FONT></P>
<P><STRONG>3. 自定义输出格式：Formatter</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;除了可以指定输出媒介之外，我们可能还希望有多种输出格式，譬如可以是普通文本、HTML表格、XML等等，以满足不同的查看需求。Logging API中的<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Formatter.html" target=_blank>Formatter</A>就是这样一个提供日志记录格式化方法接口的类。默认提供了两种Formatter：<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/SimpleFormatter.html" target=_blank>SimpleFormatter</A>：标准日志格式，就是我们通常在启动一些诸如<A href="http://jakarta.apache.org/tomcat/" target=_blank>Tomcat</A>、<A href="http://www.jboss.org/" target=_blank>JBoss</A>之类的服务器的时候经常能在控制台下看到的那种形式，就像这样： <BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>2004-12-20 23:08:52 org.apache.coyote.http11.Http11Protocol init<BR>信息: Initializing Coyote HTTP/1.1 on http-8080<BR><BR>2004-12-20 23:08:56 org.apache.coyote.http11.Http11Protocol init<BR>信息: Initializing Coyote HTTP/1.1 on http-8443 </FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/XMLFormatter.html" target=_blank>XMLFormatter</A>：XML形式的日志格式，你的Logger如果add了一个new XMLFormatter()，那么在控制台下就会看到下面这样的形式，不过更常用的是使用上面介绍的FileHandler输出到XML文件中： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>&lt;?xml version="1.0" encoding="GBK" standalone="no"?&gt;<BR>&lt;!DOCTYPE log SYSTEM "logger.dtd"&gt;<BR>&lt;log&gt;<BR>&lt;record&gt;<BR>&nbsp;&nbsp;&lt;date&gt;2004-12-20T23:47:56&lt;/date&gt;<BR>&nbsp;&nbsp;&lt;millis&gt;1103557676224&lt;/millis&gt;<BR>&nbsp;&nbsp;&lt;sequence&gt;0&lt;/sequence&gt;<BR>&nbsp;&nbsp;&lt;logger&gt;Test&lt;/logger&gt;<BR>&nbsp;&nbsp;&lt;level&gt;WARNING&lt;/level&gt;<BR>&nbsp;&nbsp;&lt;class&gt;Test&lt;/class&gt;<BR>&nbsp;&nbsp;&lt;method&gt;main&lt;/method&gt;<BR>&nbsp;&nbsp;&lt;thread&gt;10&lt;/thread&gt;<BR>&nbsp;&nbsp;&lt;message&gt;warning message&lt;/message&gt;<BR>&lt;/record&gt;</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;与Handler类似，我们也可以编写自己的格式化处理器，譬如API里没有将日志输出为我们可通过浏览器查看的HTML表格形式的Formatter，我们只需要重写3个方法： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Formatter.html#format(java.util.logging.LogRecord)" target=_blank>format</A>：格式化LogRecord中包含的信息。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Formatter.html#getHead(java.util.logging.Handler)" target=_blank>getHead</A>：输出信息的头部。<BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Formatter.html#getTail(java.util.logging.Handler)" target=_blank>getTail</A>：输出信息的尾部。</FONT></TD></TR></TBODY></TABLE><FONT size=-1></FONT><BR></P>
<P><FONT size=-1><STRONG>4. 定义日志级别：Level</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;大家可能都知道Windows的“事件查看器”，里面有三种事件类型：“信息”、“警告”、“错误”。这其实就是日志级别的一种描述。Java日志级别用<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html" target=_blank>Level</A>类表示，一个日志级别对应的是一个整数值，范围和整型值的范围是一致的，该整数值愈大，说明警戒级别愈高。Level有9个内置的级别，分别是： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="40%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH><FONT size=-1>类型</FONT></TH>
<TH><FONT size=-1>对应的整数</FONT></TH></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#OFF" target=_blank>OFF</A></FONT></TD>
<TD><FONT size=-1>最大整数（<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html" target=_blank>Integer</A>.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html#MAX_VALUE" target=_blank>MAX_VALUE</A>）</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#SEVERE" target=_blank>SEVERE</A></FONT></TD>
<TD><FONT size=-1>1000</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#WARNING" target=_blank>WARNING</A></FONT></TD>
<TD><FONT size=-1>900</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#INFO" target=_blank>INFO</A></FONT></TD>
<TD><FONT size=-1>800</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#CONFIG" target=_blank>CONFIG</A></FONT></TD>
<TD><FONT size=-1>700</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#FINE" target=_blank>FINE</A></FONT></TD>
<TD><FONT size=-1>500</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#FINER" target=_blank>FINER</A></FONT></TD>
<TD><FONT size=-1>400</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#FINEST" target=_blank>FINEST</A></FONT></TD>
<TD><FONT size=-1>300</FONT></TD></TR>
<TR>
<TD><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html#ALL" target=_blank>ALL</A></FONT></TD>
<TD><FONT size=-1>最小整数（Integer.<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html#MIN_VALUE" target=_blank>MIN_VALUE</A>）</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;你也可以定义自己的日志级别，但要注意的是，不是直接创建Level的对象（因为它的构造函数是protected的），而是通过继承Level的方式，譬如： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><SPAN class=style3><FONT color=#ff0000 size=-1><STRONG>class</STRONG></FONT></SPAN><FONT size=-1> AlertLevel <SPAN class=style3><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> java.util.logging.Level<BR>{<BR>&nbsp;&nbsp;<SPAN class=style3><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> AlertLevel()<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;super(<SPAN class=style4><FONT color=#ff00ff>"ALERT"</FONT></SPAN>, <SPAN class=style5><FONT color=#0000ff>950</FONT></SPAN>);<BR>&nbsp;&nbsp;}<BR>}<BR>...<BR><SPAN class=style6><STRONG><FONT color=#990000>Logger</FONT></STRONG></SPAN> logger = <SPAN class=style6><STRONG><FONT color=#990000>Logger</FONT></STRONG></SPAN>.getAnonymousLogger();<BR>logger.log(<SPAN class=style3><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> AlertLevel(), <SPAN class=style4><FONT color=#ff00ff>"A dangerous action!"</FONT></SPAN>);</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;上面定义了一个高于WARNING但低于SEVERE的日志级别。<BR>&nbsp;&nbsp;&nbsp;&nbsp;于是可能有朋友会兴冲冲地用以下的语句来记录一个事件： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><SPAN class=style6><FONT color=#990000 size=-1><STRONG>Logger</STRONG></FONT></SPAN><FONT size=-1> logger = <SPAN class=style6><STRONG><FONT color=#990000>Logger</FONT></STRONG></SPAN>.getAnonymousLogger();<BR>logger.fine(<SPAN class=style4><FONT color=#ff00ff>"Everything seems ok."</FONT></SPAN>);<BR><SPAN class=style7><FONT color=#006600>//或者是<BR>//logger.log(Level.FINE, "Everything seems ok.");</FONT></SPAN></FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;但是一程序运行，奇怪了，怎么没有打印出任何消息呢？下一小节我们就来谈这个问题。</FONT></P>
<P><FONT size=-1><STRONG>5. 日志过滤器：Filter</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;所谓过滤器是控制哪些日志该输出哪些不该输出的一种组件。上面你写的那条日志没有能在控制台显示出来，是因为logging API预先设定的缺省级别是INFO，也就是说只有级别不低于INFO（即其整数值不小于800）的日志才会被输出，这个就是Filter的功能。所以我们可以看到SEVERE、WARNING、INFO以及上面我们定义的ALERT消息，但看不到FINE、FINER和FINEST消息。当然，你尽可以用Logger的<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#setLevel(java.util.logging.Level)" target=_blank>setLevel</A>方法或者修改配置文件的方法（什么是配置文件，我们后面将会看到）来重新定义Logger输出的最低级别。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Filter.html" target=_blank>Filter</A>不仅仅可以按日志级别过滤，你也可以定义自己的Filter，实现其中的<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Filter.html#isLoggable(java.util.logging.LogRecord)" target=_blank>isLoggable</A>方法，随便按照LogRecord携带的任何信息进行过滤，譬如（顺便复习一下匿名类，呵呵）： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><SPAN class=style6><FONT color=#990000 size=-1><STRONG>Logger</STRONG></FONT></SPAN><FONT size=-1> logger = <SPAN class=style6><STRONG><FONT color=#990000>Logger</FONT></STRONG></SPAN>.getAnonymousLogger();<BR>logger.setFilter(<SPAN class=style3><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style6><STRONG><FONT color=#990000>Filter</FONT></STRONG></SPAN>()<BR>{<BR>&nbsp;&nbsp;<SPAN class=style3><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style3><STRONG><FONT color=#ff0000>boolean</FONT></STRONG></SPAN> isLoggable(<SPAN class=style6><STRONG><FONT color=#990000>LogRecord</FONT></STRONG></SPAN> rec)<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#006600><SPAN class=style7>//从LogRecord里得到过滤信息</SPAN><BR></FONT>&nbsp;&nbsp;}<BR>});</FONT></TD></TR></TBODY></TABLE><STRONG><BR>6. 预定义参数<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;LogManager是一个实现了Singleton模式的全局对象（由于是一个唯一的对象，LogManager需要是线程安全的），它管理着程序启动以后所有已注册（包层次）或匿名的Logger，以及相关配置信息。这里的配置信息通常是从<STRONG>&lt;JAVA_HOME&gt;\jre\lib\logging.properties</STRONG>文件得到的。logging.properties对于logging API来说是一个很重要的文件，它的内容一般是：<BR><FONT size=-1></FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>
<P>############################################################<BR># Default Logging Configuration File<BR>#<BR># You can use a different file by specifying a filename<BR># with the java.util.logging.config.file system property. <BR># For example java -Djava.util.logging.config.file=myfile<BR>############################################################</P>
<P>############################################################<BR># Global properties<BR>############################################################</P>
<P># "handlers" specifies a comma separated list of log Handler <BR># classes. These handlers will be installed during VM startup.<BR># Note that these classes must be on the system classpath.<BR># By default we only configure a ConsoleHandler, which will only<BR># show messages at the INFO and above levels.<BR>handlers= java.util.logging.ConsoleHandler</P>
<P># To also add the FileHandler, use the following line instead.<BR>#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler</P>
<P># Default global logging level.<BR># This specifies which kinds of events are logged across<BR># all loggers. For any given facility this global level<BR># can be overriden by a facility specific level<BR># Note that the ConsoleHandler also has a separate level<BR># setting to limit messages printed to the console.<BR>.level= INFO</P>
<P>############################################################<BR># Handler specific properties.<BR># Describes specific configuration info for Handlers.<BR>############################################################</P>
<P># default file output is in user's home directory.<BR>java.util.logging.FileHandler.pattern = %h/java%u.log<BR>java.util.logging.FileHandler.limit = 50000<BR>java.util.logging.FileHandler.count = 1<BR>java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter</P>
<P># Limit the message that are printed on the console to INFO and above.<BR>java.util.logging.ConsoleHandler.level = INFO<BR>java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter<BR></P>
<P>############################################################<BR># Facility specific properties.<BR># Provides extra control for each logger.<BR>############################################################</P>
<P># For example, set the com.xyz.foo logger to only log SEVERE<BR># messages:<BR>com.xyz.foo.level = SEVERE</P></FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;你可以通过修改这个配置文件来改变运行时Logger的行为，譬如：.level定义的是上面所说的默认输出的最低日志级别；XXXHandler相关属性定义了各种输出媒介等等。<BR>&nbsp;&nbsp;&nbsp;&nbsp;这里比较有意思的是关于日志文件，也就是FileHandler，当然，你可以在程序中创建一个FileHandler，然后添加到logger中： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><SPAN class=style6><FONT color=#990000 size=-1><STRONG>FileHandler</STRONG></FONT></SPAN><FONT size=-1> fhd = <SPAN class=style3><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style6><STRONG><FONT color=#990000>FileHandler</FONT></STRONG></SPAN>(<SPAN class=style4><FONT color=#ff00ff>"%h/java%u.log"</FONT></SPAN>, <SPAN class=style5><FONT color=#0000ff>5000</FONT></SPAN>, <SPAN class=style5><FONT color=#0000ff>1</FONT></SPAN>, <SPAN class=style3><STRONG><FONT color=#ff0000>true</FONT></STRONG></SPAN>);<BR>fhd.setLevel(<SPAN class=style6><STRONG><FONT color=#990000>Level</FONT></STRONG></SPAN>.ALL);<BR>fhd.setFormatter(<SPAN class=style3><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style6><STRONG><FONT color=#990000>XMLFormatter</FONT></STRONG></SPAN>());<BR>logger.addHandler(fhd);</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;这段代码等价于上面logging.properties中的文字段： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>java.util.logging.FileHandler.pattern = %h/java%u.log<BR>java.util.logging.FileHandler.limit = 50000<BR>java.util.logging.FileHandler.count = 1<BR>java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;这里的pattern代表用转义字符定义的一个日志文件名： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="50%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH><FONT size=-1>转义字符串</FONT></TH>
<TH><FONT size=-1>含义</FONT></TH></TR>
<TR>
<TD><FONT size=-1>%t</FONT></TD>
<TD><FONT size=-1>临时目录</FONT></TD></TR>
<TR>
<TD><FONT size=-1>%h</FONT></TD>
<TD><FONT size=-1>用户目录，即系统属性“user.home”对应的值</FONT></TD></TR>
<TR>
<TD><FONT size=-1>%g</FONT></TD>
<TD><FONT size=-1>一个随机生成的数字，可以重复</FONT></TD></TR>
<TR>
<TD><FONT size=-1>%u</FONT></TD>
<TD><FONT size=-1>一个随机生成的非重复数字</FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;以上面的“%h/java%u.log”为例，在Windows 2000下代表日志文件可能就是：C:\Documents and Settings\Administrator\java<STRONG>x</STRONG>.log。这里x代表一个不重复的数字，如果是第一次，那么就是java0.log；如果在该目录下已经存在了一个java0.log的文件，那么logger就产生一个java1.log的新的日志文件。<BR>&nbsp;&nbsp;&nbsp;&nbsp;当然，你可以在别的地方使用自己写的配置文件，不过在启动程序时候需要指定<STRONG>java.logging.config.file</STRONG>属性： </FONT>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1>java -Djava.logging.config.file=...</FONT></TD></TR></TBODY></TABLE><BR><STRONG>7. 资源与本地化</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;Logger里还有个方法叫<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Logger.html#logrb(java.util.logging.Level,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String)" target=_blank>logrb</A>，可能初学者不太会用到。如果你安装的JDK是国际版的，那么你将会看到在中文Windows平台下日志输出的INFO、WARNING显示的是“信息”、“警告”等中文字样。因为logrb是一个和Java i18n/l10n相关的方法，你可以定义自己的“资源包”（Resource Bundle），然后在logrb方法中指定相应的资源名称，那么在输出日志中你就能看到用自己定义的本地语言、时间等显示的信息。如果你对i18n/l10n感兴趣，可以参考<A href="http://java.sun.com/j2se/1.4.2/docs/guide/intl/index.html" target=_blank>Java Localization文档</A>。<BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;了解以上组件后，我们回顾一个完整的日志处理的工作过程：<BR>&nbsp;&nbsp;&nbsp;&nbsp;程序启动日志服务，创建Logger对象，LogManager按照namespace的层次结构组织Logger，在同一个namespace里子Logger将继承父Logger的属性；同时，LogManager从logging.properties中读取相应的属性对Logger进行初始化，如果在程序中设置了属性则使用新的配置。当应用程序产生一条日志，Logger将创建一个LogRecord对象，该对象封装了一条日志的全部信息。Logger需要根据当前设置的Filter来判断这条日志是否需要输出，并将有用的日志传给相应的Handler处理，而Handler根据当前设置的Formatter和Resource Bundle将日志消息转换成一定的显示格式，然后输出到预定的媒介（控制台、文件等）中去。整个过程大致如图1所示： <BR></P>
<P align=center><IMG title=日志处理流程 height=151 alt=o_logging1.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_logging1.gif" width=632 border=0></P>
<P align=center>图1<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;前面我们在介绍Handler的时候提到过一个特殊的类叫<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/MemoryHandler.html" target=_blank>MemoryHandler</A>，这里我们要了解一下“Handler链”的概念，日志在输出之前可能经过多个Handler的处理，MemoryHandler在这种情况下就是一个中间角色，它维持一个内存中的日志缓冲区，当日志没有填满缓冲区时就将全部日志送到下一个Handler，否则新进来的日志将会覆盖最老的那些日志，因此，使用MemoryHandler可以维护一定容量的日志，另外，MemoryHandler也可以不需要使用Formatter来进行格式化，从而具有较高的效率。一个使用Handler链的例子如图2所示： </P>
<P></P>
<CENTER><IMG title="一个 Handler 链" height=149 alt=o_logging2.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_logging2.gif" width=700 border=0><BR>图2</CENTER>
<P></P>
<P><STRONG><U><FONT color=#cc0000><SPAN class=style2><FONT size=3>青出于蓝：Apache Jakarta log4j日志工具包</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;应付日常的日志需求，J2SE的Logging API可以说已经做得相当出色了，但追求完美的开发人员可能需要可扩展性更好的专业日志处理工具，log4j正是当前比较流行的一个工具包，它提供更多的输出媒介、输出格式和配置选择，你会发现原来在J2SE里一些仍需要自己手工构建的功能在log4j当中都已经为你实现了。关于log4j我可能谈得不会太多，可以看看文后所附的“<A href="http://www.smth.org/pc/pcmanage.php?userid=jungleford&amp;act=edit&amp;nid=105614#ref">参考资料</A>”，网上也有很详细的介绍，我在这里做的是一个对比，因为log4j和J2SE 1.4 Logging API的用法是很相似的，一些名称不同的组件你会发现他们所处的地位其实是一样的： <BR>
<TABLE cellSpacing=0 cellPadding=0 width="60%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TH>&nbsp;</TH>
<TH><FONT size=-1>J2SE 1.4中的类</FONT></TH>
<TH><FONT size=-1>log4j中的类</FONT></TH></TR>
<TR>
<TH><FONT size=-1>日志记录器</FONT></TH>
<TD><FONT size=-1>Logger</FONT></TD>
<TD><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/Logger.html" target=_blank>Logger</A></FONT></TD></TR>
<TR>
<TH><FONT size=-1>日志管理器</FONT></TH>
<TD><FONT size=-1>LogManager</FONT></TD>
<TD><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/LogManager.html" target=_blank>LogManager</A></FONT></TD></TR>
<TR>
<TH><FONT size=-1>日志对象</FONT></TH>
<TD><FONT size=-1>LogRecord</FONT></TD>
<TD><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/spi/LoggingEvent.html" target=_blank>LoggingEvent</A></FONT></TD></TR>
<TR>
<TH><FONT size=-1>输出媒介控制</FONT></TH>
<TD><FONT size=-1>Handler</FONT></TD>
<TD><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/Appender.html" target=_blank>Appender</A></FONT></TD></TR>
<TR>
<TH><FONT size=-1>格式化</FONT></TH>
<TD><FONT size=-1>Formatter</FONT></TD>
<TD><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/Layout.html" target=_blank>Layout</A></FONT></TD></TR>
<TR>
<TH><FONT size=-1>级别</FONT></TH>
<TD><FONT size=-1>Level</FONT></TD>
<TD><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/Layout.html" target=_blank>Level</A></FONT></TD></TR>
<TR>
<TH><FONT size=-1>过滤器</FONT></TH>
<TD><FONT size=-1>Filter</FONT></TD>
<TD><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/spi/Filter.html" target=_blank>Filter</A></FONT></TD></TR></TBODY></TABLE><FONT size=-1><BR>&nbsp;&nbsp;&nbsp;&nbsp;log4j可以做到更精细更完善的控制，譬如J2SE里没有现成向数据库里写日志的方法，但log4j却有<A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/jdbc/JDBCAppender.html" target=_blank>JDBCAppender</A>，它甚至还能向GUI图形界面（<A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/lf5/LF5Appender.html" target=_blank>LF5Appender</A>，一种以JTree方式显示的层次结构）、Windows NT事件查看器（<A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/nt/NTEventLogAppender.html" target=_blank>NTEventLogAppender</A>）、UNIX的syslogd服务（<A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/net/SyslogAppender.html" target=_blank>SyslogAppender</A>）、电子邮箱（<A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/net/SMTPAppender.html" target=_blank>SMTPAppender</A>）、Telnet终端（<A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/net/TelnetAppender.html" target=_blank>TelnetAppender</A>）、JMS消息（<A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/net/JMSAppender.html" target=_blank>JMSAppender</A>）输出日志，牛吧；J2SE里默认只能用%JAVA_HOME%\jre\lib\logging.properties做配置文件，但log4j却可以在代码中设置其它路径下的properties文件或XML格式的配置文件。log4j的其它方面同样很丰富，总之，log4j的最大的特点就是“灵活”，无论是Appender、Layout还是Configurator，你可以把日志轻松地弄成几乎任何你想要的形式。</FONT></P>
<P><STRONG><U><FONT color=#cc0000><SPAN class=style2><FONT size=3>框架与标准：JSR议案</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;从时间顺序上讲，log4j要比J2SE Logging API来得早，很多概念都是log4j先有的，但成为一个标准，则是在<A href="http://www.jcp.org/en/jsr/detail?id=47" target=_blank>JSR 47</A>的形成。可能有人还不太了解<A href="http://www.jcp.org/en/jsr/overview" target=_blank>JSR</A>，这还要谈到<A href="http://www.jcp.org/en/home/index" target=_blank>JCP</A>，即“Java Community Process”，它是一个于1998年成立的旨在为Java技术制定民间标准的开放组织，你可以通过<A href="http://www.jcp.org/en/participation/membership" target=_blank>http://www.jcp.org/en/participation/membership</A>申请成为它的付费或免费会员，JCP的主要工作就是制定和发布JSR（Java Specification Requests），JSR对于Java的意义就相当于RFC对于网络技术的意义，由于JCP会员们的集思广益，使得JSR成为Java界的一个重要标准。JSR 47即“Logging API Specification”，制定了调试和日志框架，J2SE Logging API正是该框架的一个实现。由于种种原因，在JSR 47出来以前，log4j就已经成为一项成熟的技术，使得log4j在选择上占据了一定的优势，但不能因此就说JSR 47是过时的规范，标准总是在发展的嘛！</P>
<P><STRONG><U><FONT color=#cc0000><SPAN class=style2><FONT size=3>并不是全部：其它日志处理工具</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;除了J2SE Logging API和log4j，日志处理方面还有别的技术：Jakarta的<A href="http://jakarta.apache.org/commons/" target=_blank>commons</A>组件项目中的<A href="http://jakarta.apache.org/commons/logging/" target=_blank>JCL</A>（Jakarta Commons Logging）是一个不错的选择，它有点类似于GSS-API（通用安全服务接口）中的思想，其日志服务机制是可以替换的，也就是说既可以用J2SE Logging API也可以用log4j，但JCL对开发人员提供一致的接口，这一点相当重要，组件可重用正是Jakarta Commons项目追求的一个目标；<A href="http://www.ibm.com/" target=_blank>IBM</A>的<A href="http://www.alphaworks.ibm.com/tech/loggingtoolkit4j" target=_blank>JLog</A>也是在J2SE Logging API之前推出的一个工具包，但JLog是一个商业产品。<BR>&nbsp;&nbsp;&nbsp;&nbsp;至于日志API的应用那可就多了，现在哪个大一点的工具或平台不用到日志模块呢？Tomcat、JBoss……<BR></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;说了这么多，我们无非需要知道的一件事就是，“调试”也是一门学问。在我们一个劲地用System.out.println(...)而且用得很爽的时候，也应该想想看，如何让这样一条菜鸟语句也能变得人性化和丰富多彩。</P>
<P><SPAN class=style2><A name=ref></A><STRONG><U><FONT color=#cc0000 size=3>参考资料</FONT></U></STRONG></SPAN> </P>
<UL>
<LI><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/guide/util/logging/index.html" target=_blank>Java Logging Documentation</A></FONT> 
<LI><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/package-summary.html" target=_blank>Java Logging APIs</A></FONT> 
<LI><FONT size=-1>J2SE进阶, by <A href="http://www.javaresearch.org/" target=_blank><EM>www.javaresearch.org</EM></A></FONT> 
<LI><FONT size=-1><A href="http://logging.apache.org/log4j/docs/manual.html" target=_blank>Short introduction to log4j</A>, by <EM>Ceki Gülcü</EM></FONT> 
<LI><FONT size=-1><A href="http://logging.apache.org/log4j/docs/api/index.html" target=_blank>log4j APIs</A></FONT> 
<LI><FONT size=-1><A href="http://logging.apache.org/log4j/docs/faq.html" target=_blank>FAQ about log4j</A></FONT></LI></UL></FONT><img src ="http://www.blogjava.net/jungleford/aggbug/2760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 21:55 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于集合框架的思考</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2759.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 13:49:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2759.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2759.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2759.html</trackback:ping><description><![CDATA[<FONT size=2><FONT face=Verdana><STRONG><U><FONT color=#cc0000><SPAN class=style4><FONT size=3>jungleford如是说<BR></FONT></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;对于Java集合框架（Java Collections Framework，JCF），Java玩家大概都不会陌生，在C++里面相似的概念是标准模板库（Standard Template Library，STL），主要是对一些数据结构和相关算法的封装。考虑到这是一个Java初学者将会经常接触的工具，所以有了以下的一些文字。主要是参考了</FONT></FONT><A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/java-onlinecourse-bytitle/4475AC55462B0B9448256B3B001F279A?OpenDocument" target=_blank><FONT face=Verdana size=2>IBM developerWorks上的一篇教程</FONT></A><FONT face=Verdana size=2>，它可能解释得更加清晰，这里算是浓缩了一下吧，真正的来龙去脉可以看看JDK文档里的“</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/collections/index.html" target=_blank><FONT face=Verdana size=2>The Collections Framework</FONT></A><FONT face=Verdana size=2>”，说明更为详细。 </FONT>
<P><STRONG><U><FONT color=#cc0000><FONT size=2><FONT face=Verdana><SPAN class=style4><FONT size=3>问题的源头</FONT></SPAN><BR></FONT></FONT></FONT></U></STRONG></P>
<UL>
<LI><STRONG><FONT face=Verdana size=2>集合：对象的容器与数据结构</FONT></STRONG></LI></UL><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;回忆一下我们在程序设计里头可能会面对一些什么，无非是两类：基本类型和复合类型，后者常见的组织方式就是类。和基本类型不同，类对象通常是需要以动态方式分配的，譬如在内存的堆空间里new一个对象，这个我们一写OO的程序就必然会用到。同时我们面对的不仅仅是单个的基本类型或对象，对多个这样的数据我们通常采用的组织方式是什么？不错，是数组，这是伴随程序设计的一个古老概念。数组的优点显而易见，像根据下标检索元素这样的操作不费吹灰之力，但缺点也很明显：空间固定而不能动态增长（像Java这样的强类型语言对数组越界是及其敏感的），插入或删除元素比较费劲。因此数组不是解决一切集合问题的方便工具。我们可能需要一些新的工具，研究这些工具常常就是研究<SPAN class=style5><FONT color=#cc0000>数据结构</FONT></SPAN>，特别的，数组本身就是一种线性有序的数据结构。<BR>&nbsp;&nbsp;&nbsp;&nbsp;数据结构的数学基础是<SPAN class=style5><FONT color=#cc0000>集合论</FONT></SPAN>，为什么这么说呢？上面说过，现在我们要研究的不是单个的基本类型或对象，多个对象的整体不就是集合吗？从OO的角度上看，集合也是一种对象，但它是一种特殊的对象：对象的容器（注意，我们这里没有继续讨论基本类型的集合，因为基本类型和存储分配方式与对象有着本质的差别）。集合论的一个根本问题就是：给定一个元素，集合必须能够回答该元素是或者不是属于这个集合。还有一个问题也很重要，就是：如果元素是属于一个集合，那该元素在集合中的地位应该是唯一的，或者说它是<SPAN class=style5><FONT color=#cc0000>唯一确定</FONT></SPAN>的。当然还有其它问题，譬如查找、遍历、排序等等，这和具体的集合类型相关，后面将会讲到。 </FONT>
<UL>
<LI><STRONG><FONT face=Verdana size=2>无序集、有序集、映射</FONT></STRONG></LI></UL><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;谈到集合的类型，我们在高中所学的集合概念是其中的一种，叫做“<SPAN class=style5><FONT color=#cc0000>无序集</FONT></SPAN>”，也就是说集合的各个元素都是平等的，没有先后的区别，于是在无序集当中就决不允许出现一模一样的元素，否则当取到这个元素的时候就不知道应该取哪一个，这就违反了上面的“唯一确定”原则。<BR>&nbsp;&nbsp;&nbsp;&nbsp;等到我们上了大学，开始知道了另一种集合类型，叫做“<SPAN class=style5><FONT color=#cc0000>有序集</FONT></SPAN>”（或者叫“线性表”，区别于以后碰到的像“树”，“图”这样的非线性的数据结构），如果是计算机专业的，大概学过离散数学当中的“代数结构”，那你就更清楚的知道，“有序集”其实是一种“二元关系”，确切的说是“偏序关系”，它是可以包含相同元素的，因为两个的相同元素的“序号”可以不同，这样根据“序号”仍可以“唯一确定”一个元素，数组就是一种有序集，有序集的另一个特点就是任意两个元素可以确定他们的顺序。<BR>&nbsp;&nbsp;&nbsp;&nbsp;无序集，有序集，难道还有第三种可能？呵呵，它还是出现在我们的高中代数课本里，叫做“<SPAN class=style5><FONT color=#cc0000>映射</FONT></SPAN>”。映射也是集合？其实自从康托尔以来，集合论就认为“万物皆集合”（但也就是这个断言导致了集合论以后的尴尬境地，有兴趣可以看看罗素或哥德尔的一些结论，或</FONT><A href="http://www.google.com/" target=_blank><FONT face=Verdana size=2>google</FONT></A><FONT face=Verdana size=2>“集合论 悖论”）。映射其实是一种“元素对”的集合，就像<SPAN class=style6><FONT style="BACKGROUND-COLOR: #cccccc">f(a)=b, f(c)=d, ...</FONT></SPAN>等效于集合（无序集）<SPAN class=style6><FONT style="BACKGROUND-COLOR: #cccccc">{(a, b), (c, d), ...}</FONT></SPAN>，在“映射”中可以看作是(原象, 象)的集合，换一种说法就是(关键字key, 值value)的集合。所以我们可以在笛卡儿正交坐标平面上画出漂亮的函数图像，因为在集合论看来，函数（映射）就是二维平面上的一个个点，明白了？这样一来上面的“有序集”也好理解了，偏序关系<SPAN class=style6><FONT style="BACKGROUND-COLOR: #cccccc">a&gt;b&gt;c&gt;d&gt;...</FONT></SPAN>（不知道“偏序关系”就把它们看作是数组<SPAN class=style6><FONT style="BACKGROUND-COLOR: #cccccc">x[1]=a, x[2]=b, x[3]=c, x[4]=d ...</FONT></SPAN>好了）等效于无序集<SPAN class=style6><FONT style="BACKGROUND-COLOR: #cccccc">{(1, a), (2, b), (3, c), (4, d), ...}</FONT></SPAN>，于是乎，所有的集合都等效于无序集！所以高中只教了我们一种集合，呵呵…… </FONT>
<P></P>
<P><FONT size=2><FONT face=Verdana><STRONG><U><FONT color=#cc0000><SPAN class=style4><FONT size=3>JCF的全家福</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;好啦好啦，这些我们都知道，又不是在上数学课，说了这么多废话，怎么还没扯到正题上来？JCF的影子我还没看见呢！列位看官别急，这就给您道来。其实上面的概念对理解JCF非常重要。<BR>&nbsp;&nbsp;&nbsp;&nbsp;JCF是个颇有点规模的家族，看看它的类层次关系图就知道了，下面这张图（图1）摘自著名的</FONT></FONT><A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT face=Verdana size=2>Thinking in Java</FONT></A><FONT face=Verdana size=2>：<BR></FONT><A name=fig1></A><IMG title="JCF 家族" height=435 alt=o_collection.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_collection.gif" width=480 border=0><FONT face=Verdana size=2><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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图1 JCF层次结构<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;哇，这么多接口和类，真有点让人无从下手的感觉。其实我们真正需要记住的只是这么一个超超easy的结构（图2）：<BR><BR></FONT><A name=fig2></A><IMG title="简化的 JCF 骨架" height=145 alt=o_collection1.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_collection1.gif" width=289 border=0><FONT face=Verdana size=2><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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图2<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;这张图看起来舒服多了吧？但它又能说明什么问题呢？它怎么就能够把握整个JCF呢？我们把</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html" target=_blank><FONT face=Verdana size=2>Collection</FONT></A><FONT face=Verdana size=2>接口置于最顶上，意思是想说：Collection其实是整个JCF家族中的“祖宗”，几乎所有的JCF成员都源自该接口，或者和它有密切的关系，Collection提供关于集合的一些通用操作的接口，包括插入（</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html#add(java.lang.Object)" target=_blank><FONT face=Verdana size=2>add</FONT></A><FONT face=Verdana size=2>()方法）、删除（</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html#remove(java.lang.Object)" target=_blank><FONT face=Verdana size=2>remove</FONT></A><FONT face=Verdana size=2>()方法）、判断一个元素是不是其成员（</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html#contains(java.lang.Object)" target=_blank><FONT face=Verdana size=2>contains</FONT></A><FONT face=Verdana size=2>()方法）、遍历（</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html#iterator()" target=_blank><FONT face=Verdana size=2>iterator</FONT></A><FONT face=Verdana size=2>()方法）等等。注意了，前面的“废话”在这里将得到体现：</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Set.html" target=_blank><FONT face=Verdana size=2>Set</FONT></A><FONT face=Verdana size=2>接口体现的是“无序集”的概念，它是不允许有重复元素出现的；</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/List.html" target=_blank><FONT face=Verdana size=2>List</FONT></A><FONT face=Verdana size=2>接口代表“有序集”；而</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" target=_blank><FONT face=Verdana size=2>Map</FONT></A><FONT face=Verdana size=2>接口则是“映射”（在早期的Java版本中并不叫Map，我们在后面会看到），其实</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.Entry.html" target=_blank><FONT face=Verdana size=2>Map.Entry</FONT></A><FONT face=Verdana size=2>接口就是代表一个“元素对”我们可以通过Map的</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html#entrySet()" target=_blank><FONT face=Verdana size=2>entrySet</FONT></A><FONT face=Verdana size=2>()方法得到这样一个由“元素对”组成的Set对象。我们注意到Set和List都是从“祖宗”Collection派生的，而Map不是，毕竟对一对元素的操作与对单个元素的操作还是有区别的，但是如果你仔细对照一下Collection和Map的源代码，以及它们的直接后代</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/AbstractCollection.html" target=_blank><FONT face=Verdana size=2>AbstractCollection</FONT></A><FONT face=Verdana size=2>和</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/AbstractMap.html" target=_blank><FONT face=Verdana size=2>AbstractMap</FONT></A><FONT face=Verdana size=2>的源代码，你将会发现很多相似的地方，所以我们仍然可以把Map看成是和Collection有着血缘关系的接口，而和Set，List一起处于并列的位置。<BR>&nbsp;&nbsp;&nbsp;&nbsp;有了“无序集”，“有序集”和“映射”，我们就可以定义各种各样的抽象数据结构了，譬如图1所示的向量，链表，堆栈，哈希表，平衡二叉树等。但我们需要记住的，仅仅是图2，置于其它的成员，在用到的时候查一下API手册不就行了？不过一般初学者还是比较容易用到一些类，像Vector、ArrayList、HashMap，我在这里列了一张表，显示了常见的JCF成员及其关系： </FONT>
<TABLE cellSpacing=0 cellPadding=0 border=1>
<TBODY>
<TR>
<TD vAlign=top width=484 colSpan=5>
<P align=center><FONT face=Verdana size=2>集合框架的祖宗： </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html" target=_blank><FONT face=Verdana size=2>Collection</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=118>
<P align=center><FONT face=Verdana size=2>历史集合 </FONT></P></TD>
<TD vAlign=top width=398 colSpan=4>
<P align=center><FONT face=Verdana size=2>新集合 </FONT></P></TD></TR>
<TR>
<TD vAlign=top width=195 colSpan=2>
<P align=center><FONT face=Verdana size=2>无序集： </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Set.html" target=_blank><FONT face=Verdana size=2>Set</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=289 colSpan=3>
<P align=center><FONT face=Verdana size=2>有序集： </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/List.html" target=_blank><FONT face=Verdana size=2>List</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=118>
<P align=center><FONT face=Verdana size=2>映射：</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Dictionary.html" target=_blank><FONT face=Verdana size=2>Dictionary</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=398 colSpan=4>
<P align=center><FONT face=Verdana size=2>映射：</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" target=_blank><FONT face=Verdana size=2>Map</FONT></A><FONT face=Verdana size=2> </FONT></P></TD></TR>
<TR>
<TD vAlign=top width=101 rowSpan=2>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/AbstractSet.html" target=_blank><FONT face=Verdana size=2>AbstractSet</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=94 rowSpan=2>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/SortedSet.html" target=_blank><FONT face=Verdana size=2>SortedSet</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=147 colSpan=2>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/AbstractList.html" target=_blank><FONT face=Verdana size=2>AbstractList</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=143>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/AbstractSequentialList.html" target=_blank><FONT face=Verdana size=2>AbstractSequentialList</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=118 rowSpan=3>
<P align=center><BR><BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Hashtable.html" target=_blank><FONT face=Verdana size=2>Hashtable</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=321 colSpan=3>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/AbstractMap.html" target=_blank><FONT face=Verdana size=2>AbstractMap</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=77>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/SortedMap.html" target=_blank><FONT face=Verdana size=2>SortedMap</FONT></A><FONT face=Verdana size=2> </FONT></P></TD></TR>
<TR>
<TD vAlign=top width=78>
<P align=center><FONT face=Verdana size=2>历史集合 </FONT></P></TD>
<TD vAlign=top width=69>
<P align=center><FONT face=Verdana size=2>新集合 </FONT></P></TD>
<TD vAlign=top width=143 rowSpan=2>
<P align=center><BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/LinkedList.html" target=_blank><FONT face=Verdana size=2>LinkedList</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=100 rowSpan=2>
<P align=center><BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/WeakHashMap.html" target=_blank><FONT face=Verdana size=2>WeakHashMap</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=112 rowSpan=2>
<P align=center><BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/IdentityHashMap.html" target=_blank><FONT face=Verdana size=2>IdentityHashMap</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=109 rowSpan=2>
<P align=center><BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/HashMap.html" target=_blank><FONT face=Verdana size=2>HashMap</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=77 rowSpan=2>
<P align=center><BR><BR><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/TreeMap.html" target=_blank><FONT face=Verdana size=2>TreeMap</FONT></A><FONT face=Verdana size=2> </FONT></P></TD></TR>
<TR>
<TD vAlign=top width=101>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/HashSet.html" target=_blank><FONT face=Verdana size=2>HashSet</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=94>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/TreeSet.html" target=_blank><FONT face=Verdana size=2>TreeSet</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=78>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Vector.html" target=_blank><FONT face=Verdana size=2>Vector</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=69>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html" target=_blank><FONT face=Verdana size=2>ArrayList</FONT></A><FONT face=Verdana size=2> </FONT></P></TD></TR>
<TR>
<TD vAlign=top width=101>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/LinkedHashSet.html" target=_blank><FONT face=Verdana size=2>LinkedHashSet</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=94><FONT face=Verdana size=2>&nbsp;</FONT></TD>
<TD vAlign=top width=78>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Stack.html" target=_blank><FONT face=Verdana size=2>Stack</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=69><FONT face=Verdana size=2>&nbsp;</FONT></TD>
<TD vAlign=top width=143><FONT face=Verdana size=2>&nbsp;</FONT></TD>
<TD vAlign=top width=118>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html" target=_blank><FONT face=Verdana size=2>Properties</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=100><FONT face=Verdana size=2>&nbsp;</FONT></TD>
<TD vAlign=top width=112><FONT face=Verdana size=2>&nbsp;</FONT></TD>
<TD vAlign=top width=109>
<P align=center><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/LinkedHashMap.html" target=_blank><FONT face=Verdana size=2>LinkedHashMap</FONT></A><FONT face=Verdana size=2> </FONT></P></TD>
<TD vAlign=top width=77><FONT face=Verdana size=2>&nbsp;</FONT></TD></TR></TBODY></TABLE><BR><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;可能有的概念您还不是太了解，譬如什么叫“历史集合”，Hashtable、HashMap、TreeMap三者之间有什么区别和联系，怎样实现对一个特定集合的快速遍历、元素查找或者排序，没关系，我们在下面将逐一进行研究。 </FONT></P>
<P><FONT size=2><FONT face=Verdana><STRONG><U><FONT color=#cc0000><SPAN class=style4><FONT size=3>细节考虑：目标与效率</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;有了JCF的层次还不够，重要的是对集合所容纳的对象的具体操作，以前我们学数据结构的时候可能老师总会让你计算一个算法的时间复杂度，可能你会对这个O(f(n))很不耐烦，但事实上算法效率是一个重要的因素。</FONT></FONT></P>
<UL><STRONG><FONT face=Verdana size=2>1. 侧重点：遍历 vs. 查找</FONT></STRONG></UL>
<P><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;对集合的有两个主要的应用：我需要知道集合有哪些元素；根据条件找到一个特定的元素。在算法上通常称为“遍历”和“查找”。不要以为我们生活中不常用哦！譬如CCTV的“幸运52”里面，李咏让参赛者报出一款PDA的准确价位，他会怎么做？“2000”“高了”“1000”“低了”“1500”“低了”……直到答对为止。可能有很多人都会选择这个策略，无论他是不是计算机专业出身的，也不知道他是不是了解“数据结构”和“折半查找”，更不用说他是不是知道还有比在无初始代价下O(log n)的时间复杂度更快的算法了，但我们经常会自然而然地用这样的方法，这和一个人的行业无关，除非这个人的rp超强，呵呵……<BR>&nbsp;&nbsp;&nbsp;&nbsp;又讲了一堆题外话了，遍历和修改似乎是一对矛盾，一个可以高效率插入删除元素的数据结构通常遍历的性能并不是最优。于是JCF在这里根据用户的目标实现了两种定制的数据结构：哈希表（包括HashSet和HashMap）和平衡二叉树（包括TreeSet和TreeMap）。由于可排序性是一种独特的要求，所以引入了SortedSet和SortedMap，它们分别是AbstractSet和AbstractMap的子接口，而TreeSet和TreeMap又分别是他们的一种实现。熟悉数据结构的人可能比较了解，哈希表在进行插入、删除、查找这样的操作是很快的，其时间复杂度是常数级O(1)；平衡二叉树虽然插入、删除操作比较麻烦（需要O(log n)的代价），但进行遍历和排序却很快。选择完全在于用户的侧重点，但由于类型转换的方便性，通常我们用哈希表构造一个集合以后，再把它转换成相应的树集进行遍历，以获得较好的效果。 <BR></FONT>
<TABLE cellSpacing=0 cellPadding=5 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=2><FONT face=Verdana><SPAN class=style7><STRONG><FONT color=#990000>Set</FONT></STRONG></SPAN> set1 = <SPAN class=style12><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style7><STRONG><FONT color=#990000>HashSet</FONT></STRONG></SPAN>();<BR>set1.add(elem1);</FONT></FONT><FONT size=2><FONT face=Verdana><FONT color=#006600><SPAN class=style10>// 通过插入元素构造集合</SPAN><BR></FONT>set1.add(elem2);<BR>set1.add(elem3);<BR><SPAN class=style7><STRONG><FONT color=#990000>Set</FONT></STRONG></SPAN> set2 = <SPAN class=style12><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style7><STRONG><FONT color=#990000>TreeSet</FONT></STRONG></SPAN>(set);<BR><SPAN class=style7><STRONG><FONT color=#990000>Iterator</FONT></STRONG></SPAN> all = set2.iterator();<BR><SPAN class=style12><STRONG><FONT color=#ff0000>while</FONT></STRONG></SPAN> (all.hasNext())<BR>{</FONT></FONT><FONT size=2><FONT face=Verdana><FONT color=#006600><SPAN class=style10>// 遍历集合</SPAN><BR></FONT>all.next();<BR>...<BR>}</FONT></FONT></TD></TR></TBODY></TABLE><BR></P>
<UL><STRONG><FONT face=Verdana size=2>2. 历史实现 vs. 新实现</FONT></STRONG></UL><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;历史实现（Legacy Implementations）是JCF的一个术语，准确的意义不是很清楚，但大致可以认为在Java 2（JDK 1.2）出现以前的老版本中JCF的一个雏形框架。在Java 2以后，JCF才开始完善健壮起来，新实现中出现了一些新的类用于替代老版本中的成员，但由于种种原因，老版本中很多类都代表了传统数据结构的精髓部分，以及一些安全原因，所以仍然被我们使用着。<BR><BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Enumeration.html" target=_blank><FONT face=Verdana size=2>Enumeration</FONT></A><FONT face=Verdana size=2> vs. </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Iterator.html" target=_blank><FONT face=Verdana size=2>Iterator</FONT></A><BR><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;Enumeration是一个传统的集合遍历工具，在新的JCF中使用的是Iterator，Iterator同样具有遍历功能，还包含一个</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Iterator.html#remove()" target=_blank><FONT face=Verdana size=2>remove</FONT></A><FONT face=Verdana size=2>()方法来删除当前得到的元素。<BR><BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Dictionary.html" target=_blank><FONT face=Verdana size=2>Dictionary</FONT></A><FONT face=Verdana size=2> vs. </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" target=_blank><FONT face=Verdana size=2>Map</FONT></A><BR><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;Dictionary是一个现在已经被标记为deprecated的类，实现了老版本中的映射功能，现在已经完全被Map取代。它们的区别是：Dictionary中key和value不能为null，但Map却允许空的关键字和值，这一点直接影响到它们的后代：Hashtable和HashMap。<BR><BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Vector.html" target=_blank><FONT face=Verdana size=2>Vector</FONT></A><FONT face=Verdana size=2> vs. </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html" target=_blank><FONT face=Verdana size=2>ArrayList</FONT></A><BR><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;Vector和ArrayList是数组在JCF中的体现，还记得前面讲过的数组的缺点么？Vector和ArrayList就是一种可以动态增长的数组。Vector是历史实现，它和ArrayList的主要区别在于，Vector是同步集合（或者说是线程安全的），但ArrayList并不是同步的，由于同步需要花一定的代价，所以ArrayList看起来要比Vector的存取访问效率更高。关于同步我们下面还将要谈到。<BR><BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Hashtable.html" target=_blank><FONT face=Verdana size=2>Hashtable</FONT></A><FONT face=Verdana size=2> vs. </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/HashMap.html" target=_blank><FONT face=Verdana size=2>HashMap</FONT></A><BR><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;Hashtable是Dictionary的子类，属于历史实现，而HashMap是Map的子类，是新实现。它们的区别除了上面所说的key和value是否可以为空之外，也有同步的差别，Hashtable是同步的，但HashMap不是。HashMap的一个经典的例子就是JSP的内置对象session。不过不要因为Hashtable是“老前辈”而瞧不起它哦，它的一个著名的子类</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html" target=_blank><FONT face=Verdana size=2>Properties</FONT></A><FONT face=Verdana size=2>我们可是经常会用到的。 </FONT>
<UL><STRONG><FONT face=Verdana size=2>3. 同步 vs. 不同步</FONT></STRONG></UL><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;从上面的描述中我们似乎可以得出这么一个印象：历史实现好像都是同步的，但新实现中却没有。需要同步操作的理由是，可能存在多个线程对同一个集合进行操作的情况：譬如一个线程正在对某集合进行遍历，但与此同时，另一个线程又在对该集合进行插入或删除，那么第一个线程的遍历结果将是不可预测的，对于同步集合，它将会抛出一个</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ConcurrentModificationException.html" target=_blank><FONT face=Verdana size=2>ConcurrentModificationException</FONT></A><FONT face=Verdana size=2>异常，JCF把这种机制成为“fail-fast”。我们对比一下Vector和ArrayList的源代码就可以发现Vector的很多方法都是有synchronized关键字修饰的，但ArrayList没有。 </FONT>
<UL><STRONG><FONT face=Verdana size=2>4. 容易遗忘的工具：</FONT></STRONG><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html" target=_blank><STRONG><FONT face=Verdana size=2>Collections</FONT></STRONG></A><STRONG><FONT face=Verdana size=2>和</FONT></STRONG><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Arrays.html" target=_blank><STRONG><FONT face=Verdana size=2>Arrays</FONT></STRONG></A><FONT face=Verdana size=2> </FONT></UL><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;在图1中右下角落里有两个类叫做Collections（注意，不是Collection！）和Arrays，这是JCF里面功能强大的工具，但初学者往往会忽视。按JCF文档的说法，这两个类提供了封装器实现（Wrapper Implementations）、数据结构算法和数组相关的应用。<BR>&nbsp;&nbsp;&nbsp;&nbsp;想必大家不会忘记上面谈到的“折半查找”、“排序”等经典算法吧，Collections类提供了丰富的静态方法帮助我们轻松完成这些在数据结构课上烦人的工作：<BR><BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#binarySearch(java.util.List,%20java.lang.Object)" target=_blank><FONT face=Verdana size=2>binarySearch</FONT></A><FONT face=Verdana size=2>：折半查找。<BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#sort(java.util.List)" target=_blank><FONT face=Verdana size=2>sort</FONT></A><FONT face=Verdana size=2>：排序，这里是一种类似于快速排序的方法，效率仍然是O(n * log n)，但却是一种稳定的排序方法。<BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#reverse(java.util.List)" target=_blank><FONT face=Verdana size=2>reverse</FONT></A><FONT face=Verdana size=2>：将线性表进行逆序操作，这个可是从前数据结构的经典考题哦！<BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#rotate(java.util.List,%20int)" target=_blank><FONT face=Verdana size=2>rotate</FONT></A><FONT face=Verdana size=2>：以某个元素为轴心将线性表“旋转”——哇，这个功能太酷了！<BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#swap(java.util.List,%20int,%20int)" target=_blank><FONT face=Verdana size=2>swap</FONT></A><FONT face=Verdana size=2>：交换一个线性表中两个元素的位置。<BR>……<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;Collections还有一个重要功能就是“封装器”（Wrapper），它提供了一些方法可以把一个集合转换成一个特殊的集合：<BR><BR>unmodifiableXXX：转换成只读集合，这里XXX代表六种基本集合接口：Collection、List、Map、Set、SortedMap和SortedSet。如果你对只读集合进行插入删除操作，将会抛出</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/UnsupportedOperationException.html" target=_blank><FONT face=Verdana size=2>UnsupportedOperationException</FONT></A><FONT face=Verdana size=2>异常。<BR>synchronizedXXX：转换成同步集合。<BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#singleton(java.lang.Object)" target=_blank><FONT face=Verdana size=2>singleton</FONT></A><FONT face=Verdana size=2>：创建一个仅有一个元素的集合，这里singleton生成的是单元素Set，</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#singletonList(java.lang.Object)" target=_blank><FONT face=Verdana size=2>singletonList</FONT></A><FONT face=Verdana size=2>和</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#singletonMap(java.lang.Object,%20java.lang.Object)" target=_blank><FONT face=Verdana size=2>singletonMap</FONT></A><FONT face=Verdana size=2>分别生成单元素的List和Map。<BR>空集：由Collections的静态属性</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#EMPTY_SET" target=_blank><FONT face=Verdana size=2>EMPTY_SET</FONT></A><FONT face=Verdana size=2>、</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#EMPTY_LIST" target=_blank><FONT face=Verdana size=2>EMPTY_LIST</FONT></A><FONT face=Verdana size=2>和</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#EMPTY_MAP" target=_blank><FONT face=Verdana size=2>EMPTY_MAP</FONT></A><FONT face=Verdana size=2>表示。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;此外，我们知道把集合转换成对象数组可以用Collection的</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html#toArray()" target=_blank><FONT face=Verdana size=2>toArray</FONT></A><FONT face=Verdana size=2>()方法，我们也可以方便地把一个对象数组转换成一个线性表（可不要告诉我你是一个一个地add哦）：Arrays.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Arrays.html#asList(java.lang.Object%5B%5D)" target=_blank><FONT face=Verdana size=2>asList</FONT></A><FONT face=Verdana size=2>()。 </FONT>
<UL><STRONG><FONT face=Verdana size=2>5. 泛型</FONT></STRONG></UL><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;目前我们了解的JCF的一个重要特征是：所有加入到集合当中的对象都将在表面上失去它们自己的特性，而看上去仅仅只是一个Object对象而已，除非你把它强制类型转换成它们原来的对象。这一点很自然，集合嘛，对象的容器，它容纳的是各种各样的对象，而不仅仅是某种特定类型的对象。J2SE 5.0出现以后，JCF开始引入泛型的特性，譬如我们经常碰到这样的应用，就是把集合转换成特定的数组，虽然Collection有toArray()的方法，但可惜的是，这个数组的所有元素都是Object类型的，我们通常的做法是用一个for循环把数组的每个元素都进行强制类型转换，虽然可行，但看上去很笨拙，如果有了泛型，我们就可以预先指定要得到的类型，然后一次toArray就可以得到我们期望的数组，里面的元素全部都是指定类型了。惭愧的是，我对5.0还不是太了解，具体可以参考</FONT><A href="http://java.sun.com/j2se/1.5.0/docs/guide/collections/index.html" target=_blank><FONT face=Verdana size=2>J2SE 5.0的JCF文档</FONT></A><FONT face=Verdana size=2>。 </FONT>
<P></P>
<P><FONT size=2><FONT face=Verdana><STRONG><U><FONT color=#cc0000><SPAN class=style4><FONT size=3>小结</FONT><BR></SPAN><BR></FONT></U></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;我这里走马观花一样把JCF的一些主要概念罗嗦了一下，Java的老手们可能比较厌烦，新手们可能更觉得像回顾了一下高中的数学课和大学的数据结构，呵呵。这只是一个小小的例子，可见基础知识对现实当中的应用还是蛮有指导意义的。大师们看数学，觉得那是很唯美很艺术的一样东西，西方一直都把数学区别于其它自然科学，而认为它更靠近于哲学，像我等这样整天还在为找工作烦得要死的俗人还是没法入道啊，sigh……<BR></FONT></FONT><STRONG><U><FONT color=#cc0000><BR><FONT face=Verdana>参考资料</FONT></FONT></U></STRONG></P>
<P class=style4><FONT face=Verdana size=2></FONT></P>
<UL>
<LI><FONT face=Verdana size=2>IBM developerWorks教程：</FONT><A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/java-onlinecourse-bytitle/4475AC55462B0B9448256B3B001F279A?OpenDocument" target=_blank><FONT face=Verdana size=2> Java 集合框架</FONT></A><FONT face=Verdana size=2> </FONT>
<LI><FONT face=Verdana size=2>J2SE Documentation: </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/collections/index.html" target=_blank><FONT face=Verdana size=2>The Collections Framework </FONT></A>
<LI><A href="http://java.sun.com/docs/books/tutorial/collections/index.html" target=_blank><FONT face=Verdana size=2>The Java Tutorial</FONT></A><FONT face=Verdana size=2> </FONT>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/guide/collections/reference.html" target=_blank><FONT face=Verdana size=2>JCF API</FONT></A></LI></UL><img src ="http://www.blogjava.net/jungleford/aggbug/2759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 21:49 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用Observer模式解决组件间通信问题</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2758.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 13:44:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2758.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2758.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2758.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2758.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2758.html</trackback:ping><description><![CDATA[<STRONG><U><FONT color=#cc0000><FONT face=Verdana>jungleford如是说<BR><BR></FONT></FONT></U></STRONG><FONT face=Verdana size=2>以前做一个界面的时候常常会遇到这样的尴尬情况：希望保留各个独立的组件（类），但又希望它们之间能够相互通信。譬如Windows中的Explorer，我们希望鼠标点击左边是树型目录的一个节点，右边的文件浏览能及时列出该节点目录下的文件和子目录，类似这样一个简单的应用，如果只有一个类继承JFrame，而树型组件和浏览文件的面板作为成员，就像：<BR></FONT>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> MainFrame <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFrame.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JFrame</FONT></STRONG></A>
<FONT face=Verdana>{
  </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JPanel.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JPanel</FONT></STRONG></A><FONT face=Verdana> treePanel;
  </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTree.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JTree</FONT></STRONG></A><FONT face=Verdana> tree;
  <SPAN class=style5><STRONG><FONT color=#990000>JPanel</FONT></STRONG></SPAN> filePanel;
  ...
}</FONT></PRE></TD></TR></TBODY></TABLE><FONT size=-1><BR><FONT face=Verdana>这样当然容易在两者之间传递消息，但是可扩展性较差。通常容易想到的是两种办法：在一个组件里保留另一个组件类型的成员，初始化时作为参数传入引用，比如：<BR></FONT></FONT>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> TreePanel <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JPanel.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JPanel</FONT></STRONG></A>
<FONT face=Verdana>{
  </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTree.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JTree</FONT></STRONG></A><FONT face=Verdana> tree;
  ...
}

<SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> FilePanel <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>JPanel</FONT></STRONG></SPAN>
{
  <SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> FilePanel(<SPAN class=style5><STRONG><FONT color=#990000>JTree</FONT></STRONG></SPAN> tree){...}
  ...
}</FONT></PRE></TD></TR></TBODY></TABLE><FONT size=-1><BR><FONT face=Verdana>或者将一个组件线程化，不停地监听另一个组件的变化，然后作出相应的反映，比如：<BR></FONT></FONT>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> TreePanel <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JPanel.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JPanel</FONT></STRONG></A>
<FONT face=Verdana>{
  </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTree.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JTree</FONT></STRONG></A><FONT face=Verdana> tree;
  ...
}

<SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> FilePanel <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>JPanel</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>implements</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Runnable.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Runnable</FONT></STRONG></A>
<FONT face=Verdana>{
  <SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>void</FONT></STRONG></SPAN> run()
  {
    <SPAN class=style4><STRONG><FONT color=#ff0000>while</FONT></STRONG></SPAN> (<SPAN class=style4><STRONG><FONT color=#ff0000>true</FONT></STRONG></SPAN>)
    {
      <SPAN class=style3><FONT color=#006600>//监听tree的变化</FONT></SPAN>
    }
    ...
  }
  ...
}</FONT></PRE></TD></TR></TBODY></TABLE><FONT size=-1><BR><FONT face=Verdana>这样确实可以达到我们的目的，但是第一种方案显然不利于松散耦合，第二种方案比较占用系统资源。通过学习设计模式，我们发现可以用Observer模式来解决这个问题。<BR><BR></FONT><FONT face=Verdana><STRONG><U><FONT size=3><FONT color=#cc0000><SPAN class=style2>1. Observer模式</SPAN><BR></FONT></FONT></U></STRONG>设计模式分为创建型、结构型和行为型，其中行为型模式专门处理对象间通信，指定交互方式等，Observer模式就是属于行为型的一种设计模式。按照“四人帮”（Gang of Four）在“Design Patterns”里的定义，Observer模式“<SPAN class=style8><STRONG><FONT size=3>定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新</FONT></STRONG></SPAN>”，这个描述正好符合我们对“组件通信”问题的需求。让我们先看看Observer模式的结构：<BR>&nbsp;<IMG title="Observer 模式的结构" height=222 alt=o_observer.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_observer.gif" width=592 border=0><BR>其中各元素的含义如下： </FONT>
<UL>
<LI><FONT face=Verdana>Subject：被观察的目标的抽象接口，它提供对观察者（Observer）的注册、注销服务，Notify方法通知Observer目标发生改变； </FONT>
<LI><FONT face=Verdana>Object：观察者的抽象接口，Update方法是当得到Subject状态变化的通知后所要采取的动作； </FONT>
<LI><FONT face=Verdana>ConcreteSubject：Subject的具体实现； </FONT>
<LI><FONT face=Verdana>ConcreteObserver：Observer的具体实现 </FONT></LI></UL><FONT face=Verdana>Observer模式在实现MVC结构时非常有用，为数据和数据表示解耦合。<BR><BR></FONT><FONT face=Verdana><STRONG><U><FONT size=3><FONT color=#cc0000><SPAN class=style2>2. Java中的Observer模式：Observer和Observable</SPAN><BR></FONT></FONT></U></STRONG>在大致了解了Observer模式的描述之后，现在我们更为关心的是它在Java中是如何应用的。幸运的是，自从JDK 1.0起，就有了专门处理这种应用的API，这就是</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observer.html" target=_blank><FONT face=Verdana color=#002c99>Observer</FONT></A><FONT face=Verdana>接口和</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html" target=_blank><FONT face=Verdana color=#002c99>Observable</FONT></A><FONT face=Verdana>类，它们是属于java.util包的一部分。看来Java的开发者们真是深谙设计模式的精髓，而Java的确是为了真正的面向对象而生的，呵呵！<BR>这里的Observer和Observable分别对应设计模式中的Observer和Subject，对比一下它们定义的方法，痕迹还是相当明显的：<BR>Observer的方法： </FONT>
<UL>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observer.html#update(java.util.Observable,%20java.lang.Object)" target=_blank><FONT face=Verdana color=#002c99>update</FONT></A><FONT face=Verdana>(Observable subject, </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html" target=_blank><FONT face=Verdana color=#002c99>Object</FONT></A><FONT face=Verdana> arg) 监控subject，当subject对象状态发生变化时Observer会有什么响应，arg是传递给Observable的notifyObservers方法的参数； </FONT></LI></UL><FONT face=Verdana>Observable的方法： </FONT>
<UL>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html#addObserver(java.util.Observer)" target=_blank><FONT face=Verdana color=#002c99>addObserver</FONT></A><FONT face=Verdana>(Observer observer) observer向该subject注册自己 </FONT>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html#hasChanged()" target=_blank><FONT face=Verdana color=#002c99>hasChanged</FONT></A><FONT face=Verdana>() 检查该subject状态是否发生变化 </FONT>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html#setChanged()" target=_blank><FONT face=Verdana color=#002c99>setChanged</FONT></A><FONT face=Verdana>() 设置该subject的状态为“已变化” </FONT>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html#notifyObservers()" target=_blank><FONT face=Verdana color=#002c99>notifyObservers</FONT></A><FONT face=Verdana>() 通知observer该subject状态发生变化 </FONT></LI></UL><FONT face=Verdana><STRONG><U><FONT size=3><FONT color=#cc0000><SPAN class=style2>3. Observer模式在Java GUI事件模型中应用</SPAN><BR></FONT></FONT></U></STRONG>其实在AWT/Swing事件模型中用到了好几种设计模式，以前的JDK 1.0 AWT使用的是“<SPAN class=style8><STRONG><FONT size=3>基于继承的事件模型</FONT></STRONG></SPAN>”，在该模型</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html" target=_blank><FONT face=Verdana color=#002c99>Component</FONT></A><FONT face=Verdana>类中定义了一系列事件处理方法，如：</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html#handleEvent(java.awt.Event)" target=_blank><FONT face=Verdana color=#002c99>handleEvent</FONT></A><FONT face=Verdana>，</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html#mouseDown(java.awt.Event,%20int,%20int)" target=_blank><FONT face=Verdana color=#002c99>mouseDown</FONT></A><FONT face=Verdana>，</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html#mouseUp(java.awt.Event,%20int,%20int)" target=_blank><FONT face=Verdana color=#002c99>mouseUp</FONT></A><FONT face=Verdana>等等，我们对事件的响应是通过对组件类继承并覆盖相应的事件处理方法的手段来实现，组件接收到事件向所在容器广播，沿着容器链直到发现事件被某个容器的handle方法所处理。这种模型有很多缺点，事件的处理不应当由事件产生者负责，而且根据“设计模式”一书中的原则，“继承”通常被认为是“对封装性的破坏”，父子类之间的紧密耦合关系降低了灵活性，同时继承容易导致家族树规模的庞大，这些都不利于组件可重用。<BR>JDK 1.1以后新的事件模型是被成为“<SPAN class=style8><STRONG><FONT size=3>基于授权的事件模型</FONT></STRONG></SPAN>”，也就是我们现在所熟悉的Listener模型，事件的处理不再由产生事件的对象负责，而由Listener负责，只有被注册过的Listener才能向组件传递事件动作。尤其在Swing组件中设计MVC结构时用到了Observer模式，众所周知，MVC表示“模型－视图－控制器”，即“数据－表示逻辑－操作”，其中数据可以对应多种表示，这样视图就处在了observer的地位，而model则是subject。大家所熟悉的JTree和JTable就是这种MVC结构：<BR>-----------------------------------------------------<BR>Model&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; View&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Controller<BR>-----------------------------------------------------<BR>TreeModel&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JTree&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TreeModelListener<BR>TableModel&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JTable&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TableModelListener<BR>-----------------------------------------------------<BR><BR></FONT><FONT face=Verdana><STRONG><U><FONT size=3><FONT color=#cc0000><SPAN class=style2>4. 简单的例子</SPAN><BR></FONT></FONT></U></STRONG>回到本文一开始的那个Explorer的例子，我们考虑做一个简单的图片浏览器，使树型选择组件和图片浏览面板在两个不同的类中，其中图片浏览面板根据所选择的树的节点显示相应的图片，所以图片浏览面板是一个observer，树是subject。由于Java单根继承的原因，我们不能同时继承JPanel和Observable，但可以用对象的组合把一个subject放到我们的类当中，并通过TreeSelectionListener触发subject的setChanged方法，并通过notifyObservers方法通知observer。<BR>例子代码如下：<BR></FONT></FONT>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><FONT face=Verdana><FONT color=#006600><SPAN class=style3>//LeftPanel.java</SPAN><BR></FONT><SPAN class=style4><STRONG><FONT color=#ff0000>package</FONT></STRONG></SPAN> com.jungleford.test; </FONT>
<P><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> java.awt.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/BorderLayout.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>BorderLayout</FONT></STRONG></A><FONT face=Verdana>;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> javax.swing.*;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> javax.swing.event.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/event/TreeSelectionListener.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>TreeSelectionListener</FONT></STRONG></A><FONT face=Verdana>;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> javax.swing.event.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/event/TreeSelectionEvent.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>TreeSelectionEvent</FONT></STRONG></A><FONT face=Verdana>;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> javax.swing.tree.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/tree/DefaultMutableTreeNode.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>DefaultMutableTreeNode</FONT></STRONG></A><FONT face=Verdana>;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> java.util.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Observable</FONT></STRONG></A><FONT face=Verdana>;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> java.util.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observer.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Observer</FONT></STRONG></A><FONT face=Verdana>;</FONT></P>
<P><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>final</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> LeftPanel <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JPanel.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JPanel</FONT></STRONG></A><BR><FONT face=Verdana>{</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 把树型选择视图布局在左边</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>private</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTree.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JTree</FONT></STRONG></A><FONT face=Verdana> tree;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 树型选择视图</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>private</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JScrollPane.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JScrollPane</FONT></STRONG></A><FONT face=Verdana> scroll;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 让视图可滚动</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>private</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>DefaultMutableTreeNode</FONT></STRONG></SPAN> root, node1, node2;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 根节点及两个叶子</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>private</FONT></STRONG></SPAN> Sensor sensor;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// sensor是一个Observable，由于只能单根继承，所以作为组合成员</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>private</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>String</FONT></STRONG></A><FONT face=Verdana> file;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 图片文件名，与RightPanel通信的内容</SPAN><BR><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> LeftPanel(<SPAN class=style5><STRONG><FONT color=#990000>Observer</FONT></STRONG></SPAN> observer)<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;file = <SPAN class=style6><FONT color=#ff00ff>""</FONT></SPAN>;<BR>&nbsp;&nbsp;&nbsp;&nbsp;sensor = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> Sensor();<BR>&nbsp;&nbsp;&nbsp;&nbsp;sensor.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html#addObserver(java.util.Observer)" target=_blank><FONT face=Verdana color=#002c99>addObserver</FONT></A><FONT face=Verdana>(observer);</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 向Observable注册Observer</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;root = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>DefaultMutableTreeNode</FONT></STRONG></SPAN>(<SPAN class=style6><FONT color=#ff00ff>"Images"</FONT></SPAN>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;tree = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>JTree</FONT></STRONG></SPAN>(root);<BR>&nbsp;&nbsp;&nbsp;&nbsp;node1 = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>DefaultMutableTreeNode</FONT></STRONG></SPAN>(<SPAN class=style6><FONT color=#ff00ff>"Rabbit"</FONT></SPAN>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;node2 = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>DefaultMutableTreeNode</FONT></STRONG></SPAN>(<SPAN class=style6><FONT color=#ff00ff>"Devastator"</FONT></SPAN>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;root.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/tree/DefaultMutableTreeNode.html#add(javax.swing.tree.MutableTreeNode)" target=_blank><FONT face=Verdana color=#002c99>add</FONT></A><FONT face=Verdana>(node1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;root.add(node2);<BR>&nbsp;&nbsp;&nbsp;&nbsp;tree.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTree.html#addTreeSelectionListener(javax.swing.event.TreeSelectionListener)" target=_blank><FONT face=Verdana color=#002c99>addTreeSelectionListener</FONT></A><FONT face=Verdana>(<SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>TreeSelectionListener</FONT></STRONG></SPAN>()<BR>&nbsp;&nbsp;&nbsp;&nbsp;{</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 树节点选择动作</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>void</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/event/TreeSelectionListener.html#valueChanged(javax.swing.event.TreeSelectionEvent)" target=_blank><FONT face=Verdana color=#002c99>valueChanged</FONT></A><FONT face=Verdana>(<SPAN class=style5><STRONG><FONT color=#990000>TreeSelectionEvent</FONT></STRONG></SPAN> e)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Object</FONT></STRONG></A><FONT face=Verdana> obj = e.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/event/TreeSelectionEvent.html#getPath()" target=_blank><FONT face=Verdana color=#002c99>getPath</FONT></A><FONT face=Verdana>().</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/tree/TreePath.html#getLastPathComponent()" target=_blank><FONT face=Verdana color=#002c99>getLastPathComponent</FONT></A><FONT face=Verdana>();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (obj <SPAN class=style4><STRONG><FONT color=#ff0000>instanceof</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>DefaultMutableTreeNode</FONT></STRONG></SPAN>)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style5><STRONG><FONT color=#990000>DefaultMutableTreeNode</FONT></STRONG></SPAN> node = (<SPAN class=style5><STRONG><FONT color=#990000>DefaultMutableTreeNode</FONT></STRONG></SPAN>)obj;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>if</FONT></STRONG></SPAN> (node == root)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file = <SPAN class=style6><FONT color=#ff00ff>""</FONT></SPAN>;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 选择根</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>if</FONT></STRONG></SPAN> (node == node1)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file = <SPAN class=style6><FONT color=#ff00ff>"rabbit.jpg"</FONT></SPAN>;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 选择node1</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>if</FONT></STRONG></SPAN> (node == node2)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file = <SPAN class=style6><FONT color=#ff00ff>"devastator.gif"</FONT></SPAN>;</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 选择node2</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sensor.setData(file);</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 改变Observable</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sensor.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html#notifyObservers()" target=_blank><FONT face=Verdana color=#002c99>notifyObservers</FONT></A><FONT face=Verdana>();</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 通知observer，对象已改变</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;});<BR>&nbsp;&nbsp;&nbsp;&nbsp;scroll = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>JScrollPane</FONT></STRONG></SPAN>(tree);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Container.html#add(java.awt.Component,%20java.lang.Object)" target=_blank><FONT face=Verdana color=#002c99>add</FONT></A><FONT face=Verdana>(scroll, <SPAN class=style5><STRONG><FONT color=#990000>BorderLayout</FONT></STRONG></SPAN>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/BorderLayout.html#CENTER" target=_blank><FONT face=Verdana color=#002c99>CENTER</FONT></A><FONT face=Verdana>);<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>Observable</FONT></STRONG></SPAN> getSensor()<BR>&nbsp;&nbsp;{</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 返回Observable对象，使Observer可以获取</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>return</FONT></STRONG></SPAN> sensor;<BR>&nbsp;&nbsp;}<BR>}</FONT></P>
<P><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> Sensor <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> </FONT><FONT face=Verdana><STRONG><FONT color=#990000><SPAN class=style5>Observable</SPAN><BR></FONT></STRONG>{</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 定义自己的Observable</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>private</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>Object</FONT></STRONG></SPAN> data;<BR><BR>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>void</FONT></STRONG></SPAN> setData(<SPAN class=style5><STRONG><FONT color=#990000>Object</FONT></STRONG></SPAN> newData)<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;data = newData;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html#setChanged()" target=_blank><FONT face=Verdana color=#002c99>setChanged</FONT></A><FONT face=Verdana>();</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 改变Observable</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>System</FONT></STRONG></A><FONT face=Verdana>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#out" target=_blank><FONT face=Verdana color=#002c99>out</FONT></A><FONT face=Verdana>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PrintStream.html#println(java.lang.String)" target=_blank><FONT face=Verdana color=#002c99>println</FONT></A><FONT face=Verdana>(<SPAN class=style6><FONT color=#ff00ff>"Data changed!"</FONT></SPAN>);<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>Object</FONT></STRONG></SPAN> getData()<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>return</FONT></STRONG></SPAN> data;<BR>&nbsp;&nbsp;}<BR>}</FONT></P></FONT></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><FONT face=Verdana><FONT color=#006600><SPAN class=style3>//RightPanel.java</SPAN><BR></FONT><SPAN class=style4><STRONG><FONT color=#ff0000>package</FONT></STRONG></SPAN> com.jungleford.test; </FONT>
<P><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> java.awt.*;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> javax.swing.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JPanel.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JPanel</FONT></STRONG></A><FONT face=Verdana>;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> java.util.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observer.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Observer</FONT></STRONG></A><FONT face=Verdana>;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> java.util.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Observable</FONT></STRONG></A><FONT face=Verdana>;</FONT></P>
<P><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> RightPanel <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>JPanel</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>implements</FONT></STRONG></SPAN> </FONT><FONT face=Verdana><STRONG><FONT color=#990000><SPAN class=style5>Observer</SPAN><BR></FONT></STRONG>{</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 把图片浏览视图布局在右边</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>private</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Image.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Image</FONT></STRONG></A><FONT face=Verdana> image;<BR><BR>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>void</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observer.html#update(java.util.Observable,%20java.lang.Object)" target=_blank><FONT face=Verdana color=#002c99>update</FONT></A><FONT face=Verdana>(<SPAN class=style5><STRONG><FONT color=#990000>Observable</FONT></STRONG></SPAN> subject, </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Object</FONT></STRONG></A><FONT face=Verdana> obj)<BR>&nbsp;&nbsp;{</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 定义接收到Observable变化后的响应动作</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>String</FONT></STRONG></A><FONT face=Verdana> file = (<SPAN class=style5><STRONG><FONT color=#990000>String</FONT></STRONG></SPAN>)((Sensor)subject).getData();<BR>&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>if</FONT></STRONG></SPAN> (!file.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html#equals(java.lang.Object)" target=_blank><FONT face=Verdana color=#002c99>equals</FONT></A><FONT face=Verdana>(<SPAN class=style6><FONT color=#ff00ff>""</FONT></SPAN>))<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image = </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Toolkit.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Toolkit</FONT></STRONG></A><FONT face=Verdana>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Toolkit.html#getDefaultToolkit()" target=_blank><FONT face=Verdana color=#002c99>getDefaultToolkit</FONT></A><FONT face=Verdana>().</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Toolkit.html#getImage(java.lang.String)" target=_blank><FONT face=Verdana color=#002c99>getImage</FONT></A><FONT face=Verdana>(file);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/MediaTracker.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>MediaTracker</FONT></STRONG></A><FONT face=Verdana> tracker = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> <SPAN class=style5><STRONG><FONT color=#990000>MediaTracker</FONT></STRONG></SPAN>(<SPAN class=style4><STRONG><FONT color=#ff0000>this</FONT></STRONG></SPAN>);</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 定义图像跟踪</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tracker.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/MediaTracker.html#addImage(java.awt.Image,%20int)" target=_blank><FONT face=Verdana color=#002c99>addImage</FONT></A><FONT face=Verdana>(image, <SPAN class=style7><FONT color=#0000ff>0</FONT></SPAN>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT><FONT face=Verdana><STRONG><FONT color=#ff0000><SPAN class=style4>try</SPAN><BR></FONT></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tracker.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/MediaTracker.html#waitForID(int)" target=_blank><FONT face=Verdana color=#002c99>waitForID</FONT></A><FONT face=Verdana>(<SPAN class=style7><FONT color=#0000ff>0</FONT></SPAN>);</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 等待图像的完全加载</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>catch</FONT></STRONG></SPAN> (</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/InterruptedException.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>InterruptedException</FONT></STRONG></A><FONT face=Verdana> e)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html#getStackTrace()" target=_blank><FONT face=Verdana color=#002c99>printStackTrace</FONT></A><FONT face=Verdana>();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><FONT face=Verdana><STRONG><FONT color=#ff0000><SPAN class=style4>else</SPAN><BR></FONT></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image = <SPAN class=style4><STRONG><FONT color=#ff0000>null</FONT></STRONG></SPAN>;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html#repaint()" target=_blank><FONT face=Verdana color=#002c99>repaint</FONT></A><FONT face=Verdana>();</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 重绘组件</SPAN><BR></FONT>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>void</FONT></STRONG></SPAN> </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics)" target=_blank><FONT face=Verdana color=#002c99>paintComponent</FONT></A><FONT face=Verdana>(</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Graphics.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Graphics</FONT></STRONG></A><FONT face=Verdana> g)<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;g.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Graphics.html#setColor(java.awt.Color)" target=_blank><FONT face=Verdana color=#002c99>setColor</FONT></A><FONT face=Verdana>(</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Color.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>Color</FONT></STRONG></A><FONT face=Verdana>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Color.html#LIGHT_GRAY" target=_blank><FONT face=Verdana color=#002c99>LIGHT_GRAY</FONT></A><FONT face=Verdana>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;g.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Graphics.html#fillRect(int,%20int,%20int,%20int)" target=_blank><FONT face=Verdana color=#002c99>fillRect</FONT></A><FONT face=Verdana>(<SPAN class=style7><FONT color=#0000ff>0</FONT></SPAN>, <SPAN class=style7><FONT color=#0000ff>0</FONT></SPAN>, </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JComponent.html#getWidth()" target=_blank><FONT face=Verdana color=#002c99>getWidth</FONT></A><FONT face=Verdana>() - <SPAN class=style7><FONT color=#0000ff>1</FONT></SPAN>, </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JComponent.html#getHeight()" target=_blank><FONT face=Verdana color=#002c99>getHeight</FONT></A><FONT face=Verdana>() - <SPAN class=style7><FONT color=#0000ff>1</FONT></SPAN>);</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 先将组件上的画面清除</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>if</FONT></STRONG></SPAN> (image != <SPAN class=style4><STRONG><FONT color=#ff0000>null</FONT></STRONG></SPAN>)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Graphics.html#drawImage(java.awt.Image,%20int,%20int,%20java.awt.image.ImageObserver)" target=_blank><FONT face=Verdana color=#002c99>drawImage</FONT></A><FONT face=Verdana>(image, 0, 0, <SPAN class=style4><STRONG><FONT color=#ff0000>this</FONT></STRONG></SPAN>);</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 绘制新的图像</SPAN><BR></FONT>&nbsp;&nbsp;}<BR>}</FONT></P></FONT></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><FONT size=-1><FONT face=Verdana><FONT color=#006600><SPAN class=style3>//MainFrame.java</SPAN><BR></FONT><SPAN class=style4><STRONG><FONT color=#ff0000>package</FONT></STRONG></SPAN> com.jungleford.test; </FONT>
<P><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> java.awt.*;<BR><SPAN class=style4><STRONG><FONT color=#ff0000>import</FONT></STRONG></SPAN> javax.swing.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFrame.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>JFrame</FONT></STRONG></A><FONT face=Verdana>;</FONT></P>
<P><FONT face=Verdana><SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>class</FONT></STRONG></SPAN> MainFrame <SPAN class=style4><STRONG><FONT color=#ff0000>extends</FONT></STRONG></SPAN> </FONT><FONT face=Verdana><STRONG><FONT color=#990000><SPAN class=style5>JFrame</SPAN><BR></FONT></STRONG>{</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 演示窗口</SPAN><BR></FONT>&nbsp;&nbsp;<SPAN class=style4><STRONG><FONT color=#ff0000>public</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>static</FONT></STRONG></SPAN> <SPAN class=style4><STRONG><FONT color=#ff0000>void</FONT></STRONG></SPAN> main(</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>String</FONT></STRONG></A><FONT face=Verdana>[] args)<BR>&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;MainFrame frame = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> MainFrame();<BR>&nbsp;&nbsp;&nbsp;&nbsp;RightPanel right = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> RightPanel();<BR>&nbsp;&nbsp;&nbsp;&nbsp;LeftPanel left = <SPAN class=style4><STRONG><FONT color=#ff0000>new</FONT></STRONG></SPAN> LeftPanel(right);</FONT><FONT face=Verdana><FONT color=#006600><SPAN class=style3>// 注册Observer</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;frame.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFrame.html#getContentPane()" target=_blank><FONT face=Verdana color=#002c99>getContentPane</FONT></A><FONT face=Verdana>().</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Container.html#add(java.awt.Component,%20java.lang.Object)" target=_blank><FONT face=Verdana color=#002c99>add</FONT></A><FONT face=Verdana>(left, </FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/BorderLayout.html" target=_blank><STRONG><FONT face=Verdana color=#002c99>BorderLayout</FONT></STRONG></A><FONT face=Verdana>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/BorderLayout.html#WEST" target=_blank><FONT face=Verdana color=#002c99>WEST</FONT></A><FONT face=Verdana>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;frame.getContentPane().add(right, <SPAN class=style5><STRONG><FONT color=#990000>BorderLayout</FONT></STRONG></SPAN>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/BorderLayout.html#CENTER" target=_blank><FONT face=Verdana color=#002c99>CENTER</FONT></A><FONT face=Verdana>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;frame.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Frame.html#setTitle(java.lang.String)" target=_blank><FONT face=Verdana color=#002c99>setTitle</FONT></A><FONT face=Verdana>(<SPAN class=style6><FONT color=#ff00ff>"Observer Test"</FONT></SPAN>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;frame.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html#setSize(int,%20int)" target=_blank><FONT face=Verdana color=#002c99>setSize</FONT></A><FONT face=Verdana>(<SPAN class=style7><FONT color=#0000ff>400</FONT></SPAN>, <SPAN class=style7><FONT color=#0000ff>300</FONT></SPAN>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;frame.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFrame.html#setDefaultCloseOperation(int)" target=_blank><FONT face=Verdana color=#002c99>setDefaultCloseOperation</FONT></A><FONT face=Verdana>(<SPAN class=style5><STRONG><FONT color=#990000>JFrame</FONT></STRONG></SPAN>.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFrame.html#EXIT_ON_CLOSE" target=_blank><FONT face=Verdana color=#002c99>EXIT_ON_CLOSE</FONT></A><FONT face=Verdana>);<BR>&nbsp;&nbsp;&nbsp;&nbsp;frame.</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html#setVisible(boolean)" target=_blank><FONT face=Verdana color=#002c99>setVisible</FONT></A><FONT face=Verdana>(<SPAN class=style4><STRONG><FONT color=#ff0000>true</FONT></STRONG></SPAN>);<BR>&nbsp;&nbsp;}<BR>}</FONT></P></FONT></TD></TR></TBODY></TABLE><BR><FONT face=Verdana size=-1>程序运行截图如下：<BR><IMG title="Observer 模式的例子" height=300 alt=o_mainframe.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_mainframe.gif" width=400 border=0><BR>启动界面<BR><BR><IMG title=点击Rabbit后出现的图像 height=300 alt=o_rabbit.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_rabbit.gif" width=400 border=0><BR>点击Rabbit显示的图像<BR><BR><IMG title=点击Devestator后出现的图像 height=300 alt=o_devestator.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_devestator.gif" width=400 border=0><BR>点击Devestator显示的图像<BR><BR></FONT><FONT size=-1><FONT face=Verdana><STRONG><U><FONT size=3><FONT color=#cc0000><SPAN class=style2>附录：Observer模式概览</SPAN><BR><BR></FONT></FONT></U></STRONG>摘自 </FONT><A href="http://www.china-pub.com/computers/common/info.asp?id=684"><FONT face=Verdana color=#002c99>设计模式</FONT></A><BR><BR>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TD><FONT face=Verdana size=-1>意图</FONT></TD>
<TD><FONT face=Verdana size=-1>定义对象间的一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都得到通知并被自动更新。</FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>动机</FONT></TD>
<TD><FONT face=Verdana size=-1>将一个系统分割成一系列相互协作的类有一个常见的副作用：需要维护相关对象间的移植性。我们不希望为了维持一致性而使各类紧密耦合，因为这样将降低它们的可重用性。</FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>适用性</FONT></TD>
<TD><FONT size=-1>
<UL>
<LI><FONT face=Verdana>当一个抽象模型有两个方面，其中一个依赖于另一个，将这二者封装在独立的对象中使它们可以各自独立地改变和复用 </FONT>
<LI><FONT face=Verdana>当对一个对象的改变需要同时改变其它对象，但不知道具体有多少对象有待改变 </FONT>
<LI><FONT face=Verdana>当一个对象必须通知其它对象，但它又不能假定其它对象是什么，亦即不希望这些对象是紧密耦合的 </FONT></LI></UL></FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>结构图</FONT></TD>
<TD><FONT face=Verdana><IMG title="Observer 模式结构图" height=222 alt=o_observer.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_observer.gif" width=592 border=0></FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>参与者</FONT></TD>
<TD><FONT size=-1>
<UL>
<LI><FONT face=Verdana>Subject（目标） </FONT>
<LI><FONT face=Verdana>Observer（观察者） </FONT>
<LI><FONT face=Verdana>ConcreteSubject（具体目标） </FONT>
<LI><FONT face=Verdana>ConcreteObserver（具体观察者）</FONT></LI></UL></FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>协作图</FONT></TD>
<TD><FONT face=Verdana><IMG title="Observer 模式的时序图" height=275 alt=o_observer2.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_observer2.gif" width=494 border=0></FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>效果</FONT></TD>
<TD><FONT face=Verdana size=-1>允许你独立的改变目标和观察者。你可以单独复用目标对象而无需同时复用其观察者，反之亦然。你也可以在不改动目标和其它观察者的前提下增加观察者</FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>应用</FONT></TD>
<TD><FONT face=Verdana size=-1>MVC模式</FONT></TD></TR>
<TR>
<TD><FONT face=Verdana size=-1>相关模式</FONT></TD>
<TD><FONT size=-1>
<UL>
<LI><FONT face=Verdana>Mediator模式 </FONT>
<LI><FONT face=Verdana>Singleton模式 </FONT></LI></UL></FONT></TD></TR></TBODY></TABLE><BR><STRONG><FONT size=3><FONT color=#cc0000><U><FONT face=Verdana><SPAN class=style2>参考资料：</SPAN><BR></FONT></U></FONT></FONT></STRONG>
<UL>
<LI><A href="http://www.china-pub.com/computers/common/info.asp?id=684" target=_blank><FONT face=Verdana color=#002c99>Design Patterns: Elements of Reusable Object-Oriented Software</FONT></A><FONT face=Verdana>, by <EM>E. Gamma, R. Helm, R. Johnson, J. Vlissides</EM> </FONT>
<LI><FONT face=Verdana>IBM developerWorks教程：</FONT><A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/java-onlinecourse-bytitle/5E86A3394F5A20DF48256B7A0011E4A6?OpenDocument" target=_blank><FONT face=Verdana color=#002c99>Java设计模式101</FONT></A><FONT face=Verdana> </FONT>
<LI><A href="http://www.amazon.com/gp/reader/0130796662/ref=sib_dp_pt/002-9007113-0490433" target=_blank><FONT face=Verdana color=#002c99>Graphic Java 2, Mastering the JFC Volumn I: AWT</FONT></A><FONT face=Verdana>, by <EM>David M. Geary</EM></FONT></LI></UL></FONT><img src ="http://www.blogjava.net/jungleford/aggbug/2758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 21:44 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从前写的一个扫雷小游戏</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2757.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 13:36:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2757.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2757.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2757.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2757.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2757.html</trackback:ping><description><![CDATA[<FONT face=Verdana size=2>JavaMine是我在大约3、4年前刚开始学Java的时候写的一个小玩意，曾经发到CSDN上，居然还被收了，FT～～～<BR>所附的是jar文件，只要在控制台下运行“java -jar JavaMine.jar”或“javaw -jar JavaMine.jar”即可。<BR>该文件已包含源代码，可以随意修改，如果需要转贴或其它公开场合使用的话，还请注明“Powered by jungleford”之类的标志。<BR><BR>功能：<BR>跟Windows的扫雷差不多，添加了一个存取盘面的功能。<BR><BR>截图：<BR><IMG title=JavaMine的界面 height=510 alt=o_javamine.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_javamine.jpg" width=600 border=0><BR><BR>下载：<BR>点击<A href="http://www.blogjava.net/Files/jungleford/JavaMine.rar">这里</A></FONT><img src ="http://www.blogjava.net/jungleford/aggbug/2757.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 21:36 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2757.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我眼中的Java经典书籍</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2750.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 09:18:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2750.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2750.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2750.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2750.html</trackback:ping><description><![CDATA[<TABLE>
<TBODY>
<TR>
<TD vAlign=top align=middle width=17><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top width=537><FONT size=-1><FONT face=Verdana>&nbsp;&nbsp;&nbsp;&nbsp; 什么叫“经典书籍”呢？<BR>&nbsp;&nbsp;&nbsp;&nbsp; 以往我们说，“一流的公司定标准，二流的公司搞技术，&nbsp;三流的公司做服务，末流的公司卖产品”，其实这话并非全无道理。厌恶</FONT><A href="http://www.microsoft.com/" target=_blank><FONT face=Verdana>M$</FONT></A><FONT face=Verdana>的人不少，但嘴上骂归骂，底下照样用</FONT><A href="http://www.microsoft.com/windows/" target=_blank><FONT face=Verdana>Windows</FONT></A><FONT face=Verdana>， 赌咒发誓不用的也未必不想暗地里钻研一下其中的技术，再狠点，干脆只看Linux的字符界面，连X都不用。 然而为啥不见哪位仁兄自己只玩自己弄的OS呢？</FONT><A href="http://www.microsoft.com/" target=_blank><FONT face=Verdana>M$</FONT></A><FONT face=Verdana>和</FONT><A href="http://www.unix.org/" target=_blank><FONT face=Verdana>UNIX</FONT></A><FONT face=Verdana>家族在OS上显然是具有语言霸权， OS搞到这份上基本上就是“标准”了，你愿意白手起家？呵呵～～～<BR>&nbsp;&nbsp;&nbsp;&nbsp; 说了这些废话，我无非想说的是，所谓</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>的“经典书籍”，最好的就是</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>的标准，也就是</FONT><A href="http://www.sun.com/" target=_blank><FONT face=Verdana>SUN</FONT></A><FONT face=Verdana>的官方资料：<BR></FONT></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle width=17><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top width=537><FONT face=Verdana><FONT color=#cc0000><B>From java.sun.com</B></FONT><BR><BR></FONT><FONT size=-1><A href="http://java.sun.com/j2se/1.4.2/docs/" target=_blank><FONT face=Verdana>Java<SUP>TM</SUP> 2 SDK, Standard Edition Documentation Version 1.4.2</FONT></A><FONT face=Verdana> [</FONT><A href="http://allimant.nerim.net/javadoc/jdk142-hh.zip"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] <BR><BR></FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/" target=_blank><FONT face=Verdana>Java<SUP>TM</SUP> 2 Platform, Standard Edition, v 1.4.2 API Specification</FONT></A><FONT face=Verdana> [</FONT><A href="http://allimant.nerim.net/javadoc/jdk142-hh.zip"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] <BR><BR></FONT><A href="http://java.sun.com/j2ee/1.4/docs/api/" target=_blank><FONT face=Verdana>Java<SUP>TM</SUP> 2 Platform Enterprise Edition, v 1.4 API Specification</FONT></A><FONT face=Verdana> [</FONT><A href="http://erzatz.free.fr/storage/j2sdkee131-hh.zip"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] <BR><BR></FONT><A href="http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html" target=_blank><FONT face=Verdana>The Java Language Specification, Second Edition</FONT></A><FONT face=Verdana> [</FONT><A href="http://erzatz.free.fr/storage/specs2.zip"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] <BR><BR></FONT><A href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html" target=_blank><FONT face=Verdana>The Java<SUP>TM</SUP> Virtual Machine Specification, Second Edition</FONT></A><FONT face=Verdana> [</FONT><A href="ftp://ftp.javasoft.com/docs/specs/vmspec.2nded.html.zip"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] <BR><BR></FONT><A href="http://java.sun.com/docs/white/index.html" target=_blank><FONT face=Verdana>Java 2 Platform, Standard Edition, White Papers</FONT></A><FONT face=Verdana> <BR><BR></FONT><A href="http://java.sun.com/docs/books/tutorial/index.html" target=_blank><FONT face=Verdana>The Java Tutorial</FONT></A><FONT face=Verdana> [</FONT><A href="http://allimant.nerim.net/javadoc/tutorial-0311.zip"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] <BR><BR></FONT><A href="http://java.sun.com/j2ee/1.4/docs/tutorial/doc/" target=_blank><FONT face=Verdana>The J2EE 1.4 Tutorial</FONT></A><FONT face=Verdana> [</FONT><A href="http://allimant.nerim.net/javadoc/j2ee-tutorials.zip"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] </FONT></FONT><BR><BR><A href="http://java.sun.com/docs/books/tutorial/uiswing/" target=_blank><FONT face=Verdana>The Swing Tutorial</FONT></A><FONT face=Verdana> <BR><BR></FONT><A href="http://java.sun.com/docs/books/" target=_blank><FONT face=Verdana>SUN官方系列推荐书目</FONT></A><FONT face=Verdana> <BR><BR></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top><FONT face=Verdana><FONT color=#cc0000><B>Thinking in Java</B></FONT><BR><B>Java 编程思想</B><BR>by <I><A href="http://www.bruceeckel.com/" target=_blank>Bruce Eckel</A></I><BR><BR></FONT>
<TABLE width=522>
<TBODY>
<TR>
<TD align=middle width="34%"><FONT face=Verdana><IMG title="TIJ 第一版中文译本" height=110 alt=o_tij.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_tij.gif" width=78 border=0><BR></FONT><FONT size=-1><FONT face=Verdana>第一版<BR>京京工作室译本<BR><BR>[</FONT><A href="http://218.30.23.90/ebook/programme/thinkinjava.chm"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>]</FONT></FONT></TD>
<TD align=middle width="33%"><FONT face=Verdana><IMG title="TIJ 第二版候捷译本" height=108 alt=o_tij2.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_tij2.gif" width=77 border=0><BR></FONT><FONT size=-1><FONT face=Verdana>第二版<BR></FONT><A href="http://jjhou.csdn.net/" target=_blank><FONT face=Verdana>候捷</FONT></A><FONT face=Verdana>译本<BR><BR>[</FONT><A href="ftp://210.25.133.55/newbook/java2.rar"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>（部分章节）]</FONT></FONT></TD>
<TD align=middle width="33%"><FONT face=Verdana><IMG title="TIJ 第三版" height=104 alt=o_tij3.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_tij3.jpg" width=78 border=0><BR></FONT><FONT size=-1><FONT face=Verdana>第三版<BR>原版<BR><BR>[</FONT><A href="http://www.wgqqh.com/shhgs/book/tij3.chm"><FONT face=Verdana>下载</FONT></A><FONT face=Verdana>] [</FONT><A href="http://www.wgqqh.com/shhgs/tij.html" target=_blank><FONT face=Verdana>翻译</FONT></A><FONT face=Verdana>（部分章节）]</FONT></FONT></TD></TR>
<TR>
<TD align=middle colSpan=3><FONT size=-1><FONT face=Verdana>[</FONT><A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT face=Verdana>本书主页</FONT></A><FONT face=Verdana>]</FONT></FONT></TD></TR></TBODY></TABLE><FONT face=Verdana><FONT color=#cc0000 size=-1><B><U>书评：</U></B></FONT> </FONT><FONT size=-1><FONT face=Verdana>(2004-5-28) <BR>&nbsp;&nbsp;&nbsp;&nbsp; 说</FONT><A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT face=Verdana>TIJ</FONT></A><FONT face=Verdana>是“经典”想必不会有什么人反对，然而也许有人说这不是一本“适合新手读的书”。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 什么叫做“新手”？经常看到有人说，“我对Java很感兴趣”，“我打算学Java了”，but， 话锋一转——“我是新手”，“推荐本入门的书吧”。千禧年21岁生日的前两天， 在合肥黄山路上一个小书店里我买到了</FONT><A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT face=Verdana>TIJ</FONT></A><FONT face=Verdana>第一版， 从那以来的3年多时间里，我一直在学习这种技术（我不称其为“语言”），现在我还觉得自己是个新手。现在</FONT><A href="http://java.sun.com/j2ee/" target=_blank><FONT face=Verdana>J2EE</FONT></A><FONT face=Verdana>搞得这么火爆，我对</FONT><A href="http://java.sun.com/j2ee/" target=_blank><FONT face=Verdana>J2EE</FONT></A><FONT face=Verdana>还没什么了解， 不是说它不好（</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>的分布式应用得到了淋漓尽致的体现，怎么能说不好呢），因为核心部分我还有不少不明白的地方，设计模式上还不能像对古典OO一样理解得那么自然。跟风不是一种应有的学习心态，</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>与几k乃至几十k的月薪是否有关系我都不关心，因为仅仅是喜好，如同历史、足球和京剧一样…… :-)<BR>&nbsp;&nbsp;&nbsp;&nbsp; 什么叫做“新手”？如果你对OO有一定的了解（最好是“理解”），用过一段时间C++， 甚至只需要用过C（这个还是要比较熟练di），</FONT><A href="http://www.mindview.net/Books/TIJ/"><FONT face=Verdana>TIJ</FONT></A><FONT face=Verdana>就不算一个什么深不可测的“武林秘笈”， 甚至可以帮你更好地理解OO；如果没有一些OO的基础，那就有可能是“新手”，只是想要快些熟悉</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>环境和写法的话， 它对你来说还只是一门语言，书店任何一本“从入门到精通”之类的， 甚至是</FONT><A href="http://www.google.com/" target=_blank><FONT face=Verdana>google</FONT></A><FONT face=Verdana>估计都足以满足你的需求了。 说</FONT><A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT face=Verdana>TIJ</FONT></A><FONT face=Verdana>“不是一本适合新手读的书”，另一个原因是大部分读者都在谴责第一版京京工作室译本的翻译质量，术语的不一致， 总之评语是“基本上毁了原书”。其实我在2000年底买的就是这本，感觉上并没有传说的那么糟糕，很多所谓“不一致”的术语基本上想一下都能知道作者本来的意思，当然，想要精读，而且e文不错的话，原版还是首选。</FONT><A href="http://jjhou.csdn.net/" target=_blank><FONT face=Verdana>候捷</FONT></A><FONT face=Verdana>译的第二版我没有仔细看过， 感觉台湾方面的术语和我们还是有一些差别的， 加上</FONT><A href="http://jjhou.csdn.net/" target=_blank><FONT face=Verdana>候捷</FONT></A><FONT face=Verdana>有点武文弄墨的爱好， 翻译过程中兴许还夹杂了一些艺术性的表达，呵呵～～～ 第三版在原来的基础上有了较大改变，把关于网络的一些部分删除了，好像是放到</FONT><A href="http://www.bruceeckel.com/" target=_blank><FONT face=Verdana>Eckel</FONT></A><FONT face=Verdana>的另一本书里了。<BR><BR></FONT></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top><FONT face=Verdana><FONT color=#cc0000><B>Core Java 2</B></FONT><BR><B>Java 2 核心技术</B><BR>by <I><A href="http://www.horstmann.com/">Cay S. Horstmann</A></I> &amp; <I><A href="http://www.thecornells.com/">Gary Cornell</A></I><BR><BR></FONT>
<TABLE width=522>
<TBODY>
<TR>
<TD align=middle width="50%"><FONT face=Verdana><IMG title="Core Java 第一卷中文译本" height=120 alt=o_core-java1.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_core-java1.gif" width=92 border=0><BR></FONT><FONT face=Verdana size=-1>Vol 1: Fundamentals<BR>第一卷：基础知识</FONT></TD>
<TD align=middle width="50%"><FONT face=Verdana><IMG title="Core Java 第二卷中文译本" height=120 alt=o_core-java2.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_core-java2.gif" width=93 border=0><BR></FONT><FONT face=Verdana size=-1>Vol 2: Advanced Features<BR>第二卷：高级特性</FONT></TD></TR>
<TR>
<TD align=middle colSpan=2><FONT size=-1><FONT face=Verdana>[</FONT><A href="http://www.horstmann.com/corejava.html" target=_blank><FONT face=Verdana>本书主页</FONT></A><FONT face=Verdana>]</FONT></FONT></TD></TR></TBODY></TABLE><FONT face=Verdana><FONT color=#cc0000 size=-1><B><U>书评：</U></B></FONT> </FONT><FONT size=-1><FONT face=Verdana>(2004-5-28) <BR>&nbsp;&nbsp;&nbsp;&nbsp; 个人感觉Core Java不是那种可以整天捧在手上一遍又一遍直至翻到发旧的那种书，确切地说， 它更适合作为案头可备查阅的工具书。和</FONT><A href="http://www.mindview.net/Books/TIJ/" target=_blank><FONT face=Verdana>TIJ</FONT></A><FONT face=Verdana>不同，Core Java的侧重点放在</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/" target=_blank><FONT face=Verdana>核心API</FONT></A><FONT face=Verdana>的使用，当你一时不知道某个类或组件怎么用时，除了查</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/" target=_blank><FONT face=Verdana>API doc</FONT></A><FONT face=Verdana>以外，它也是个不错的选择。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 当然现成的例子别处还有不少，譬如</FONT><A href="http://java.sun.com/docs/books/tutorial/" target=_blank><FONT face=Verdana>The Java Tutorial</FONT></A><FONT face=Verdana>，</FONT><A href="http://javaalmanac.com/" target=_blank><FONT face=Verdana>Almanac</FONT></A><FONT face=Verdana>等，但有一本译成中文的手册常备在身边不是更好吗？<BR><BR></FONT></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top><FONT face=Verdana><FONT color=#cc0000><B>Java Network Programming</B></FONT><BR><B>Java 网络编程</B><BR>by <I>Elliotte Rusty Harold</I><BR><BR></FONT>
<TABLE width=522>
<TBODY>
<TR align=middle>
<TD><FONT face=Verdana><IMG title="Java Network Programming 第二版中文译本" height=120 alt=o_jnp.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_jnp.gif" width=93 border=0><BR><FONT size=-1>[<A href="http://www.oreilly.com/catalog/javanp2/" target=_blank>本书主页</A>]</FONT> </FONT></TD></TR></TBODY></TABLE><FONT face=Verdana><FONT color=#cc0000 size=-1><B><U>书评：</U></B></FONT> </FONT><FONT size=-1><FONT face=Verdana>(2004-5-28) <BR>&nbsp;&nbsp;&nbsp;&nbsp; 如果说Core Java是“大而全”的话，那么JNP就似乎应该称得上是“小而精”——其实也不算“小”了，它的内容真的是很丰富！不要忘记Harold的另一部名著“Java I/O”，这两本书几乎告诉你了有关于线程、</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/io/" target=_blank><FONT face=Verdana>I/O</FONT></A><FONT face=Verdana>、流操作、</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/net/" target=_blank><FONT face=Verdana>socket</FONT></A><FONT face=Verdana>等等一切……<BR>&nbsp;&nbsp;&nbsp;&nbsp; 网络是实现分布式环境的基础，</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>既然声称是一种“跨平台的”“分布式计算语言”，网络应用与接口 必须足够健壮才行。幸运的是，</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/api/" target=_blank><FONT face=Verdana>核心API</FONT></A><FONT face=Verdana>对包括</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/net/" target=_blank><FONT face=Verdana>socket</FONT></A><FONT face=Verdana>、</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/rmi/" target=_blank><FONT face=Verdana>RMI</FONT></A><FONT face=Verdana>的支持相当出色而且简单。 有人说</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>没有raw socket，因而无法进行IP层的访问，诚然如此，但这个代价却换得了</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>所更加注重的</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/security/" target=_blank><FONT face=Verdana>安全性</FONT></A><FONT face=Verdana>， 这是后话了……<BR>&nbsp;&nbsp;&nbsp;&nbsp; 要是你不满足于仅仅照例子写一个自己的</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/net/" target=_blank><FONT face=Verdana>socket</FONT></A><FONT face=Verdana>实现，并且希望挖掘更多内幕的话，那么JNP想必不会让你失望。<BR><BR></FONT></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top><FONT face=Verdana><FONT color=#cc0000><B>Graphic Java 2 - Mastering the JFC</B></FONT><BR><B>Java 2 图形设计</B><BR>by <I>David M. Geary</I><BR><BR></FONT>
<TABLE width=522>
<TBODY>
<TR>
<TD align=middle width="50%"><FONT face=Verdana><IMG title="Graphic Java 2 第一卷中文译本" height=203 alt=o_graphic-java1.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_graphic-java1.jpg" width=147 border=0><BR></FONT><FONT face=Verdana size=-1>Vol 1: AWT<BR>第一卷：AWT</FONT></TD>
<TD align=middle width="50%"><FONT face=Verdana><IMG title="Graphic Java 2 第二卷中文译本" height=203 alt=o_graphic-java2.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_graphic-java2.jpg" width=142 border=0><BR></FONT><FONT face=Verdana size=-1>Vol 2: Swing<BR>第二卷：Swing</FONT></TD></TR>
<TR>
<TD align=middle colSpan=2><FONT size=-1><FONT face=Verdana>[本书主页 </FONT><A href="http://www.amazon.com/gp/reader/0130796662/ref=sib_dp_pt/102-8185550-1867348" target=_blank><FONT face=Verdana>1</FONT></A><FONT face=Verdana> </FONT><A href="http://www.amazon.com/exec/obidos/tg/detail/-/0130796670/102-8185550-1867348?v=glance" target=_blank><FONT face=Verdana>2</FONT></A><FONT face=Verdana>]</FONT></FONT></TD></TR></TBODY></TABLE><FONT face=Verdana><FONT color=#cc0000 size=-1><B><U>书评：</U></B></FONT> </FONT><FONT size=-1><FONT face=Verdana>(2004-5-28) <BR>&nbsp;&nbsp;&nbsp;&nbsp; 可能会有不少刚开始学程序设计的人醉心于花哨的GUI，我们不能说这是眼光短浅，图形界面设计并不是“a piece of cake”，现在的IDE还不能达到可视化的尽善尽美，我时常会为了实现界面上的一点小小的trick而花不少时间找代码。</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>生来就不适合做桌面系统吗？非也，以下内容可能会让你赞叹不已：<BR>&nbsp;&nbsp;&nbsp;&nbsp; ★</FONT><A href="http://www.sun.com/" target=_blank><FONT face=Verdana>SUN</FONT></A><FONT face=Verdana>正在极力打造的</FONT><A href="http://wwws.sun.com/software/javadesktopsystem/" target=_blank><FONT face=Verdana>Java Desktop System</FONT></A><FONT face=Verdana>★<BR>&nbsp;&nbsp;&nbsp;&nbsp; ★</FONT><A href="http://community.java.net/javadesktop/" target=_blank><FONT face=Verdana>JavaDesktop社区</FONT></A><FONT face=Verdana>★<BR>&nbsp;&nbsp;&nbsp;&nbsp; ★桌面应用程序集中营</FONT><A href="http://java.sun.com/products/jfc/tsc/sightings/" target=_blank><FONT face=Verdana>Swing Slightings</FONT></A><FONT face=Verdana>★<BR>&nbsp;&nbsp;&nbsp;&nbsp; 如果你是一个热衷于GUI设计和图形图像处理的玩家，如果你希望深入了解</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/awt/" target=_blank><FONT face=Verdana>AWT</FONT></A><FONT face=Verdana>或</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/swing/" target=_blank><FONT face=Verdana>Swing</FONT></A><FONT face=Verdana>图形组件以及窗口事件模型，如果你还不满足这些，希望学习</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/2d/" target=_blank><FONT face=Verdana>Java 2D</FONT></A><FONT face=Verdana>这样的高级图形技术，这部书都是不错的选择。<BR><BR></FONT></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top><FONT face=Verdana><FONT color=#cc0000><B>Java Security</B></FONT><BR><B>Java 安全</B><BR>by <I>Scott Oaks</I><BR><BR></FONT>
<TABLE width=522>
<TBODY>
<TR align=middle>
<TD><FONT face=Verdana><IMG title="Java Security 第二版中文译本" height=200 alt=o_java-security.gif src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_java-security.gif" width=154 border=0><BR><FONT size=-1>[<A href="http://www.oreilly.com/catalog/javasec2/" target=_blank>本书主页</A>]</FONT> </FONT></TD></TR></TBODY></TABLE><FONT face=Verdana><FONT color=#cc0000 size=-1><B><U>书评：</U></B></FONT> </FONT><FONT size=-1><FONT face=Verdana>(2004-5-29) <BR>&nbsp;&nbsp;&nbsp;&nbsp; 因为自己专业上的原因，对</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/security/" target=_blank><FONT face=Verdana>安全性</FONT></A><FONT face=Verdana>需要作一点研究。</FONT><A href="http://java.sun.com/" target=_blank><FONT face=Verdana>Java</FONT></A><FONT face=Verdana>号称是“一种安全的平台”， 这种安全我想不仅是单单一个</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/security/spec/security-spec.doc1.html" target=_blank><FONT face=Verdana>sandbox模型</FONT></A><FONT face=Verdana>就能完全概括的，它还包括了许多成分，从核心级别到优秀的安全服务应用。要知道，</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/security/" target=_blank><FONT face=Verdana>安全性</FONT></A><FONT face=Verdana>是</FONT><A href="http://java.sun.com/j2ee/" target=_blank><FONT face=Verdana>J2EE</FONT></A><FONT face=Verdana>的一个重要支柱。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 本书与Helton所著Java Security Solutions都是研习</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/guide/security/" target=_blank><FONT face=Verdana>Java安全性</FONT></A><FONT face=Verdana>的参考。 </FONT></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle><FONT face=Verdana><IMG height=15 alt=o_item.GIF src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_item.GIF" width=15 border=0>&nbsp;</FONT></TD>
<TD vAlign=top><FONT face=Verdana><FONT color=#cc0000><B>J2EE Security</B></FONT><BR>by <I><A href="http://www.pankaj-k.net/" target=_blank>Pankaj Kumar</A></I><BR><BR></FONT>
<TABLE width=522>
<TBODY>
<TR align=middle>
<TD><FONT face=Verdana><IMG title="J2EE Security" height=190 alt=o_j2ee-security.jpg src="http://www.blogjava.net/images/blogjava_net/jungleford/924/o_j2ee-security.jpg" width=144 border=0><BR><FONT size=-1>[<A href="http://www.j2ee-security.net/" target=_blank>本书主页</A>]</FONT> </FONT></TD></TR></TBODY></TABLE><FONT face=Verdana><FONT color=#cc0000 size=-1><B><U>书评：</U></B></FONT> </FONT><FONT size=-1><FONT face=Verdana>(2004-5-29) <BR>&nbsp;&nbsp;&nbsp;&nbsp; 前些天刚从单位图书馆借到了这本书， 今年</FONT><A href="http://www.hp.com/" target=_blank><FONT face=Verdana>HP</FONT></A><FONT face=Verdana>刚出的新书，当然还没有中文版，看看封底， 哇，$49！我半个月的补助！不过看了一段时间，觉得写得还真是不错，作者提供了一些实用的工具程序， 功能比</FONT><A href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html" target=_blank><FONT face=Verdana>keytool</FONT></A><FONT face=Verdana>还要丰富。</FONT></FONT></TD></TR></TBODY></TABLE><FONT size=2></FONT><img src ="http://www.blogjava.net/jungleford/aggbug/2750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 17:18 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2SE 主要工具程序</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2749.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2749.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2749.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2749.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2749.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2749.html</trackback:ping><description><![CDATA[<FONT size=2><FONT face=Verdana>摘自 </FONT><EM><A href="http://www.china-pub.com/computers/common/info.asp?id=12158" target=_blank><FONT face=Verdana>Java安全性编程实例</FONT></A><BR><BR>
<TABLE width="90%">
<TBODY>
<TR>
<TD style="TEXT-JUSTIFY-TRIM: punctuation">
<TABLE class="" cellSpacing=3 cellPadding=0 border=1 MsoNormal?Table?>
<TBODY>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; WIDTH: 427.6pt; PADDING-TOP: 0cm" vAlign=top width=570 colSpan=2>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><B><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>基本工具</FONT></SPAN></B></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>javac</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>编译器</FONT></SPAN></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>java</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>字节码解释器</FONT></SPAN></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>javadoc</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana>API<SPAN style="FONT-FAMILY: 宋体">文档生成器</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>appletviewer</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana>applet<SPAN style="FONT-FAMILY: 宋体">运行和调试</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>jar</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana>jar<SPAN style="FONT-FAMILY: 宋体">包生成和管理</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>jdb</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>简单的控制台下的调试器</FONT></SPAN></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>javah</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">生成</SPAN>JNI <SPAN style="FONT-FAMILY: 宋体">本地存根和</SPAN>C <SPAN style="FONT-FAMILY: 宋体">头文件</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>javap</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">类分解器，课显示字节码的包，以及</SPAN>public<SPAN style="FONT-FAMILY: 宋体">、</SPAN>protected<SPAN style="FONT-FAMILY: 宋体">的属性和方法</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>extcheck</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">检测</SPAN>jar<SPAN style="FONT-FAMILY: 宋体">文件的版本冲突</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; WIDTH: 427.6pt; PADDING-TOP: 0cm" vAlign=top width=570 colSpan=2>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><FONT face=Verdana><B>RMI</B><B><SPAN style="FONT-FAMILY: 宋体">工具</SPAN></B></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>rmic</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">生成远程对象的</SPAN>skeleton<SPAN style="FONT-FAMILY: 宋体">和</SPAN>stub</FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>rmiregistry</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">提供远程对象的注册服务，</SPAN>RMI<SPAN style="FONT-FAMILY: 宋体">客户端程序可通过该服务找到远程对象</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>rmid</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>启动激活系统后台程序</FONT></SPAN></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>serialver</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">返回类的</SPAN>serialVersionUID</FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; WIDTH: 427.6pt; PADDING-TOP: 0cm" vAlign=top width=570 colSpan=2>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><B><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>国际化工具</FONT></SPAN></B></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>native2ascii</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">讲本地编码的文本转换为</SPAN>unicode<SPAN style="FONT-FAMILY: 宋体">编码</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; WIDTH: 427.6pt; PADDING-TOP: 0cm" vAlign=top width=570 colSpan=2>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><B><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>安全工具</FONT></SPAN></B></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>keytool</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>管理密钥库和证书</FONT></SPAN></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>jarsigner</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">签名</SPAN>/<SPAN style="FONT-FAMILY: 宋体">验证</SPAN>jar <SPAN style="FONT-FAMILY: 宋体">文件</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>policytool</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">基于</SPAN>GUI<SPAN style="FONT-FAMILY: 宋体">的策略文件管理器</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; WIDTH: 427.6pt; PADDING-TOP: 0cm" vAlign=top width=570 colSpan=2>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><FONT face=Verdana><B>IDL</B><B><SPAN style="FONT-FAMILY: 宋体">和</SPAN>IIOP</B><B><SPAN style="FONT-FAMILY: 宋体">工具</SPAN></B></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>tnameserv</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><SPAN style="FONT-FAMILY: 宋体"><FONT face=Verdana>提供名字访问服务</FONT></SPAN></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>idlj</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">根据</SPAN>IDL <SPAN style="FONT-FAMILY: 宋体">文件生成</SPAN>Java <SPAN style="FONT-FAMILY: 宋体">绑定，是</SPAN>Java <SPAN style="FONT-FAMILY: 宋体">程序可以使用</SPAN>CORBA</FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>orbd</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">在</SPAN>CORBA <SPAN style="FONT-FAMILY: 宋体">环境下是客户透明定位和执行服务器上的</SPAN>persistent <SPAN style="FONT-FAMILY: 宋体">对象</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>servertool</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana>persistent<SPAN style="FONT-FAMILY: 宋体">服务器管理工具</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; WIDTH: 427.6pt; PADDING-TOP: 0cm" vAlign=top width=570 colSpan=2>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><B><FONT face=Verdana>Java Plug-in</FONT></B></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>unregbean</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ccffcc; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">注销</SPAN>bean <SPAN style="FONT-FAMILY: 宋体">组件</SPAN></FONT></P></TD></TR>
<TR>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 66.55pt; PADDING-TOP: 0cm" vAlign=top width=89>
<P class=MsoNormal><FONT face=Verdana>htmlconverter</FONT></P></TD>
<TD style="PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BACKGROUND: #ffffc1; PADDING-BOTTOM: 0cm; WIDTH: 359.05pt; PADDING-TOP: 0cm" vAlign=top width=479>
<P class=MsoNormal><FONT face=Verdana><SPAN style="FONT-FAMILY: 宋体">将</SPAN>HTML <SPAN style="FONT-FAMILY: 宋体">文件中的</SPAN>&lt;applet&gt; <SPAN style="FONT-FAMILY: 宋体">标记按一定格式转换称</SPAN>&lt;object&gt; <SPAN style="FONT-FAMILY: 宋体">标记</SPAN></FONT></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></EM></FONT><img src ="http://www.blogjava.net/jungleford/aggbug/2749.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 17:01 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2749.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么程序员不应调用“sun”包？（译文）</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2748.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2748.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2748.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2748.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2748.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2748.html</trackback:ping><description><![CDATA[<P><SPAN class=style2><STRONG><U><FONT face=Verdana color=#ff0000 size=4>jungleford如是说<BR><BR></FONT></U></STRONG></SPAN><A href="http://java.sun.com/j2se/1.4.2/docs/api/" target=_blank><FONT face=Verdana size=2>J2SE API Documents</FONT></A><FONT face=Verdana size=2>是我们最常用的参考手册，但细心的朋友可能会发现，在“%JAVA_HOME%\jre\lib\rt.jar”中包含比API文档更多的类，那么其它的类为什么没有在帮助文档中出现呢？在SUN的JDK FAQ中有一篇“</FONT><A title=阅读原文 href="http://java.sun.com/products/jdk/faq/faq-sun-packages.html" target=_blank><FONT face=Verdana size=2>Why Developers Should Not Write Programs That Call 'sun' Packages</FONT></A><FONT face=Verdana size=2>”，该文部分地解答了这个问题，我将原文翻译如下</FONT></P>
<P><FONT face=Verdana size=2>=====================================================================</FONT></P>
<P><FONT face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; J2SE中的类大致可以划分为以下的各个包：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.*，javax.*，org.*，sun.*<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 除了“sun”包，其它各个包都是Java平台的标准实现，并且今后也将被继续支持。一般说来，“sun”之类的包并不包含在Java平台的标准中，它与操作系统相关，在不同的操作系统（如Solaris，Windows，Linux，Mac等等）中的实现也各不相同，并且可能随着J2SE版本不定期变化。因此，直接调用“sun”包的程序代码并不是100％的Java实现。也就是说：<BR>“java.*”包，“javax.*”包，“org.*”包是作为J2SE的API公开接口的一部分，如果程序直接调用这些包中的API，那么程序是可以运行在所有Java平台上，而与操作系统无关；但“sun.*”包并不是API公开接口的一部分，调用“sun”包的程序并不能确保工作在所有Java平台上，事实上，这样的程序并不能工作在今后的Java平台上。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 正因为如此，“sun.*”包中的类并没有提供API文档。平台无关性是Java语言最大的优势之一，此外，SUN和Java许可证确保维持了今后API的向上兼容性（以后修改的那些有严重bug的代码除外）。这种兼容性意味着你写好的程序编译成的cl ass文件仍然可以工作在将来的版本当中。<BR>每家实现Java平台的厂商都可以使用他们自己的方式。“sun.*”包中的类是SUN 对Java平台的实现方式，它们工作在Java 2 SDK的下层，这些类未必被其它Java 平台开发商支持。比如你的Java程序如果调用了一个名为“sun.package.Foo”的类，将有可能产生“ClassNotFoundError”的错误，同时你也将失去利用Java的一个主要的优点。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从技术上讲，并不能防止你的程序调用“sun.*”包中的类。在版本的变迁当中，这些类可能会被删除或转移到其它包路径下，而且它的接口（包括名称、标签等）也很有可能发生变化，（根据SUN的观点，我们应当能够通过对“sun.*”包的修改来提高Java平台的性能。）在这种情况下，即便你希望程序仅仅运行在SUN的实现平台下，你仍将承受新的版本给你的系统带来破坏的风险。总之，编写依赖于“sun.*”包的Java程序是不安全的，他们将变得无法移植，无法被很好地支持。</FONT></P><img src ="http://www.blogjava.net/jungleford/aggbug/2748.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 16:57 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2748.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写自己的登录与访问控制模块</title><link>http://www.blogjava.net/jungleford/archive/2005/04/02/2747.html</link><dc:creator>jungleford</dc:creator><author>jungleford</author><pubDate>Sat, 02 Apr 2005 08:20:00 GMT</pubDate><guid>http://www.blogjava.net/jungleford/archive/2005/04/02/2747.html</guid><wfw:comment>http://www.blogjava.net/jungleford/comments/2747.html</wfw:comment><comments>http://www.blogjava.net/jungleford/archive/2005/04/02/2747.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jungleford/comments/commentRss/2747.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jungleford/services/trackbacks/2747.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: jungleford如是说&nbsp;&nbsp;&nbsp;&nbsp; 安全性是Java鼓吹得最多的特性之一，的确，Java的安全特性涵盖了从应用级别到语言级别乃至JVM本身。以前大家都知道有个Sandbox，但仅有Sandbox尚不能满足，或者说不能很方便地做到我们所需要的全部安全需求，譬如现在一个系统首先起码需要一个登录功能，更进一步的话，还需要对用户访问资源的行为进行约束，下面我想大致讲...&nbsp;&nbsp;<a href='http://www.blogjava.net/jungleford/archive/2005/04/02/2747.html'>阅读全文</a><img src ="http://www.blogjava.net/jungleford/aggbug/2747.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jungleford/" target="_blank">jungleford</a> 2005-04-02 16:20 <a href="http://www.blogjava.net/jungleford/archive/2005/04/02/2747.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>