﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-鹰翔宇空-文章分类-java</title><link>http://www.blogjava.net/TrampEagle/category/9796.html</link><description>学习和生活
</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 10:32:42 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 10:32:42 GMT</pubDate><ttl>60</ttl><item><title>Swt常用控件中文教程</title><link>http://www.blogjava.net/TrampEagle/articles/99625.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 13 Feb 2007 02:54:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/99625.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/99625.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/99625.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/99625.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/99625.html</trackback:ping><description><![CDATA[
		<div class="forumcontent">
				<br />1、Eclipse中swt的配置<br />建议配置：jdk1.4.2以及eclipse3.1<br />在代码中调用swt控件之前，首先建立一个项目，然后选择该项目的properties -&gt; Java Build Path，将standard Widget ToolKit加入到Library页当中。如下图所示：<br />接下来可以建立第一个eclipse小程序，新建一个class，并且在该class所对应的代码中输入如下程序，其中package以及class名称根据实际情况来确定名称。<br />package mypakage;<br />import org.eclipse.swt.widgets.*;<br />import org.eclipse.swt.*;<br />/*导入需要的类库*/<br />public class Myfrm1 {<br />public Myfrm1() {<br />super();<br />}<br />public static void main(String[] args) {<br />Display display = new Display();<br />Shell shell = new Shell(display);<br />/*shell为一个窗口对象*/<br />Label label = new Label(shell, SWT.NONE);<br />label.setText("Hello, World!"); /*创建一个标签对象并且设置标题文字*/<br />label.pack();<br />shell.pack();<br />shell.open(); /*打开并显示窗口*/<br /><br />while(!shell.isDisposed())<br />if(!display.readAndDispatch())<br />display.sleep(); /*在窗口没有销毁之前，显示对象一直处于等待状态*/<br /><br />display.dispose(); /*否则，销毁对象，释放对象所占据的资源*/<br />label.dispose();<br />}<br />}<br /><br />运行上述代码（run -&gt; debug -&gt; swt application）将产生如下所示的一个窗口 <br /><br />2、button的使用<br />按钮可能的类型有很多，例如：<br />SWT.BORDER 含有边框的按钮<br /><br />SWT.CHECK 复选按钮<br /><br />SWT.PUSH 普通按钮<br /><br />SWT.RADIO 单选按钮<br /><br />3、Text的使用<br />文本框的类型也有很多种选择，例如：<br />SWT.BORDER 含有边框<br /><br />SWT.READ_ONLY 只读<br /><br />下图为包含按钮以及文本框的窗口<br /><br /><br />设计上述窗口所对应的代码为：<br />package mypakage;<br />import org.eclipse.swt.widgets.*;<br />import org.eclipse.swt.SWT;<br />import org.eclipse.swt.events.*;<br />import org.eclipse.swt.layout.*;<br />public class Myfrm1 {<br />public Myfrm1() {<br />super();<br />}<br />public static void main(String[] args) {<br />Display display = new Display( );<br />Shell shell = new Shell(display);<br />shell.setSize(300, 200);<br />shell.setLayout(new RowLayout( ));<br />shell.setText("Button Example");<br />final Button button = new Button(shell, SWT.BORDER);<br />button.setText("Click Me");<br />final Text text = new Text(shell, SWT.BORDER); <br />shell.open( );<br /><br />while(!shell.isDisposed( )) {<br />if(!display.readAndDispatch( )) display.sleep( );<br />}<br />display.dispose( );<br />}<br />}<br /><br />如果想对控件的位置以及大小进行精确的设置，可以使用setBounds(x, y, width, height)方法来取代shell.setLayout(new RowLayout( ))。例如：button.setBounds(80, 80, 90, 20);<br /><br />button的监听及事件处理<br />对按钮单击事件处理的代码：<br />button.addSelectionListener(new SelectionListener( )<br />{<br />public void widgetSelected(SelectionEvent event)<br />{<br />text.setText("No worries!");<br />}<br />public void widgetDefaultSelected(SelectionEvent event)<br /><br />{<br />text.setText("No worries!");<br />}<br />});<br /><br />将以上代码加入到shell.open之前，当点击按钮时产生以下效果：<br /><br /><br />分析：由于为button按钮增加了一个监听器，按钮时刻处于被“监控”的状态，当按钮控件被选择（点击）既选择事件发生时，对文本控件进行赋值”No worries”。<br /><br />根据监听事件的原理，设计如下程序，该程序能够获得鼠标的X坐标，显示在文本框中：<br /><br />package mypakage;<br />import org.eclipse.swt.widgets.*;<br />import org.eclipse.swt.SWT;<br />import org.eclipse.swt.events.*;<br />import org.eclipse.swt.layout.*;<br /><br />public class Myfrm1 {<br />public Myfrm1() {<br />super();<br />}<br />public static void main(String[] args) {<br />Display display = new Display( );<br />Shell shell = new Shell(display);<br />shell.setSize(300, 200);<br />shell.setLayout(new RowLayout( ));<br />final Text text = new Text(shell, SWT.SHADOW_IN);<br /><br />shell.addMouseMoveListener(new MouseMoveListener( )<br />{<br />public void mouseMove(MouseEvent e)<br />{<br />Integer y=new Integer(e.x); /*将x坐标转换为Integer类型的对象*/<br />text.setText(y.toString()); <br />}<br />});<br /><br />shell.open( );<br />while(!shell.isDisposed( )) {<br />if(!display.readAndDispatch( )) display.sleep( );<br />}<br />display.dispose( );<br />}<br />}<br /><br />监听方式：<br />ControlListener 用于处理移动以及尺寸变化<br /><br />FocusListener 用于处理得到焦点以及失去焦点<br /><br />KeyListener 处理按键的输入<br /><br />MouseListener , MouseMoveListener, MouseTrackListener 对鼠标的动作进行处理<br /><br />SelectionListener 处理控件的选择行为（包括按钮的点击）<br /><br />注意：监听方式与其所能够处理的事件具有一定的关联性，既监听方式决定了所能够处理事件的种类，例如：<br /><br />shell.addMouseListener(new MouseListener( )<br />{<br />public void mouseMove(MouseEvent e)<br />{text.setText("mousemove");}<br />public void mouseDoubleClick(MouseEvent e)<br />{text.setText("mousedbclc");}<br />public void mouseDown(MouseEvent e)<br />{}<br />public void mouseUp(MouseEvent e)<br />{}<br />});<br /><br />你会发现在鼠标移动时，text.setText("mousemove");始终不能够执行；并且mouseDown、mouseUp事件不能够省略，原因就在于MouseListener只能处理mouseDoubleClick、mouseDown、mouseUp三类事件，而且这三类事件不能够分离。<br /><br />3、List控件<br />List控件的样式包括：<br />SWT.BORDER 含有边框<br /><br />SWT.H_SCROLL 含有水平滚动条<br /><br />SWT.V_SCROLL 含有垂直滚动条<br /><br />SWT.SINGLE 允许单选<br /><br />SWT.MULTI 允许复选<br /><br />若要创建一个含有从11个元素的List，可以通过以下代码来实现<br />final List list = new List (shell, SWT.SINGLE);<br />for (int i=0;i&lt;=10;i++)<br />list.add("item"+i);<br /><br /><br /><br />以下实例能够判断List控件中所选择的选项，并且输出显示在控制台中：<br />package mypakage;<br />import org.eclipse.swt.widgets.*;<br />import org.eclipse.swt.SWT;<br />import org.eclipse.swt.events.*;<br />import org.eclipse.swt.layout.*;<br />public class Myfrm1 {<br />public Myfrm1() {<br />super();<br />}<br />public static void main(String[] args) {<br />Display display = new Display ( );<br />Shell shell = new Shell (display);<br />shell.setText("List Example");<br />shell.setSize(300, 200);<br />shell.setLayout(new FillLayout(SWT.VERTICAL));<br />final List list = new List (shell, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); <br />for (int loopIndex = 0; loopIndex &lt; 100; loopIndex++){ <br />list.add("Item " + loopIndex);<br />}<br />list.addSelectionListener(new SelectionListener( )<br />{<br />public void widgetSelected(SelectionEvent event)<br />{<br />int selections[] = list.getSelectionIndices ( );<br />String outText = "";<br />for (int loopIndex = 0; loopIndex &lt; selections.length; <br />loopIndex++) outText += selections[loopIndex] + " ";<br />System.out.println ("You selected: " + outText);<br />}<br />public void widgetDefaultSelected(SelectionEvent event)<br />{<br />int [] selections = list.getSelectionIndices ( );<br />String outText = "";<br />for (int loopIndex = 0; loopIndex &lt; selections.length; loopIndex++) <br />outText += selections[loopIndex] + " ";<br />System.out.println ("You selected: " + outText);<br />}<br />});<br />shell.open ( );<br />while (!shell.isDisposed ( )) {<br />if (!display.readAndDispatch ( )) display.sleep ( );<br />}<br />display.dispose ( );<br />}<br />}<br /><br />效果图：<br /><br /><br />You selected: 4 5 6 7 8 9 10<br />分析：list.getSelectionIndices ( )方法将会获得被选择项目的集合， selections[]或者[] elections表示动态一维数组。<br /><br />4、Menu控件<br />建立菜单的一般步骤为：<br />1、在建立菜单时，首先需要建立一个菜单栏，需要使用SWT.BAR属性<br />Menu menuBar = new Menu(shell, SWT.BAR);<br /><br />2、在菜单栏的基础之上，创建下拉菜单的所对应的顶级菜单项，需要使用SWT.CASCADE属性<br />fileMenuHeader = new MenuItem(menuBar, SWT.CASCADE);<br />fileMenuHeader.setText("&amp;File");<br /><br />3、建立与顶级菜单项相关的下拉式菜单<br />dropMenu1 = new Menu(shell, SWT.DROP_DOWN);<br /><br />4、将顶级菜单项与下拉菜单关联<br />MenuHeader1.setMenu(dropMenu1);<br /><br />5、为下拉菜单添加子菜单项<br />dropitem1= new MenuItem(dropMenu1, SWT.PUSH);<br />dropitem1.setText("open");<br />…<br />…<br /><br />6、最后，在窗口中指定需要显示的菜单栏<br />shell.setMenuBar(menuBar);<br /><br /><br /><br />菜单的监听及事件<br />参照按钮的监听以及事件，设计如下程序，当点击 File子菜单下的“open”时，在文本框中显示“click open menu!”<br />dropitem1.addSelectionListener(new SelectionListener()<br />{<br />public void widgetSelected(SelectionEvent event)<br />{<br />text.setText("click open menu!");<br />}<br />public void widgetDefaultSelected(SelectionEvent event)<br />{<br />text.setText("click open menu!");<br />}<br />});<br /><br /><br />5、使用工具栏toobar<br />建立工具栏可以通过如下方式：ToolBar toolbar = new ToolBar(shell, SWT.NONE);<br />在工具栏的基础之上创建工具栏子按钮，并且设置子按钮的标题：<br /><br />ToolItem item1 = new ToolItem(toolbar, SWT.PUSH);<br />item1.setText("item1");<br /><br />例如：<br />ToolBar toolbar = new ToolBar(shell, SWT.NONE);<br />ToolItem item1 = new ToolItem(toolbar, SWT.PUSH);<br />item1.setText("item1");<br />ToolItem item2 = new ToolItem(toolbar, SWT.PUSH);<br />item2.setText("item2");<br /><br /><br /><br />工具栏的监听及事件<br />实例：创建一个监听对象，将该监听对象应用于每一个按钮，最终来判断鼠标点击的是哪一个按钮，效果图如下。<br />Listener listener = new Listener( ) {<br />public void handleEvent(Event event) {<br />ToolItem item =(ToolItem)event.widget;<br />String string = item.getText( );<br />text.setText("You selected:" + string); }<br />};<br />item1.addListener(SWT.Selection, listener);<br />item2.addListener(SWT.Selection, listener);<br />item3.addListener(SWT.Selection, listener);<br />item4.addListener(SWT.Selection, listener);<br /><br /><br /><br />6、滚动条slider的使用<br />滚动条分为有边框、垂直、水平三种类型，利用slider.setBounds方法可以指定滚动条所在的位置。<br />滚动条所能够处理事件的包括：<br />SWT.ARROW_DOWN 向下或向右按钮被点击<br /><br />SWT.ARROW_UP 向左或向上按钮被点击<br /><br />SWT.DRAG 滑块按钮被托动<br /><br />SWT.END 滑块到达终点<br /><br />SWT.HOME 滑块到达起点<br /><br />SWT.PAGE_DOWN 下方或右侧的滚动条被点击<br /><br />SWT.PAGE_UP 上方或左侧的滚动条被点击<br />实例：根据滑块的位置移动按钮位置<br />slider.addListener(SWT.Selection, new Listener( ) {<br /><br />public void handleEvent(Event event) {<br /><br />switch(event.detail) {<br /><br />case SWT.ARROW_DOWN: button.setBounds(slider.getSelection(),0,20,10); <br /><br />break;<br /><br />case SWT.ARROW_UP:button.setBounds(slider.getSelection(),0,20,10); <br /><br />break;<br /><br />case SWT.DRAG:button.setBounds(slider.getSelection(),0,20,10); <br /><br />break;<br /><br />case SWT.END:button.setBounds(slider.getSelection(),0,20,10); <br /><br />break;<br /><br />case SWT.HOME:button.setBounds(slider.getSelection(),0,20,10); <br /><br />break;<br /><br />case SWT.PAGE_DOWN:button.setBounds(slider.getSelection(),0,20,10); <br /><br />break;<br /><br />case SWT.PAGE_UP:button.setBounds(slider.getSelection(),0,20,10); <br /><br />break;<br /><br />}<br />}<br /><br />});<br /><br /><br /><br /><br />7、树形控件Tree <br />树形控件使用的方法为，首先创建一个Tree类型的对象，其次在该对象的基础之上继续扩展节点，以及扩展节点的子节点。<br />final Tree tree = new Tree(shell, SWT.BORDER);<br />可以利用tree.setSize方法来改变树形控件的大小。在创建节点时，需要指明该节点所依赖的父节点的名称，如TreeItem item0 = new TreeItem(tree, 0);，那么item0将成为tree对象中的0级（顶级）节点。<br />如下程序将在tree对象的基础之上产生9个节点：<br />final Tree tree = new Tree(shell, SWT.BORDER);<br /><br />tree.setSize(290, 290);<br /><br />for(int loopIndex1 = 2000; loopIndex1 &lt;= 2008; loopIndex1++) {<br /><br />TreeItem item0 = new TreeItem(tree, 0);<br /><br />item0.setText("Year " + loopIndex1);<br /><br />}<br /><br />在上述实例的基础上为每一个0级节点的基础上扩展出12个节点：<br />for(int loopIndex1 = 2000; loopIndex1 &lt;= 2008; loopIndex1++) {<br /><br />TreeItem item0 = new TreeItem(tree, 0);<br /><br />item0.setText("Year " + loopIndex1);<br /><br />for(int loopIndex2 = 1; loopIndex2 &lt;= 12; loopIndex2++) {<br /><br />TreeItem item1 = new TreeItem(item0, 0);<br /><br />item1.setText("Month " + loopIndex2);<br />}<br /><br />}<br /><br /><br />8、对话框dialog<br />对话框是一个依托于主窗体的子窗体，如图所示。<br /><br />例如：当在主窗体中点击按钮时，弹出一个对话框dialog，当关闭对话框时按钮显示“dialog is disposed”<br /><br />Display display = new Display( );<br /><br />final Shell shell = new Shell(display);<br /><br />shell.setSize(300, 200);<br /><br />shell.setText("main");<br /><br />final Button opener = new Button(shell, SWT.PUSH);<br /><br />opener.setText("Click Me");<br /><br />opener.setBounds(20, 20, 50, 25);<br /><br />final Shell dialog = new Shell(shell, SWT.APPLICATION_MODAL |<br /><br />SWT.DIALOG_TRIM);<br /><br />dialog.setText("dialog");<br /><br />dialog.setBounds(10,10,50,60);<br /><br />dialog.addDisposeListener(new DisposeListener(){<br /><br />public void widgetDisposed(DisposeEvent e){<br /><br />opener.setText("dialog is disposed");<br /><br />}<br />});<br /><br />Listener openerListener = new Listener( ) {<br /><br />public void handleEvent(Event event) {<br /><br />dialog.open( );<br /><br />}<br /><br />};<br /><br />opener.addListener(SWT.Selection, openerListener);<br /><br />shell.open( );<br /><br />while(!dialog.isDisposed( )) {<br /><br />if(!display.readAndDispatch( )) display.sleep( );<br /><br />} <br /><br />while (!shell.isDisposed( )) {<br /><br />if (!display.readAndDispatch( ))<br /><br />display.sleep( );<br /><br />}<br /><br />display.dispose( );<br /></div>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/99625.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2007-02-13 10:54 <a href="http://www.blogjava.net/TrampEagle/articles/99625.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SWT学习网站</title><link>http://www.blogjava.net/TrampEagle/articles/99487.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 12 Feb 2007 09:23:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/99487.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/99487.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/99487.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/99487.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/99487.html</trackback:ping><description><![CDATA[
		<p>
				<a href="http://www.80x86.cn/article.asp?id=611">http://www.80x86.cn/article.asp?id=611</a>
		</p>
		<p>
				<a href="http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html">http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html</a>
				<br />
				<br />
		</p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/99487.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2007-02-12 17:23 <a href="http://www.blogjava.net/TrampEagle/articles/99487.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SWT 全接触(转)</title><link>http://www.blogjava.net/TrampEagle/articles/99485.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 12 Feb 2007 09:17:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/99485.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/99485.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/99485.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/99485.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/99485.html</trackback:ping><description><![CDATA[
		<a href="http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html">http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html</a>
		<br />
		<br />
		<blockquote>Java 世界的人似乎一直都对 Java 的桌面应用程序十分不满，从 AWT 到 SWING，从默认的 Theme到第三方的产品，不是太难看（AWT）就是在某些平台有 BUG（SWING，Quaqua--一个Windows平台下的仿Mac的主题包），再不就是对中文支持不好（某些第三方 LookAndFeel）。于是，如果想要获得和本机平台一致的用户界面和比较稳定的性能，SWT就成了一个不可忽视的选择。SWT 是一个独立于平台的，可以脱离 Eclipse 框架单独使用的图形组件，用JNI技术提供与本机系统同样的用户界面组件的观感，较好的运行效率，稳定的平台表现。</blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>当然，虽然这个专题名叫"全接触"，但毕竟不可能面面俱到，在一篇文章中兼收并蓄SWT的全部内容也不现实。但不管怎么说，我都将尽力展示SWT的使用细节，希望能为那些对SWT感兴趣的人提供一些帮助。 </p>
		<p>
				<a name="N10041">
						<span class="atitle">1．SWT简介</span>
				</a>
		</p>
		<p>SWT-"Standard Widget Toolkit"，它是一个Java平台下开放源码的Native GUI组件库，也是Eclipse平台的UI组件之一。从功能上来说，SWT与AWT/SWING是基本等价的。SWT以方便有效的方式提供了便携式的（即Write Once，Run Away）带有本地操作系统观感的UI组件：</p>
		<br />
		<img height="100" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image002.jpg" width="572" border="0" />
		<br />
		<p>由于widget系统的固有复杂性以及平台之间微妙的差异，即使在理想情况下，能够达到工业标准的跨平台的widget类库也是很难编写和维护的。最早的AWT组件现在被认为是样貌丑陋的，而且存在很多问题；SWING组件虽然也是缺点多多，但是随着JDK版本的不断升高，它仍在不断进行着改进。我认为，SWT在功能上与AWT/SWING不相伯仲，但是组件更为丰富，平台表现稳定，BUG也相对较少。如果你的应用程序真的需要在多个平台上运行，需要更为美观的界面，又不那么依赖于其他基于AWT/SWING的图形库，那么SWT或许是一个比AWT/SWING更好的选择。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N1005E">
						<span class="atitle">2． SWT起步</span>
				</a>
		</p>
		<p>
				<a name="N10064">
						<span class="smalltitle">
								<strong>
										<font face="Arial">2.1 SWT的HelloWorld</font>
								</strong>
						</span>
				</a>
		</p>
		<p>一如介绍其他程序的起始，我们都需要来一个HelloWorld来帮助我们入门，SWT的HelloWorld如下：</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">import org.eclipse.swt.widgets.*;
public class HelloWorld 
{
    public static void main(String[] args) 
    {
        Display display = new Display();
		Shell shell = new Shell(display);
		shell.setText("Hello World");
		shell.setSize(200, 100);
		shell.open();
		while (!shell.isDisposed()) 
{
		    if (!display.readAndDispatch()) 
			display.sleep ();
		}
		display.dispose ();
   	 }
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>运行这个程序就会得到如下结果：</p>
		<br />
		<img height="105" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image006.jpg" width="209" border="0" />
		<br />
		<p>下面我讲逐一介绍这个程序所包含的内容。</p>
		<ul>
				<li>Display<br />这是一个顶层容器组件，类似于Container或Component的功能，它主要负责与底层的窗口系统之间的连接。在具体含义上，它代表"屏幕"。<br />一个Display可以包含多个Shell（也是容器组件，下面会介绍到）。<br />通常情况下，一个应用程序只含一个Display，即Display通常是一个单例组件（Singleton）。 
</li>
				<li>Shell<br />它表示位于"屏幕"上面的"窗口"，是Composite组件和Control组件构成的组件树的根。<br />在我们的HelloWorld程序中，我们可以设置标题（setText()），设置大小（setSize()），然后通过open()方法来显示这个窗口。怎么样，感觉很像JFrame吧？其实功能上差不多。 
</li>
				<li>Composite<br />可以包含其它Composite和Control的容器 
</li>
				<li>Control<br />这是一个重量级（HeavyWeight）系统对象。像按钮（Button），标签（Label），表格，工具栏和树形结构这些组件都是Control的子类，Conposite和Shell也不例外。 </li>
		</ul>
		<p>
				<b>2.1.1 消息循环</b>
		</p>
		<p>我们可以看到，上面的代码中有这样的语句：</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">while (!shell.isDisposed()) 
{
    if (!display.readAndDispatch()) 
		display.sleep ();
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如果你像我一样是由Java语言起步的，那么你会对这个消息循环的代码感到比较陌生，毕竟在SWING中我们主要利用事件驱动模型而不这样利用类似于Windows程序设计中的消息循环的方法来处理事件。但是这段代码意义还算简单明了，就是反复的读取和分派（dispatch）事件，并在没有事件的时候把控制权还给CPU。</p>
		<p>
				<b>2.1.2 资源的释放</b>
		</p>
		<p>最后一条语句是display.dispose ();，这告诉我们操作系统的资源是由程序员显示释放的。资源的释放遵循以下两条规则：</p>
		<p>1. 如果你创建了某个资源，那么你就有责任释放它。</p>
		<p>2. 释放父组件资源的同时也释放了其子组件的资源。</p>
		<p>
				<b>2.1.3 标准构造函数</b>
		</p>
		<p>窗口组件被创建的时候必须伴随一个他的上层组件，例如，我要建立一个按钮就可以采用如下方法：Button button = new Button(shell, SWT.PUSH);</p>
		<p>其中，Button的父组件Shell是必不可少的，这样就限定了我们生成组件的顺序。</p>
		<p>第二个参数被称为"Style Bit"，表示了这个组件的显示特性，每种特性占一位，如下例所示：</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">Text test=new Text(group, SWT.SINGLE|SWT.BORDER);
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这条代码生成了一个单一的，有边框的文本框。这显然又与习惯了JavaBeans模型，总是用setXXX()来设置属性的我们不太适应--毕竟是IBM的东西啊，秉承了其产品不易上手的传统。</p>
		<p>
				<b>2.1.4 错误与异常</b>
		</p>
		<br />
		<img height="192" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image008.jpg" width="304" border="0" />
		<br />
		<p>SWTError指的是不能修复的错误，以及一些操作系统错误。</p>
		<p>SWTException指的是一些可恢复的错误以及无效的线程访问之类的错误。</p>
		<p>IllegalArgumentException指可修复的错误或参数为null之类的错误。</p>
		<p>
				<b>2.1.5 Item</b>
		</p>
		<p>Item类是一个轻量级的系统对象，总是作为基本的单位元素与其他一些类配合使用。比如Tree中的元素即为TreeItem，Table的单位元素则是TableItem，而MenuItem就是Menu的基本单位元素了。</p>
		<p>
				<b>2.1.6 SWT的类阶层体系结构</b>
		</p>
		<br />
		<img height="306" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image010.jpg" width="572" border="0" />
		<br />
		<p>最后让我们来整体认识一下整个SWT窗口组件的层次结构，如下所示：以上的部分给我们以整体的认识，即一个SWT引用程序应该怎么创建，其基本的运行规则和相关类的体系结构。我想我就不用再对每一个控件的API或使用方面费唇舌了，熟悉这些东西是体力劳动，而网上有很多例子可供参考。下面一节我将详细介绍有关SWT布局的相关知识。</p>
		<p>
				<a name="N10120">
						<span class="smalltitle">
								<strong>
										<font face="Arial">2.2 SWT的布局管理</font>
								</strong>
						</span>
				</a>
		</p>
		<p>相信对于组件的布局（Layout）大家都不会太陌生，它的存在就是提供给我们一种可以在组件位置移动或更改大小时重新绘制组件的机制。设置组件的布局我们可以采用Composite.setLayout()方法来实现。</p>
		<p>每种布局都有其相应的数据（Layout Data），可以通过Control.setLayoutData()方法来进行关联。以下是一些布局类及其显示效果:</p>
		<ul>
				<li>FillLayout:让所有子组件等大小的"填满"整个面板空间。<br />FillLayout是最简单的一个布局类，它将所有窗口组件放置到一行或一列中，并强制他们的大小也相等。FillLayout不能外覆（wrap），也不能定制边框和距离。很显然这样的限制让这个布局类最适合作类似于计算器面板的布局，或者为Taskbar和Toolbar上面的按钮作布局使用。<br /><img height="109" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image012.jpg" width="209" border="0" /></li>
				<li>RowLayout：类似于AWT中的FlowLayout，让所有组件按行排列，一行排不下就放到下一行。<br />RowLayout比FillLayout用得更广泛一些，原因很简单，就是RowLayout支持FillLayout所部支持的功能，例如能够外覆，能够修改边框和间距等等。另外，每一个位于RowLayout中的窗口组件都可以通过设定一个RowData类来指定其在RowLayout中的宽度和高度。<br /><img height="140" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image014.jpg" width="228" border="0" /></li>
				<li>GridLayout： GridLayout是3个标准布局类中最有用的，但同时也是最复杂的--没办法，强大的功能必定伴随着一定程度的复杂性。通过GridLayout，一个Composite的子窗口组件被放置在一个网格（Grid）之中。GridLayout有很多配置字段，并且和RowLayout一样，每一个布局于其中的窗口组件都可以有一个与之相关联的布局数据类，称为GridData。GridLayout的强大功能是通过对于每一个窗口组件的GridData的灵活控制来实现的。<br />鉴于GridLayout的复杂性（原本我就怀疑它根本就不是为手工书写代码而设计的），我并不建议各位直接手动书写GridData，最好借助可视化的工具（如VI）来帮助我们完成用GridLayout进行的界面设计。这样我们只需要书写少量控制代码，就可以获得复杂的界面布局了。<br /><img height="157" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image016.jpg" width="299" border="0" /></li>
				<li>FormLayout:如图所示<br /><img height="180" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image018.jpg" width="173" border="0" /></li>
				<li>StackLayout:几乎完全等同于CardLayout的功能。<br /><img height="58" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image020.jpg" width="515" border="0" /></li>
		</ul>
		<p>在SWT中，位置和大小的变化并非自动发生的。应用程序既可以在Composite子类的构造函数中指定初始位置和大小，也可以在一个改变窗口大小的监听器中用布局类来定位和改变Composite子类的大小。</p>
		<p>下面的一幅图包含了我们将要讨论的有关布局的大部分细节。一个Composite类的可显示区域分为三个部分，分别是Location，clientArea和trim。Composite的大小就是clientArea和trim的区域之和。一个布局类（Layout）的主要功能就是管理Composite子组件的大小和位置。通过布局类，我们可以管理子组件之间的距离-即间距（Spaceing），子组件与布局边缘之间的距离-即边距（margin）。布局的大小同时也是Composite的clientArea的大小。</p>
		<br />
		<img height="275" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image022.jpg" width="505" border="0" />
		<br />
		<p>至此，关于SWT的基础部分就告一段落，希望能够给大家以一个对于SWT的总体认识。下面的部分将主要介绍SWT的弱项-绘图。JGraph的一个作者就表达了对SWT/JFace/Draw2D的不满，认为SWT在执行效率上并没有什么改善，而且缺乏一些有用的API实现。话虽如此，但SWT的基本绘图功能还是不错的，如果有足够的时间和耐心的话还是可以绘出想要的图形的。下面就让我们看看SWT如何绘制2D和3D图形。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10189">
						<span class="atitle">3. 用SWT绘制2D图形</span>
				</a>
		</p>
		<p>用SWT绘图通常由两种方法，一种是借助Graphics Context，另一种是利用Draw2D。然而Draw2D是一个基于SWT Composite的轻量级组件，于是在效率上，它无法体现出SWT的Native Code的速度优势。故其虽然强大，但仅适用于绘图工作不是系统瓶颈的应用程序。所以我在这里只介绍第一种方法。</p>
		<p>
				<a name="N10192">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.1 Graphics Context</font>
								</strong>
						</span>
				</a>
		</p>
		<p>我们可以在任何实现了org.eclipse.swt.graphics.Drawable接口的类上绘制图形，这包括一个控件，一幅图像，一个显示设备或一个打印设备。类org.eclipse.swt.graphics.GC是一个封装了所有可执行的绘图操作的图形上下文（Graphics Context）。两种使用GC的方式我们已经在本节前言中提过，稍后会作详细说明。</p>
		<p>
				<a name="N1019B">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.2 在一幅图像上绘制图形</font>
								</strong>
						</span>
				</a>
		</p>
		<p>下面一段代码创建了一个带有图像的GC并在上面绘制了两条线：</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">Image image = new Image(display,"C:/music.gif"); 
GC gc = new GC(image); 
Rectangle bounds = image.getBounds(); 
gc.drawLine(0,0,bounds.width,bounds.height); 
gc.drawLine(0,bounds.height,bounds.width,0); 
gc.dispose(); 
image.dispose();
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="104" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image024.jpg" width="244" border="0" />
		<br />
		<p>一旦你创建了一个GC，你就有责任通过它的dispose方法释放它的资源。一个由应用程序创建的GC需要立即被绘制，然后尽快释放掉。这是因为每个GC都需要一个底层的系统资源，而在某些操作系统中这些资源是稀缺的，像Win98就只允许同时创建五个GC对象。</p>
		<p>
				<a name="N101C1">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.3 在Control上绘图</font>
								</strong>
						</span>
				</a>
		</p>
		<p>类org.eclipse.swt.widgets.Control是可绘制的，所以你可以用像在图像上一样的方式来绘制图形。而和在图像上绘制所不同的是，如果你使用GC在一个Control上绘制图形，你需要知道当操作系统自身要绘制这个control的时候，它将覆盖掉你的改动。所以在一个Control上绘制图形的正确方法是加入其绘制事件的监听器。监听器类为org.eclipse.swt.events.PaintListener，其回调函数的参数是一个org.eclipse.swt.events.PaintEvent类的实例。这个PaintEvent实例中包含一个GC的引用，你可以向这个GC发送消息。下面的代码示例说明了如何建立这种类型的绘图：</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">Shell shell = new Shell(display); 
shell.addPaintListener(new PaintListener(){ 
        public void paintControl(PaintEvent e){ 
            Rectangle clientArea = shell.getClientArea(); 
            e.gc.drawLine(0,0,clientArea.width,clientArea.height); 
        } 
    }); 
shell.setSize(150,150)
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="180" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image026.jpg" width="256" border="0" />
		<br />
		<p>
				<a name="N101E4">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.4 剪切（Clipping）</font>
								</strong>
						</span>
				</a>
		</p>
		<p>GC的剪切域是可见绘图发生的部分。在缺省情况下，一个GC是一个被构造的可视部分边界。改变一个GC的剪切域可以让我们构造出各种图形效果。其中的一个例子是如果你想填充一个缺失了边缘的矩形。一种方法是绘制多边形矩形来组成所需要的图形，另一种方法就是剪切GC，然后对其剪切部分进行填充。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">shell.addPaintListener(new PaintListener() { 
        public void paintControl(PaintEvent e) { 
            Rectangle clientArea = shell.getClientArea(); 
            int width = clientArea.width; 
            int height = clientArea.height; 
            e.gc.setClipping(20,20,width - 40, height - 40); 
            e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN)); 
            e.gc.fillPolygon(new int[] {0,0,width,0,width/2,height}); 
        } 
    });
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这段代码在Shell上的显示的过程效果如下：</p>
		<br />
		<img height="171" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image028.jpg" width="539" border="0" />
		<br />
		<p>
				<a name="N1020A">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.5 画板（Canvas）</font>
								</strong>
						</span>
				</a>
		</p>
		<p>虽然任何Control都可以通过自身的paintEvent来绘制图形，但其子类org.eclipse.swt.widgets.Canvas是专门被设计用来进行图形操作的特殊的绘图类。我们既可以使用一个Canvas，再加入一个绘图监听器来实现绘图，也可以通过继承来建立一个可重用的自定义Control。Canvas有很多style bit，可以在绘图发生时产生作用。</p>
		<p>
				<a name="N10213">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.6 绘制直线和图形</font>
								</strong>
						</span>
				</a>
		</p>
		<p>我们有很多方法可以在一个GC上画线，包括在两点之间，一系列离散的点之间或一个预定义的图形上都可以。直线是以GC的前景色来绘制的，我们可以通过GC绘制拥有不同厚度的各式直线。对于一个Paint事件，GC有着与Control组件一样的属性，即激发事件且缺省的直线样式固定为1个像素宽。</p>
		<p>GC.drawLine(int x1, int y1, int x2, int y2);这条语句在可绘制的面板上的两点间花了一条直线，起始点为(x1,y1)，终止点为(x2，y2)。终止点包含在画好的直线中。如果起始点等于终止点的话，将会有一个独立的象素点被绘制出来。</p>
		<p>GC.drawPolyline(int[] pointArray);这条语句绘制了一系列互相连接的线段，作为参数的数组用于描述点的位置。语句gc.drawPolyline(new int[] { 25,5,45,45,5,45 })；绘制了如下的图形：</p>
		<br />
		<img height="92" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image030.jpg" width="160" border="0" />
		<br />
		<p>GC.drawPolygon(int[] pointArray);与drawPolyline(int[])是类似的，唯一区别在于最后一个点和低一个点是连接的。gc.drawPolygon(new int[] { 25,5,45,45,5,45 });将会获得与上图一样的结果。</p>
		<p>
				<b>GC.drawRectangle(int x, int y, int width, int height);</b>这条语句从左上角的（X，Y）点，用参数中的宽和高画出了一个矩形。gc.drawRectangle(5,5,90,45);将会绘制出如下图形：</p>
		<br />
		<img height="94" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image032.jpg" width="172" border="0" />
		<br />
		<p>
				<b>GC.drawRoundedRectangle(int x,int y,int width,int height,int arcWidth,int arcHeight);</b>一个圆矩形与标准矩形的区别就在于其四个角是圆的。圆矩形的每一个角都可以被想象成为1/4个椭圆，并且arcWidth和arcHeight由完整的椭圆的宽和高决定。gc.drawRoundedRectangle(5,5,90,45,25,15);绘制了一个左上角位置为5.5的圆矩形，右边的图形是放大后的效果：</p>
		<br />
		<img height="138" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image034.jpg" width="445" border="0" />
		<br />
		<p>
				<b>GC.drawOval(int x, int y, int width, int height);</b>一个椭圆是由其相对应的矩形的左上角的位置（x，y）来确定绘制位置的，其宽和高即为对应矩形的宽和高。对于圆形来说，只需要另宽和高相等即可。</p>
		<br />
		<img height="98" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image036.jpg" width="150" border="0" />
		<br />
		<p>
				<b>GC.drawArc(int x, int y, int width, int height, int startAngle, int endAngle);</b>曲线的绘制也是与一个相应的矩形有关，即其左上角的位置与宽和高都是相应矩形的属性。StartAntle是从横向的X开始计算的，所以0度指向的是东而不是北。曲线的绘制是从StartAngle到endAngle以逆时针方向执行。gc.drawArc(5,5,90,45,90,200);所绘制的图形如下：</p>
		<br />
		<img height="98" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image038.jpg" width="154" border="0" />
		<br />
		<p>
				<b>GC.setLineStyle(int style);</b>可以设置所绘制曲线的样式，下面列出了一些曲线样式常量（在org.eclipse.swt.SWT中定义）和与之对应的曲线的图像：</p>
		<br />
		<img height="116" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image040.jpg" width="464" border="0" />
		<br />
		<p>
				<b>GC.setLineWidth(int width);</b>可以用于指定所要绘制的曲线的宽度。缺省情况下的曲线宽度为1个像素。</p>
		<br />
		<img height="50" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image042.jpg" width="420" border="0" />
		<br />
		<p>由于直线的样式和宽度挥作用到所有的绘图操作上，所以我们可以作出如点矩形或粗线椭圆这样的图形：</p>
		<br />
		<img height="89" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image044.jpg" width="162" border="0" />
		<br />
		<p>
				<a name="N102CE">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.7 绘制文本</font>
								</strong>
						</span>
				</a>
		</p>
		<p>文本可以被绘制在一个GC上， 字形是用GC的前景色和字体来绘制的，并且它所占用的区域是用GC背景色绘制的。要绘制文本，你需要定义要绘制文本的左上角，宽度和高度。有两组方法可以用来绘制文本，第一组方法的名字里都带有一个Text，并将会处理直线定界符和制表符。第二组API方法集的名字里都带有String，它们没有制表符或回车的处理，并主要用于控制像Eclipse的Java编辑器StyledText这样复杂的Control。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">GC.drawText(String text, int x, int y);
Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC); 
// ... 
gc.drawText("Hello World",5,5); 
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE)); 
gc.setFont(font); 
gc.drawText("Hello\tThere\nWide\tWorld",5,25); 
// ... 
font.dispose();
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="107" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image046.jpg" width="196" border="0" />
		<br />
		<p>drawText API将控制字符\t处理为制表符，将\n处理为回车符。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">GC.drawString(String text, int x, int y);
Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC); 
// ... 
gc.drawString("Hello World",5,5); 
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE)); 
gc.setFont(font); 
gc.drawString("Hello\tThere\nWide\tWorld",5,25); 
// ... 
font.dispose()
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="97" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image048.jpg" width="285" border="0" />
		<br />
		<p>当使用drawString时，制表符和回车符将不会被处理。</p>
		<p>在一个GC上绘制字符的时候，一个字符串所占用的大小取决于它的内容以及GC的字体。想要确定一个字符串在被绘制之后所占用的区域可以使用方法：GC.stringExtent(String text), 或 GC.textExtent(String text)。这两个方法都返回一个Point类，这个Point的X和Y是渲染参数字符串所需要的宽和高。</p>
		<p>
				<a name="N10314">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.8 图形填充</font>
								</strong>
						</span>
				</a>
		</p>
		<p>直线是用GC前景色绘制的，而图形的填充用的是GC的背景色。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">GC.fillPolygon(int[]);
gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE)); 
gc.fillPolygon(new int[] { 25,5,45,45,5,45 }) 
 
GC.fillRectangle(int x, int y, int width, int height);
gc.fillRectangle(5,5,90,45); 
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="96" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image049.gif" width="164" border="0" />
		<br />
		<br />
		<img height="98" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image050.gif" width="165" border="0" />
		<br />
		<p>需要注意的是，当一个矩形被填充的时候，右面和下面的边缘是不被包括在内的。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">GC.fillRoundedRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight);
gc.fillRoundRectangle(5,5,90,45,25,15);
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="92" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image052.jpg" width="149" border="0" />
		<br />
		<p>像GC.fillRectangle(...)方法一样，右面和下面的边缘不被包含在内，于是右下角的坐标为（94，49）而不是（95，50）。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">GC.fillOval(int x, int y, int width, int height);
gc.fillOval(5,5,90,45); 
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="92" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image054.jpg" width="148" border="0" />
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">GC.fillArc(int x, int y, int widt4h., int height, int startAngle, int endAngle);
gc.fillArc(5,5,90,45,90,200);
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="75" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image056.jpg" width="147" border="0" />
		<br />
		<p>fillArc()的参数和drawArc()的参数是类似的，偏移量是从右面的轴开始填充，然后沿逆时针方向旋转给定的角度（endAngle-startAngle）。</p>
		<p>
				<b>GC.fillGradientRectangle(int x, int y, int width. int height, vertical boolean);</b>
		</p>
		<p>这个方法让我们可以指定图形在填充时所用的颜色可以从GC的前景色按梯度变化（渐变）到背景色。梯度既可以是横向的也可以是纵向的。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">gc.setBackgrouind(display,getSystemColor(SWT.COLOR_BLUE)); 
gc.fillGradientRectangle(5,5,90,45,false); 
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>上面两条语句建立了一个使用黑色背景的从左至右的横向梯度填充。和其他填充方法一样，左面和下面的边缘不被包括在内，所以由下角的位置缩小一个像素。</p>
		<br />
		<img height="93" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image058.jpg" width="119" border="0" />
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE)); 
gc.setForeground(display.getSystemColor(SWT.COLOR_CYAN)); 
gc.fillGradientRectangle(5,5,90,45,true); 
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>上面这3行代码的含义为在纵向自顶向下用前cyan（景色）开始，并以蓝色（背景色）结束的填充。</p>
		<br />
		<img height="94" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image060.jpg" width="144" border="0" />
		<br />
		<p>
				<a name="N103E0">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.9 异或（XOR）</font>
								</strong>
						</span>
				</a>
		</p>
		<p>如果你设置了GC的XOR模式为true的话，将会发生如下情况：对于每一个像素点，原来被显示的红，绿，蓝的值将被已存在的红，绿，蓝色进行异或操作，所得结果既作为新的目标像素。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">shell.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); 
// ... 
gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE)); 
gc.fillRectangle(5,5,90,45); 
gc.setXORMode(true); 
gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); 
gc.fillRectangle(20,20,50,50); 
gc.setBackground(display.getSystemColor(SWT.COLOR_RED)); 
gc.fillOval(80,20,50,50);
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<img height="111" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image062.jpg" width="237" border="0" />
		<br />
		<p>
				<a name="N10403">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3.10 绘制图像（Draw Image）</font>
								</strong>
						</span>
				</a>
		</p>
		<p>类org.eclipse.swt.graphics.Image被用来表示准备要在像打印机，显示器这样的设备上显示的图形。建立一个图像最简单的方法就是从组织好的文件格式中装载它。SWT所支持的图像格式有：GIF，BMP，JGP，PNG和TIFF。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">Image image = new Image(display,"C:/eclipse_lg.gif"); 
GC.drawImage(Image image, int x, int y);
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>每幅图像都有用其边界决定的尺寸。例如，图象eclipse_lg.gif的大小为115*164，我们可以通过image.getBounds()方法来进行设定。当一幅图像被绘制的时候，它将会以自身定义的边界作为显示之后的宽和高。<code>gc.drawImage(image,5,5);</code></p>
		<p>至此，SWT在2D绘图方面的讲解告一段落，上面所提到的内容涵盖了SWT的大部分绘图功能，并在每个部分都给出了要注意的细节。至于具体实现就要靠各位的聪明才智了。下面让我们进入最后的部分-SWT的3D绘图。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N1041F">
						<span class="atitle">4 SWT与OpenGL编程</span>
				</a>
		</p>
		<p>相较于Java3D API来说，SWT以前在3D图形绘制方面一直没有什么好的表现。OpenGL的加入会不会使SWT在3D领域有所作为还尚未可知，不过起码IBM的程序员们给了SWT机会。当大家了解了这个正处于试验阶段的组合之后，我们在SWT上绘制3D图形就不再是噩梦。</p>
		<p>OpenGL是一个为创建高性能2D，3D图形而设计的多平台的标准。其硬件和软件的实现存在于多个系统之中，包括Windows，Linux和MacOS。OpenGL可以用于渲染简单的2D图形或复杂的3D游戏图形（OpenGL最主要的应用领域就是游戏）。作为一个正在处于事件阶段的Eclipse插件，我将在下面的小节中介绍如何在SWT窗口组件上用SWT绘制图形。在Eclipse最新的3.2版中，对OpenGL的支持被集成到org.eclipse.swt项目中，所以我们在实现的时候即可以选择以插件方式进行，也可以直接利用已经集成好的组件来进行图形操作。在本节，我们将以插件方式为例对代码进行说明。</p>
		<p>
				<a name="N1042B">
						<span class="smalltitle">
								<strong>
										<font face="Arial">4.1 SWT OpenGL插件</font>
								</strong>
						</span>
				</a>
		</p>
		<p>SWT实现了OpenGL1.1全部功能。包括三个核心类和一个数据类。核心类为GLContext，GL和GLU。GLContext架起了SWT和OpenGL之间的桥接。一个Context必须用Drawable，通常是用Canvas来创建，OpenGL可以在Drawable上渲染场景。需要注意的是，当context不再被使用的时候就应该将它释放掉。同样，一旦某个context被释放掉之后，就不应该再次试图去渲染它。每次Drawable改变大小的时候，context都需要通过调用其resize方法在通知这一事件。这个方法的调用让context调整自己的view port和视图参数。在下一节中将描述一个处理这一部分任务的类。</p>
		<p>当context可用的时候，我们就可以通过定义在GL和GLU的一系列方法调用来绘制场景。一个GL类大概有超过330条命令。在GL和GLU中定义的这些函数和他们的Native实现几乎是一一对应的。下图给出了一个绘制矩形的例子，我们可以看到用C写成的API和SWT OpenGL API是何其相似：</p>
		<br />
		<img height="222" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image064.jpg" width="572" border="0" />
		<br />
		<p>
				<a name="N10448">
						<span class="smalltitle">
								<strong>
										<font face="Arial">4.2 SWT OpenGL编程基础</font>
								</strong>
						</span>
				</a>
		</p>
		<p>在下面的小节中，我将描述一个显示四幅3D图像的应用程序。应用程序采用了GLSense，这是一个用于显示OpenGL场景的工具类。它和SWT的Canvas很像，所区别的是它所展现的内容是用OpenGL命令渲染的，而不是使用GC来绘制。要做到这一点，我们需要将一个GLContext类和一个SWT Canvas相关联，并且无论何时，当前上下文中的内容都应该是由在drawScene中定义的命令来渲染的。</p>
		<br />
		<img height="323" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image066.jpg" width="560" border="0" />
		<br />
		<p>在构造函数中，一个SWT Canvas被创建出来。这就是那个要和一个GLContext相关联的Canvas实例。紧接着，这个Canvas又注册了两个监听器。第一个监听器的作用是确保这个Canvas无论何时被改变大小，其相应的GLContex也会收到通知并适当的改变大小。第二个监听器主要用于确保一旦Canvas被释放之后，其相对应的GLContext的也同时被释放。为了确保渲染区域是一个非零大小的区域，父组件的客户矩形区被取出来用于设置该Canvas的初始大小。这个初始大小可以在稍后用布局管理器或用户Action来修改。</p>
		<br />
		<img height="176" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image068.jpg" width="511" border="0" />
		<br />
		<p>GLScene将Canvas的全部区域用于绘图。无论Canvas何时调整其尺寸，我们都要获取客户区并将新的宽度和高度传递给Contex，而context将根据新的宽度和高度适当的调整视图。</p>
		<br />
		<table border="1">
				<tbody>
						<tr>
								<td style="COLOR: #ff0000">XML error: The image is not displayed because the width is greater than the maximum of 572 pixels. Please decrease the image width.</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>GLScene被分割为两个部分：初始化Context和初始化OpenGL的状态机。对于Context来说，我们只是简单的建立一个新的GLContext并使它成为当前被使用的Context。OpenGL的渲染总是在当前的context上进行绘制，因此如果你有超过一个活动的GLScene，很重要的一点是要在所有绘制动作发生之前将它的Context设置为当前的Context。initGL方法最开始提供清除颜色缓存颜色，随后建立了一个深度缓存（depth buffer）.第47行指出了深度值如何进行比较。这一比较函数主要用于拒绝或接受正在引用的像素。GL.GL_LEQUAL选项指定接受那些在视图上更接近或有相同距离的像素。第48行启动了深度测试（depth test），紧接的一行设定阴影模型为GL.GL_SMOOTH，这一设定的效果是如果表面上的两个顶点颜色不同的话，系统将对颜色进行插值。最后，第50行要求渲染引擎在计算颜色和纹理协调插值运算的时候起到关键的作用。</p>
		<br />
		<table border="1">
				<tbody>
						<tr>
								<td style="COLOR: #ff0000">XML error: The image is not displayed because the width is greater than the maximum of 572 pixels. Please decrease the image width.</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>GLScene类的最后两个方法用于处理重绘和场景绘制。当场景何时需要重绘的时候，第一个方法为其他类提供重绘操作的接口。第二个方法主要用于让继承GLScene的子类覆写。其缺省实现只是简单的清除了颜色和深度缓存，通过装在鉴别矩阵（identify matrix）重新恢复调整系统。</p>
		<p>
				<a name="N104A1">
						<span class="smalltitle">
								<strong>
										<font face="Arial">4.3 3D Chart</font>
								</strong>
						</span>
				</a>
		</p>
		<p>利用上一节的准备，我们已经将主应用程序进行了划分。这个图像显示了4组数据。每一组数据都是由相同的固定点所组成，每个点都是从0.0到10.0之间的一个正值。</p>
		<p>示例程序运行在一个非常简单的Eclipse view上，唯一值得注意的是Refresher，这个线程将强迫OpenGL场景被周期性的重绘。通过这种方法，当视图被移动或旋转的时候，component总能进行有效的更新渲染效果。run()方法调用的时间间隔为100毫秒，所以理论上的图像速度能达到每秒10帧。</p>
		<br />
		<img height="259" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image074.jpg" width="533" border="0" />
		<br />
		<p>每个数据集合的点的值是用圆柱体来表示的。通过执行3个GLU调用，我们就能够绘制圆柱体：其中的两个用于渲染圆柱体两头的圆盘部分，另外一个用于渲染圆柱体的四周。例如，要渲染两个单元高的圆柱体，你可以用下面的代码来实现：</p>
		<br />
		<img height="148" alt="" src="http://www.ibm.com/developerworks/cn/opensource/os-swt/images/image076.jpg" width="470" border="0" />
		<br />
		<p>第一行申请了绘制圆盘和圆柱所需的二次曲面。然后整个场景被逆时针旋转了90度，以便圆柱体可以被垂直绘制。下一步，底部的圆盘被渲染，然后是圆柱体的四周。在我们能够绘制顶部圆盘的时候，通过场景转换（scene translation），我们可以在Z轴移动两个单元。最后一个圆盘随后被绘制出来，调整系统通过向回移动两个单元来进行恢复。最后，由第一行申请的二次曲面被释放掉。</p>
		<p>按照上述方法运行程序是很费时间的。当仅绘制一个圆柱体的时候，效率低下不是一个很严重的问题，但如果要绘制成百个对象的话就会严重影响程序的执行性能。对于这种情况，OpenGL给出了一个解决这个问题的技巧，就是使用显示列表（display list）。</p>
		<p>一个显示列表是一组已编译的OpenGL命令。定义命令集合的列表被放在glNewList(int list, int mode) 和 glEndList()方法调用之间。第一个参数必须是一个正整数，可以用来唯一的表示一个被创建的显示列表。你可以让GL用glGenLists(int n)方法为你生成多个列表标识符。第二个参数用于指定列表是否被编译或编译之后立即被执行。大多数情况下你都需要编译这个列表。然后，你可以使用glCallList(int list)方法来显示整个列表。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N104DB">
						<span class="atitle">5 结束语</span>
				</a>
		</p>
		<p>至此，有关于SWT与OpenGL图形有关的粗略功能就介绍完了，有鉴于3D图形对象和OpenGL的复杂性，一篇这样篇幅的文章肯定不能覆盖其每一个角落，我只能给各位一个动手尝试机会。希望整篇专题没有让你枯燥得睡着，并因此有了一个不错的SWT的基础，我的目的就达到了。</p>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">关于作者</span>
				</a>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="3">
										<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
										</p>
								</td>
								<td>
										<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" />
								</td>
								<td width="100%">
										<p>薛笛，是黑龙江大学研究生。他目前在黑龙江大学信息技术研究所工作，从事传感器网络和分布式数据库的研究，对Java技术特别感兴趣。可以通过 <a href="mailto:jxuedi@gmail.com?cc="><font color="#5c81a7">jxuedi@gmail.com</font></a> 与他联系。</p>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
<img src ="http://www.blogjava.net/TrampEagle/aggbug/99485.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2007-02-12 17:17 <a href="http://www.blogjava.net/TrampEagle/articles/99485.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Can I use “==” to compare two String?(转)</title><link>http://www.blogjava.net/TrampEagle/articles/69428.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 13 Sep 2006 09:45:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/69428.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/69428.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/69428.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/69428.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/69428.html</trackback:ping><description><![CDATA[引自：<a href="http://www.mengyan.org/blog/archives/2004/08/14/87.html">http://www.mengyan.org/blog/archives/2004/08/14/87.html</a><br /><br /><p>These days, when I’m reading some other guy’s code, I encounter some problem. In these code, I can read following codes:</p><div class="hl-surround"><ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number."><li class="hl-firstline"><span style="COLOR: blue">JButton</span><span style="COLOR: gray"> </span><span style="COLOR: blue">button</span><span style="COLOR: gray"> = </span><span style="COLOR: green">new</span><span style="COLOR: gray"> </span><span style="COLOR: blue">JButton</span><span style="COLOR: olive">(</span><span style="COLOR: #8b0000">"</span><span style="COLOR: red">OK</span><span style="COLOR: #8b0000">"</span><span style="COLOR: olive">)</span><span style="COLOR: gray">;</span></li><li><span style="COLOR: gray"></span><span style="COLOR: blue">button</span><span style="COLOR: gray">.</span><span style="COLOR: blue">setActionCommand</span><span style="COLOR: olive">(</span><span style="COLOR: #8b0000">"</span><span style="COLOR: red">OK</span><span style="COLOR: #8b0000">"</span><span style="COLOR: olive">)</span><span style="COLOR: gray">;</span></li><li><span style="COLOR: gray"></span><span style="COLOR: blue">button</span><span style="COLOR: gray">.</span><span style="COLOR: blue">addActionListener</span><span style="COLOR: olive">(</span><span style="COLOR: green">this</span><span style="COLOR: olive">)</span><span style="COLOR: gray">;</span></li><li><span style="COLOR: gray">......</span></li><li><span style="COLOR: gray"></span><span style="COLOR: blue">actionPerformed</span><span style="COLOR: olive">(</span><span style="COLOR: blue">e</span><span style="COLOR: olive">)</span><span style="COLOR: gray"> </span><span style="COLOR: olive">{</span><span style="COLOR: gray"></span></li><li><span style="COLOR: gray">    </span><span style="COLOR: blue">String</span><span style="COLOR: gray"> </span><span style="COLOR: blue">s</span><span style="COLOR: gray"> = </span><span style="COLOR: blue">e</span><span style="COLOR: gray">.</span><span style="COLOR: blue">getActionCommand</span><span style="COLOR: olive">()</span><span style="COLOR: gray">;</span></li><li><span style="COLOR: gray">    </span><span style="COLOR: green">if</span><span style="COLOR: olive">(</span><span style="COLOR: blue">s</span><span style="COLOR: gray">==</span><span style="COLOR: #8b0000">"</span><span style="COLOR: red">OK</span><span style="COLOR: #8b0000">"</span><span style="COLOR: olive">)</span><span style="COLOR: gray"></span></li><li><span style="COLOR: gray">    ......</span></li><li><span style="COLOR: gray"></span><span style="COLOR: olive">}</span></li></ol></div><p>Look, it use “<em>==</em>” but not “<em>equals</em>” here. “Oh, it must be a bug!”, I told myself. But, wait, when I excuted the program, it worked quite well. And till this moment, I remembered, current powerful JVM will maintain a constant pool for String, so maybe two “OK” will reference the same place. So, such mechanism will work. </p><p>But, does Java Language Specification has such standard? and all JVM will follow this standard? I continue to dig into it.</p><p>First, I found such item in JLS 3.10.5 — String literals. </p><p>Abstract some points here:</p><blockquote><p>Literal strings within the same class ($8) in the same package ($7) represent references to the same String object ($4.3.1).<br />Literal strings within different classes in the same package represent references to the same String object.<br />Literal strings within different classes in different packages likewise represent references to the same String object.<br />Strings computed by constant expressions ($15.28) are computed at compile time and then treated as if they were literals.<br />Strings computed at run time are newly created and therefore distinct.<br />The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents. </p></blockquote><p>So, this is the feature of String class, every JVM should follow. And, when I read the code of String.java, I found such comments in method “<em>intern</em>“.</p><blockquote><p>public String intern()<br />Returns a canonical representation for the string object.<br />A pool of strings, initially empty, is maintained privately by the class String.<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~So good:-)</p><p>When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.<br />It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.<br />All literal strings and string-valued constant expressions are interned. String literals are defined in $3.10.5 of the Java Language Specification.</p></blockquote><p>It’s quite clear about the case I encountered. Then I understand, this is not a bug, but the trick <img class="wp-smiley" alt=":P" src="http://www.mengyan.org/blog/wp-includes/images/smilies/icon_razz.gif" /> . But, why he use “<em>==</em>” but not “<em>equals</em>“, I think that will be more clear and more easy to read, I think there must be other guys to be puzzled as me <img class="wp-smiley" alt=":-)" src="http://www.mengyan.org/blog/wp-includes/images/smilies/icon_smile.gif" /> . </p><p>I think the author must care more about the performance than the code, the performance of “<em>==</em>” sure if better than “<em>equals</em>“, especially when the String is long~.</p><p>Ok, everything clear now, and sure, not only String has such pool. For example: </p><div class="hl-surround"><ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number."><li class="hl-firstline"><span style="COLOR: blue">Integer</span><span style="COLOR: gray"> </span><span style="COLOR: blue">i</span><span style="COLOR: gray"> = </span><span style="COLOR: maroon">1</span><span style="COLOR: gray">;</span></li><li><span style="COLOR: gray"></span><span style="COLOR: blue">Integer</span><span style="COLOR: gray"> </span><span style="COLOR: blue">j</span><span style="COLOR: gray"> = </span><span style="COLOR: maroon">1</span><span style="COLOR: gray">;</span></li></ol></div><p>what will System.out.println(i==l) output? I think you can get the right answer. <img class="wp-smiley" alt=":P" src="http://www.mengyan.org/blog/wp-includes/images/smilies/icon_razz.gif" /></p><p><strong>Update (2005.11.09):</strong></p><p>"String"’s method "equalsIgnoreCase" also take advantage of this string pool:</p><div class="hl-surround"><ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number."><li class="hl-firstline"><span style="COLOR: green">public</span><span style="COLOR: gray"> </span><span class="hl-types">boolean</span><span style="COLOR: gray"> </span><span style="COLOR: blue">equalsIgnoreCase</span><span style="COLOR: olive">(</span><span style="COLOR: blue">String</span><span style="COLOR: gray"> </span><span style="COLOR: blue">anotherString</span><span style="COLOR: olive">)</span><span style="COLOR: gray"> </span><span style="COLOR: olive">{</span><span style="COLOR: gray"></span></li><li><span style="COLOR: gray">    </span><span style="COLOR: green">return</span><span style="COLOR: gray"> </span><span style="COLOR: olive">(</span><span style="COLOR: green">this</span><span style="COLOR: gray"> == </span><span style="COLOR: blue">anotherString</span><span style="COLOR: olive">)</span><span style="COLOR: gray"> ? </span><span style="COLOR: green">true</span><span style="COLOR: gray"> :</span></li><li><span style="COLOR: gray">           </span><span style="COLOR: olive">(</span><span style="COLOR: blue">anotherString</span><span style="COLOR: gray"> != </span><span style="COLOR: green">null</span><span style="COLOR: olive">)</span><span style="COLOR: gray"> &amp;&amp; </span><span style="COLOR: olive">(</span><span style="COLOR: blue">anotherString</span><span style="COLOR: gray">.</span><span style="COLOR: blue">count</span><span style="COLOR: gray"> == </span><span style="COLOR: blue">count</span><span style="COLOR: olive">)</span><span style="COLOR: gray"> &amp;&amp;</span></li><li><span style="COLOR: gray">    </span><span style="COLOR: blue">regionMatches</span><span style="COLOR: olive">(</span><span style="COLOR: green">true</span><span style="COLOR: gray">, </span><span style="COLOR: maroon">0</span><span style="COLOR: gray">, </span><span style="COLOR: blue">anotherString</span><span style="COLOR: gray">, </span><span style="COLOR: maroon">0</span><span style="COLOR: gray">, </span><span style="COLOR: blue">count</span><span style="COLOR: olive">)</span><span style="COLOR: gray">;</span></li><li><span style="COLOR: gray"></span><span style="COLOR: olive">}</span></li></ol></div><p>If the two string point the same one, simply return true.</p><p>So, if you need to compare Strings frequently, you should "Intern" them.</p><p>For example, in <a href="http://www.mengyan.org/blog/archives/2004/08/14/lucene.apache.org/">Lucene</a>’s source code, you need to compare the Field name very often, so, they intern the field name.</p><div class="hl-surround"><ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number."><li class="hl-firstline"><span style="COLOR: green">this</span><span style="COLOR: gray">.</span><span style="COLOR: blue">name</span><span style="COLOR: gray"> = </span><span style="COLOR: blue">name</span><span style="COLOR: gray">.</span><span style="COLOR: blue">intern</span><span style="COLOR: olive">()</span><span style="COLOR: gray">;              </span><span style="COLOR: #ffa500">// field names are interned</span></li></ol></div><p class="akpc_pop">Popularity: 9%</p><p class="postmetadata alt"><small>This entry was posted on Saturday, August 14th, 2004 at 5:25 pm and is filed under <a title="View all posts in 技术" href="http://www.mengyan.org/blog/archives/category/%e6%8a%80%e6%9c%af/" rel="category tag">技术</a>. You can follow any responses to this entry through the <a href="http://www.mengyan.org/blog/archives/2004/08/14/87.html/feed/">RSS 2.0</a> feed. You can <a href="http://www.mengyan.org/blog/archives/2004/08/14/87.html#respond">leave a response</a>, or <a href="http://www.mengyan.org/blog/archives/2004/08/14/87.html/trackback/">trackback</a> from your own site. </small></p><img src ="http://www.blogjava.net/TrampEagle/aggbug/69428.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-09-13 17:45 <a href="http://www.blogjava.net/TrampEagle/articles/69428.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC 导致服务器挂起（转）</title><link>http://www.blogjava.net/TrampEagle/articles/60948.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 31 Jul 2006 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/60948.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/60948.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/60948.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/60948.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/60948.html</trackback:ping><description><![CDATA[原文引自：<a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html">http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html</a><br /><br /><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top"><font style="COLOR: rgb(0,0,0)" color="#009900"><b>问题描述</b></font><br />在通过由应用程序或 WebLogic Server 本身使用的 JDBC 连接进行调用时，此连接会在整个调用期间内阻塞一个 WebLogic Server 执行线程。尽管在 SQL 查询上阻塞的线程需要等待，但 JVM 将通过其线程调度机制确保 CPU 获得可运行线程。但是，由 JDBC 调用占用的线程将保留给应用程序使用，直至该调用从 SQL 查询返回。<br /><br />即使事务超时也不会终止由在此事务中登记的资源完成的任何操作，或者使其超时。这些操作将正常地运行，而不会出现中断。事务超时只是在事务上设置一个标记，将其标记为回滚，这样提交此事务的任何后续请求都将失败，系统抛出 <font style="FONT-FAMILY: courier new" size="-1">TimedOutException</font> 或 <font style="FONT-FAMILY: courier new" size="-1">RollbackException</font>。但是，如前所述，长时间运行的 JDBC 调用会导致 WebLogic Server 执行线程阻塞，如果所有线程均被阻塞，没有能够处理传入请求执行线程，则最终可导致实例挂起。<br /><br />最新版本的 WebLogic Server 具有健全性检查功能，能够定期检查线程不响应的时间是否达到特定时长（缺省值为 600 秒）。如果是这样，系统会向日志文件输出一条如下的错误消息：<br /></td></tr></tbody></table><br /><table style="WIDTH: 615px" cellspacing="2" cellpadding="2" border="1"><tbody><tr><td style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><font style="FONT-FAMILY: courier new" size="-1">####&lt;Nov 6, 2004 1:42:30 PM EST&gt; &lt;Warning&gt; &lt;WebLogicServer&gt; &lt;mydomain&gt; &lt;myserver&gt; &lt;CoreHealthMonitor&gt; &lt;kernel identity&gt; &lt;&gt; <br />&lt;000337&gt; &lt;ExecuteThread: '64' for queue: 'default' has been busy for "740" seconds working on the request "Scheduled Trigger", <br />which is more than the configured time (StuckThreadMaxTime) of "600" seconds.&gt;</font><font size="-1"><br /></font></td></tr></tbody></table><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top"><a name="1"></a>这并不会中断线程，而只是提供给管理员的一项通知。阻塞线程恢复为正常状态的唯一途径是等待它正处理的请求完成。这种情况下，WebLogic Server 日志文件中将出现一条如下的消息：<br /></td></tr></tbody></table><br /><table style="WIDTH: 615px" cellspacing="2" cellpadding="2" border="1"><tbody><tr><td style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><font style="FONT-FAMILY: courier new" size="-1">####&lt;Nov 7, 2004 4:17:34 PM EST&gt; &lt;Info&gt; &lt;WebLogicServer&gt;&lt;mydomain&gt; &lt;myserver&gt; &lt;ExecuteThread: '66' for queue: 'default'&gt;<br />&lt;kernel identity&gt; &lt;&gt; &lt;000339&gt; &lt;ExecuteThread: '66' for queue: 'default' has become "unstuck".&gt;</font><font size="-1"><br /></font></td></tr></tbody></table><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top">健康检查功能的时间间隔是可以配置的。请检查 <font style="FONT-FAMILY: courier new" size="-1">config.xml</font> 文件 <span style="FONT-STYLE: italic">&lt;Server&gt;</span> 标记中的 <font style="FONT-FAMILY: courier new" size="-1">StuckThreadMaxTime</font> 属性：<a href="http://e-docs.bea.com/wls/docs81/config_xml/Server.html#StuckThreadMaxTime">http://e-docs.bea.com/wls/docs81/config_xml/Server.html#StuckThreadMaxTime</a> (English) 或 WebLogic Server 管理控制台帮助中的“Detecting stuck threads”一节：<a href="http://e-docs.bea.com/wls/docs81/perform/WLSTuning.html#stuckthread">http://e-docs.bea.com/wls/docs81/perform/WLSTuning.html#stuckthread</a> (English)。<br /><br /><font size="-1"><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#TOP">返回页首</a></font><br /><br style="COLOR: rgb(0,0,0)" /><font style="COLOR: rgb(0,0,0)" color="#009900"><b><a name="Problem_Troubleshooting"></a>故障排除</b></font><br />不同的编程技术或 JDBC 连接池配置可以造成死锁或长时间运行的 JDBC 调用，进而使 WebLogic Server 实例挂起。<a href="http://support.bea.com/application_content/product_portlets/support_patterns/wls/Generic_Server_Hang_Pattern.html">常规服务器挂起模式</a>中提供了有关如何排除和分析挂起 WebLogic Server 实例的一般信息。<br /><br />本模式讨论导致服务器挂起的 JDBC 调用，并从 JDBC 角度讨论导致 WebLogic Server 实例挂起的常见问题的原因。本模式中引用的其它支持模式位于 <a href="http://support.bea.com/application_content/product_portlets/support_patterns/wls/wls_support_patterns.jsp">WebLogic Server Support Patterns Site</a> (English)。<br /><br /><span style="FONT-WEIGHT: bold">快速链接</span><font color="#009900"><b><br /></b></font><ul><li><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Why_does_the_problem_occur"><span style="COLOR: rgb(0,0,238)">为什么发生此问题？</span></a></li><li><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Analysis_of_a_hanging_WebLogic_Server"><span style="COLOR: rgb(0,0,238)">分析挂起的 WebLogic Server 实例</span></a></li><li><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Tips_and_Tricks_to_optimize_your_JDBC"><span style="COLOR: rgb(0,0,238)">优化 JDBC 代码和 JDBC 连接池配置的技巧</span></a></li></ul><br /><a name="Why_does_the_problem_occur"></a><span style="FONT-WEIGHT: bold">为什么发生此问题？</span><br />以下是在 JDBC 调用时导致 WebLogic Server 实例挂起的各种可能原因：<br /><ul><li>在 JDBC 代码中使用 <a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#DriverManager.getConnection">DriverManager.getConnection()</a>。 
</li><li>发布给数据库的 <a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Long_Running_SQL_Queries">SQL 查询</a>返回时间异常的长。 
</li><li>配置了 JDBC 连接池的<a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Hanging_Database">数据库</a>挂起且不及时从调用返回。 
</li><li>低速或超载的<a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Slow_Network">网络</a>导致数据库调用速度减慢或挂起。<br /></li><li><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Deadlock">死锁</a>导致所有执行线程挂起和永久等待。 
</li><li>JDBC 连接池中的 <a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#RefreshMinutes">RefreshMinutes 或 TestFrequencySeconds</a> 属性导致 WebLogic Server 中出现挂起期间。 
</li><li><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Pool_Shrinking">JDBC 连接池收缩</a>和数据库连接的重新创建使响应时间变长。 </li></ul><font size="-1"><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#TOP">返回页首</a></font><br /><br /><a name="DriverManager.getConnection"></a><span style="FONT-WEIGHT: bold">同步的 DriverManager.getConnection()</span><br />旧版本的 JDBC 应用程序代码有时使用 <font style="FONT-FAMILY: courier new" size="-1">DriverManager.getConnection()</font> 调用来通过特定驱动程序取得数据库连接。不建议使用此项技术，因为它可能会导致死锁，或者至少相对降低连接请求的性能。究其原因，是因为所有 DriverManager 调用都采用类同步模式，也就是说一个线程中的一个 DriverManager 调用将阻塞一个 WebLogic Server 实例内任何其它线程中的所有其它 DriverManager 调用。<br /><br />此外，<font style="FONT-FAMILY: courier new" size="-1">SQLException</font> 的构造器会构造一个 DriverManager 调用，而且大多数驱动程序使用 <font style="FONT-FAMILY: courier new" size="-1">DriverManager.println() </font>调用进行日志记录，这其中的任何一项都会阻塞发出 DriverManager 调用的所有其它线程。<br /><br /><font style="FONT-FAMILY: courier new" size="-1">DriverManager.getConnection()</font> 在返回为数据库建立的物理连接之前可能会需要相对较长的时间。即使不发生死锁，所有其它调用也需要等到这个线程获得连接。在像 WebLogic Server 这样的多线程系统中，这不是最佳的方式。<br /></td></tr></tbody></table><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top">此处的信息来自 <a href="http://forums.bea.com/bea//thread.jspa?forumID=2022&amp;threadID=200063365&amp;messageID=202311284&amp;start=-1#202311284">http://forums.bea.com/bea//thread.jspa?forumID=2022&amp;threadID=200063365&amp;message<br />ID=202311284&amp;start=-1#202311284</a> (English)。 </td></tr></tbody></table><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top">此外，我们的文档也明确指出不应使用 <font style="FONT-FAMILY: courier new" size="-1">DriverManager.getConnection()</font>：<a href="http://e-docs.bea.com/wls/docs81/faq/jdbc.html#501044">http://e-docs.bea.com/wls/docs81/faq/jdbc.html#501044</a> (English)。<br /><br /><a name="2"></a>如果愿意在 JDBC 代码中使用 JDBC 连接，应使用 WebLogic Server JDBC 连接池，为其定义一个数据源，并从此数据源获得连接。这样您将享有池的所有优点（资源共享、连接重用、数据库关闭后的连接刷新等等）。它还将帮助您避免 DriverManager 调用可能发生的死锁。有关如何使用 JDBC 连接池、数据源及 WebLogic Server 中的其它 JDBC 对象的详细信息，请参阅：<a href="http://e-docs.bea.com/wls/docs81/jdbc/intro.html#1036718">http://e-docs.bea.com/wls/docs81/jdbc/intro.html#1036718</a> (English) 和 <a href="http://e-docs.bea.com/wls/docs81/jdbc/programming.html#1054307">http://e-docs.bea.com/wls/docs81/jdbc/programming.html#1054307</a> (English)。<br /><br />在 <font style="FONT-FAMILY: courier new" size="-1">DriverManager.getConnection()</font> 调用中阻塞的典型线程如下：<br /></td></tr></tbody></table><table style="WIDTH: 615px" cellspacing="2" cellpadding="2" border="1"><tbody><tr><td style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><font style="FONT-FAMILY: courier new" size="-1">"ExecuteThread-39" daemon prio=5 tid=0x401660 nid=0x33 waiting for monitor entry [0xd247f000..0xd247fc68]<br />  at java.sql.DriverManager.getConnection(DriverManager.java:188)<br />  at com.bla.updateDataInDatabase(MyClass.java:296)<br />  at javax.servlet.http.HttpServlet.service(HttpServlet.java:865)<br />  at weblogic.servlet.internal.ServletStubImpl.invokeServlet<br />(ServletStubImpl.java:120)<br />  at weblogic.servlet.internal.ServletContextImpl.invokeServlet<br />(ServletContextImpl.java:945)<br />  at weblogic.servlet.internal.ServletContextImpl.invokeServlet<br />(ServletContextImpl.java:909)<br />  at weblogic.servlet.internal.ServletContextManager.invokeServlet<br />(ServletContextManager.java:269)<br />  at weblogic.socket.MuxableSocketHTTP.invokeServlet<br />(MuxableSocketHTTP.java:392)<br />  at weblogic.socket.MuxableSocketHTTP.execute(MuxableSocketHTTP.java:274)<br />  at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:130)</font><br /><br /></td></tr></tbody></table><br /><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top"><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#TOP">返回页首</a><br /><br /><a name="Long_Running_SQL_Queries"></a><span style="FONT-WEIGHT: bold">长时间运行的 SQL 查询</span><br />长时间运行的 SQL 查询在其执行期间将阻塞执行线程，直至它们将结果返回给发出调用的应用程序。这就意味着，需要修改 WebLogic Server 实例的配置来处理应用程序负载要求的足够多的调用。这种情况的限制因素是执行线程数和 JDBC 连接池中的连接数。一般的经验方法是将池中的连接数设置为等于执行线程数，以便能够实现最优的资源利用。如果使用 JTS，则池中的可用连接应更多一些，因为某些连接可能会保留给实际处于非活动状态的事务。<br /><br />对于在长时间运行的 SQL 调用期间挂起的线程，其在 Thread Dump 中的堆栈与<a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Hanging_Database">挂起的数据库</a>的堆栈十分相似。有关详细信息，请对比下一小节的内容。<br /><br /><a name="Hanging_Database"></a><span style="FONT-WEIGHT: bold">挂起的数据库</span><br />对于依赖于数据库的应用程序来说，良好的数据库性能是其性能的关键。因此，挂起的数据库可能会阻塞 WebLogic Server 实例中许多或所有可用的执行线程并最终导致服务器挂起。要诊断这一问题，应从挂起的 WebLogic Server 实例获得 5 到 10 个 Thread Dump，并检查您的执行线程（在缺省队列或您的应用程序线程队列中）当前是否在 SQL 调用之中并在等待来自数据库的结果。当前发出 SQL 查询的线程的典型堆栈跟踪如下例所示：<br /></td></tr></tbody></table><br /><table style="WIDTH: 615px" cellspacing="2" cellpadding="2" border="1"><tbody><tr><td style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><font style="FONT-FAMILY: courier new" size="-1">"ExecuteThread: '4' for queue: 'weblogic.kernel.Default'" daemon prio=5 <br />tid=0x8e93c8 nid=0x19 runnable [e137f000..e13819bc]<br />  at java.net.SocketInputStream.socketRead0(Native Method)<br />  at java.net.SocketInputStream.read(SocketInputStream.java:129)<br />  at oracle.net.ns.Packet.receive(Unknown Source)<br />  at oracle.net.ns.DataPacket.receive(Unknown Source)<br />  at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)<br />  at oracle.net.ns.NetInputStream.read(Unknown Source)<br />  at oracle.net.ns.NetInputStream.read(Unknown Source)<br />  at oracle.net.ns.NetInputStream.read(Unknown Source)<br />  at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:931)<br />  at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:893)<br />  at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:375)<br />  at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1983)<br />  at oracle.jdbc.ttc7.TTC7Protocol.fetch(TTC7Protocol.java:1250)<br />  - locked &lt;e8c68f00&gt; (a oracle.jdbc.ttc7.TTC7Protocol)<br />  at oracle.jdbc.driver.OracleStatement.doExecuteQuery(OracleStatement.java:2529)<br />  at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:2857)<br />  at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate<br />(OraclePreparedStatement.java:608)<br />  - locked &lt;e5cc44d0&gt; (a oracle.jdbc.driver.OraclePreparedStatement)<br />  - locked &lt;e8c544c8&gt; (a oracle.jdbc.driver.OracleConnection)<br />  at oracle.jdbc.driver.OraclePreparedStatement.executeQuery<br />(OraclePreparedStatement.java:536)<br />  - locked &lt;e5cc44d0&gt; (a oracle.jdbc.driver.OraclePreparedStatement)<br />  - locked &lt;e8c544c8&gt; (a oracle.jdbc.driver.OracleConnection)<br />  at weblogic.jdbc.wrapper.PreparedStatement.executeQuery(PreparedStatement.java:80)<br />  at myPackage.query.getAnalysis(MyClass.java:94)<br />  at jsp_servlet._jsp._jspService(__jspService.java:242)<br />  at weblogic.servlet.jsp.JspBase.service(JspBase.java:33)<br />  at weblogic.servlet.internal.ServletStubImpl$ServletInvocationAction.run<br />(ServletStubImpl.java:971)<br />  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:402)<br />  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:305)<br />  at weblogic.servlet.internal.RequestDispatcherImpl.includ<br />e(RequestDispatcherImpl.java:607)<br />  at weblogic.servlet.internal.RequestDispatcherImpl.include<br />(RequestDispatcherImpl.java:400)<br />  at weblogic.servlet.jsp.PageContextImpl.include(PageContextImpl.java:154)<br />  at jsp_servlet._jsp.__mf1924jq._jspService(__mf1924jq.java:563)<br />  at weblogic.servlet.jsp.JspBase.service(JspBase.java:33)<br />  at weblogic.servlet.internal.ServletStubImpl$ServletInvocationAction.run<br />(ServletStubImpl.java:971)<br />  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:402)<br />  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:305)<br />  at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run<br />(WebAppServletContext.java:6350)<br />  at weblogic.security.acl.internal.AuthenticatedSubject.doAs<br />(AuthenticatedSubject.java:317)<br />  at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:118)<br />  at weblogic.servlet.internal.WebAppServletContext.invokeServlet<br />(WebAppServletContext.java:3635)<br />  at weblogic.servlet.internal.ServletRequestImpl.execute(ServletRequestImpl.java:2585)<br />  at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:197)<br />  at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:170)</font><br /></td></tr></tbody></table><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top">线程将处于运行状态。您应比较不同 Thread Dump 中的线程，查看它们是否及时接收 SQL 调用的返回结果或者它们是否在此同一调用中长时间挂起。如果 Thread Dump 似乎指示 SQL 调用的响应时间较长，则应检查相应的数据库日志，查看是不是数据库中的问题导致这种执行速度缓慢或挂起的状况。<br /><br /><font size="-1"><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#TOP">返回页首</a></font><br /><br /><a name="Slow_Network"></a><span style="FONT-WEIGHT: bold">低速网络</span><br />WebLogic Server 与数据库之间的通信依赖于性能良好且可靠的网络，来及时地处理请求。因此，网络性能低下可导致正在等待 SQL 查询结果的执行线程被挂起或阻塞。相关的堆栈跟踪将与上面<a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Hanging_Database">挂起的数据库</a>小节中的示例相似。仅仅通过分析 WebLogic Server Thread Dump 不可能找到挂起或 SQL 查询速度低下的根本原因。它们给出 SQL 调用的性能存在问题的第一个提示。下一步是检查是否存在导致 SQL 调用性能不佳的数据库网络或网络问题。<br /><br /><a name="Deadlock"></a><span style="FONT-WEIGHT: bold">死锁</span><br />应用程序级的死锁与数据库级的死锁都可导致线程挂起。您应检查 Thread Dump，查看是否存在应用程序级的死锁。有关如何执行这一操作的信息在<a href="http://support.bea.com/application_content/product_portlets/support_patterns/wls/ServerHang_Application_Deadlock_Pattern.html">服务器挂起 - 应用程序死锁模式</a>中提供。数据库死锁可以在数据库日志中检测，或者通过可在 WebLogic Server 日志文件中找到的“SQL 异常”检测。下面是相关“SQL 异常”的一个示例：<br /></td></tr></tbody></table><br /><table style="WIDTH: 615px" cellspacing="2" cellpadding="2" border="1"><tbody><tr><td style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><font style="FONT-FAMILY: courier new" size="-1">java.sql.SQLException: ORA-00060: deadlock detected while waiting for resource<br />  at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:170)<br />  at oracle.jdbc.oci8.OCIDBAccess.check_error(OCIDBAccess.java:1614)<br />  at oracle.jdbc.oci8.OCIDBAccess.executeFetch(OCIDBAccess.java:1225)<br />  at oracle.jdbc.oci8.OCIDBAccess.parseExecuteFetch(OCIDBAccess.java:1338)<br />  at oracle.jdbc.driver.OracleStatement.executeNonQuery(OracleStatement.java:1722)<br />  at oracle.jdbc.driver.OracleStatement.doExecuteOther(OracleStatement.java:1647)<br />  at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout<br />(OracleStatement.java:2167)<br />  at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate<br />(OraclePreparedStatement.java:404)</font><font size="-1"><br /></font></td></tr></tbody></table><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top">数据库检测死锁并通过回滚引发死锁的一个或多个事务来解决死锁这一过程通常需要一些时间，因此在回滚结束之前，会有一个或多个执行线程被阻塞。<br /><br /><a name="RefreshMinutes"></a><span style="FONT-WEIGHT: bold">RefreshMinutes 或 TestFrequencySeconds</span><br />如果发现数据库性能低下、SQL 调用速度缓慢或连接高峰的时期反复出现，其原因可能在于 JDBC 连接池中 <font style="FONT-FAMILY: courier new" size="-1">RefreshMinutes</font> 或 <font style="FONT-FAMILY: courier new" size="-1">TestFrequencySeconds</font><span style="FONT-STYLE: italic"></span>配置属性的设置。有关内容在<a href="http://support.bea.com/application_content/product_portlets/support_patterns/wls/Investigating_JDBC_Problems_Pattern.html">探查 JDBC 故障模式</a>中详细介绍。除非 WebLogic Server 实例与数据库之间没有防火墙，否则您应禁用此功能。<br /><br /><a name="Pool_Shrinking"></a><span style="FONT-WEIGHT: bold">池收缩</span><br />数据库的物理连接是应当打开一次并尽可能长时间保持打开的资源，因为新的连接请求对于数据库、操作系统内核及 WebLogic Server 而言是一个相当大的资源开销。因此，应在生产系统中禁用池收缩，使这一开销保持最小程度。如果启用池收缩，一旦对该池发出的连接请求不能得到满足，空闲的池连接就将被关闭，然后重新打开。<br /><br />这些活动可能会需要一些时间，因此相关应用程序请求需要的时间可能会异常的长，使用户以为系统挂起。有关如何优化 JDBC 连接池配置的信息在<a href="http://support.bea.com/application_content/product_portlets/support_patterns/wls/Investigating_JDBC_Problems_Pattern.html">探查 JDBC 故障模式</a>中提供。<br /><br /><font size="-1"><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#TOP">返回页首</a></font><br /><br style="COLOR: rgb(0,0,0)" /><span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,0)"><a name="Analysis_of_a_hanging_WebLogic_Server"></a>分析挂起的 WebLogic Server 实例</span><br />有关如何分析挂起的 WebLogic Server 实例的一般信息在<a href="http://support.bea.com/application_content/product_portlets/support_patterns/wls/Generic_Server_Hang_Pattern.html">常规服务器挂起模式</a>中提供。<br /><br />大多数情况下，首先从挂起的系统获得 Thread Dump 对于了解进展情况（例如不同的线程在做些什么以及它们为什么挂起）是非常有益的。通常，可以在生产系统上获得 Thread Dump，但是对于很早以前的 JVM 版本 (&lt;1.3.1_09) 则应小心，因为它们可能会在 Thread Dump 期间崩溃。此外，如果 WebLogic Server 实例有大量线程，则意味着完成 Thread Dump 需要一段时间，而其余线程将被阻塞。<br /><br />请进行多个 Thread Dump（5 到 10 个），这些 Thread Dump 彼此之间有若干秒钟的延迟。这使得您可以检查不同进程的进度情况。而且，它还将指示系统是否确实挂起（根本没有进度）或者吞吐速度是否极低，看起来像是系统已挂起。<br /><br />有关如何进行 Thread Dump 的信息在“常规服务器挂起”支持模式或我们的文档中提供：<a href="http://e-docs.bea.com/wls/docs81/cluster/trouble.html">http://e-docs.bea.com/wls/docs81/cluster/trouble.html</a> (English)。<br /><br />此外，还请检查是整个 WebLogic Server 实例挂起还是应用程序挂起。“常规服务器挂起”支持模式也包括此信息。<br /><br /><a name="3"></a>分析 Thread Dump 可以指示出实例的挂起是否确实是由于前一小节<a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Why_does_the_problem_occur">为什么发生此问题？</a>中提到的某一种原因。例如，如果所有线程都在一个 DriverManager 方法（如 <font style="FONT-FAMILY: courier new" size="-1">getConnection()</font>）中，则您已经确定出挂起的根本原因，并需要更改应用程序，以使用数据源或 <font style="FONT-FAMILY: courier new" size="-1">Driver.connect()</font> 来代替 <font style="FONT-FAMILY: courier new" size="-1">DriverManager.getConnection()</font>。<br /><br />Samurai 是一个非常有用的工具，可用于分析 Thread Dump 并监视不同 Thread Dump 之间线程的进度。可从 dev2dev 下载此工具，网址：<a href="http://dev2dev.bea.com/resourcelibrary/utilitiestools/adminmgmt.jsp">http://dev2dev.bea.com/resourcelibrary/utilitiestools/adminmgmt.jsp</a> (English)。<br /><br />dev2dev 上有关分析 Thread Dump 的白皮书：<a href="http://dev2dev.bea.com/products/wlplatform81/articles/thread_dumps.jsp">http://dev2dev.bea.com/products/wlplatform81/articles/thread_dumps.jsp</a> (English) 也有助于深入探究 Thread Dump，以了解有关服务器挂起的更多内容。<br /><br /><font size="-1"><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#TOP">返回页首</a></font><br /><br style="COLOR: rgb(0,0,0)" /><span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,0)"><a name="Tips_and_Tricks_to_optimize_your_JDBC"></a>优化 JDBC 代码和 JDBC 连接池配置的技巧</span><br />对于开发 JDBC 代码和配置 JDBC 连接池，有一些最佳的惯例，可以帮助避免常见问题并优化资源利用，避免服务器实例挂起。<br /><br /><span style="FONT-WEIGHT: bold"><a name="JDBC_Programming"></a>JDBC 编程</span><br />为了优化 WebLogic Server 中资源的利用，节约数据库资源，应使用 JDBC 连接池进行应用程序的 JDBC 调用。在应用程序代码中建立和破坏的连接会造成不必要的开销，应当避免。要获得 JDBC 编程的一般文档，请参阅：<a href="http://e-docs.bea.com/wls/docs81/jdbc/rmidriver.html#1028977">http://e-docs.bea.com/wls/docs81/jdbc/rmidriver.html#1028977</a> (English)。此外，有关 JDBC 性能调整的详细信息位于：<a href="http://e-docs.bea.com/wls/docs81/jdbc/performance.html#1027791">http://e-docs.bea.com/wls/docs81/jdbc/performance.html#1027791</a> (English)。<br /><br />可以在 dev2dev Java Database Connectivity (English) 页上查看有关 JDBC 的综合性信息，它有助于优化 JDBC 代码和 JDBC 资源的利用，网址为：<a href="http://dev2dev.bea.com/technologies/jdbc/index.jsp">http://dev2dev.bea.com/technologies/jdbc/index.jsp</a> (English)。<br /><br /><span style="FONT-WEIGHT: bold"><a name="JDBC_Connection_Pool_Configuration"></a>JDBC 连接池配置</span><br />关于如何针对生产环境配置连接池的建议，请参阅<a href="http://support.bea.com/application_content/product_portlets/support_patterns/wls/Investigating_JDBC_Problems_Pattern.html">探查 JDBC 故障模式</a>。为避免出现挂起或者性能不佳的情况，应考虑这些配置技巧。<br /></td></tr></tbody></table><br /><br /><table style="WIDTH: 615px; TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top"><a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#TOP">返回页首</a><br /><br /><font style="COLOR: rgb(0,0,0)" color="#009900"><b><a name="Known_Issues"></a>已知问题</b></font><br /><span style="COLOR: rgb(0,0,0)">您可以定期查看所用 WLS 版本的“Release Notes”，了解 Service Pack 中的“Known Issues”或“Resolved Issues”的详细信息及浏览与 JDBC 服务器挂起有关的问题。</span><span style="COLOR: rgb(0,0,0)">方便起见，下面提供了这些发行说明的链接：<br /></span><ul><li><span style="COLOR: rgb(0,0,0)"><a href="http://edocs/wls/docs81/notes/index.html">WLS 8.1 Release Notes (English)</a></span></li><li><span style="COLOR: rgb(0,0,0)"><a href="http://edocs/wls/docs70/notes/index.html">WLS 7.0 Release Notes (English)</a></span></li><li><span style="COLOR: rgb(0,0,0)"><a href="http://edocs/wls/docs61/notes/index.html">WLS 6.1 Release Notes (English)</a></span></li></ul><span style="COLOR: rgb(0,0,0)">请注意，<a href="http://e-docs.bea.com/wls/docs81/notes/resolved_sp03.html#1817208">WLS 8.1 SP3</a> 中已经进行一些更改来解决 CR134921，其中对于特定的 JDBC 连接，用于回滚事务的调用没有立即处理，因为驱动程序必须等待任何当前正执行的语句返回。<br /><span style="COLOR: rgb(0,0,0)"><br />使用搜索功能也可以搜索到“Release Notes”，还可以搜索到其它支持解决办法及与 CR 有关的信息，如<a href="http://www.bea.com.cn/support_pattern/JDBC_Causes_Server_Hang_Pattern.html#Need_Further_Help?">需要更多帮助？</a>中所提到的内容。如果客户签订了技术支持合同，则可以登录 </span><span style="FONT-FAMILY: 'Times New Roman'"><a href="http://support.bea.com/">http://support.bea.com/</a>，登录后会看到为 Solutions 和 Bug Central 提供的 Browse portlet，可在其中按产品版本浏览最新提供的 CR。</span><br /></span></td></tr></tbody></table><br /><table style="WIDTH: 615px; COLOR: rgb(0,0,0); TEXT-ALIGN: left" cellspacing="2" cellpadding="2"><tbody><tr><td style="VERTICAL-ALIGN: top"><span><span style="FONT-WEIGHT: bold"><a name="Need_Further_Help?"></a>需要更多帮助？</span></span><br /><span style="FONT-FAMILY: 'Times New Roman'">如果您已经理解这个模式，但仍需要更多帮助，您可以：<br /></span><ol><li><span style="FONT-FAMILY: 'Times New Roman'">在 </span><span style="FONT-FAMILY: 'Times New Roman'"><a href="http://support.bea.com/">http://support.bea.com/</a></span><span style="FONT-FAMILY: 'Times New Roman'"></span><span style="FONT-FAMILY: 'Times New Roman'">上查询 AskBEA（例如，使用</span><span style="FONT-FAMILY: 'Times New Roman'">“jdbc server hang”），以查找其它已发布的解决办法。</span><span style="COLOR: rgb(0,0,0); FONT-FAMILY: 'Times New Roman'">技术支持合同客户：确保已经登录，可以访问提供的与 CR 有关的信息。</span></li><li><span style="FONT-FAMILY: 'Times New Roman'">在 </span><a href="http://newsgroups.bea.com/">http://newsgroups.bea.com/</a> 上，向 BEA 的某个新闻组提出更详细具体的问题。<br /></li></ol>如果这还不能解决您的问题，并且您拥有有效的技术支持合同，您可以通过登录以下网站来打开支持案例：<span style="FONT-FAMILY: 'Times New Roman'"><a href="http://support.bea.com/">http://support.bea.com/</a></span>。</td></tr></tbody></table><img src ="http://www.blogjava.net/TrampEagle/aggbug/60948.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-07-31 10:19 <a href="http://www.blogjava.net/TrampEagle/articles/60948.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中四种操作xml方式的比较</title><link>http://www.blogjava.net/TrampEagle/articles/49413.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 01 Jun 2006 01:48:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/49413.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/49413.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/49413.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/49413.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/49413.html</trackback:ping><description><![CDATA[
		<table class="tablebody2" style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" width="90%" border="0">
				<tbody>
						<tr>
								<td style="FONT-SIZE: 9pt; LINE-HEIGHT: 12pt" width="100%">
										<img alt="发贴心情" src="http://www.3doing.net/forums/Skins/Default/topicface/face1.gif" align="absMiddle" border="0" /> <b>java中四种操作xml方式的比较</b><br />1. 介绍 <br /><br />1）DOM(JAXP Crimson解析器) <br />DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构，然后才能做任何工作。由于它是基于信息层次的，因而DOM被认为是基于树或基于对象的。DOM以及广义的基于树的处理具有几个优点。首先，由于树在内存中是持久的，因此可以修改它以便应用程序能对数据和结构作出更改。它还可以在任何时候在树中上下导航，而不是像SAX那样是一次性的处理。DOM使用起来也要简单得多。 <br /><br />2）SAX <br /><br />SAX处理的优点非常类似于流媒体的优点。分析能够立即开始，而不是等待所有的数据被处理。而且，由于应用程序只是在读取数据时检查数据，因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上，应用程序甚至不必解析整个文档；它可以在某个条件得到满足时停止解析。一般来说，SAX还比它的替代者DOM快许多。 <br />　　选择DOM还是选择SAX？ 对于需要自己编写代码来处理XML文档的开发人员来说， 选择DOM还是SAX解析模型是一个非常重要的设计决策。 DOM采用建立树形结构的方式访问XML文档，而SAX采用的事件模型。 <br /><br />　　DOM解析器把XML文档转化为一个包含其内容的树，并可以对树进行遍历。用DOM解析模型的优点是编程容易，开发人员只需要调用建树的指令，然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用DOM解析器的时候需要处理整个XML文档，所以对性能和内存的要求比较高，尤其是遇到很大的XML文件的时候。由于它的遍历能力，DOM解析器常用于XML文档需要频繁的改变的服务中。 <br /><br />　　SAX解析器采用了基于事件的模型，它在解析XML文档的时候可以触发一系列的事件，当发现给定的tag的时候，它可以激活一个回调方法，告诉该方法制定的标签已经找到。SAX对内存的要求通常会比较低，因为它让开发人员自己来决定所要处理的tag。特别是当开发人员只需要处理文档中所包含的部分数据时，SAX这种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难，而且很难同时访问同一个文档中的多处不同数据。 <br /><br />3）JDOM <a href="http://www.jdom.org/" target="_blank"><font color="#006699">http://www.jdom.org</font></a><br /><br />JDOM的目的是成为Java特定文档模型，它简化与XML的交互并且比使用DOM实现更快。由于是第一个Java特定模型，JDOM一直得到大力推广和促进。正在考虑通过“Java规范请求JSR-102”将它最终用作“Java标准扩展”。从2000年初就已经开始了JDOM开发。 <br /><br />　　JDOM与DOM主要有两方面不同。首先，JDOM仅使用具体类而不使用接口。这在某些方面简化了API，但是也限制了灵活性。第二，API大量使用了Collections类，简化了那些已经熟悉这些类的Java开发者的使用。 <br /><br />　　JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%)。JDOM对于大多数Java/XML应用程序来说当然是有用的，并且大多数开发者发现API比DOM容易理解得多。JDOM还包括对程序行为的相当广泛检查以防止用户做任何在XML中无意义的事。然而，它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习DOM或JDOM接口都更有意义的工作。 <br /><br />　　JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档。JDOM是在Apache许可证变体下发布的开放源码。 <br /><br />4）DOM4J <a href="http://dom4j.sourceforge.net/" target="_blank"><font color="#006699">http://dom4j.sourceforge.net</font></a><br /><br />虽然DOM4J代表了完全独立的开发结果，但最初，它是JDOM的一种智能分支。它合并了许多超出基本XML文档表示的功能，包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项，它通过DOM4J API和标准DOM接口具有并行访问功能。从2000下半年开始，它就一直处于开发之中。 <br /><br />　　为支持所有这些功能，DOM4J使用接口和抽象基本类方法。DOM4J大量使用了API中的Collections类，但是在许多情况下，它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是，虽然DOM4J付出了更复杂的API的代价，但是它提供了比JDOM大得多的灵活性。 <br /><br />　　在添加灵活性、XPath集成和对大文档处理的目标时，DOM4J的目标与JDOM是一样的：针对Java开发者的易用性和直观操作。它还致力于成为比JDOM更完整的解决方案，实现在本质上处理所有Java/XML问题的目标。在完成该目标时，它比JDOM更少强调防止不正确的应用程序行为。 <br /><br />　　DOM4J是一个非常非常优秀的Java XML API，具有性能优异、功能强大和极端易用使用的特点，同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML，特别值得一提的是连Sun的JAXM也在用DOM4J。 <br /><br /><br />2.. 比较 <br /><br />1）DOM4J性能最好，连Sun的JAXM也在用DOM4J。目前许多开源项目中大量采用DOM4J，例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性，那就采用DOM4J. <br /><br />2）JDOM和DOM在性能测试时表现不佳，在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM。虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题，但是从性能观点来看，它确实没有值得推荐之处。另外，DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础，因为它正式获得W3C推荐(与基于非标准的Java模型相对)，所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。 <br /><br />3）SAX表现较好，这要依赖于它特定的解析方式－事件驱动。一个SAX检测即将到来的XML流，但并没有载入到内存(当然当XML流被读入时，会有部分文档暂时隐藏在内存中)。 <br /><br />3. 四种xml操作方式的基本使用方法 <br /><br />xml文件： <br /><br /><br /><div class="HtmlCode">＜?xml version="1.0" encoding="GB2312"?＞ <br />＜RESULT＞ <br />＜VALUE＞ <br />　　 ＜NO＞A1234＜/NO＞ <br />　　 ＜ADDR＞四川省XX县XX镇XX路X段XX号＜/ADDR＞ <br />＜/VALUE＞ <br />＜VALUE＞ <br />　　 ＜NO＞B1234＜/NO＞ <br />　 　＜ADDR＞四川省XX市XX乡XX村XX组＜/ADDR＞ <br />＜/VALUE＞ <br />＜/RESULT＞ <br /><br />1）DOM <br /><br />import java.io.*; <br />import java.util.*; <br />import org.w3c.dom.*; <br />import javax.xml.parsers.*; <br /><br />public class MyXMLReader{ <br />　public static void main(String arge[]){ <br /><br />　　long lasting =System.currentTimeMillis(); <br />　　try{ <br />　　　File f=new File("data_10k.xml"); <br />　　　DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); <br />　　　DocumentBuilder builder=factory.newDocumentBuilder(); <br />　　　Document doc = builder.parse(f); <br />　　　NodeList nl = doc.getElementsByTagName("VALUE"); <br />　　　for (int i=0;i＜nl.getLength();i++){ <br />　　　　System.out.print("车牌号码:" + doc.getElementsByTagName("NO").item(i).getFirstChild().getNodeValue()); <br />　　　　System.out.println("车主地址:" + doc.getElementsByTagName("ADDR").item(i).getFirstChild().getNodeValue()); <br />　 　} <br />　　}catch(Exception e){ <br />　　　e.printStackTrace(); <br />} <br /><br />2）SAX <br /><br />import org.xml.sax.*; <br />import org.xml.sax.helpers.*; <br />import javax.xml.parsers.*; <br /><br />public class MyXMLReader extends DefaultHandler { <br /><br />　java.util.Stack tags = new java.util.Stack(); <br />　public MyXMLReader() { <br />　　super(); <br />} <br /><br />　public static void main(String args[]) { <br />　　long lasting = System.currentTimeMillis(); <br />　　try { <br />　　　SAXParserFactory sf = SAXParserFactory.newInstance(); <br />　　　SAXParser sp = sf.newSAXParser(); <br />　　　MyXMLReader reader = new MyXMLReader(); <br />　　　sp.parse(new InputSource("data_10k.xml"), reader); <br />　　} catch (Exception e) { <br />　　　e.printStackTrace(); <br />　　} <br /><br />　　System.out.println("运行时间：" + (System.currentTimeMillis() - lasting) + "毫秒");} <br />　　public void characters(char ch[], int start, int length) throws SAXException { <br />　　String tag = (String) tags.peek(); <br />　　if (tag.equals("NO")) { <br />　　　System.out.print("车牌号码：" + new String(ch, start, length)); <br />} <br />if (tag.equals("ADDR")) { <br />　　System.out.println("地址:" + new String(ch, start, length)); <br />} <br />} <br /><br />　　public void startElement(String uri,String localName,String qName,Attributes attrs) { <br />　　tags.push(qName);} <br />} <br /><br />3） JDOM <br /><br />import java.io.*; <br />import java.util.*; <br />import org.jdom.*; <br />import org.jdom.input.*; <br /><br />public class MyXMLReader { <br /><br />　public static void main(String arge[]) { <br />　　long lasting = System.currentTimeMillis(); <br />　　try { <br />　　　SAXBuilder builder = new SAXBuilder(); <br />　　　Document doc = builder.build(new File("data_10k.xml")); <br />　　　Element foo = doc.getRootElement(); <br />　　　List allChildren = foo.getChildren(); <br />　　　for(int i=0;i＜allChildren.size();i++) { <br />　　　　System.out.print("车牌号码:" + ((Element)allChildren.get(i)).getChild("NO").getText()); <br />　　　　System.out.println("车主地址:" + ((Element)allChildren.get(i)).getChild("ADDR").getText()); <br />　　　} <br />　　} catch (Exception e) { <br />　　　e.printStackTrace(); <br />} <br /><br />} <br /><br />4）DOM4J <br /><br />import java.io.*; <br />import java.util.*; <br />import org.dom4j.*; <br />import org.dom4j.io.*; <br /><br />public class MyXMLReader { <br /><br />　public static void main(String arge[]) { <br />　　long lasting = System.currentTimeMillis(); <br />　　try { <br />　　　File f = new File("data_10k.xml"); <br />　　　SAXReader reader = new SAXReader(); <br />　　　Document doc = reader.read(f); <br />　　　Element root = doc.getRootElement(); <br />　　　Element foo; <br />　　　for (Iterator i = root.elementIterator("VALUE"); i.hasNext()<img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://forum.javaeye.com/images/smiles/icon_wink.gif" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" border="0" /> { <br />　　　　foo = (Element) i.next(); <br />　　　　System.out.print("车牌号码:" + foo.elementText("NO")); <br />　　　　System.out.println("车主地址:" + foo.elementText("ADDR")); <br />　　　} <br />　　} catch (Exception e) { <br />　　　e.printStackTrace(); <br />} <br />} </div></td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/49413.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-06-01 09:48 <a href="http://www.blogjava.net/TrampEagle/articles/49413.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AOP研究</title><link>http://www.blogjava.net/TrampEagle/articles/49410.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 01 Jun 2006 01:42:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/49410.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/49410.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/49410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/49410.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/49410.html</trackback:ping><description><![CDATA[引自：<a href="http://www.3doing.net/forums/dispbbs.asp?boardID=11&amp;ID=138&amp;page=1">http://www.3doing.net/forums/dispbbs.asp?boardID=11&amp;ID=138&amp;page=1</a><br /><br /><br /><img alt="发贴心情" src="http://www.3doing.net/forums/Skins/Default/topicface/face1.gif" align="absMiddle" border="0" /> <b>AOP研究</b><br /><p><b>为什么要区分J2EE容器和J2EE应用系统？</b></p><p>　　我们知道，J2EE应用系统只有部署在J2EE容器中才能运行，那么为什么划分为J2EE容器和J2EE应用系统？ 通过对J2EE容器运行机制的分析（见我的电子教材“EJB实用原理”），我们可以发现：实际上J2EE容器分离了一般应用系统的一些通用功能，例如事务机制、安全机制以及对象池或线程池等性能优化机制。</p><p>　　这些功能机制是每个应用系统几乎都需要的，因此可以从具体应用系统中分离出来，形成一个通用的框架平台，而且，这些功能机制的设计开发有一定难度，同时运行的稳定性和快速性都非常重要，必须经过长时间调试和运行经验积累而成，因此，形成了专门的J2EE容器服务器产品，如Tomcat JBoss、Websphere、WebLogic等。</p><p>　　从J2EE系统划分为J2EE容器和J2EE应用系统两个方面，我们已经看到一种分散关注的思路（separation of concerns）。<br /><br /><b>分散关注</b><br /><br />　　将通用需求功能从不相关类之中分离出来；同时，能够使得很多类共享一个行为，一旦行为发生变化，不必修改很多类，只要修改这个行为就可以。<br /><br />　　 AOP就是这种实现分散关注的编程方法，它将“关注”封装在“方面”中。</p><p><b>AOP是什么？</b></p><p>　　AOP是OOP的延续，是Aspect Oriented Programming的缩写，意思是面向方面编程。AOP实际是GoF设计模式的延续，设计模式孜孜不倦追求的是调用者和被调用者之间的解耦，AOP可以说也是这种目标的一种实现。</p><p>　　举例：假设有在一个应用系统中，有一个共享的数据必须被并发同时访问，首先，将这个数据封装在数据对象中，称为Data Class，同时，将有多个访问类，专门用于在同一时刻访问这同一个数据对象。</p><p>　　为了完成上述并发访问同一资源的功能，需要引入锁Lock的概念，也就是说，某个时刻，当有一个访问类访问这个数据对象时，这个数据对象必须上锁Locked，用完后就立即解锁unLocked，再供其它访问类访问。</p><p>　　使用传统的编程习惯，我们会创建一个抽象类，所有的访问类继承这个抽象父类，如下：</p><center></center><div align="center"></div><table cellspacing="0" cellpadding="0" width="80%" align="center" border="0"><tbody><tr><td bgcolor="#cccccc"><p>abstract class Worker{</p><p>　　abstract void locked();<br />　　abstract void accessDataObject();<br />　　abstract void unlocked();</p><p>}</p></td></tr></tbody></table><br />　　缺点： <br /><br /><br /><ul><li>accessDataObject()方法需要有“锁”状态之类的相关代码。 <br /><br /><br /></li><li>Java只提供了单继承，因此具体访问类只能继承这个父类，如果具体访问类还要继承其它父类，比如另外一个如Worker的父类，将无法方便实现。 <br /><br /><br /></li><li>重用被打折扣，具体访问类因为也包含“锁”状态之类的相关代码，只能被重用在相关有“锁”的场合，重用范围很窄。<br /><br />仔细研究这个应用的“锁”，它其实有下列特性： <br /><br /><br /></li><li>“锁”功能不是具体访问类的首要或主要功能，访问类主要功能是访问数据对象，例如读取数据或更改动作。 <br /><br /><br /></li><li>“锁”行为其实是和具体访问类的主要功能可以独立、区分开来的。 <br /><br /><br /></li><li>“锁”功能其实是这个系统的一个纵向切面，涉及许多类、许多类的方法。如下图：<br /><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jdon.com/AOPdesign/images/lock.png" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" /><br /></li></ul><p>　　因此，一个新的程序结构应该是关注系统的纵向切面，例如这个应用的“锁”功能，这个新的程序结构就是aspect（方面）</p><p>　　在这个应用中，“锁”方面（aspect）应该有以下职责：</p><p>　　提供一些必备的功能，对被访问对象实现加锁或解锁功能。以保证所有在修改数据对象的操作之前能够调用lock()加锁，在它使用完成后，调用unlock()解锁。</p><p><b>AOP应用范围</b><br /><br />　　很明显，AOP非常适合开发J2EE容器服务器，目前JBoss 4.0正是使用AOP框架进行开发。<br />　　具体功能如下：<br />Authentication 权限<br />Caching 缓存<br />Context passing 内容传递<br />Error handling 错误处理<br />Lazy loading　懒加载<br />Debugging　　调试<br />logging, tracing, profiling and monitoring　记录跟踪　优化　校准<br />Performance optimization　性能优化<br />Persistence　　持久化<br />Resource pooling　资源池<br />Synchronization　同步<br />Transactions 事务</p><p><b>AOP有必要吗？</b></p><p>　　当然，上述应用范例在没有使用AOP情况下，也得到了解决，例如JBoss 3.XXX也提供了上述应用功能，但是没有使用AOP。</p><p>　　但是，使用AOP可以让我们从一个更高的抽象概念来理解软件系统，AOP也许提供一种有价值的工具。可以这么说：因为使用AOP结构，现在JBoss 4.0的源码要比JBoss 3.X容易理解多了，这对于一个大型复杂系统来说是非常重要的。</p><p>　　从另外一个方面说，好像不是所有的人都需要关心AOP，它可能是一种架构设计的选择，如果选择J2EE系统，AOP关注的上述通用方面都已经被J2EE容器实现了，J2EE应用系统开发者可能需要更多地关注行业应用方面aspect。</p><p><b>AOP具体实现</b></p><p>　　AOP是一个概念，并没有设定具体语言的实现，它能克服那些只有单继承特性语言的缺点（如Java），目前AOP具体实现有以下几个项目：</p><p>　　AspectJ (TM)：　创建于Xerox PARC. 有近十年历史，成熟<br />　　缺点：过于复杂；破坏封装；需要专门的Java编译器。</p><p>　　动态AOP：使用JDK的动态代理API或字节码Bytecode处理技术。<br /><br />　动态AOP：使用JDK的动态代理API或字节码Bytecode处理技术。</p><p>　　基于动态代理API的具体项目有：<br />　　JBoss 4.0 JBoss 4.0服务器<br />　　nanning　这是以中国南宁命名的一个项目，搞不清楚为什么和中国相关？是中国人发起的？</p><p>　　基于字节码的项目有：<br />　　aspectwerkz　<br />　　spring　<br />在以后其它文章中，我将继续对AOP概念进行分析，和大家一起学习进步。<br /></p><blockquote><table class="tablebody2" style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" width="90%" border="0"><tbody><tr><td style="FONT-SIZE: 9pt; LINE-HEIGHT: 12pt" width="100%"><img alt="发贴心情" src="http://www.3doing.net/forums/Skins/Default/topicface/face1.gif" align="absMiddle" border="0" /> <b></b><br /><h3 align="center">AOP和AspectJ</h3><p align="center"><a href="http://www.jdon.com/aboutme.htm" target="_blank"><font color="#000000">板桥里人</font></a><a href="http://www.jdon.com/" target="_blank" 2004="" 01="" 10=""><font color="#000000">http://www.jdon.com 2004/01/10</font></a></p><p><b>需求和问题</b></p><p>　　以上篇《<a href="http://www.jdon.com/AOPdesign/jdon-aop.htm" target="_blank"><font color="#000000">AOP是什么</font></a>》中并发访问应用为例子：</p><p>　　多个访问类同时访问一个共享数据对象时，每个访问类在访问这个数据对象时，需要将数据对象上锁，访问完成后，再实行解锁，供其它并发线程访问，这是我们处理并发访问资源的方式。</p><p>　　为了实现这个需求，先实现传统的编程，这里我们假定有一个写锁，对数据对象实行写之前，首先对这个对象进行上写锁，写操作完毕后，必须释放写锁。</p><p>　　首先，我们需要一个锁，这个锁可以是数据对象中一个字段或其它，这里使用<a href="http://gee.cs.oswego.edu/dl/" target="_blank"><font color="#000000">Doug Le</font></a>a的<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/ReentrantWriterPreferenceReadWriteLock.html" target="_blank"><font color="#000000">ReentrantWriterPreferenceReadWriteLock</font></a>作为我们的锁资源。</p><table cellspacing="0" cellpadding="0" width="80%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>import EDU.oswego.cs.dl.util.concurrent.*;</p><p>public class Worker extends Thread {</p><p>　　Data data;<br /><br />　　ReentrantWriterPreferenceReadWriteLock rwl =　<br />　　　　new ReentrantWriterPreferenceReadWriteLock(); <br /><br />　　public boolean createData() {<br />　　　try {<br />　　　　rwl.writeLock().acquire();　//上锁<br /><br />　　　　//对data实行写逻辑操作　<br />　　　　　　　 <br />　　　}catch() {<br />　　 　 return false;<br />　　　}finally{<br />　　 　 rwl.writeLock().release(); 　//解锁<br />　　　}<br />　　　return true;<br />　　}<br /><br />　　public boolean updateData() {<br />　　　try {<br />　　　　rwl.writeLock().acquire();//上锁<br /><br />　　　　//对data实行写逻辑操作　<br />　　　　　　　 <br />　　　}catch() {<br />　　 　 return false;<br />　　　}finally{<br />　　 　 rwl.writeLock().release(); //解锁<br />　　　}<br />　　　return true;<br />　　}<br /><br />　　public void run() {<br />　　　　//执行createData()或updateData()<br />　　}<br />}</p></td></tr></tbody></table><p>假设可能存在另外一个访问类，也将对数据对象实现写操作，代码如下：</p><table cellspacing="0" cellpadding="0" width="80%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>import EDU.oswego.cs.dl.util.concurrent.*;</p><p>public class AnotherWorker extends Thread {</p><p>　　Data data;<br /><br />　　ReentrantWriterPreferenceReadWriteLock rwl =　<br />　　　　new ReentrantWriterPreferenceReadWriteLock(); <br />　　<br />　　public boolean updateData() {<br />　　　try {<br />　　　　rwl.writeLock().acquire();//上锁<br /><br />　　　　//对data实行写逻辑操作　<br />　　　　　　　 <br />　　　}catch() {<br />　　 　 return false;<br />　　　}finally{<br />　　 　 rwl.writeLock().release(); //解锁<br />　　　}<br />　　　return true;<br />　　}<br /><br />　　public void run() {<br />　　　　//执行updateData()<br />　　}<br />}</p></td></tr></tbody></table><p>　　以上是Java传统编程的实现，这种锁的实现方式是在每个具体类中实现，如下图：</p><p align="left"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jdon.com/AOPdesign/images/aop1.png" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" /></p><p>这种实现方式的缺点很多：</p><ul><li>冗余：有很多重复的编码，如rwl.writeLock().acquire()等； 
</li><li>减少重用：worker的updateData()方法重用性几乎为零。 
</li><li>"数据对象写操作必须使用锁控制这个设计目的"不容易显现，如果更换了一个新的程序员，他可能编写一段不使用锁机制就对这个数据对象写操作的代码。 
</li><li>如果上述代码有读功能，那么我们需要在代码中实现先上读锁，当需要写时，解读锁，再上写锁等等，如果稍微不小心，上锁解锁次序搞错，系统就隐含大的BUG，这种可能性会随着这个数据对象永远存在下去，系统设计大大的隐患啊！ </li></ul><p>　　那么我们使用AOP概念来重新实现上述需求，AOP并没有什么新花招，只是提供了观察问题的一个新视角度。</p><p>　　这里我们可以抛开新技术迷人雾障，真正核心还是新思维、新视点，人类很多问题如果换一个脑筋看待理解，也许结果真的是翻天覆地不一样啊，所以，作为人自身，首先要重视和你世界观和思维方式不一样的人进行交流和沟通。</p><p>　　现实生活中有很多"不公平"，例如某个小学毕业生成了千万富翁，你就怀疑知识无用，也许你认为他的机会好，其实你可能不知道，他的观察问题的视角比你独特，或者他可能会经常换不同的角度来看待问题和解决问题，而你由于过分陷入一个视角的具体实现细节中，迷失了真正的方向，要不说是读书人脑子僵化呢？</p><p>　　言归正传，我们看看AOP是如何从一个新的视角解决上述问题的。</p><p>　　如果说上面代码在每个类中实现上锁或解锁，类似横向解决方式，那么AOP是从纵向方面来解决上述问题，纵向解决之道示意图如下：</p><p><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jdon.com/AOPdesign/images/aop2.png" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" /></p><p>　　AOP把这个纵向切面cross-cuts称为Aspect（方面），其实我认为AOP翻译成<b>面向切面编程</b>比较好，不知哪个糊涂者因为先行一步，翻译成“面向方面编程”如此抽象，故弄玄虚。</p><p><b>AspectJ实现</b></p><p>　　下面我们使用AOP的实现之一AspectJ来对上述需求改写。AspectJ是AOP最早成熟的Java实现，它稍微扩展了一下Java语言，增加了一些Keyword等，pointcut的语法如下：</p><p>public pointcut 方法名：call(XXXX)</p><p>　　AspectJ增加了pointcut, call是pointcut类型，有关AspectJ更多基本语法见<a href="http://today.java.net/pub/a/today/2003/12/26/ch3AspectJSyntaxBasics.html" target="_blank"><font color="#000000">这里</font></a>。因为AspectJ使用了一些特别语法，所以Java编译器就不能用SUN公司提供javac了，必须使用其专门的编译器，也许SUN在以后JDK版本中会引入AOP。</p><p>　　使用AspectJ如何实现上图所谓切面式的编程呢？首先,我们将上图纵向切面称为Aspect，那么我们建立一个类似Class的Aspect，Java中建立一个Class代码如下：</p><p>public class MyClass{<br />　　//属性和方法 ...<br />}</p><p>　　同样，建立一个Aspect的代码如下：</p><p>public aspect MyAspect{<br />　　//属性和方法 ...<br />}</p><p>建立一个Aspect名为Lock，代码如下：</p><table cellspacing="0" cellpadding="0" width="80%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>import EDU.oswego.cs.dl.util.concurrent.*;</p><p>public aspect Lock {<br /><br />　　......<br />　　ReentrantWriterPreferenceReadWriteLock rwl =　<br />　　　　new ReentrantWriterPreferenceReadWriteLock(); </p><p>　　public <b>pointcut</b><font color="#0000ff">writeOperations</font>(): <br />　　　　execution(<font color="#ff0000">public boolean Worker.createData()</font>) ||<br />　　　　execution(<font color="#ff0000">public boolean Worker.updateData()</font>) ||<br />　　　　execution(<font color="#ff0000">public boolean AnotherWorker.updateData()</font>) ;<br /><br /><br />　　<b>before</b>() : <font color="#0000ff">writeOperations</font>() { <br />　　　　rwl.writeLock().acquire();//上锁　advice body<br />　　}</p><p>　　<b>after</b>() : <font color="#0000ff">writeOperations</font>() {<br />　　 　 rwl.writeLock().release(); //解锁 advice body<br />　　}</p><p>　　......<br />}</p></td></tr></tbody></table><p>　　上述代码关键点是pointcut，意味切入点或触发点，那么在那些条件下该点会触发呢？是后面红字标识的一些情况，在执行Worker的createData()方法，Worker的update方法等时触发。</p><p>　　before代表触发之前做什么事情？<br />　　答案是上锁。 </p><p>　　after代表触发之后做什么事情？<br />　　答案是上锁。 </p><p>　　通过引入上述aspect，那么Worker代码可以清洁如下：</p><table cellspacing="0" cellpadding="0" width="80%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>public class Worker extends Thread {</p><p>　　Data data;<br /><br />　　public boolean createData() {<br />　　　try {<br />　　　　//对data实行写逻辑操作　　　　　　　　 <br />　　　}catch() {<br />　　 　 return false;<br />　　　}<br />　　　return true;<br />　　}<br /><br />　　public boolean updateData() {<br />　　　try {<br />　　　　//对data实行写逻辑操作　　　　　　　　 <br />　　　}catch() {<br />　　 　 return false;<br />　　　}finally{<br />　　　}<br />　　　return true;<br />　　}<br /><br />　　public void run() {<br />　　　　//执行createData()或updateData()<br />　　}<br />}</p></td></tr></tbody></table><p>　　Worker中关于“锁”的代码都不见了，纯粹变成了数据操作的主要方法。</p><p><b>AOP术语</b></p><p>　　通过上例已经知道AspectJ如何从切面crosscutting来解决并发访问应用需求的，其中最重要的是引入了一套类似事件触发机制。</p><p>　　Pointcut类似触发器，是事件Event发生源，一旦pointcut被触发，将会产生相应的动作Action，这部分Action称为Advice。</p><p>　　Advice在AspectJ有三种：before、 after、Around之分，上述aspect Lock代码中使用了Advice的两种before和after。</p><p>　　所以AOP有两个基本的术语：Pointcut和Advice。你可以用事件机制的Event和Action来类比理解它们。上述并发访问应用中pointcut和advice如下图所示：</p><p><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jdon.com/AOPdesign/images/aop3.png" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" /></p><p>小结如下：<br />advice - 真正的执行代码，或者说关注的实现。 类似Action。<br />join point - 代码中激活advice被执行的触发点。<br />pointcut - 一系列的join point称为pointcut，pointcut有时代指join point</p><p>其中advice部分又有：<br />Interceptor - 解释器并没有在AspectJ出现，在使用JDK动态代理API实现的AOP框架中使用，解释有方法调用或对象构造或者字段访问等事件，是调用者和被调用者之间的纽带，综合了Decorator/代理模式甚至职责链等模式。</p><p>Introduction - 修改一个类，以增加字段、方法或构造或者执行新的接口，包括Mixin实现。</p><p>例如上述并发访问应用中，如果想为每个Data对象生成相应的aspect Lock，那么可以在aspect Lock中人为数据对象增加一个字段lock，如下：</p><table cellspacing="0" cellpadding="0" width="80%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>aspect Lock {<br /><br />　　Data sharedDataInstance;<br />　　Lock( Data d ) {<br />　　　　 sharedDataInstance = d;<br />　　}<br /><br />　　introduce Lock Data.lock;　//修改Data类，增加一字段lock<br /><br />　　advise Data() { //Data构造时触发<br />　　　　 static after {<br />　　　　　　 //当Data对象生成时，将Data中lock字段赋值为aspect Lock<br />　　　　　　 //为每个Data对象生成相应的aspect Lock<br />　　　　　　 thisObject.lock = new Lock( thisObject );<br />　　　　 }<br />　　 }<br />　　....</p><p>}</p></td></tr></tbody></table><p>上述代码等于在Data类中加入一行：</p><table cellspacing="0" cellpadding="0" width="80%" border="0"><tbody><tr><td bgcolor="#cccccc"><p>public class Data{<br />　　......<br />　　Lock lock = new Lock();<br />　　...... <br />}</p></td></tr></tbody></table><p>还有其它两个涉及AOP代码运行方式：</p><p>weaving - 将aspect代码插入到相应代码中的过程，一般是编译完成或在运行时动态完成。取决于具体AOP产品，例如AspectJ是使用特殊编译器在编译完成weaving，而nanning、JBoss AOP是使用动态代理API，因此在运行时动态完成weaving的。<br />instrumentor - 用来实现weaving功能的工具。</p></td></tr></tbody></table></blockquote><p></p><table class="tablebody2" style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" width="90%" border="0"><tbody><tr><td style="FONT-SIZE: 9pt; LINE-HEIGHT: 12pt" width="100%"><img alt="发贴心情" src="http://www.3doing.net/forums/Skins/Default/topicface/face1.gif" align="absMiddle" border="0" /> <b></b><br /><h3 align="center">AOP与权限控制实现</h3><p align="center"><a href="http://www.jdon.com/aboutme.htm" target="_blank"><font color="#000000">板桥里人</font></a><a href="http://www.jdon.com/" target="_blank" 2004="" 01="" 10=""><font color="#000000">http://www.jdon.com 2004/01/10</font></a></p><p>　　以往在J2EE系统中，访问权限控制系统的实现主要有两种：应用程序实现和J2EE容器实现。</p><p><b>传统的应用程序实现</b></p><p>　　这是最直接的、传统的一种解决方式，通常是在具体方法前加一个权限判断语句，如下：</p><table cellspacing="2" cellpadding="2" width="90%" bgcolor="#cccccc" border="0"><tbody><tr><td>public class ForumFactoryProxy extends ForumFactory {<br />　　......<br />　　public Forum createForum(String name, String description)<br />　　　　throws UnauthorizedException, ForumAlreadyExistsException<br />　　{<br />　　　　if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {<br />　　　　　　Forum newForum = factory.createForum(name, description);<br />　　　　　　return new ForumProxy(newForum, authorization, permissions);<br />　　　　}else {<br />　　　　　　throw new UnauthorizedException();<br />　　　　}<br />　　}<br />　　......<br />}</td></tr></tbody></table><p>　　上述代码是Jive论坛中一段创建论坛功能的代码，在创建论坛前，首先进行权限角色检验，如果当前用户是系统管理员，那么可以实现真正的创建。</p><p>　　这种在具体功能前加入权限操作检验的实现方式有很多缺点：<br />　　1.每个功能类都需要相应的权限检验代码，将程序功能和权限检验混淆在一起，存在紧密的耦合性，扩展修改难度大。<br />　　2.如果类似Jive，以代理模式为每个功能类实现一个相应的代理类，虽然解耦了程序功能和权限检验，但是，从某个角色的权限检验这个切面考虑，涉及具体Proxy类太多，扩展修改难度大。</p><p><b>J2EE容器实现</b></p><p>　　在AOP概念没有诞生前，J2EE规范已经提供了关于权限控制的容器实现标准，这种变迁结果如下图所示：</p><p align="center"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jdon.com/AOPdesign/images/acl.png" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" /></p><p>　　原来需要每个应用程序实现的权限Proxy转为整个容器的Proxy实现，其中JDK1.3以后的动态代理API为这种转换实现提供了技术保证。</p><p>　　非常明显，通过容器实现权限控制验证可以大大简化应用程序的设计，分离了应用系统的权限关注，将权限控制变成了对J2EE容器服务器的配置工作，具体技术细节参考我的书籍《<a href="http://www.jdon.com/mybook/front.htm" target="_blank"><font color="#000000">Java实用系统开发指南</font></a>》第六章。</p><p>　　其实，容器的权限实现也是一种从一个切面来解决问题方式，AOP概念诞生后，权限控制实现由此也带来了两个方向的变化：<br />　　1. J2EE容器级别的权限实现，也就是容器自身的权限实现。<br />　　2. J2EE应用程序级别的权限实现。</p><p>　　权限控制在容器级别实现似乎使得J2EE开发者感觉没有灵活性和可扩展性，其实象JBoss 4.0这样的J2EE容器，由于引入了AOP概念，使得J2EE开发者在自己的应用系统中能够直接操纵容器的一些行为。容器和应用系统由于AOP引入的Aspect切面，变得可以成为一体了。（如果使用BEA的EJBC编辑要浪费多少时间？）</p><p>　　对于J2EE应用系统开发者，能够做到上述境界，必须的条件是对JBoss之类J2EE容器必须有足够的了解，因为这些方式并不是J2EE标准，有可能在移植到新的J2EE容器，这些知识和投入变得无用（也有可能将来J2EE扩展其标准）。</p><p>　　很显然，使用AOP实现J2EE应用系统级别的权限控制，是解决上述移植风险的一个主要方法，但是带来的缺点是必须亲自从零开始做起，耗费时间不会很短。</p><p><b>AOP下的应用程序权限控制实现</b></p><p>　　引入AOP概念后的权限实现已经不是前面Jive实例那样“落后”，我们对这个实例进行重整（Refactorying）如下：</p><p>创建一个Aspect，专门用于权限检查，如下：</p><table cellspacing="2" cellpadding="2" width="90%" border="0"><tbody><tr><td bgcolor="#cccccc"><p><b>private </b><b>static </b><b>aspect </b>PermissionCheckAspect { </p><p>　　<b>private </b><b>pointcut </b>permissionCheckedExecution() :<br />　　 <b>execution </b>( public Forum ForumFactory.createForum(String , String ));</p><p><b>　　<b></b>before </b>() : permissionCheckedExecution() { <br /><b>　　</b><b>　　</b>if !(permissions.get(ForumPermissions.SYSTEM_ADMIN)) {<br />　　　　　　throw new UnauthorizedException();<br />　　　　}<br />　　 <b></b>} </p><p>} </p></td></tr></tbody></table><p>该段代码功能是：当系统运行ForumFactory.createForum方法之前，将首先检查是否有权限操作。</p><p>代码中pointcut触发的条件是createForum方法执行，如果有其它需要系统管理员身份才能执行的方法加入，将写成如下代码：</p><p><b>private </b><b>pointcut </b>permissionCheckedExecution() :<br />　　 <b>execution </b>( public Forum ForumFactory.createForum(String , String )) ||<br /><b>　　 <b></b>execution </b>( public Forum ForumFactory.deleteForum(String , String )) ||<br />　　 <b></b>......<br /><b>　　 <b></b>execution </b>( public Forum ForumFactory.deleteThread(String , String )); </p><p>这些方法陈列比较琐碎，依据AspectJ语法，可以简化如下：<br /><b>private </b><b>pointcut </b>permissionCheckedExecution() :<br />　　 <b>execution </b>( public * ForumFactory .*(..)); </p><p>有兴趣者可以将Jive论坛中相关权限Proxy部分使用AOP重整，另外，由于Jive没有引入角色概念，导致权限和用户HardCode在编码中，如何实现权限和用户解耦，最小限度的降低HardCode量，角色概念在其中起着不可忽视的重要作用。这是另外一个研究课题了。</p></td></tr></tbody></table><table class="tablebody2" style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" width="90%" border="0"><tbody><tr><td style="FONT-SIZE: 9pt; LINE-HEIGHT: 12pt" width="100%"><img alt="发贴心情" src="http://www.3doing.net/forums/Skins/Default/topicface/face1.gif" align="absMiddle" border="0" /> <b></b><br /><h3 align="center">Ioc模式</h3><p align="center"><a href="http://www.jdon.com/aboutme.htm" target="_blank"><font color="#000000">板桥里人</font></a><a href="http://www.jdon.com/" target="_blank" 2004="" 01="" 31=""><font color="#000000">http://www.jdon.com 2004/01/31</font></a></p><p align="left">　　分离关注（ Separation of Concerns : SOC）是Ioc模式和AOP产生最原始动力，通过功能分解可得到关注点，这些关注可以是 组件Components, 方面Aspects或服务Services。</p><p align="left">　　从GoF设计模式中，我们已经习惯一种思维编程方式：Interface Driven Design 接口驱动，接口驱动有很多好处，可以提供不同灵活的子类实现，增加代码稳定和健壮性等等，但是接口一定是需要实现的，也就是如下语句迟早要执行：</p><p align="left">　　AInterface a = new AInterfaceImp(); </p><p align="left">　　AInterfaceImp是接口AInterface的一个子类，Ioc模式可以延缓接口的实现，根据需要实现，有个比喻：接口如同空的模型套，在必要时，需要向模型套注射石膏，这样才能成为一个模型实体，因此，我们将人为控制接口的实现成为“注射”。</p><p align="left">　　Ioc英文为 Inversion of Control，即反转模式，这里有著名的好莱坞理论：你呆着别动，到时我会找你。</p><p align="left">　　其实Ioc模式也是解决调用者和被调用者之间的一种关系，上述AInterface实现语句表明当前是在调用被调用者AInterfaceImp，由于被调用者名称写入了调用者的代码中，这产生了一个接口实现的原罪：彼此联系，调用者和被调用者有紧密联系，在UML中是用依赖 Dependency 表示。</p><p align="left">　　但是这种依赖在分离关注的思维下是不可忍耐的，必须切割，实现调用者和被调用者解耦，新的Ioc模式 Dependency Injection 模式由此产生了， Dependency Injection模式是依赖注射的意思，也就是将依赖先剥离，然后在适当时候再注射进入。</p><p align="left">Ioc模式（Dependency Injection模式）有三种：</p><table cellspacing="2" cellpadding="2" width="100%" border="1"><tbody><tr><td width="20%">第一种类型</td><td width="46%">从JNDI或ServiceManager等获得被调用者，这里类似ServiceLocator模式。</td><td width="34%">1. EJB/J2EE<br />2. Avalon（Apache的一个复杂使用不多的项目）</td></tr><tr><td>第二种类型</td><td>使用JavaBeans的setter方法</td><td>1. Spring Framework,<br />2. WebWork/XWork</td></tr><tr><td>第三种类型</td><td>在构造方法中实现依赖</td><td>1. PicoContainer,<br />2. HiveMind</td></tr></tbody></table><p>　　有过EJB开发经验的人都知道，每个EJB的调用都需要通过JNDI寻找到工厂性质的Home接口，在我的教程<a href="http://www.jdon.com/ejbtur.htm" target="_blank"><font color="#000000">EJB是什么</font></a>章节中，我也是从依赖和工厂模式角度来阐述EJB的使用。</p><p>　　在通常传统情况下，为了实现调用者和被调用者解耦，分离，一般是通过工厂模式实现的，下面将通过比较工厂模式和Ioc模式不同，加深理解Ioc模式。</p><h4>工厂模式和Ioc</h4><p>　　假设有两个类B 和 C：B作为调用者，C是被调用者，在B代码中存在对C的调用：</p><table bordercolor="#cccccc" cellspacing="2" cellpadding="2" width="80%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>public class B{<br />　　 private C comp; <br />　　......<br />} </p></td></tr></tbody></table><p>　　实现comp实例有两种途径：单态工厂模式和Ioc。</p><p>工厂模式实现如下：</p><table bordercolor="#cccccc" cellspacing="2" cellpadding="2" width="87%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>public class B{<br />　　 private C comp; <br />　　private final static MyFactory myFactory = MyFactory.getInstance();</p><p>　　public B(){<br />　　　　this.comp = myFactory.createInstanceOfC();</p><p>　　}<br />　　 public void someMethod(){<br />　　　　this.comp.sayHello();<br />　 } <br />　　......<br />} </p></td></tr></tbody></table><p>特点：</p><ul><li>每次运行时，MyFactory可根据配置文件XML中定义的C子类实现，通过createInstanceOfC()生成C的具体实例。 </li></ul><p>使用Ioc依赖性注射( Dependency Injection )实现Picocontainer如下，B类如同通常POJO类，如下：</p><table bordercolor="#cccccc" cellspacing="2" cellpadding="2" width="87%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>public class B{<br />　　 private C comp; <br />　　public B(C comp){<br />　　　　this.comp = comp;<br />　　 }<br />　　 public void someMethod(){<br />　　　　this.comp.sayHello();<br />　　 }<br />　　......<br />} </p></td></tr></tbody></table><p>假设C接口/类有有一个具体实现CImp类。当客户端调用B时，使用下列代码：</p><table bordercolor="#cccccc" cellspacing="2" cellpadding="2" width="87%" bgcolor="#cccccc" border="0"><tbody><tr><td><p>public class client{<br />　　 public static void main( String[] args ) {<br />　　　　DefaultPicoContainer container = new DefaultPicoContainer();<br />　　　　container.registerComponentImplementation(CImp.class);<br />　　　　container.registerComponentImplementation(B.class);<br />　　　　B b = (B) container.getComponentInstance(B.class);<br />　　　　b.someMethod();<br />　　 }<br />} </p></td></tr></tbody></table><p>　　因此，当客户端调用B时，分别使用工厂模式和Ioc有不同的特点和区别：</p><p>　　主要区别体现在B类的代码，如果使用Ioc，在B类代码中将不需要嵌入任何工厂模式等的代码，因为这些工厂模式其实还是与C有些间接的联系，这样，使用Ioc彻底解耦了B和C之间的联系。</p><p>　　使用Ioc带来的代价是：需要在客户端或其它某处进行B和C之间联系的组装。</p><p>　　所以，Ioc并没有消除B和C之间这样的联系，只是转移了这种联系。<br />　　这种联系转移实际也是一种分离关注，它的影响巨大，它提供了AOP实现的可能。</p><h4>Ioc和AOP</h4><p>　　AOP我们已经知道是一种面向切面的编程方式，由于Ioc解放自由了B类，而且可以向B类实现注射C类具体实现，如果把B类想像成运行时的横向动作，无疑注入C类子类就是AOP中的一种Advice，如下图：</p><p><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jdon.com/AOPdesign/images/aopIoc.png" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" /></p><p>　　通过下列代码说明如何使用Picocontainer实现AOP，该例程主要实现是记录logger功能，通过Picocontainer可以使用简单一行，使所有的应用类的记录功能激活。</p><p>首先编制一个记录接口：</p><table cellspacing="2" cellpadding="2" width="100%" bgcolor="#cccccc" border="0"><tbody><tr bgcolor="#cccccc"><td><p>public interface Logging {</p><p>　　public void enableLogging(Log log);</p><p>}</p></td></tr></tbody></table><p>有一个LogSwitcher类，主要用来激活具体应用中的记录功能：</p><table cellspacing="2" cellpadding="2" width="100%" bgcolor="#cccccc" border="0"><tbody><tr><td>import org.apache.commons.logging.Log;<br />public class LogSwitcher<br />{<br />　 protected Log m_log;<br />　 public void enableLogging(Log log) {<br />　　　　m_log = log;<br />　　　　m_log.info("Logging Enabled");<br />　　}<br />}</td></tr></tbody></table><p>一般的普通应用JavaBeans都可以继承这个类，假设PicoUserManager是一个用户管理类，代码如下：</p><table cellspacing="2" cellpadding="2" width="100%" bgcolor="#cccccc" border="0"><tbody><tr><td>public class PicoUserManager extends LogSwitcher<br />{ 
<p>　　..... //用户管理功能<br />}<br />public class PicoXXXX1Manager extends LogSwitcher<br />{ </p><p>　　..... //业务功能<br />}<br />public class PicoXXXX2Manager extends LogSwitcher<br />{ </p><p>　　..... //业务功能<br />}</p></td></tr></tbody></table><p>注意LogSwitcher中Log实例是由外界赋予的，也就是说即将被外界注射进入，下面看看使用Picocontainer是如何注射Log的具体实例的。</p><table cellspacing="2" cellpadding="2" width="100%" bgcolor="#cccccc" border="0"><tbody><tr><td><p><br />DefaultPicoContainer container = new DefaultPicoContainer();<br />container.registerComponentImplementation(PicoUserManager.class);<br />container.registerComponentImplementation(PicoXXXX1Manager.class); <br />container.registerComponentImplementation(PicoXXXX2Manager.class);<br />..... </p><p>Logging logging = (Logging) container.getComponentMulticaster();<br /><br />logging.enableLogging(new SimpleLog("pico"));//激活log<br /><br /></p></td></tr></tbody></table><p>　　由上代码可见，通过使用简单一行logging.enableLogging()方法使所有的应用类的记录功能激活。这是不是类似AOP的advice实现？</p><p>　　总之，使用Ioc模式，可以不管将来具体实现，完全在一个抽象层次进行描述和技术架构，因此，Ioc模式可以为容器、框架之类的软件实现提供了具体的实现手段，属于架构技术中一种重要的模式应用。J道的<a href="http://www.jdon.com/product.htm" target="_blank"><font color="#000000">JdonSD框架</font></a>也使用了Ioc模式。</p></td></tr></tbody></table><br /><br /><br /><img src ="http://www.blogjava.net/TrampEagle/aggbug/49410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-06-01 09:42 <a href="http://www.blogjava.net/TrampEagle/articles/49410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Java实现断点续传(HTTP)转</title><link>http://www.blogjava.net/TrampEagle/articles/48978.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 30 May 2006 06:20:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48978.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48978.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48978.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48978.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48978.html</trackback:ping><description><![CDATA[引自：<a href="http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=432&amp;page=2">http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=432&amp;page=2</a><br /><br /><p>作者：钟华<a><br /><br />(一)断点续传的原理</a><br />其实断点续传的原理很简单，就是在Http的请求上和一般的下载有所不同而已。<br />打个比方，浏览器请求服务器上的一个文时，所发出的请求如下：<br />假设服务器域名为wwww.sjtu.edu.cn，文件名为down.zip。<br />GET /down.zip HTTP/1.1<br />Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-<br />excel, application/msword, application/vnd.ms-powerpoint, */*<br />Accept-Language: zh-cn<br />Accept-Encoding: gzip, deflate<br />User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)<br />Connection: Keep-Alive<br /></p><p>服务器收到请求后，按要求寻找请求的文件，提取文件的信息，然后返回给浏览器，返回信息如下：<br /></p><blockquote>200<br />Content-Length=106786028<br />Accept-Ranges=bytes<br />Date=Mon, 30 Apr 2001 12:56:11 GMT<br />ETag=W/"02ca57e173c11:95b"<br />Content-Type=application/octet-stream<br />Server=Microsoft-IIS/5.0<br />Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT<br /></blockquote><br /><br /><br /><p>所谓断点续传，也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给<br />Web服务器的时候要多加一条信息--从哪里开始。<br />下面是用自己编的一个"浏览器"来传递请求信息给Web服务器，要求从2000070字节开始。<br />GET /down.zip HTTP/1.0<br />User-Agent: NetFox<br />RANGE: bytes=2000070-<br />Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2<br /></p><p>仔细看一下就会发现多了一行RANGE: bytes=2000070-<br />这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传，前面的字节不用传了。<br />服务器收到这个请求以后，返回的信息如下：<br />206<br />Content-Length=106786028<br />Content-Range=bytes 2000070-106786027/106786028<br />Date=Mon, 30 Apr 2001 12:55:20 GMT<br />ETag=W/"02ca57e173c11:95b"<br />Content-Type=application/octet-stream<br />Server=Microsoft-IIS/5.0<br />Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT<br /></p><p>和前面服务器返回的信息比较一下，就会发现增加了一行：<br />Content-Range=bytes 2000070-106786027/106786028<br />返回的代码也改为206了，而不再是200了。<br /></p><p>知道了以上原理，就可以进行断点续传的编程了。<br /></p><p><a>(二)Java实现断点续传的关键几点</a></p><ol><li>(1)用什么方法实现提交RANGE: bytes=2000070-。<br />当然用最原始的Socket是肯定能完成的，不过那样太费事了，其实Java的net包中提供了这种功能。代码如下：<br /><br />URL url = new URL("http://www.sjtu.edu.cn/down.zip");<br />HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();<br /><br /><br />//设置User-Agent<br />httpConnection.setRequestProperty("User-Agent","NetFox");<br />//设置断点续传的开始位置<br />httpConnection.setRequestProperty("RANGE","bytes=2000070");<br />//获得输入流<br />InputStream input = httpConnection.getInputStream();<br /><br /><br /><br /><p>从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。<br />大家看，其实断点续传用Java实现起来还是很简单的吧。<br />接下来要做的事就是怎么保存获得的流到文件中去了。<br /></p></li><li>保存文件采用的方法。<br />我采用的是IO包中的RandAccessFile类。<br />操作相当简单，假设从2000070处开始保存文件，代码如下：<br />RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");<br />long nPos = 2000070;<br />//定位文件指针到nPos位置<br />oSavedFile.seek(nPos);<br />byte[] b = new byte[1024];<br />int nRead;<br />//从输入流中读入字节流，然后写到文件中<br />while((nRead=input.read(b,0,1024)) &gt; 0)<br />{<br />oSavedFile.write(b,0,nRead);<br />}<br /></li></ol><p>怎么样，也很简单吧。<br />接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。<br /></p><p><b>(三)断点续传内核的实现</b><br />主要用了6个类，包括一个测试类。<br />SiteFileFetch.java负责整个文件的抓取，控制内部线程(FileSplitterFetch类)。<br />FileSplitterFetch.java负责部分文件的抓取。<br />FileAccess.java负责文件的存储。<br />SiteInfoBean.java要抓取的文件的信息，如文件保存的目录，名字，抓取文件的URL等。<br />Utility.java工具类，放一些简单的方法。<br />TestMethod.java测试类。<br /></p><p>下面是源程序：</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>/*<br />**SiteFileFetch.java<br />*/<br />package NetFox;<br />import java.io.*;<br />import java.net.*;<br /><br /><br />public class SiteFileFetch extends Thread {<br /><br /><br />SiteInfoBean siteInfoBean = null; //文件信息Bean<br />long[] nStartPos; //开始位置<br />long[] nEndPos; //结束位置<br />FileSplitterFetch[] fileSplitterFetch; //子线程对象<br />long nFileLength; //文件长度<br />boolean bFirst = true; //是否第一次取文件<br />boolean bStop = false; //停止标志<br />File tmpFile; //文件下载的临时信息<br />DataOutputStream output; //输出到文件的输出流<br /><br /><br />public SiteFileFetch(SiteInfoBean bean) throws IOException<br />{<br />siteInfoBean = bean;<br />//tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));<br />tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");<br />if(tmpFile.exists ())<br />{<br />bFirst = false;<br />read_nPos();<br />}<br />else<br />{<br />nStartPos = new long[bean.getNSplitter()];<br />nEndPos = new long[bean.getNSplitter()];<br />}<br /><br /><br /><br />}<br /><br /><br />public void run()<br />{<br />//获得文件长度<br />//分割文件<br />//实例FileSplitterFetch<br />//启动FileSplitterFetch线程<br />//等待子线程返回<br />try{<br />if(bFirst)<br />{<br />nFileLength = getFileSize();<br />if(nFileLength == -1)<br />{<br />System.err.println("File Length is not known!");<br />}<br />else if(nFileLength == -2)<br />{<br />System.err.println("File is not access!");<br />}<br />else<br />{<br />for(int i=0;i&lt;nStartPos.length;i++)<br />{<br />nStartPos[i] = (long)(i*(nFileLength/nStartPos.length));<br />}<br />for(int i=0;i&lt;nEndPos.length-1;i++)<br />{<br />nEndPos[i] = nStartPos[i+1];<br />}<br />nEndPos[nEndPos.length-1] = nFileLength;<br />}<br />}<br /><br /><br />//启动子线程<br />fileSplitterFetch = new FileSplitterFetch[nStartPos.length];<br />for(int i=0;i&lt;nStartPos.length;i++)<br />{<br />fileSplitterFetch[i] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),<br />siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),<br />nStartPos[i],nEndPos[i],i);<br />Utility.log("Thread " + i + " , nStartPos = " + nStartPos[i] + ", nEndPos = " + nEndPos[i]);<br />fileSplitterFetch[i].start();<br />}<br />// fileSplitterFetch[nPos.length-1] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),<br />siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1);<br />// Utility.log("Thread " + (nPos.length-1) + " , nStartPos = " + nPos[nPos.length-1] + ",<br />nEndPos = " + nFileLength);<br />// fileSplitterFetch[nPos.length-1].start();<br /><br /><br />//等待子线程结束<br />//int count = 0;<br />//是否结束while循环<br />boolean breakWhile = false;<br /><br /><br />while(!bStop)<br />{<br />write_nPos();<br />Utility.sleep(500);<br />breakWhile = true;<br /><br /><br />for(int i=0;i&lt;nStartPos.length;i++)<br />{<br />if(!fileSplitterFetch[i].bDownOver)<br />{<br />breakWhile = false;<br />break;<br />}<br />}<br />if(breakWhile)<br />break;<br /><br /><br />//count++;<br />//if(count&gt;4)<br />// siteStop();<br />}<br /><br /><br />System.err.println("文件下载结束！");<br />}<br />catch(Exception e){e.printStackTrace ();}<br />}<br /><br /><br />//获得文件长度<br />public long getFileSize()<br />{<br />int nFileLength = -1;<br />try{<br />URL url = new URL(siteInfoBean.getSSiteURL());<br />HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();<br />httpConnection.setRequestProperty("User-Agent","NetFox");<br /><br /><br />int responseCode=httpConnection.getResponseCode();<br />if(responseCode&gt;=400)<br />{<br />processErrorCode(responseCode);<br />return -2; //-2 represent access is error<br />}<br /><br /><br />String sHeader;<br /><br /><br />for(int i=1;;i++)<br />{<br />//DataInputStream in = new DataInputStream(httpConnection.getInputStream ());<br />//Utility.log(in.readLine());<br />sHeader=httpConnection.getHeaderFieldKey(i);<br />if(sHeader!=null)<br />{<br />if(sHeader.equals("Content-Length"))<br />{<br />nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));<br />break;<br />}<br />}<br />else<br />break;<br />}<br />}<br />catch(IOException e){e.printStackTrace ();}<br />catch(Exception e){e.printStackTrace ();}<br /><br /><br />Utility.log(nFileLength);<br /><br /><br />return nFileLength;<br />}<br /><br /><br />//保存下载信息（文件指针位置）<br />private void write_nPos()<br />{<br />try{<br />output = new DataOutputStream(new FileOutputStream(tmpFile));<br />output.writeInt(nStartPos.length);<br />for(int i=0;i&lt;nStartPos.length;i++)<br />{<br />// output.writeLong(nPos[i]);<br />output.writeLong(fileSplitterFetch[i].nStartPos);<br />output.writeLong(fileSplitterFetch[i].nEndPos);<br />}<br />output.close();<br />}<br />catch(IOException e){e.printStackTrace ();}<br />catch(Exception e){e.printStackTrace ();}<br />}<br /><br /><br />//读取保存的下载信息（文件指针位置）<br />private void read_nPos()<br />{<br />try{<br />DataInputStream input = new DataInputStream(new FileInputStream(tmpFile));<br />int nCount = input.readInt();<br />nStartPos = new long[nCount];<br />nEndPos = new long[nCount];<br />for(int i=0;i&lt;nStartPos.length;i++)<br />{<br />nStartPos[i] = input.readLong();<br />nEndPos[i] = input.readLong();<br />}<br />input.close();<br />}<br />catch(IOException e){e.printStackTrace ();}<br />catch(Exception e){e.printStackTrace ();}<br />}<br /><br /><br />private void processErrorCode(int nErrorCode)<br />{<br />System.err.println("Error Code : " + nErrorCode);<br />}<br /><br /><br />//停止文件下载<br />public void siteStop()<br />{<br />bStop = true;<br />for(int i=0;i&lt;nStartPos.length;i++)<br />fileSplitterFetch[i].splitterStop();<br /><br /><br />}<br />}<br />/*<br />**FileSplitterFetch.java<br />*/<br />package NetFox;<br /><br /><br />import java.io.*;<br />import java.net.*;<br /><br /><br />public class FileSplitterFetch extends Thread {<br /><br /><br />String sURL; //File URL<br />long nStartPos; //File Snippet Start Position<br />long nEndPos; //File Snippet End Position<br />int nThreadID; //Thread's ID<br />boolean bDownOver = false; //Downing is over<br />boolean bStop = false; //Stop identical<br />FileAccessI fileAccessI = null; //File Access interface<br /><br /><br />public FileSplitterFetch(String sURL,String sName,long nStart,long nEnd,int id) throws IOException<br />{<br />this.sURL = sURL;<br />this.nStartPos = nStart;<br />this.nEndPos = nEnd;<br />nThreadID = id;<br />fileAccessI = new FileAccessI(sName,nStartPos);<br />}<br /><br /><br />public void run()<br />{<br />while(nStartPos &lt; nEndPos &amp;&amp; !bStop)<br />{<br /><br /><br />try{<br />URL url = new URL(sURL);<br />HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();<br />httpConnection.setRequestProperty("User-Agent","NetFox");<br />String sProperty = "bytes="+nStartPos+"-";<br />httpConnection.setRequestProperty("RANGE",sProperty);<br />Utility.log(sProperty);<br /><br /><br />InputStream input = httpConnection.getInputStream();<br />//logResponseHead(httpConnection);<br /><br /><br />byte[] b = new byte[1024];<br />int nRead;<br />while((nRead=input.read(b,0,1024)) &gt; 0 &amp;&amp; nStartPos &lt; nEndPos &amp;&amp; !bStop)<br />{<br />nStartPos += fileAccessI.write(b,0,nRead);<br />//if(nThreadID == 1)<br />// Utility.log("nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);<br />}<br /><br /><br />Utility.log("Thread " + nThreadID + " is over!");<br />bDownOver = true;<br />//nPos = fileAccessI.write (b,0,nRead);<br />}<br />catch(Exception e){e.printStackTrace ();}<br />}<br />}<br /><br /><br />//打印回应的头信息<br />public void logResponseHead(HttpURLConnection con)<br />{<br />for(int i=1;;i++)<br />{<br />String header=con.getHeaderFieldKey(i);<br />if(header!=null)<br />//responseHeaders.put(header,httpConnection.getHeaderField(header));<br />Utility.log(header+" : "+con.getHeaderField(header));<br />else<br />break;<br />}<br />}<br /><br /><br />public void splitterStop()<br />{<br />bStop = true;<br />}<br /><br /><br />}<br /><br /><br />/*<br />**FileAccess.java<br />*/<br />package NetFox;<br />import java.io.*;<br /><br /><br />public class FileAccessI implements Serializable{<br /><br /><br />RandomAccessFile oSavedFile;<br />long nPos;<br /><br /><br />public FileAccessI() throws IOException<br />{<br />this("",0);<br />}<br /><br /><br />public FileAccessI(String sName,long nPos) throws IOException<br />{<br />oSavedFile = new RandomAccessFile(sName,"rw");<br />this.nPos = nPos;<br />oSavedFile.seek(nPos);<br />}<br /><br /><br />public synchronized int write(byte[] b,int nStart,int nLen)<br />{<br />int n = -1;<br />try{<br />oSavedFile.write(b,nStart,nLen);<br />n = nLen;<br />}<br />catch(IOException e)<br />{<br />e.printStackTrace ();<br />}<br /><br /><br />return n;<br />}<br /><br /><br />}<br /><br /><br />/*<br />**SiteInfoBean.java<br />*/<br />package NetFox;<br /><br /><br />public class SiteInfoBean {<br /><br /><br />private String sSiteURL; //Site's URL<br />private String sFilePath; //Saved File's Path<br />private String sFileName; //Saved File's Name<br />private int nSplitter; //Count of Splited Downloading File<br /><br /><br />public SiteInfoBean()<br />{<br />//default value of nSplitter is 5<br />this("","","",5);<br />}<br /><br /><br />public SiteInfoBean(String sURL,String sPath,String sName,int nSpiltter)<br />{<br />sSiteURL= sURL;<br />sFilePath = sPath;<br />sFileName = sName;<br />this.nSplitter = nSpiltter;<br /><br /><br />}<br /><br /><br />public String getSSiteURL()<br />{<br />return sSiteURL;<br />}<br /><br /><br />public void setSSiteURL(String value)<br />{<br />sSiteURL = value;<br />}<br /><br /><br />public String getSFilePath()<br />{<br />return sFilePath;<br />}<br /><br /><br />public void setSFilePath(String value)<br />{<br />sFilePath = value;<br />}<br /><br /><br />public String getSFileName()<br />{<br />return sFileName;<br />}<br /><br /><br />public void setSFileName(String value)<br />{<br />sFileName = value;<br />}<br /><br /><br />public int getNSplitter()<br />{<br />return nSplitter;<br />}<br /><br /><br />public void setNSplitter(int nCount)<br />{<br />nSplitter = nCount;<br />}<br />}<br /><br /><br />/*<br />**Utility.java<br />*/<br />package NetFox;<br /><br /><br />public class Utility {<br /><br /><br />public Utility()<br />{<br /><br /><br />}<br /><br /><br />public static void sleep(int nSecond)<br />{<br />try{<br />Thread.sleep(nSecond);<br />}<br />catch(Exception e)<br />{<br />e.printStackTrace ();<br />}<br />}<br /><br /><br />public static void log(String sMsg)<br />{<br />System.err.println(sMsg);<br />}<br /><br /><br />public static void log(int sMsg)<br />{<br />System.err.println(sMsg);<br />}<br />}<br /><br /><br />/*<br />**TestMethod.java<br />*/<br />package NetFox;<br /><br /><br />public class TestMethod {<br /><br /><br />public TestMethod()<br />{ ///xx/weblogic60b2_win.exe<br />try{<br />SiteInfoBean bean = new SiteInfoBean("http://localhost/xx/weblogic60b2_win.exe","L:\\temp","weblogic60b2_win.exe",5);<br />//SiteInfoBean bean = new SiteInfoBean("http://localhost:8080/down.zip","L:\\temp","weblogic60b2_win.exe",5);<br />SiteFileFetch fileFetch = new SiteFileFetch(bean);<br />fileFetch.start();<br />}<br />catch(Exception e){e.printStackTrace ();}<br /><br /><br />}<br /><br /><br />public static void main(String[] args)<br />{<br />new TestMethod();<br />}<br />}<br /></code></pre></td></tr></tbody></table><img src ="http://www.blogjava.net/TrampEagle/aggbug/48978.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-30 14:20 <a href="http://www.blogjava.net/TrampEagle/articles/48978.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简易的TTS程序</title><link>http://www.blogjava.net/TrampEagle/articles/48974.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 30 May 2006 06:11:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48974.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48974.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48974.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48974.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48974.html</trackback:ping><description><![CDATA[
		<p>原文引自：<a href="http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=567&amp;page=2">http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=567&amp;page=2</a><br /><br />1。应用程序RobTTSApp.java</p>
		<p>import java.awt.*; <br />import javax.swing.*; </p>
		<p>/** <br />* <br />* &lt;p&gt;Title: MyJava&lt;/p&gt; <br />* &lt;p&gt;Description: &lt;/p&gt; <br />* &lt;p&gt;Copyright: Copyright (c) 2004－2008&lt;/p&gt; <br />* &lt;p&gt;Company: b9527&lt;/p&gt; <br />* @author robertb9527 <br />* @version 1.0 <br />*/ <br />public class RobTTSApp { <br />  boolean packFrame = false; </p>
		<p>  //Construct the application <br />  public RobTTSApp() { <br />    TTSFrame frame = new TTSFrame(); <br />    //Validate frames that have preset sizes <br />    //Pack frames that have useful preferred size info, e.g. from their layout <br />    if (packFrame) { <br />      frame.pack(); <br />    } <br />    else { <br />      frame.validate(); <br />    } <br />    //Center the window <br />    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); <br />    Dimension frameSize = frame.getSize(); <br />    if (frameSize.height &gt; screenSize.height) { <br />      frameSize.height = screenSize.height; <br />    } <br />    if (frameSize.width &gt; screenSize.width) { <br />      frameSize.width = screenSize.width; <br />    } <br />    frame.setLocation( (screenSize.width - frameSize.width) / 2, <br />                      (screenSize.height - frameSize.height) / 2); <br />    frame.setVisible(true); <br />  } </p>
		<p>  //Main method <br />  public static void main(String[] args) { <br />    try { <br />      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); <br />    } <br />    catch (Exception e) { <br />      e.printStackTrace(); <br />    } <br />    new RobTTSApp(); <br />  } <br />} </p>
		<p>2。窗体TTSFrame.java <br /></p>
		<p>import java.util.*; </p>
		<p>import java.awt.*; <br />import java.awt.event.*; <br />import javax.swing.*; </p>
		<p>import com.sun.speech.freetts.*; </p>
		<p>/** <br />* <br />* &lt;p&gt;Title: MyJava&lt;/p&gt; <br />* &lt;p&gt;Description: &lt;/p&gt; <br />* &lt;p&gt;Copyright: Copyright (c) 2004－2008&lt;/p&gt; <br />* &lt;p&gt;Company: b9527&lt;/p&gt; <br />* @author robertb9527 <br />* @version 1.0 <br />*/ <br />public class TTSFrame <br />    extends JFrame { <br />  JPanel contentPane; <br />  JScrollPane jScrollPane1 = new JScrollPane(); <br />  JScrollPane jScrollPane2 = new JScrollPane(); <br />  JTextArea from = new JTextArea(); <br />  JTextArea to = new JTextArea(); <br />  JButton jButton1 = new JButton(); <br />  JButton jButton2 = new JButton(); <br />  JLabel jLabel1 = new JLabel(); </p>
		<p>  //Construct the frame <br />  public TTSFrame() { <br />    enableEvents(AWTEvent.WINDOW_EVENT_MASK); <br />    try { <br />      jbInit(); <br />    } <br />    catch (Exception e) { <br />      e.printStackTrace(); <br />    } <br />  } </p>
		<p>  //Component initialization <br />  private void jbInit() throws Exception { <br />    contentPane = (JPanel)this.getContentPane(); <br />    contentPane.setLayout(null); <br />    this.setSize(new Dimension(479, 406)); <br />    this.setTitle("robTTS"); <br />    jScrollPane1.setBounds(new Rectangle(108, 25, 282, 124)); <br />    jScrollPane2.setBounds(new Rectangle(108, 172, 282, 122)); <br />    jButton1.setBounds(new Rectangle(14, 176, 74, 27)); <br />    jButton1.setText("转换"); <br />    jButton1.addActionListener(new TTSFrame_jButton1_actionAdapter(this)); <br />    jButton2.setBounds(new Rectangle(194, 321, 76, 25)); <br />    jButton2.setText("朗读"); <br />    jButton2.addActionListener(new TTSFrame_jButton2_actionAdapter(this)); <br />    jLabel1.setText("输入原文："); <br />    jLabel1.setBounds(new Rectangle(20, 26, 71, 31)); <br />    from.setText(""); <br />    to.setText(""); </p>
		<p>    from.setLineWrap(true); <br />    contentPane.add(jLabel1, null); <br />    contentPane.add(jButton1, null); <br />    contentPane.add(jScrollPane1, null); <br />    contentPane.add(jScrollPane2, null); <br />    contentPane.add(jButton2, null); <br />    jScrollPane2.getViewport().add(to, null); <br />    jScrollPane1.getViewport().add(from, null); <br />  } </p>
		<p>  //Overridden so we can exit when window is closed <br />  protected void processWindowEvent(WindowEvent e) { <br />    super.processWindowEvent(e); <br />    if (e.getID() == WindowEvent.WINDOW_CLOSING) { <br />      System.exit(0); <br />    } <br />  } </p>
		<p>  void jButton1_actionPerformed(ActionEvent e) { <br />    if (from.getText().length() == 0) { <br />      JOptionPane.showMessageDialog(from, "输入原文窗体框为空!", "Warning", <br />                                    JOptionPane.WARNING_MESSAGE); <br />    } <br />    String text = from.getText(); <br />    to.setText(TTSKernel.getFullSpell(text)); <br />    // c.pack(); <br />  } </p>
		<p>  void jButton2_actionPerformed(ActionEvent e) { <br />    String textTo = to.getText(); </p>
		<p>    String voiceName = "kevin16"; <br />    System.out.println(); <br />    System.out.println("使用声音: " + voiceName); </p>
		<p>    /* The VoiceManager manages all the voices for FreeTTS. <br />     */ <br />    VoiceManager voiceManager = VoiceManager.getInstance(); <br />    Voice helloVoice = voiceManager.getVoice(voiceName); </p>
		<p>    if (helloVoice == null) { <br />      System.err.println( <br />          "找不到你所指定的声音. " <br />          + voiceName + "请选择另一种."); <br />      System.exit(1); <br />    } </p>
		<p>    /* Allocates the resources for the voice. <br />     */ <br />    helloVoice.allocate(); <br />    Locale a = helloVoice.getLocale(); <br />    System.out.println(a.getLanguage()); <br />    /* Synthesize speech. <br />     */ <br />    helloVoice.speak(textTo); <br />    /* Clean up and leave. <br />     */ <br />    helloVoice.deallocate(); </p>
		<p>  } </p>
		<p>} </p>
		<p>class TTSFrame_jButton1_actionAdapter <br />    implements java.awt.event.ActionListener { <br />  TTSFrame adaptee; </p>
		<p>  TTSFrame_jButton1_actionAdapter(TTSFrame adaptee) { <br />    this.adaptee = adaptee; <br />  } </p>
		<p>  public void actionPerformed(ActionEvent e) { <br />    adaptee.jButton1_actionPerformed(e); <br />  } <br />} </p>
		<p>class TTSFrame_jButton2_actionAdapter <br />    implements java.awt.event.ActionListener { <br />  TTSFrame adaptee; </p>
		<p>  TTSFrame_jButton2_actionAdapter(TTSFrame adaptee) { <br />    this.adaptee = adaptee; <br />  } </p>
		<p>  public void actionPerformed(ActionEvent e) { <br />    adaptee.jButton2_actionPerformed(e); <br />  } <br />} </p>
		<p>3。汉字转拼音TTSChange.java</p>
		<p>import java.util.*; </p>
		<p>/** <br />* <br />* &lt;p&gt;Title: MyJava&lt;/p&gt; <br />* &lt;p&gt;Description: &lt;/p&gt; <br />* &lt;p&gt;Copyright: Copyright (c) 2004－2008&lt;/p&gt; <br />* &lt;p&gt;Company: b9527&lt;/p&gt; <br />* @author robertb9527 <br />* @version 1.0 <br />*/ <br />public class TTSChange { <br />  private static LinkedHashMap spellMap = null; </p>
		<p>  static { <br />    if (spellMap == null) { <br />      spellMap = new LinkedHashMap(400); <br />    } <br />    initialize(); <br />    System.out.println("转换准备完成。"); <br />  } </p>
		<p>  private TTSKernel() { <br />  } </p>
		<p>  private static void spellPut(String spell, int ascii) { <br />    spellMap.put(spell, new Integer(ascii)); <br />  } </p>
		<p>  private static void initialize() { <br />    spellPut("a", -20319); <br />    spellPut("ai", -20317); <br />    spellPut("an", -20304); <br />    spellPut("ang", -20295); <br />    spellPut("ao", -20292); <br />    spellPut("ba", -20283); <br />    spellPut("bai", -20265); <br />    spellPut("ban", -20257); <br />    spellPut("bang", -20242); <br />    spellPut("bao", -20230); <br />    spellPut("bei", -20051); <br />    spellPut("ben", -20036); <br />    spellPut("beng", -20032); <br />    spellPut("bi", -20026); <br />    spellPut("bian", -20002); <br />    spellPut("biao", -19990); <br />    spellPut("bie", -19986); <br />    spellPut("bin", -19982); <br />    spellPut("bing", -19976); <br />    spellPut("bo", -19805); <br />    spellPut("bu", -19784); <br />    spellPut("ca", -19775); <br />    spellPut("cai", -19774); <br />    spellPut("can", -19763); <br />    spellPut("cang", -19756); <br />    spellPut("cao", -19751); <br />    spellPut("ce", -19746); <br />    spellPut("ceng", -19741); <br />    spellPut("cha", -19739); <br />    spellPut("chai", -19728); <br />    spellPut("chan", -19725); <br />    spellPut("chang", -19715); <br />    spellPut("chao", -19540); <br />    spellPut("che", -19531); <br />    spellPut("chen", -19525); <br />    spellPut("cheng", -19515); <br />    spellPut("chi", -19500); <br />    spellPut("chong", -19484); <br />    spellPut("chou", -19479); <br />    spellPut("chu", -19467); <br />    spellPut("chuai", -19289); <br />    spellPut("chuan", -19288); <br />    spellPut("chuang", -19281); <br />    spellPut("chui", -19275); <br />    spellPut("chun", -19270); <br />    spellPut("chuo", -19263); <br />    spellPut("ci", -19261); <br />    spellPut("cong", -19249); <br />    spellPut("cou", -19243); <br />    spellPut("cu", -19242); <br />    spellPut("cuan", -19238); <br />    spellPut("cui", -19235); <br />    spellPut("cun", -19227); <br />    spellPut("cuo", -19224); <br />    spellPut("da", -19218); <br />    spellPut("dai", -19212); <br />    spellPut("dan", -19038); <br />    spellPut("dang", -19023); <br />    spellPut("dao", -19018); <br />    spellPut("de", -19006); <br />    spellPut("deng", -19003); <br />    spellPut("di", -18996); <br />    spellPut("dian", -18977); <br />    spellPut("diao", -18961); <br />    spellPut("die", -18952); <br />    spellPut("ding", -18783); <br />    spellPut("diu", -18774); <br />    spellPut("dong", -18773); <br />    spellPut("dou", -18763); <br />    spellPut("du", -18756); <br />    spellPut("duan", -18741); <br />    spellPut("dui", -18735); <br />    spellPut("dun", -18731); <br />    spellPut("duo", -18722); <br />    spellPut("e", -18710); <br />    spellPut("en", -18697); <br />    spellPut("er", -18696); <br />    spellPut("fa", -18526); <br />    spellPut("fan", -18518); <br />    spellPut("fang", -18501); <br />    spellPut("fei", -18490); <br />    spellPut("fen", -18478); <br />    spellPut("feng", -18463); <br />    spellPut("fo", -18448); <br />    spellPut("fou", -18447); <br />    spellPut("fu", -18446); <br />    spellPut("ga", -18239); <br />    spellPut("gai", -18237); <br />    spellPut("gan", -18231); <br />    spellPut("gang", -18220); <br />    spellPut("gao", -18211); <br />    spellPut("ge", -18201); <br />    spellPut("gei", -18184); <br />    spellPut("gen", -18183); <br />    spellPut("geng", -18181); <br />    spellPut("gong", -18012); <br />    spellPut("gou", -17997); <br />    spellPut("gu", -17988); <br />    spellPut("gua", -17970); <br />    spellPut("guai", -17964); <br />    spellPut("guan", -17961); <br />    spellPut("guang", -17950); <br />    spellPut("gui", -17947); <br />    spellPut("gun", -17931); <br />    spellPut("guo", -17928); <br />    spellPut("ha", -17922); <br />    spellPut("hai", -17759); <br />    spellPut("han", -17752); <br />    spellPut("hang", -17733); <br />    spellPut("hao", -17730); <br />    spellPut("he", -17721); <br />    spellPut("hei", -17703); <br />    spellPut("hen", -17701); <br />    spellPut("heng", -17697); <br />    spellPut("hong", -17692); <br />    spellPut("hou", -17683); <br />    spellPut("hu", -17676); <br />    spellPut("hua", -17496); <br />    spellPut("huai", -17487); <br />    spellPut("huan", -17482); <br />    spellPut("huang", -17468); <br />    spellPut("hui", -17454); <br />    spellPut("hun", -17433); <br />    spellPut("huo", -17427); <br />    spellPut("ji", -17417); <br />    spellPut("jia", -17202); <br />    spellPut("jian", -17185); <br />    spellPut("jiang", -16983); <br />    spellPut("jiao", -16970); <br />    spellPut("jie", -16942); <br />    spellPut("jin", -16915); <br />    spellPut("jing", -16733); <br />    spellPut("jiong", -16708); <br />    spellPut("jiu", -16706); <br />    spellPut("ju", -16689); <br />    spellPut("juan", -16664); <br />    spellPut("jue", -16657); <br />    spellPut("jun", -16647); <br />    spellPut("ka", -16474); <br />    spellPut("kai", -16470); <br />    spellPut("kan", -16465); <br />    spellPut("kang", -16459); <br />    spellPut("kao", -16452); <br />    spellPut("ke", -16448); <br />    spellPut("ken", -16433); <br />    spellPut("keng", -16429); <br />    spellPut("kong", -16427); <br />    spellPut("kou", -16423); <br />    spellPut("ku", -16419); <br />    spellPut("kua", -16412); <br />    spellPut("kuai", -16407); <br />    spellPut("kuan", -16403); <br />    spellPut("kuang", -16401); <br />    spellPut("kui", -16393); <br />    spellPut("kun", -16220); <br />    spellPut("kuo", -16216); <br />    spellPut("la", -16212); <br />    spellPut("lai", -16205); <br />    spellPut("lan", -16202); <br />    spellPut("lang", -16187); <br />    spellPut("lao", -16180); <br />    spellPut("le", -16171); <br />    spellPut("lei", -16169); <br />    spellPut("leng", -16158); <br />    spellPut("li", -16155); <br />    spellPut("lia", -15959); <br />    spellPut("lian", -15958); <br />    spellPut("liang", -15944); <br />    spellPut("liao", -15933); <br />    spellPut("lie", -15920); <br />    spellPut("lin", -15915); <br />    spellPut("ling", -15903); <br />    spellPut("liu", -15889); <br />    spellPut("long", -15878); <br />    spellPut("lou", -15707); <br />    spellPut("lu", -15701); <br />    spellPut("lv", -15681); <br />    spellPut("luan", -15667); <br />    spellPut("lue", -15661); <br />    spellPut("lun", -15659); <br />    spellPut("luo", -15652); <br />    spellPut("ma", -15640); <br />    spellPut("mai", -15631); <br />    spellPut("man", -15625); <br />    spellPut("mang", -15454); <br />    spellPut("mao", -15448); <br />    spellPut("me", -15436); <br />    spellPut("mei", -15435); <br />    spellPut("men", -15419); <br />    spellPut("meng", -15416); <br />    spellPut("mi", -15408); <br />    spellPut("mian", -15394); <br />    spellPut("miao", -15385); <br />    spellPut("mie", -15377); <br />    spellPut("min", -15375); <br />    spellPut("ming", -15369); <br />    spellPut("miu", -15363); <br />    spellPut("mo", -15362); <br />    spellPut("mou", -15183); <br />    spellPut("mu", -15180); <br />    spellPut("na", -15165); <br />    spellPut("nai", -15158); <br />    spellPut("nan", -15153); <br />    spellPut("nang", -15150); <br />    spellPut("nao", -15149); <br />    spellPut("ne", -15144); <br />    spellPut("nei", -15143); <br />    spellPut("nen", -15141); <br />    spellPut("neng", -15140); <br />    spellPut("ni", -15139); <br />    spellPut("nian", -15128); <br />    spellPut("niang", -15121); <br />    spellPut("niao", -15119); <br />    spellPut("nie", -15117); <br />    spellPut("nin", -15110); <br />    spellPut("ning", -15109); <br />    spellPut("niu", -14941); <br />    spellPut("nong", -14937); <br />    spellPut("nu", -14933); <br />    spellPut("nv", -14930); <br />    spellPut("nuan", -14929); <br />    spellPut("nue", -14928); <br />    spellPut("nuo", -14926); <br />    spellPut("o", -14922); <br />    spellPut("ou", -14921); <br />    spellPut("pa", -14914); <br />    spellPut("pai", -14908); <br />    spellPut("pan", -14902); <br />    spellPut("pang", -14894); <br />    spellPut("pao", -14889); <br />    spellPut("pei", -14882); <br />    spellPut("pen", -14873); <br />    spellPut("peng", -14871); <br />    spellPut("pi", -14857); <br />    spellPut("pian", -14678); <br />    spellPut("piao", -14674); <br />    spellPut("pie", -14670); <br />    spellPut("pin", -14668); <br />    spellPut("ping", -14663); <br />    spellPut("po", -14654); <br />    spellPut("pu", -14645); <br />    spellPut("qi", -14630); <br />    spellPut("qia", -14594); <br />    spellPut("qian", -14429); <br />    spellPut("qiang", -14407); <br />    spellPut("qiao", -14399); <br />    spellPut("qie", -14384); <br />    spellPut("qin", -14379); <br />    spellPut("qing", -14368); <br />    spellPut("qiong", -14355); <br />    spellPut("qiu", -14353); <br />    spellPut("qu", -14345); <br />    spellPut("quan", -14170); <br />    spellPut("que", -14159); <br />    spellPut("qun", -14151); <br />    spellPut("ran", -14149); <br />    spellPut("rang", -14145); <br />    spellPut("rao", -14140); <br />    spellPut("re", -14137); <br />    spellPut("ren", -14135); <br />    spellPut("reng", -14125); <br />    spellPut("ri", -14123); <br />    spellPut("rong", -14122); <br />    spellPut("rou", -14112); <br />    spellPut("ru", -14109); <br />    spellPut("ruan", -14099); <br />    spellPut("rui", -14097); <br />    spellPut("run", -14094); <br />    spellPut("ruo", -14092); <br />    spellPut("sa", -14090); <br />    spellPut("sai", -14087); <br />    spellPut("san", -14083); <br />    spellPut("sang", -13917); <br />    spellPut("sao", -13914); <br />    spellPut("se", -13910); <br />    spellPut("sen", -13907); <br />    spellPut("seng", -13906); <br />    spellPut("sha", -13905); <br />    spellPut("shai", -13896); <br />    spellPut("shan", -13894); <br />    spellPut("shang", -13878); <br />    spellPut("shao", -13870); <br />    spellPut("she", -13859); <br />    spellPut("shen", -13847); <br />    spellPut("sheng", -13831); <br />    spellPut("shi", -13658); <br />    spellPut("shou", -13611); <br />    spellPut("shu", -13601); <br />    spellPut("shua", -13406); <br />    spellPut("shuai", -13404); <br />    spellPut("shuan", -13400); <br />    spellPut("shuang", -13398); <br />    spellPut("shui", -13395); <br />    spellPut("shun", -13391); <br />    spellPut("shuo", -13387); <br />    spellPut("si", -13383); <br />    spellPut("song", -13367); <br />    spellPut("sou", -13359); <br />    spellPut("su", -13356); <br />    spellPut("suan", -13343); <br />    spellPut("sui", -13340); <br />    spellPut("sun", -13329); <br />    spellPut("suo", -13326); <br />    spellPut("ta", -13318); <br />    spellPut("tai", -13147); <br />    spellPut("tan", -13138); <br />    spellPut("tang", -13120); <br />    spellPut("tao", -13107); <br />    spellPut("te", -13096); <br />    spellPut("teng", -13095); <br />    spellPut("ti", -13091); <br />    spellPut("tian", -13076); <br />    spellPut("tiao", -13068); <br />    spellPut("tie", -13063); <br />    spellPut("ting", -13060); <br />    spellPut("tong", -12888); <br />    spellPut("tou", -12875); <br />    spellPut("tu", -12871); <br />    spellPut("tuan", -12860); <br />    spellPut("tui", -12858); <br />    spellPut("tun", -12852); <br />    spellPut("tuo", -12849); <br />    spellPut("wa", -12838); <br />    spellPut("wai", -12831); <br />    spellPut("wan", -12829); <br />    spellPut("wang", -12812); <br />    spellPut("wei", -12802); <br />    spellPut("wen", -12607); <br />    spellPut("weng", -12597); <br />    spellPut("wo", -12594); <br />    spellPut("wu", -12585); <br />    spellPut("xi", -12556); <br />    spellPut("xia", -12359); <br />    spellPut("xian", -12346); <br />    spellPut("xiang", -12320); <br />    spellPut("xiao", -12300); <br />    spellPut("xie", -12120); <br />    spellPut("xin", -12099); <br />    spellPut("xing", -12089); <br />    spellPut("xiong", -12074); <br />    spellPut("xiu", -12067); <br />    spellPut("xu", -12058); <br />    spellPut("xuan", -12039); <br />    spellPut("xue", -11867); <br />    spellPut("xun", -11861); <br />    spellPut("ya", -11847); <br />    spellPut("yan", -11831); <br />    spellPut("yang", -11798); <br />    spellPut("yao", -11781); <br />    spellPut("ye", -11604); <br />    spellPut("yi", -11589); <br />    spellPut("yin", -11536); <br />    spellPut("ying", -11358); <br />    spellPut("yo", -11340); <br />    spellPut("yong", -11339); <br />    spellPut("you", -11324); <br />    spellPut("yu", -11303); <br />    spellPut("yuan", -11097); <br />    spellPut("yue", -11077); <br />    spellPut("yun", -11067); <br />    spellPut("za", -11055); <br />    spellPut("zai", -11052); <br />    spellPut("zan", -11045); <br />    spellPut("zang", -11041); <br />    spellPut("zao", -11038); <br />    spellPut("ze", -11024); <br />    spellPut("zei", -11020); <br />    spellPut("zen", -11019); <br />    spellPut("zeng", -11018); <br />    spellPut("zha", -11014); <br />    spellPut("zhai", -10838); <br />    spellPut("zhan", -10832); <br />    spellPut("zhang", -10815); <br />    spellPut("zhao", -10800); <br />    spellPut("zhe", -10790); <br />    spellPut("zhen", -10780); <br />    spellPut("zheng", -10764); <br />    spellPut("zhi", -10587); <br />    spellPut("zhong", -10544); <br />    spellPut("zhou", -10533); <br />    spellPut("zhu", -10519); <br />    spellPut("zhua", -10331); <br />    spellPut("zhuai", -10329); <br />    spellPut("zhuan", -10328); <br />    spellPut("zhuang", -10322); <br />    spellPut("zhui", -10315); <br />    spellPut("zhun", -10309); <br />    spellPut("zhuo", -10307); <br />    spellPut("zi", -10296); <br />    spellPut("zong", -10281); <br />    spellPut("zou", -10274); <br />    spellPut("zu", -10270); <br />    spellPut("zuan", -10262); <br />    spellPut("zui", -10260); <br />    spellPut("zun", -10256); <br />    spellPut("zuo", -10254); <br />    spellPut("zz", -10247); <br />  } </p>
		<p>  /** <br />   * 获得单个汉字的Ascii. <br />   */ <br />  public static int getCnAscii(char cn) { <br />    byte[] bytes = (String.valueOf(cn)).getBytes(); <br />    if (bytes == null || bytes.length &gt; 2 || bytes.length &lt;= 0) { //错误 <br />      return 0; <br />    } <br />    if (bytes.length == 1) { //英文字符 <br />      return bytes[0]; <br />    } <br />    if (bytes.length == 2) { //中文字符 <br />      int hightByte = 256 + bytes[0]; <br />      int lowByte = 256 + bytes[1]; </p>
		<p>      int ascii = (256 * hightByte + lowByte) - 256 * 256; </p>
		<p>      //System.out.println("ASCII=" + ascii); </p>
		<p>      return ascii; <br />    } </p>
		<p>    return 0; //错误 <br />  } </p>
		<p>  /** <br />   * 根据ASCII码到SpellMap中查找对应的拼音 <br />   */ <br />  public static String getSpellByAscii(int ascii) { <br />    if (ascii &gt; 0 &amp;&amp; ascii &lt; 160) { //单字符 <br />      return String.valueOf( (char) ascii); <br />    } </p>
		<p>    if (ascii &lt; -20319 || ascii &gt; -10247) { //不知道的字符 <br />      return null; <br />    } </p>
		<p>    Set keySet = spellMap.keySet(); <br />    Iterator it = keySet.iterator(); </p>
		<p>    String spell0 = null; ; <br />    String spell = null; </p>
		<p>    int asciiRang0 = -20319; <br />    int asciiRang; <br />    while (it.hasNext()) { </p>
		<p>      spell = (String) it.next(); <br />      Object valObj = spellMap.get(spell); <br />      if (valObj instanceof Integer) { <br />        asciiRang = ( (Integer) valObj).intValue(); </p>
		<p>        if (ascii &gt;= asciiRang0 &amp;&amp; ascii &lt; asciiRang) { //区间找到 <br />          return (spell0 == null) ? spell : spell0; <br />        } <br />        else { <br />          spell0 = spell; <br />          asciiRang0 = asciiRang; <br />        } <br />      } <br />    } </p>
		<p>    return null; </p>
		<p>  } </p>
		<p>  /** <br />   * 返回字符串的全拼,是汉字转化为全拼,其它字符不进行转换 <br />   */ <br />  public static String getFullSpell(String cnStr) { <br />    if (null == cnStr || "".equals(cnStr.trim())) { <br />      return cnStr; <br />    } </p>
		<p>    char[] chars = cnStr.toCharArray(); <br />    StringBuffer retuBuf = new StringBuffer(); <br />    for (int i = 0, Len = chars.length; i &lt; Len; i++) { <br />      int ascii = getCnAscii(chars[i]); <br />      if (ascii == 0) { //取ascii时出错 <br />        retuBuf.append(chars[i]); <br />        retuBuf.append(" "); <br />      } <br />      else { <br />        String spell = getSpellByAscii(ascii); <br />        if (spell == null) { <br />          retuBuf.append(chars[i]); <br />          retuBuf.append(" "); <br />        } <br />        else { <br />          retuBuf.append(spell); <br />          retuBuf.append(" "); <br />        } // end of if spell == null <br />      } // end of if ascii &lt;= -20400 <br />    } // end of for </p>
		<p>    return retuBuf.toString(); <br />  } </p>
		<p>  public static String getFirstSpell(String cnStr) { <br />    return null; <br />  } </p>
		<p>  } <br />} </p>
		<p>说明：1。freeTTS可以从网上免费获得源码。</p>
		<p>      2。汉字转拼音只是初级的，未能区分多音字等，也不能分词。</p>
		<p>      3。freeTTS对于中文的朗读效果不好。<br /></p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/48974.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-30 14:11 <a href="http://www.blogjava.net/TrampEagle/articles/48974.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>StringUtils类使用</title><link>http://www.blogjava.net/TrampEagle/articles/48973.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 30 May 2006 06:09:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48973.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48973.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48973.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48973.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48973.html</trackback:ping><description><![CDATA[
		<p>原文引自：<a href="http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=1351&amp;page=1">http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=1351&amp;page=1</a><br /><br />StringUtils类使用<br /></p>
		<p>
				<b>检查字符串是否为空或null或仅仅包含空格<br /></b>  String test = "";<br />  String test1=" ";<br />  String test2 = "\n\n\t";<br />  String test3 = null;<br />  System.out.println( "test blank? " + StringUtils.isBlank( test ) ); <br />  System.out.println( "test1 blank? " + StringUtils.isBlank( test1 ) );<br />  System.out.println( "test2 blank? " + StringUtils.isBlank( test2 ) );<br />  System.out.println( "test3 blank? " + StringUtils.isBlank( test3 ) );<br />  运行结果：<br />  test blank? true<br />  test1 blank? true<br />  test2 blank? true<br />  test3 blank? true<br />  相对应的还有一个StringUtils.isNotBlank(String str)<br />  StringUtils.isEmpty(String str)则检查字符串是否为空或null（不检查是否仅仅包含空格）<br />  <br />  <b>分解字符串</b><br />  StringUtils.split(null, *, *)            = null<br />  StringUtils.split("", *, *)              = []<br />  StringUtils.split("ab de fg", null, 0)   = ["ab", "cd", "ef"]<br />  StringUtils.split("ab   de fg", null, 0) = ["ab", "cd", "ef"]<br />  StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]<br />  StringUtils.split("ab:cd:ef", ":", 1)    = ["ab:cd:ef"]<br />  StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]<br />  StringUtils.split(String str,String separatorChars,int max) str为null时返回null<br />  separatorChars为null时默认为按空格分解，max为0或负数时分解没有限制，max为1时返回整个字符串，max为分解成的个数（大于实际则无效）<br />  <br />  <b>去除字符串前后指定的字符</b><br />  StringUtils.strip(null, *)          = null<br />  StringUtils.strip("", *)            = ""<br />  StringUtils.strip("abc", null)      = "abc"<br />  StringUtils.strip(" abc ", null)    = "abc"<br />  StringUtils.strip("  abcyx", "xyz") = "  abc"<br />  StringUtils.strip(String str,String stripChars) str为null时返回null,stripChars为null时默认为空格</p>
		<p>  <b>创建醒目的Header（调试时用）</b><br />  public String createHeader( String title ) {<br />    int width = 30;<br />    String stars = StringUtils.repeat( "*", width);<br />    String centered = StringUtils.center( title, width, "*" );<br />    String heading = StringUtils.join(new Object[]{stars, centered, stars}, "\n");<br />    return heading;<br />  }<br />  调用createHeader("TEST")的输出结果:<br />  ******************************<br />  ************ TEST ************<br />  ******************************</p>
		<p>  <b>字符的全部反转及以单个词为单位的反转<br /></b>  String original = "In time, I grew tired of his babbling nonsense.";<br />  StringUtils.reverse( original )   = ".esnesnon gnilbbab sih fo derit werg I ,emit nI"<br />  以单个词为单位的反转<br />  public Sentence reverseSentence(String sentence) {<br />    String reversed = StringUtils.chomp( sentence, "." );<br />    reversed = StringUtils.reverseDelimited( reversed, ' ' );<br />    reversed = reversed + ".";<br />    return reversed;<br />  }<br />  String sentence = "I am Susan."<br />  reverseSentence( sentence ) )   = "Susan am I."</p>
		<p>  <b>检查字符串是否仅仅包含数字、字母或数字和字母的混合<br /></b>  String test1 = "ORANGE";<br />  String test2 = "ICE9";<br />  String test3 = "ICE CREAM";<br />  String test4 = "820B Judson Avenue";<br />  String test5 = "1976";<br />  结果：<br />  boolean t1val = StringUtils.isAlpha( test1 ); // returns true<br />  boolean t2val = StringUtils.isAlphanumeric( test2 ); // returns true<br />  boolean t3val = StringUtils.isAlphaSpace( test3 ); // returns true<br />  boolean t4val = StringUtils.isAlphanumericSpace( test4 ); // returns true<br />  boolean t5val = StringUtils.isNumeric( test5 ); // returns true</p>
		<p>
				<u>
						<font color="#0000ff" size="5">
								<b>ArrayUtils类使用</b>
						</font>
				</u>
		</p>
		<p>primitive 数组克隆及反转排序<br />  long[] array = { 1, 3, 2, 3, 5, 6 };<br />  long[] reversed = ArrayUtils.clone( array );<br />  ArrayUtils.reverse( reversed );<br />  System.out.println( "Original: " + ArrayUtils.toString( array ) );   //打印<br />  System.out.println( "Reversed: " + ArrayUtils.toString( reversed ) );<br />  <br />  <b>对象数组克隆及反转排序<br /></b>  Long[] array = new Long[] { new Long(3), new Long(56), new Long(233) };<br />  Long[] reversed = ArrayUtils.clone( array );<br />  ArrayUtils.reverse( reversed );<br />  <br />  <b>primitive 数组与对象数组之间的转换</b><br />  long[] primitiveArray = new long[] { 12, 100, 2929, 3323 };<br />  Long[] objectArray = ArrayUtils.toObject( primitiveArray );<br />  Double[] doubleObjects = new Double[] { new Double( 3.22, 5.222, 3.221 ) };<br />  double[] doublePrimitives = ArrayUtils.toPrimitive( doubleObject );<br />  注意：对象数组可以含有null元素，primitive 数组则不容许含有null元素，所以对象数组转换为primitive 数组时，可以添入第二个参数，当碰到为null的元素时用其代替（如下，Double.NaN）。如果不添入第二个参数，当碰到为null的元素时，则会抛出NullPointerException 。<br />  double[] result = ArrayUtils.toPrimitive( resultObjArray, Double.NaN  );<br />  <br /><b>  查找一个数组中是否含有特定的元素（查找对象数组时，比较的是对象的equals()方法），及特定元素的第一次出现位置和最后一次出现位置<br /></b>  String[] stringArray = { "Red", "Orange", "Blue", "Brown", "Red" };<br />  boolean containsBlue = ArrayUtils.contains( stringArray, "Blue" );<br />  int indexOfRed = ArrayUtils.indexOf( stringArray, "Red");<br />  int lastIndexOfRed = ArrayUtils.lastIndexOf( string, "Red" );  <br />  <br />  <b>由二维对象数组创建一个 Map<br /></b>  Object[] weightArray = <br />    new Object[][] { {"H" , new Double( 1.007)},<br />                     {"He", new Double( 4.002)},<br />                     {"Li", new Double( 6.941)},<br />                     {"Be", new Double( 9.012)},<br />                     {"B",  new Double(10.811)},<br />                     {"C",  new Double(12.010)},<br />                     {"N",  new Double(14.007)},<br />                     {"O",  new Double(15.999)},<br />                     {"F",  new Double(18.998)},<br />                     {"Ne", new Double(20.180)} };</p>
		<p>  Map weights = ArrayUtils.toMap( weightArray );<br />  Double hydrogenWeight = （Double)weights.get( "H" );<br />  注意：当二维对象数组"key"值重复时，创建的Map，后面的键-值对会把前面的覆盖掉</p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/48973.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-30 14:09 <a href="http://www.blogjava.net/TrampEagle/articles/48973.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最佳开源软件一览(转载)</title><link>http://www.blogjava.net/TrampEagle/articles/48917.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 30 May 2006 03:25:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48917.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48917.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48917.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48917.html</trackback:ping><description><![CDATA[
		<ul>
				<li>
						<a href="http://www.7-zip.org/" target="_blank">
								<font color="#000000">7-Zip 4.16 Beta</font>
						</a>：文件压缩工具，可与Windows资源管理器集成<br /><br /></li>
				<li>
						<a href="http://a-note.sourceforge.net/" target="_blank">
								<font color="#000000">A Note 4.2.1</font>
						</a>：可在Windows桌面放置便笺，并可提供闹钟提醒功能<br /><br /></li>
				<li>
						<a href="http://www.xs4all.nl/%7Eedienske" target="_blank">
								<font color="#000000">Abakt 0.9</font>
						</a>：能够以压缩方式对文档进行备份<br /><br /></li>
				<li>
						<a href="http://www.abisource.com/" target="_blank">
								<font color="#000000">Abiword 2.27</font>
						</a>：Windows写字板的替代程序，功能有所加强<br /><br /></li>
				<li>
						<a href="http://www.thekompany.com/home" target="_blank">
								<font color="#000000">Aethera 1.21</font>
						</a>：提供日历、通讯录、任务表及提醒功能，并且内置了e-mail客户端<br /><br /></li>
				<li>
						<a href="http://www.antp.be/software/moviecatalog/" target="_blank">
								<font color="#000000">Ant Movie Catalog 3.5</font>
						</a>：将你收藏的DVD影碟归档，并添加说明信息<br /><br /></li>
				<li>
						<a href="http://www.antp.be/software/renamer" target="_blank">
								<font color="#000000">Ant Renamer 2.0.8</font>
						</a>：易用的文件重命名工具，并具备灵活的筛选机制<br /><br /></li>
				<li>
						<a href="http://audacity.sourceforge.net/" target="_blank">
								<font color="#000000">Audacity 1.2.3</font>
						</a>：对音频文件进行编辑、优化并添加特效<br /><br /></li>
				<li>
						<a href="http://axcrypt.sourceforge.net/" target="_blank">
								<font color="#000000">Axcrypt 1.6.1</font>
						</a>：对程序进行加密，可与Windows资源管理器集成<br /><br /></li>
				<li>
						<a href="http://www.blender3d.com/" target="_blank">
								<font color="#000000">Blender 3D 2.36</font>
						</a>：三维对象的建模、渲染<br /><br /></li>
				<li>
						<a href="http://borg-calendar.sourceforge.net/" target="_blank">
								<font color="#000000">Borg Calendar 1.4.2</font>
						</a>：提供桌面日历、任务列表、通讯录功能，支持多用户<br /><br /></li>
				<li>
						<a href="http://cdexos.sourceforge.net/" target="_blank">
								<font color="#000000">Cdex 1.51</font>
						</a>：将音乐CD转换为wav或者mp3格式<br /><br /></li>
				<li>
						<a href="http://cinepaint.movieeditor.com/" target="_blank">
								<font color="#000000">Cinepaint</font>
						</a> 0.19：专业的图像编辑软件<br /><br /></li>
				<li>
						<a href="http://www.clamwin.com/" target="_blank">
								<font color="#000000">Clam Win 0.83</font>
						</a>：病毒扫描工具<br /><br /></li>
				<li>
						<a href="http://www.coolmon.org/" target="_blank">
								<font color="#000000">Cool Mon 1.0.1003</font>
						</a>：系统检测工具<br /><br /></li>
				<li>
						<a href="http://coolplayer.sourceforge.net/" target="_blank">
								<font color="#000000">Cool Player 215</font>
						</a>：一款精简的音频播放软件<br /><br /></li>
				<li>
						<a href="http://www.gnome.org/projects/dia" target="_blank">
								<font color="#000000">Dia 0.94</font>
						</a>：绘制图表和流程图<br /><br /></li>
				<li>
						<a href="http://doubletype.sourceforge.net/" target="_blank">
								<font color="#000000">Double Type 0.2.1</font>
						</a>：设计自己的Truetype字体<br /><br /></li>
				<li>
						<a href="http://www.egroupware.org/" target="_blank">
								<font color="#000000">Egroupware 1.0.0.006</font>
						</a>：一款包含日历、新闻、联系人等模块的工作流系统<br /><br /></li>
				<li>
						<a href="http://www.heidi.ie/" target="_blank">
								<font color="#000000">Eraser 5.7</font>
						</a>：永久地删除硬盘中的数据<br /><br /></li>
				<li>
						<a href="http://filezilla.sourceforge.net/" target="_blank">
								<font color="#000000">Filezilla 2.2.12c</font>
						</a>：FTP客户端<br /><br /></li>
				<li>
						<a href="http://www.mozilla.net.cn/firefox/" target="_blank">
								<font color="#000000">Firefox 2</font>
						</a>：Web浏览器，支持并列显示多个网页<br /><br /></li>
				<li>
						<a href="http://freemind.sourceforge.net/" target="_blank">
								<font color="#000000">Freemind 0.8</font>
						</a>：能以直观形象的图示建立起各个概念之间的联系<br /><br /></li>
				<li>
						<a href="http://gaim.sourceforge.net/" target="_blank">
								<font color="#000000">Gaim 1.4.0</font>
						</a>：同时支持ICQ、Aim、MSN、Yahoo的即时通信软件<br /><br /></li>
				<li>
						<a href="http://ganttproject.sourceforge.net/" target="_blank">
								<font color="#000000">Ganttproject 1.11.1</font>
						</a>：项目管理软件，帮助你进行时间安排及资源分配<br /><br /></li>
				<li>
						<a href="http://www.gnupg.org/" target="_blank">
								<font color="#000000">GnuPG Add-ons</font>
						</a>：对邮件进行加密<br /><br /></li>
				<li>
						<a href="http://healthmonitor.sourceforge.net/" target="_blank">
								<font color="#000000">Health Monitor 2.1 Monitors</font>
						</a>：Windows状态监测工具，出现问题时可以给出警报<br /><br /></li>
				<li>
						<a href="http://www.inkscape.org/" target="_blank">
								<font color="#000000">Inkscape 0.41</font>
						</a>：向量图形设计工具，可用来绘制地图、技术图纸或公司logo<br /><br /></li>
				<li>
						<a href="http://www.jdictionary.info/" target="_blank">
								<font color="#000000">JDictionary 1.8</font>
						</a>：超过140万词条的百科辞典<br /><br /></li>
				<li>
						<a href="http://keepass.sourceforge.net/" target="_blank">
								<font color="#000000">Kee Pass 0.99b</font>
						</a>：管理你的密码<br /><br /></li>
				<li>
						<a href="http://www.tranglos.com/" target="_blank">
								<font color="#000000">Keynote 1.6.5</font>
						</a>：字处理软件和数据库的结合体，带有良好的搜索机制<br /><br /></li>
				<li>
						<a href="http://mediaportal.sourceforge.net/" target="_blank">
								<font color="#000000">Media Portal 0.1.1.1</font>
						</a>：视频、DVD、音频、图片播放工具，同时支持电视和电台广播<br /><br /></li>
				<li>
						<a href="http://massid3lib.sourceforge.net/" target="_blank">
								<font color="#000000">MP3 Tag Tools 1.2.008</font>
						</a>：mp3的文件ID标签编辑工具<br /><br /></li>
				<li>
						<a href="http://mp3gain.sourceforge.net/" target="_blank">
								<font color="#000000">MP3 Gain 1.2.5</font>
						</a>：在不影响音质的情况下调节mp3歌曲的音量<br /><br /></li>
				<li>
						<a href="http://www.mozilla.nightrat.net/nvu" target="_blank">
								<font color="#000000">NVU 1.0</font>
						</a>：所见即所得的HTML编辑工具，带有相当专业的网页制作功能<br /><br /></li>
				<li>
						<a href="http://www.openoffice.org/" target="_blank">
								<font color="#000000">Open Office 2.0 Beta</font>
						</a>：文字处理、电子数据表、演示工具和数据库<br /><br /></li>
				<li>
						<a href="http://sector7g.wurzel6.de/pdfcreator/index_en.htm" target="_blank">
								<font color="#000000">PDF-Creator 0.8.1</font>
						</a>：可被安装为打印机并将文档输出为pdf文件<br /><br /></li>
				<li>
						<a href="http://poptray.crause.co.za/" target="_blank">
								<font color="#000000">Poptray 3.10</font>
						</a>：在后台监控邮件账号，当有新邮件进入时对你进行提醒<br /><br /></li>
				<li>
						<a href="http://sourceforge.net/projects/pwgen-win" target="_blank">
								<font color="#000000">PW-Gen 1.4.0</font>
						</a>：为你生成64位到128位的安全密码<br /><br /></li>
				<li>
						<a href="http://www.rssowl.org/" target="_blank">
								<font color="#000000">RSS-Owl 1.1</font>
						</a>：RSS阅读器<br /><br /></li>
				<li>
						<a href="http://smartision-sc.sourceforge.net/" target="_blank">
								<font color="#000000">Screencopy 2.3</font>
						</a>：屏幕拷贝工具<br /><br /></li>
				<li>
						<a href="http://syn.sourceforge.net/" target="_blank">
								<font color="#000000">Syn Text Editor 2.1.0.46</font>
						</a>：文本编辑器，支持多种程序语言的命令语法<br /><br /></li>
				<li>
						<a href="http://taskswitchxp.sourceforge.net/" target="_blank">
								<font color="#000000">Task SwitchXP Pro 1.1.2</font>
						</a>：扩展了Windows任务管理器的功能和外观<br /><br /></li>
				<li>
						<a href="http://www.gimp.org/" target="_blank">
								<font color="#000000">The Gimp 2.2.4</font>
						</a>：支持图层管理、特效润饰的图像编辑软件<br /><br /></li>
				<li>
						<a href="http://www.mozilla.com/thunderbird/" target="_blank">
								<font color="#000000">Thunderbird 1.07</font>
						</a>：e-mail客户端，支持Imap/Pop3账户，带有垃圾邮件过滤器和虚拟文件夹<br /><br /></li>
				<li>
						<a href="http://truecrypt.sourceforge.net/" target="_blank">
								<font color="#000000">True Crypt 3.1a</font>
						</a>：对文件或硬盘分区进行加密，也可以对U盘等移动存储介质进行加密<br /><br /></li>
				<li>
						<a href="http://francis.dupont.free.fr/truedownloader" target="_blank">
								<font color="#000000">True Downloader 0.82</font>
						</a>：FTP和HTTP链接的下载管理工具，可以监视剪贴板<br /><br /></li>
				<li>
						<a href="http://www.tvbrowser.org/" target="_blank">
								<font color="#000000">TV-Browser 1.0.1</font>
						</a>：自动更新每天的电视节目表<br /><br /></li>
				<li>
						<a href="http://www.virtualdub.org/" target="_blank">
								<font color="#000000">Virtual Dub 1.5.10</font>
						</a>：视频编辑和捕获软件，支持mpeg-1和avi视频格式<br /><br /></li>
				<li>
						<a href="http://virtuawin.sourceforge.net/" target="_blank">
								<font color="#000000">Virtual Win 2.1</font>
						</a>：可管理最多9个虚拟桌面，你可以用热键进行桌面切换<br /><br /></li>
				<li>
						<a href="http://www.videolan.org/" target="_blank">
								<font color="#000000">VLC Media Player 0.8.1</font>
						</a>：媒体播放器，支持DVD、VCD、CD、mpeg和DivX等格式<br /><br /></li>
				<li>
						<a href="http://www.httrack.com/" target="_blank">
								<font color="#000000">Web HTTrack 3.33</font>
						</a>：离线浏览器，可将Internet网页保存到本地硬盘中<br /><br /></li>
				<li>
						<a href="http://www.palma.com.au/winroll" target="_blank">
								<font color="#000000">Winroll 2.0</font>
						</a>：点击标题栏后就可将程序窗口最小化<br /><br /></li>
				<li>
						<a href="http://musik.berlios.de/" target="_blank">
								<font color="#000000">WX Musik 0.4.1</font>
						</a>：音频播放及管理软件<br /><br /></li>
				<li>
						<a href="http://www.apachefriends.org/" target="_blank">
								<font color="#000000">Xampp 1.42</font>
						</a> ：Web服务器软件包，包含Apache、PHP和MySQL <br /></li>
		</ul>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/48917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-30 11:25 <a href="http://www.blogjava.net/TrampEagle/articles/48917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用jawin完成调用window中dll的调用</title><link>http://www.blogjava.net/TrampEagle/articles/48914.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 30 May 2006 03:24:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48914.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48914.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48914.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48914.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48914.html</trackback:ping><description><![CDATA[
		<p>转自<a href="/gf7/" target="_blank"><font color="#000000">风之语</font></a>： <a href="/gf7/archive/2005/12/22/25041.html" target="_blank"><font color="#000000">http://www.blogjava.net/gf7/archive/2005/12/22/25041.html</font></a></p>
		<p>最近由于项目的特殊需求，我们必须在程序调用window的dll。<br />开始我们用jni，后来由于调用的dll太多，而且很烦琐。所以，我们决定用开源的jawin调用。<br />jawin 可以对dll中的方法进行调用，也可以调用com中的方法.内部还提供了一个工具，直接对 com组件导出成 java的类，个人认为很方便。<br /><br />下面是我们作的一个测试，很顺利便通过了。<br />1、下载jawin：<a href="http://jawinproject.sourceforge.net/" target="_blank"><font color="#000000">http://jawinproject.sourceforge.net/</font></a>。<br />2、配置：<br />    》将jawin.jar放于%JAVA_HOME%\jre\lib\ext下 。<br />    》将jawin.dll放于c:\winnt\system32下。否则将出现错误：COMException : no jawin in java.library.path； <br />    也可将jawin.dll放于每个项目目录下。 </p>
		<p>   》至此在Editplus中调试Jawin/NJawin的例子，可以通过。 而在Eclipse中有时还会出上面的错误：COMException : no jawin in java.library.path。 <br />   》在Eclipse中，菜单-&gt;window-&gt;preference-&gt;Java-&gt;installed JREs 将原来的remove,重新建一个指到你的java sdk目录。 <br />   》 ok了。<br />3、程序测试：<br /><br /></p>
		<p>     》调用 dll，dll 的方式不需要导出了,直接调用就可以了，下面是下载的包中提供的一个例子:<br />     》我在win2000下，测试通过。<br />/*<br />* Created on Dec 22, 2005<br />*<br />*/<br />import org.jawin.FuncPtr;</p>
		<p>import org.jawin.ReturnFlags;</p>
		<p>/**<br />* @author gf  mail to <a href="http://www.3doing.net/forums/mailtgf@163.com" target="_blank"><font color="#000000">gf@163.com</font></a><br />*<br />* TODO To change the template for this generated type comment go to<br />* Window - Preferences - Java - Code Style - Code Templates<br />*/<br />public class GfJawinTest {</p>
		<p>       public static void main(String[] args) {</p>
		<p>              try {</p>
		<p>                     FuncPtr msgBox = new FuncPtr("USER32.DLL", "MessageBoxW");</p>
		<p>                     </p>
		<p>                     msgBox.invoke_I(0, "Hello From a DLL", "From Jawin", 0, ReturnFlags.CHECK_NONE);</p>
		<p>              } catch (Exception e) {</p>
		<p>                     e.printStackTrace();</p>
		<p>              }</p>
		<p>       }</p>
		<p>}</p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/48914.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-30 11:24 <a href="http://www.blogjava.net/TrampEagle/articles/48914.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java实现汉字转换为拼音(转)</title><link>http://www.blogjava.net/TrampEagle/articles/48754.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 29 May 2006 07:05:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48754.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48754.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48754.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48754.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48754.html</trackback:ping><description><![CDATA[
		<blockquote>
				<p>
						<font face="Verdana">import java.util.Iterator;<br />import java.util.LinkedHashMap;<br />import java.util.Set;</font>
				</p>
				<p>
						<font face="Verdana">/**<br /> *<br /> 汉字转化为全拼<br /></font>
						<font face="Verdana"> *<br /></font>
						<font face="Verdana"> JDK版本:</font>
						<font face="Verdana"> 1.4<br /></font>
						<font face="Verdana">
								<br />public class CnToSpell {<br />    private static LinkedHashMap spellMap = null;</font>
				</p>
				<p>
						<font face="Verdana">    static {<br />        if (spellMap == null) {<br />            spellMap = new LinkedHashMap(400);<br />        }<br />        initialize();<br />        System.out.println("Chinese transfer Spell Done.");<br />    }</font>
				</p>
				<p>
						<font face="Verdana">    private CnToSpell() {<br />    }</font>
				</p>
				<p>
						<font face="Verdana">    private static void spellPut(String spell, int ascii) {<br />        spellMap.put(spell, new Integer(ascii));<br />    }</font>
				</p>
				<p>
						<font face="Verdana">    private static void initialize() {<br />        spellPut("a", -20319);<br />        spellPut("ai", -20317);<br />        spellPut("an", -20304);<br />        spellPut("ang", -20295);<br />        spellPut("ao", -20292);<br />        spellPut("ba", -20283);<br />        spellPut("bai", -20265);<br />        spellPut("ban", -20257);<br />        spellPut("bang", -20242);<br />        spellPut("bao", -20230);<br />        spellPut("bei", -20051);<br />        spellPut("ben", -20036);<br />        spellPut("beng", -20032);<br />        spellPut("bi", -20026);<br />        spellPut("bian", -20002);<br />        spellPut("biao", -19990);<br />        spellPut("bie", -19986);<br />        spellPut("bin", -19982);<br />        spellPut("bing", -19976);<br />        spellPut("bo", -19805);<br />        spellPut("bu", -19784);<br />        spellPut("ca", -19775);<br />        spellPut("cai", -19774);<br />        spellPut("can", -19763);<br />        spellPut("cang", -19756);<br />        spellPut("cao", -19751);<br />        spellPut("ce", -19746);<br />        spellPut("ceng", -19741);<br />        spellPut("cha", -19739);<br />        spellPut("chai", -19728);<br />        spellPut("chan", -19725);<br />        spellPut("chang", -19715);<br />        spellPut("chao", -19540);<br />        spellPut("che", -19531);<br />        spellPut("chen", -19525);<br />        spellPut("cheng", -19515);<br />        spellPut("chi", -19500);<br />        spellPut("chong", -19484);<br />        spellPut("chou", -19479);<br />        spellPut("chu", -19467);<br />        spellPut("chuai", -19289);<br />        spellPut("chuan", -19288);<br />        spellPut("chuang", -19281);<br />        spellPut("chui", -19275);<br />        spellPut("chun", -19270);<br />        spellPut("chuo", -19263);<br />        spellPut("ci", -19261);<br />        spellPut("cong", -19249);<br />        spellPut("cou", -19243);<br />        spellPut("cu", -19242);<br />        spellPut("cuan", -19238);<br />        spellPut("cui", -19235);<br />        spellPut("cun", -19227);<br />        spellPut("cuo", -19224);<br />        spellPut("da", -19218);<br />        spellPut("dai", -19212);<br />        spellPut("dan", -19038);<br />        spellPut("dang", -19023);<br />        spellPut("dao", -19018);<br />        spellPut("de", -19006);<br />        spellPut("deng", -19003);<br />        spellPut("di", -18996);<br />        spellPut("dian", -18977);<br />        spellPut("diao", -18961);<br />        spellPut("die", -18952);<br />        spellPut("ding", -18783);<br />        spellPut("diu", -18774);<br />        spellPut("dong", -18773);<br />        spellPut("dou", -18763);<br />        spellPut("du", -18756);<br />        spellPut("duan", -18741);<br />        spellPut("dui", -18735);<br />        spellPut("dun", -18731);<br />        spellPut("duo", -18722);<br />        spellPut("e", -18710);<br />        spellPut("en", -18697);<br />        spellPut("er", -18696);<br />        spellPut("fa", -18526);<br />        spellPut("fan", -18518);<br />        spellPut("fang", -18501);<br />        spellPut("fei", -18490);<br />        spellPut("fen", -18478);<br />        spellPut("feng", -18463);<br />        spellPut("fo", -18448);<br />        spellPut("fou", -18447);<br />        spellPut("fu", -18446);<br />        spellPut("ga", -18239);<br />        spellPut("gai", -18237);<br />        spellPut("gan", -18231);<br />        spellPut("gang", -18220);<br />        spellPut("gao", -18211);<br />        spellPut("ge", -18201);<br />        spellPut("gei", -18184);<br />        spellPut("gen", -18183);<br />        spellPut("geng", -18181);<br />        spellPut("gong", -18012);<br />        spellPut("gou", -17997);<br />        spellPut("gu", -17988);<br />        spellPut("gua", -17970);<br />        spellPut("guai", -17964);<br />        spellPut("guan", -17961);<br />        spellPut("guang", -17950);<br />        spellPut("gui", -17947);<br />        spellPut("gun", -17931);<br />        spellPut("guo", -17928);<br />        spellPut("ha", -17922);<br />        spellPut("hai", -17759);<br />        spellPut("han", -17752);<br />        spellPut("hang", -17733);<br />        spellPut("hao", -17730);<br />        spellPut("he", -17721);<br />        spellPut("hei", -17703);<br />        spellPut("hen", -17701);<br />        spellPut("heng", -17697);<br />        spellPut("hong", -17692);<br />        spellPut("hou", -17683);<br />        spellPut("hu", -17676);<br />        spellPut("hua", -17496);<br />        spellPut("huai", -17487);<br />        spellPut("huan", -17482);<br />        spellPut("huang", -17468);<br />        spellPut("hui", -17454);<br />        spellPut("hun", -17433);<br />        spellPut("huo", -17427);<br />        spellPut("ji", -17417);<br />        spellPut("jia", -17202);<br />        spellPut("jian", -17185);<br />        spellPut("jiang", -16983);<br />        spellPut("jiao", -16970);<br />        spellPut("jie", -16942);<br />        spellPut("jin", -16915);<br />        spellPut("jing", -16733);<br />        spellPut("jiong", -16708);<br />        spellPut("jiu", -16706);<br />        spellPut("ju", -16689);<br />        spellPut("juan", -16664);<br />        spellPut("jue", -16657);<br />        spellPut("jun", -16647);<br />        spellPut("ka", -16474);<br />        spellPut("kai", -16470);<br />        spellPut("kan", -16465);<br />        spellPut("kang", -16459);<br />        spellPut("kao", -16452);<br />        spellPut("ke", -16448);<br />        spellPut("ken", -16433);<br />        spellPut("keng", -16429);<br />        spellPut("kong", -16427);<br />        spellPut("kou", -16423);<br />        spellPut("ku", -16419);<br />        spellPut("kua", -16412);<br />        spellPut("kuai", -16407);<br />        spellPut("kuan", -16403);<br />        spellPut("kuang", -16401);<br />        spellPut("kui", -16393);<br />        spellPut("kun", -16220);<br />        spellPut("kuo", -16216);<br />        spellPut("la", -16212);<br />        spellPut("lai", -16205);<br />        spellPut("lan", -16202);<br />        spellPut("lang", -16187);<br />        spellPut("lao", -16180);<br />        spellPut("le", -16171);<br />        spellPut("lei", -16169);<br />        spellPut("leng", -16158);<br />        spellPut("li", -16155);<br />        spellPut("lia", -15959);<br />        spellPut("lian", -15958);<br />        spellPut("liang", -15944);<br />        spellPut("liao", -15933);<br />        spellPut("lie", -15920);<br />        spellPut("lin", -15915);<br />        spellPut("ling", -15903);<br />        spellPut("liu", -15889);<br />        spellPut("long", -15878);<br />        spellPut("lou", -15707);<br />        spellPut("lu", -15701);<br />        spellPut("lv", -15681);<br />        spellPut("luan", -15667);<br />        spellPut("lue", -15661);<br />        spellPut("lun", -15659);<br />        spellPut("luo", -15652);<br />        spellPut("ma", -15640);<br />        spellPut("mai", -15631);<br />        spellPut("man", -15625);<br />        spellPut("mang", -15454);<br />        spellPut("mao", -15448);<br />        spellPut("me", -15436);<br />        spellPut("mei", -15435);<br />        spellPut("men", -15419);<br />        spellPut("meng", -15416);<br />        spellPut("mi", -15408);<br />        spellPut("mian", -15394);<br />        spellPut("miao", -15385);<br />        spellPut("mie", -15377);<br />        spellPut("min", -15375);<br />        spellPut("ming", -15369);<br />        spellPut("miu", -15363);<br />        spellPut("mo", -15362);<br />        spellPut("mou", -15183);<br />        spellPut("mu", -15180);<br />        spellPut("na", -15165);<br />        spellPut("nai", -15158);<br />        spellPut("nan", -15153);<br />        spellPut("nang", -15150);<br />        spellPut("nao", -15149);<br />        spellPut("ne", -15144);<br />        spellPut("nei", -15143);<br />        spellPut("nen", -15141);<br />        spellPut("neng", -15140);<br />        spellPut("ni", -15139);<br />        spellPut("nian", -15128);<br />        spellPut("niang", -15121);<br />        spellPut("niao", -15119);<br />        spellPut("nie", -15117);<br />        spellPut("nin", -15110);<br />        spellPut("ning", -15109);<br />        spellPut("niu", -14941);<br />        spellPut("nong", -14937);<br />        spellPut("nu", -14933);<br />        spellPut("nv", -14930);<br />        spellPut("nuan", -14929);<br />        spellPut("nue", -14928);<br />        spellPut("nuo", -14926);<br />        spellPut("o", -14922);<br />        spellPut("ou", -14921);<br />        spellPut("pa", -14914);<br />        spellPut("pai", -14908);<br />        spellPut("pan", -14902);<br />        spellPut("pang", -14894);<br />        spellPut("pao", -14889);<br />        spellPut("pei", -14882);<br />        spellPut("pen", -14873);<br />        spellPut("peng", -14871);<br />        spellPut("pi", -14857);<br />        spellPut("pian", -14678);<br />        spellPut("piao", -14674);<br />        spellPut("pie", -14670);<br />        spellPut("pin", -14668);<br />        spellPut("ping", -14663);<br />        spellPut("po", -14654);<br />        spellPut("pu", -14645);<br />        spellPut("qi", -14630);<br />        spellPut("qia", -14594);<br />        spellPut("qian", -14429);<br />        spellPut("qiang", -14407);<br />        spellPut("qiao", -14399);<br />        spellPut("qie", -14384);<br />        spellPut("qin", -14379);<br />        spellPut("qing", -14368);<br />        spellPut("qiong", -14355);<br />        spellPut("qiu", -14353);<br />        spellPut("qu", -14345);<br />        spellPut("quan", -14170);<br />        spellPut("que", -14159);<br />        spellPut("qun", -14151);<br />        spellPut("ran", -14149);<br />        spellPut("rang", -14145);<br />        spellPut("rao", -14140);<br />        spellPut("re", -14137);<br />        spellPut("ren", -14135);<br />        spellPut("reng", -14125);<br />        spellPut("ri", -14123);<br />        spellPut("rong", -14122);<br />        spellPut("rou", -14112);<br />        spellPut("ru", -14109);<br />        spellPut("ruan", -14099);<br />        spellPut("rui", -14097);<br />        spellPut("run", -14094);<br />        spellPut("ruo", -14092);<br />        spellPut("sa", -14090);<br />        spellPut("sai", -14087);<br />        spellPut("san", -14083);<br />        spellPut("sang", -13917);<br />        spellPut("sao", -13914);<br />        spellPut("se", -13910);<br />        spellPut("sen", -13907);<br />        spellPut("seng", -13906);<br />        spellPut("sha", -13905);<br />        spellPut("shai", -13896);<br />        spellPut("shan", -13894);<br />        spellPut("shang", -13878);<br />        spellPut("shao", -13870);<br />        spellPut("she", -13859);<br />        spellPut("shen", -13847);<br />        spellPut("sheng", -13831);<br />        spellPut("shi", -13658);<br />        spellPut("shou", -13611);<br />        spellPut("shu", -13601);<br />        spellPut("shua", -13406);<br />        spellPut("shuai", -13404);<br />        spellPut("shuan", -13400);<br />        spellPut("shuang", -13398);<br />        spellPut("shui", -13395);<br />        spellPut("shun", -13391);<br />        spellPut("shuo", -13387);<br />        spellPut("si", -13383);<br />        spellPut("song", -13367);<br />        spellPut("sou", -13359);<br />        spellPut("su", -13356);<br />        spellPut("suan", -13343);<br />        spellPut("sui", -13340);<br />        spellPut("sun", -13329);<br />        spellPut("suo", -13326);<br />        spellPut("ta", -13318);<br />        spellPut("tai", -13147);<br />        spellPut("tan", -13138);<br />        spellPut("tang", -13120);<br />        spellPut("tao", -13107);<br />        spellPut("te", -13096);<br />        spellPut("teng", -13095);<br />        spellPut("ti", -13091);<br />        spellPut("tian", -13076);<br />        spellPut("tiao", -13068);<br />        spellPut("tie", -13063);<br />        spellPut("ting", -13060);<br />        spellPut("tong", -12888);<br />        spellPut("tou", -12875);<br />        spellPut("tu", -12871);<br />        spellPut("tuan", -12860);<br />        spellPut("tui", -12858);<br />        spellPut("tun", -12852);<br />        spellPut("tuo", -12849);<br />        spellPut("wa", -12838);<br />        spellPut("wai", -12831);<br />        spellPut("wan", -12829);<br />        spellPut("wang", -12812);<br />        spellPut("wei", -12802);<br />        spellPut("wen", -12607);<br />        spellPut("weng", -12597);<br />        spellPut("wo", -12594);<br />        spellPut("wu", -12585);<br />        spellPut("xi", -12556);<br />        spellPut("xia", -12359);<br />        spellPut("xian", -12346);<br />        spellPut("xiang", -12320);<br />        spellPut("xiao", -12300);<br />        spellPut("xie", -12120);<br />        spellPut("xin", -12099);<br />        spellPut("xing", -12089);<br />        spellPut("xiong", -12074);<br />        spellPut("xiu", -12067);<br />        spellPut("xu", -12058);<br />        spellPut("xuan", -12039);<br />        spellPut("xue", -11867);<br />        spellPut("xun", -11861);<br />        spellPut("ya", -11847);<br />        spellPut("yan", -11831);<br />        spellPut("yang", -11798);<br />        spellPut("yao", -11781);<br />        spellPut("ye", -11604);<br />        spellPut("yi", -11589);<br />        spellPut("yin", -11536);<br />        spellPut("ying", -11358);<br />        spellPut("yo", -11340);<br />        spellPut("yong", -11339);<br />        spellPut("you", -11324);<br />        spellPut("yu", -11303);<br />        spellPut("yuan", -11097);<br />        spellPut("yue", -11077);<br />        spellPut("yun", -11067);<br />        spellPut("za", -11055);<br />        spellPut("zai", -11052);<br />        spellPut("zan", -11045);<br />        spellPut("zang", -11041);<br />        spellPut("zao", -11038);<br />        spellPut("ze", -11024);<br />        spellPut("zei", -11020);<br />        spellPut("zen", -11019);<br />        spellPut("zeng", -11018);<br />        spellPut("zha", -11014);<br />        spellPut("zhai", -10838);<br />        spellPut("zhan", -10832);<br />        spellPut("zhang", -10815);<br />        spellPut("zhao", -10800);<br />        spellPut("zhe", -10790);<br />        spellPut("zhen", -10780);<br />        spellPut("zheng", -10764);<br />        spellPut("zhi", -10587);<br />        spellPut("zhong", -10544);<br />        spellPut("zhou", -10533);<br />        spellPut("zhu", -10519);<br />        spellPut("zhua", -10331);<br />        spellPut("zhuai", -10329);<br />        spellPut("zhuan", -10328);<br />        spellPut("zhuang", -10322);<br />        spellPut("zhui", -10315);<br />        spellPut("zhun", -10309);<br />        spellPut("zhuo", -10307);<br />        spellPut("zi", -10296);<br />        spellPut("zong", -10281);<br />        spellPut("zou", -10274);<br />        spellPut("zu", -10270);<br />        spellPut("zuan", -10262);<br />        spellPut("zui", -10260);<br />        spellPut("zun", -10256);<br />        spellPut("zuo", -10254);<br />    }</font>
				</p>
				<p>
						<font face="Verdana">    /**<br />     * 获得单个汉字的Ascii.<br />     * @param cn char<br />     * 汉字字符<br />     * @return int<br />     * 错误返回 0,否则返回ascii<br />     */<br />    public static int getCnAscii(char cn) {<br />        byte[] bytes = (String.valueOf(cn)).getBytes();<br />        if (bytes == null || bytes.length &gt; 2 || bytes.length &lt;= 0) { //错误<br />            return 0;<br />        }<br />        if (bytes.length == 1) { //英文字符<br />            return bytes[0];<br />        }<br />        if (bytes.length == 2) { //中文字符<br />            int hightByte = 256 + bytes[0];<br />            int lowByte = 256 + bytes[1];</font>
				</p>
				<p>
						<font face="Verdana">            int ascii = (256 * hightByte + lowByte) - 256 * 256;</font>
				</p>
				<p>
						<font face="Verdana">//System.out.println("ASCII=" + ascii);</font>
				</p>
				<p>
						<font face="Verdana">            return ascii;<br />        }</font>
				</p>
				<p>
						<font face="Verdana">        return 0; //错误<br />    }</font>
				</p>
				<p>
						<font face="Verdana">    /**<br />     * 根据ASCII码到SpellMap中查找对应的拼音<br />     * @param ascii int<br />     * 字符对应的ASCII<br />     * @return String<br />     * 拼音,首先判断ASCII是否&gt;0&amp;&lt;160,如果是返回对应的字符,<br />     *<br />     否则到SpellMap中查找,如果没有找到拼音,则返回null,如果找到则返回拼音.<br />     */<br />    public static String getSpellByAscii(int ascii) {<br />        if (ascii &gt; 0 &amp;&amp; ascii &lt; 160) { //单字符<br />            return String.valueOf((char) ascii);<br />        }</font>
				</p>
				<p>
						<font face="Verdana">        if (ascii &lt; -20319 || ascii &gt; -10247) { //不知道的字符<br />            return null;<br />        }</font>
				</p>
				<p>
						<font face="Verdana">        Set keySet = spellMap.keySet();<br />        Iterator it = keySet.iterator();</font>
				</p>
				<p>
						<font face="Verdana">        String spell0 = null; ;<br />        String spell = null;</font>
				</p>
				<p>
						<font face="Verdana">        int asciiRang0 = -20319;<br />        int asciiRang;<br />        while (it.hasNext()) {</font>
				</p>
				<p>
						<font face="Verdana">            spell = (String) it.next();<br />            Object valObj = spellMap.get(spell);<br />            if (valObj instanceof Integer) {<br />                asciiRang = ((Integer) valObj).intValue();</font>
				</p>
				<p>
						<font face="Verdana">                if (ascii &gt;= asciiRang0 &amp;&amp; ascii &lt; asciiRang) { //区间找到<br />                    return (spell0 == null) ? spell : spell0;<br />                } else {<br />                    spell0 = spell;<br />                    asciiRang0 = asciiRang;<br />                }<br />            }<br />        }</font>
				</p>
				<p>
						<font face="Verdana">        return null;</font>
				</p>
				<p>
						<font face="Verdana">    }</font>
				</p>
				<p>
						<font face="Verdana">    /**<br />     * 返回字符串的全拼,是汉字转化为全拼,其它字符不进行转换<br />     * @param cnStr String<br />     * 字符串<br />     * @return String<br />     * 转换成全拼后的字符串<br />     */<br />    public static String getFullSpell(String cnStr) {<br />        if (null == cnStr || "".equals(cnStr.trim())) {<br />            return cnStr;<br />        }</font>
				</p>
				<p>
						<font face="Verdana">        char[] chars = cnStr.toCharArray();<br />        StringBuffer retuBuf = new StringBuffer();<br />        for (int i = 0, Len = chars.length; i &lt; Len; i++) {<br />            int ascii = getCnAscii(chars[i]);<br />            if (ascii == 0) { //取ascii时出错<br />                retuBuf.append(chars[i]);<br />            } else {<br />                String spell = getSpellByAscii(ascii);<br />                if (spell == null) {<br />                    retuBuf.append(chars[i]);<br />                } else {<br />                    retuBuf.append(spell);<br />                } // end of if spell == null<br />            } // end of if ascii &lt;= -20400<br />        } // end of for</font>
				</p>
				<p>
						<font face="Verdana">        return retuBuf.toString();<br />    }</font>
				</p>
				<p>
						<font face="Verdana">    public static String getFirstSpell(String cnStr) {<br />        return null;<br />    }</font>
				</p>
				<p>
						<font face="Verdana">    public static void main(String[] args) {<br />        String str = null;<br />        str = "小红帽";<br />        System.out.println("Spell=" + CnToSpell.getFullSpell(str));</font>
						<font face="Verdana">  }<br />}</font>
				</p>
		</blockquote>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/48754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-29 15:05 <a href="http://www.blogjava.net/TrampEagle/articles/48754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个获得汉字拼音首字母的java程序（转）</title><link>http://www.blogjava.net/TrampEagle/articles/48756.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 29 May 2006 07:05:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48756.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48756.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48756.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48756.html</trackback:ping><description><![CDATA[
		<p>
				<font face="Verdana">public class GetCh2Spell {<br />    public static int compare(String str1, String str2)<br />     {<br />         int result = 0;<br />         String m_s1 = null;<br />         String m_s2 = null;<br />         try<br />         {<br />             m_s1 = new String(str1.getBytes(_FromEncode_), _ToEncode_);<br />             m_s2 = new String(str2.getBytes(_FromEncode_), _ToEncode_);<br />         }<br />         catch(Exception e)<br />         {<br />             return str1.compareTo(str2);<br />         }<br />         result = chineseCompareTo(m_s1, m_s2);<br />         return result;<br />     }</font>
		</p>
		<p>
				<font face="Verdana">     public static int getCharCode(String s)<br />     {<br />         if(s == null &amp;&amp; s.equals(""))<br />             return -1;<br />         byte b[] = s.getBytes();<br />         int value = 0;<br />         for(int i = 0; i &lt; b.length &amp;&amp; i &lt;= 2; i++)<br />             value = value * 100 + b[i];</font>
		</p>
		<p>
				<font face="Verdana">         return value;<br />     }</font>
		</p>
		<p>
				<font face="Verdana">     public static int chineseCompareTo(String s1, String s2)<br />     {<br />         int len1 = s1.length();<br />         int len2 = s2.length();<br />         int n = Math.min(len1, len2);<br />         for(int i = 0; i &lt; n; i++)<br />         {<br />             int s1_code = getCharCode(s1.charAt(i) + "");<br />             int s2_code = getCharCode(s2.charAt(i) + "");<br />             if(s1_code * s2_code &lt; 0)<br />                 return Math.min(s1_code, s2_code);<br />             if(s1_code != s2_code)<br />                 return s1_code - s2_code;<br />         }</font>
		</p>
		<p>
				<font face="Verdana">         return len1 - len2;<br />     }</font>
		</p>
		<p>
				<font face="Verdana">     public static String getBeginCharacter(String res)<br />     {<br />         String a = res;<br />         String result = "";<br />         for(int i = 0; i &lt; a.length(); i++)<br />         {<br />             String current = a.substring(i, i + 1);<br />             if(compare(current, "\u554A") &lt; 0)<br />                 result = result + current;<br />             else<br />             if(compare(current, "\u554A") &gt;= 0 &amp;&amp; compare(current, "\u5EA7") &lt;= 0)<br />                 if(compare(current, "\u531D") &gt;= 0)<br />                     result = result + "z";<br />                 else<br />                 if(compare(current, "\u538B") &gt;= 0)<br />                     result = result + "y";<br />                 else<br />                 if(compare(current, "\u6614") &gt;= 0)<br />                     result = result + "x";<br />                 else<br />                 if(compare(current, "\u6316") &gt;= 0)<br />                     result = result + "w";<br />                 else<br />                 if(compare(current, "\u584C") &gt;= 0)<br />                     result = result + "t";<br />                 else<br />                 if(compare(current, "\u6492") &gt;= 0)<br />                     result = result + "s";<br />                 else<br />                 if(compare(current, "\u7136") &gt;= 0)<br />                     result = result + "r";<br />                 else<br />                 if(compare(current, "\u671F") &gt;= 0)<br />                     result = result + "q";<br />                 else<br />                 if(compare(current, "\u556A") &gt;= 0)<br />                     result = result + "p";<br />                 else<br />                 if(compare(current, "\u54E6") &gt;= 0)<br />                     result = result + "o";<br />                 else<br />                 if(compare(current, "\u62FF") &gt;= 0)<br />                     result = result + "n";<br />                 else<br />                 if(compare(current, "\u5988") &gt;= 0)<br />                     result = result + "m";<br />                 else<br />                 if(compare(current, "\u5783") &gt;= 0)<br />                     result = result + "l";<br />                 else<br />                 if(compare(current, "\u5580") &gt;= 0)<br />                     result = result + "k";<br />                 else<br />                 if(compare(current, "\u51FB") &gt; 0)<br />                     result = result + "j";<br />                 else<br />                 if(compare(current, "\u54C8") &gt;= 0)<br />                     result = result + "h";<br />                 else<br />                 if(compare(current, "\u5676") &gt;= 0)<br />                     result = result + "g";<br />                 else<br />                 if(compare(current, "\u53D1") &gt;= 0)<br />                     result = result + "f";<br />                 else<br />                 if(compare(current, "\u86FE") &gt;= 0)<br />                     result = result + "e";<br />                 else<br />                 if(compare(current, "\u642D") &gt;= 0)<br />                     result = result + "d";<br />                 else<br />                 if(compare(current, "\u64E6") &gt;= 0)<br />                     result = result + "c";<br />                 else<br />                 if(compare(current, "\u82AD") &gt;= 0)<br />                     result = result + "b";<br />                 else<br />                 if(compare(current, "\u554A") &gt;= 0)<br />                     result = result + "a";<br />         }</font>
		</p>
		<p>
				<font face="Verdana">         return result;<br />     }</font>
		</p>
		<p>
				<font face="Verdana">     public static String getFirstStr(String str)<br />     {<br />         char a = str.charAt(0);<br />         char aa[] = {<br />             a<br />         };<br />         String sss = new String(aa);<br />         if(Character.isDigit(aa[0]))<br />             sss = "data";<br />         else<br />         if(a &gt;= 'a' &amp;&amp; a &lt;= 'z' || a &gt;= 'A' &amp;&amp; a &lt;= 'Z')<br />             sss = "character";<br />         else<br />             sss = getBeginCharacter(sss);<br />         return sss;<br />     }</font>
		</p>
		<p>
				<font face="Verdana">     private static String _FromEncode_ = "GBK";<br />     private static String _ToEncode_ = "GBK";</font>
		</p>
		<p>
				<font face="Verdana">}</font>
		</p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/48756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-29 15:05 <a href="http://www.blogjava.net/TrampEagle/articles/48756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaMail API详解</title><link>http://www.blogjava.net/TrampEagle/articles/48327.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 26 May 2006 06:21:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48327.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48327.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48327.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48327.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48327.html</trackback:ping><description><![CDATA[原文引自：<a href="http://www.matrix.org.cn/resource/article/44/44101_JavaMail.html">http://www.matrix.org.cn/resource/article/44/44101_JavaMail.html</a><br /><br /><br /><div class="center"><h4>摘要:</h4>JavaMail API是读取、撰写、发送电子信息的可选包。我们可用它来建立如Eudora、Foxmail、MS Outlook Express一般的邮件用户代理程序（Mail User Agent,简称MUA）。让我们看看JavaMail API是如何提供信息访问功能的吧！JavaMail API被设计用于以不依赖协议的方式去发送和接收电子信息，文中着重：如何以不依赖于协议的方式发送接收电子信息，这也是本文所要描述的. </div><div class="right"><div class="help"><h4>文章工具</h4></div></div><!-- end of summary line --><div class="overflow" id="text"><span style="COLOR: red">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span><br />作者:cleverpig(作者的Blog:<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">http://blog.matrix.org.cn/page/cleverpig</a>)<br />原文:<a href="http://www.matrix.org.cn/resource/article/44/44101_JavaMail.html" target="_new">http://www.matrix.org.cn/resource/article/44/44101_JavaMail.html</a><br />关键字:java,mail,pop,smtp<br /><br /><span style="COLOR: blue">一、JavaMail API简介</span><br />JavaMail API是读取、撰写、发送电子信息的可选包。我们可用它来建立如Eudora、Foxmail、MS Outlook Express一般的邮件用户代理程序（Mail User Agent,简称MUA）。而不是像sendmail或者其它的邮件传输代理（Mail Transfer Agent，简称MTA）程序那样可以传送、递送、转发邮件。从另外一个角度来看，我们这些电子邮件用户日常用MUA程序来读写邮件，而MUA依赖着MTA处理邮件的递送。<br />在清楚了到MUA与MTA之间的关系后，让我们看看JavaMail API是如何提供信息访问功能的吧！JavaMail API被设计用于以不依赖协议的方式去发送和接收电子信息，这个API被分为两大部分：<br /><br />基本功能：如何以不依赖于协议的方式发送接收电子信息，这也是本文所要描述的，不过在下文中，大家将看到这只是一厢情愿而已。<br />第二个部分则是依赖特定协议的，比如SMTP、POP、IMAP、NNTP协议。在这部分的JavaMail API是为了和服务器通讯，并不在本文的内容中。<br /><br /><span style="COLOR: blue">二、相关协议一览</span><br />在我们步入JavaMail API之前，先看一下API所涉及的协议。以下便是大家日常所知、所乐于使用的4大信息传输协议：<br />SMTP<br />POP<br />IMAP<br />MIME<br />当然，上面的4个协议，并不是全部，还有NNTP和其它一些协议可用于传输信息，但是由于不常用到，所以本文便不提及了。理解这4个基本的协议有助于我们更好的使用JavaMail API。然而JavaMail API是被设计为与协议无关的，目前我们并不能克服这些协议的束缚。确切的说，如果我们使用的功能并不被我们选择的协议支持，那么JavaMail API并不可能如魔术师一样神奇的赋予我们这种能力。<br /><br /><span style="COLOR: blue">1．SMTP</span><br />简单邮件传输协议定义了递送邮件的机制。在下文中，我们将使用基于Java-Mail的程序与公司或者ISP的SMTP服务器进行通讯。这个SMTP服务器将邮件转发到接收者的SMTP服务器，直至最后被接收者通过POP或者IMAP协议获取。这并不需要SMTP服务器使用支持授权的邮件转发，但是却的确要注意SMTP服务器的正确设置（SMTP服务器的设置与JavaMail API无关）。<br /><br /><span style="COLOR: blue">2．POP</span><br />POP是一种邮局协议，目前为第3个版本，即众所周知的POP3。POP定义了一种用户如何获得邮件的机制。它规定了每个用户使用一个单独的邮箱。大多数人在使用POP时所熟悉的功能并非都被支持，例如查看邮箱中的新邮件数量。而这个功能是微软的Outlook内建的，那么就说明微软Outlook之类的邮件客户端软件是通过查询最近收到的邮件来计算新邮件的数量来实现前面所说的功能。因此在我们使用JavaMail API时需要注意，当需要获得如前面所讲的新邮件数量之类的信息时，我们不得不自己进行计算。<br /><br /><span style="COLOR: blue">3．IMAP</span><br />IMAP使用在接收信息的高级协议，目前版本为第4版，所以也被称为IMAP4。需要注意的是在使用IMAP时，邮件服务器必须支持该协议。从这个方面讲，我们并不能完全使用IMAP来替代POP，不能期待IMAP在任何地方都被支持。假如邮件服务器支持IMAP，那么我们的邮件程序将能够具有以下被IMAP所支持的特性：每个用户在服务器上可具有多个目录，这些目录能在多个用户之间共享。<br />其与POP相比高级之处显而易见，但是在尝试采取IMAP时，我们认识到它并不是十分完美的：由于IMAP需要从其它服务器上接收新信息，将这些信息递送给用户，维护每个用户的多个目录，这都为邮件服务器带来了高负载。并且IMAP与POP的一个不同之处是POP用户在接收邮件时将从邮件服务器上下载邮件，而IMAP允许用户直接访问邮件目录，所以在邮件服务器进行备份作业时，由于每个长期使用此邮件系统的用户所用的邮件目录会占有很大的空间，这将直接导致邮件服务器上磁盘空间暴涨。<br /><br /><span style="COLOR: blue">4．MIME</span><br />MIME并不是用于传送邮件的协议，它作为多用途邮件的扩展定义了邮件内容的格式：信息格式、附件格式等等。一些RFC标准都涉及了MIME：RFC 822, RFC 2045, RFC 2046, RFC 2047，有兴趣的Matrixer可以阅读一下。而作为JavaMail API的开发者，我们并不需关心这些格式定义，但是这些格式被用在了程序中。<br /><br /><span style="COLOR: blue">5．NNTP和其它的第三方协议</span><br />正因为JavaMail API在设计时考虑到与第三方协议实现提供商之间的分离，故我们可以很容易的添加一些第三方协议。SUN维护着一个第三方协议实现提供商的列表：<a href="http://java.sun.com/products/javamail/Third_Party.html" target="_new"><font color="#002c99">http://java.sun.com/products/javamail/Third_Party.html</font></a>，通过此列表我们可以找到所需要的而又不被SUN提供支持的第三方协议：比如NNTP这个新闻组协议和S/MIME这个安全的MIME协议。<br /><br /><span style="COLOR: blue">三、安装</span><br /><span style="COLOR: blue">1．安装JavaMail</span><br />为了使用JavaMail API，需要从<a href="ttp://java.sun.com/products/javamail/downloads/index.html" target="_new"><font color="#002c99">http://java.sun.com/products/javamail/downloads/index.html</font></a>下载文件名格式为javamail-[version].zip的文件（这个文件中包括了JavaMail实现），并将其中的mail.jar文件添加到CLASSPATH中。这个实现提供了对SMTP、IMAP4、POP3的支持。<br />注意：在安装JavaMail实现之后，我们将在demo目录中发现许多有趣的简单实例程序。<br />在安装了JavaMail之后,我们还需要安装JavaBeans Activation Framework，因为这个框架是JavaMail API所需要的。如果我们使用J2EE的话，那么我们并无需单独下载JavaMail，因为它存在于J2EE.jar中，只需将J2EE.jar加入到CLASSPATH即可。<br /><br /><span style="COLOR: blue">2．安装JavaBeans Activation Framework</span><br />从<a href="http://java.sun.com/products/javabeans/glasgow/jaf.html" target="_new"><font color="#002c99">http://java.sun.com/products/javabeans/glasgow/jaf.html</font></a>下载JavaBeans Activation Framework，并将其添加到CLASSPATH中。此框架增加了对任何数据块的分类、以及对它们的处理的特性。这些特性是JavaMail API需要的。虽然听起来这些特性非常模糊，但是它对于我们的JavaMail API来说只是提供了基本的MIME类型支持。<br />到此为止，我们应当把mail.jar和activation.jar都添加到了CLASSPATH中。<br />当然如果从方便的角度讲，直接把这两个Jar文件复制到JRE目录的lib/ext目录中也可以。<br /><br /><span style="COLOR: blue">四、初次认识JavaMail API</span><br /><span style="COLOR: blue">1．了解我们的JavaMail环境</span><br /><span style="COLOR: green">A．纵览JavaMail核心类结构</span><br />打开JavaMail.jar文件，我们将发现在javax.mail的包下面存在着一些核心类：Session、Message、Address、Authenticator、Transport、Store、Folder。而且在javax.mail.internet包中还有一些常用的子类。<br /><span style="COLOR: green">B．Session</span><br />Session类定义了基本的邮件会话。就像Http会话那样，我们进行收发邮件的工作都是基于这个会话的。Session对象利用了java.util.Properties对象获得了邮件服务器、用户名、密码信息和整个应用程序都要使用到的共享信息。<br />Session类的构造方法是私有的，所以我们可以使用Session类提供的getDefaultInstance()这个静态工厂方法获得一个默认的Session对象：<br /><pre class="overflow"><br />Properties props = new Properties();<br />// fill props with any information<br />Session session = Session.getDefaultInstance(props, null);<br /></pre><br />或者使用getInstance()这个静态工厂方法获得自定义的Session: <br /><pre class="overflow"><br />Properties props = new Properties();<br />// fill props with any information<br />Session session = Session.getInstance(props, null);<br /></pre><br />从上面的两个例子中不难发现，getDefaultInstance()和getInstance()方法的第二个参数都是null，这是因为在上面的例子中并没有使用到邮件授权，下文中将对授权进行详细介绍。<br />从很多的实例看，在对mail server进行访问的过程中使用共享的Session是足够的，即使是工作在多个用户邮箱的模式下也不例外。<br /><br /><span style="COLOR: green">C．Message</span><br />当我们建立了Session对象后，便可以被发送的构造信息体了。在这里SUN提供了Message类型来帮助开发者完成这项工作。由于Message是一个抽象类，大多数情况下，我们使用javax.mail.internet.MimeMessage这个子类，该类是使用MIME类型、MIME信息头的邮箱信息。信息头只能使用US-ASCII字符，而非ASCII字符将通过编码转换为ASCII的方式使用。<br />为了建立一个MimeMessage对象，我们必须将Session对象作为MimeMessage构造方法的参数传入：<br /><pre class="overflow"><br />MimeMessage message = new MimeMessage(session);<br /></pre><br />注意：对于MimeMessage类来讲存在着多种构造方法，比如使用输入流作为参数的构造方法。<br /><br />在建立了MimeMessage对象后，我们需要设置它的各个part，对于MimeMessage类来说，这些part就是MimePart接口。最基本的设置信息内容的方法就是通过表示信息内容和米么类型的参数调用setContent()方法：<br /><pre class="overflow"><br />message.setContent("Hello", "text/plain");<br /></pre><br />然而，如果我们所使用的MimeMessage中信息内容是文本的话，我们便可以直接使用setText()方法来方便的设置文本内容。<br /><pre class="overflow"><br />message.setText("Hello");<br /></pre><br />前面所讲的两种方法，对于文本信息，后者更为合适。而对于其它的一些信息类型，比如HTML信息，则要使用前者。<br />别忘记了，使用setSubject()方法对邮件设置邮件主题：<br /><pre class="overflow"><br />message.setSubject("First");<br /></pre><br /><br /><span style="COLOR: green">D．Address</span><br />到这里，我们已经建立了Session和Message，下面将介绍如何使用邮件地址类：Address。像Message一样，Address类也是一个抽象类，所以我们将使用javax.mail.internet.InternetAddress这个子类。<br />通过传入代表邮件地址的字符串，我们可以建立一个邮件地址类：<br /><pre class="overflow"><br />Address address = new InternetAddress("president@whitehouse.gov"); <br /></pre><br />如果要在邮件地址后面增加名字的话，可以通过传递两个参数：代表邮件地址和名字的字符串来建立一个具有邮件地址和名字的邮件地址类：<br /><pre class="overflow"><br />Address address = new InternetAddress("president@whitehouse.gov", "George Bush"); <br /></pre><br />本文在这里所讲的邮件地址类是为了设置邮件信息的发信人和收信人而准备的，在建立了邮件地址类后，我们通过message的setFrom()和setReplyTo()两种方法设置邮件的发信人：<br /><pre class="overflow"><br />message.setFrom(address);<br />message.setReplyTo(address);<br /></pre><br />若在邮件中存在多个发信人地址，我们可用addForm()方法增加发信人：<br /><pre class="overflow"><br />Address address[] = ...;<br />message.addFrom(address);<br /></pre><br />为了设置收信人，我们使用addRecipient()方法增加收信人，此方法需要使用Message.RecipientType的常量来区分收信人的类型：<br /><pre class="overflow"><br />message.addRecipient(type, address)<br /></pre><br />下面是Message.RecipientType的三个常量:<br />Message.RecipientType.TO<br />Message.RecipientType.CC<br />Message.RecipientType.BCC<br />因此，如果我们要发送邮件给总统，并发用一个副本给第一夫人的话，下面的方法将被用到：<br /><pre class="overflow"><br />Address toAddress = new InternetAddress("vice.president@whitehouse.gov");<br />Address ccAddress = new InternetAddress("first.lady@whitehouse.gov");<br />message.addRecipient(Message.RecipientType.TO, toAddress);<br />message.addRecipient(Message.RecipientType.CC, ccAddress);<br /></pre><br />JavaMail API并没有提供检查邮件地址有效性的机制。当然我们可以自己完成这个功能：验证邮件地址的字符是否按照RFC822规定的格式书写或者通过DNS服务器上的MX记录验证等。<br /><br /><span style="COLOR: green">E．Authenticator</span><br />像java.net类那样，JavaMail API通过使用授权者类（Authenticator）以用户名、密码的方式访问那些受到保护的资源，在这里“资源”就是指邮件服务器。在javax.mail包中可以找到这个JavaMail的授权者类（Authenticator）。<br />在使用Authenticator这个抽象类时，我们必须采用继承该抽象类的方式，并且该继承类必须具有返回PasswordAuthentication对象（用于存储认证时要用到的用户名、密码）getPasswordAuthentication()方法。并且要在Session中进行注册，使Session能够了解在认证时该使用哪个类。<br />下面代码片断中的MyAuthenticator就是一个Authenticator的子类。<br /><pre class="overflow"><br />Properties props = new Properties();<br />// fill props with any information<br />Authenticator auth = new MyAuthenticator();<br />Session session = Session.getDefaultInstance(props, auth);<br /></pre><br /><br /><span style="COLOR: green">F．Transport</span><br />在发送信息时，Transport类将被用到。这个类实现了发送信息的协议（通称为SMTP），此类是一个抽象类，我们可以使用这个类的静态方法send()来发送消息：<br /><pre class="overflow"><br />Transport.send(message);<br /></pre><br />当然，方法是多样的。我们也可由Session获得相应协议对应的Transport实例。并通过传递用户名、密码、邮件服务器主机名等参数建立与邮件服务器的连接，并使用sendMessage()方法将信息发送，最后关闭连接：<br /><pre class="overflow"><br />message.saveChanges(); // implicit with send()<br />Transport transport = session.getTransport("smtp");<br />transport.connect(host, username, password);<br />transport.sendMessage(message, message.getAllRecipients());<br />transport.close();<br /></pre><br />评论：上面的方法是一个很好的方法，尤其是在我们在同一个邮件服务器上发送多个邮件时。因为这时我们将在连接邮件服务器后连续发送邮件，然后再关闭掉连接。send()这个基本的方法是在每次调用时进行与邮件服务器的连接的，对于在同一个邮件服务器上发送多个邮件来讲可谓低效的方式。<br />注意：如果需要在发送邮件过程中监控mail命令的话，可以在发送前设置debug标志：<br /><pre class="overflow"><br />session.setDebug(true)。<br /></pre><br /><br /><span style="COLOR: green">G．Store和Folder</span><br />接收邮件和发送邮件很类似都要用到Session。但是在获得Session后，我们需要从Session中获取特定类型的Store，然后连接到Store，这里的Store代表了存储邮件的邮件服务器。在连接Store的过程中，极有可能需要用到用户名、密码或者Authenticator。<br /><pre class="overflow"><br />// Store store = session.getStore("imap");<br />Store store = session.getStore("pop3");<br />store.connect(host, username, password);<br /></pre><br />在连接到Store后，一个Folder对象即目录对象将通过Store的getFolder()方法被返回，我们可从这个Folder中读取邮件信息：<br /><pre class="overflow"><br />Folder folder = store.getFolder("INBOX");<br />folder.open(Folder.READ_ONLY);<br />Message message[] = folder.getMessages();<br /></pre><br />上面的例子首先从Store中获得INBOX这个Folder（对于POP3协议只有一个名为INBOX的Folder有效），然后以只读（Folder.READ_ONLY）的方式打开Folder，最后调用Folder的getMessages()方法得到目录中所有Message的数组。<br /><br />注意：对于POP3协议只有一个名为INBOX的Folder有效，而对于IMAP协议，我们可以访问多个Folder（想想前面讲的IMAP协议）。而且SUN在设计Folder的getMessages()方法时采取了很智能的方式：首先接收新邮件列表，然后再需要的时候（比如读取邮件内容）才从邮件服务器读取邮件内容。<br />在读取邮件时，我们可以用Message类的getContent()方法接收邮件或是writeTo()方法将邮件保存，getContent()方法只接收邮件内容（不包含邮件头），而writeTo()方法将包括邮件头。<br /><pre class="overflow"><br />System.out.println(((MimeMessage)message).getContent());<br /></pre><br />在读取邮件内容后，别忘记了关闭Folder和Store。<br /><pre class="overflow"><br />folder.close(aBoolean);<br />store.close();<br /></pre><br />传递给Folder.close()方法的boolean 类型参数表示是否在删除操作邮件后更新Folder。 <br /><br /><span style="COLOR: green">H．继续向前进！</span><br />在讲解了以上的七个Java Mail核心类定义和理解了简单的代码片断后，下文将详细讲解怎样使用这些类实现JavaMail API所要完成的高级功能。<br /><br /><span style="COLOR: blue">五、使用JavaMail API</span><br />在明确了JavaMail API的核心部分如何工作后，本人将带领大家学习一些使用Java Mail API任务案例。<br /><span style="COLOR: blue">1．发送邮件</span><br />在获得了Session后，建立并填入邮件信息，然后发送它到邮件服务器。这便是使用Java Mail API发送邮件的过程，在发送邮件之前，我们需要设置SMTP服务器：通过设置Properties的mail.smtp.host属性。<br /><pre class="overflow"><br />String host = ...;<br />String from = ...;<br />String to = ...;<br /><br />// Get system properties<br />Properties props = System.getProperties();<br /><br />// Setup mail server<br />props.put("mail.smtp.host", host);<br /><br />// Get session<br />Session session = Session.getDefaultInstance(props, null);<br /><br />// Define message<br />MimeMessage message = new MimeMessage(session);<br />message.setFrom(new InternetAddress(from));<br />message.addRecipient(Message.RecipientType.TO, <br />  new InternetAddress(to));<br />message.setSubject("Hello JavaMail");<br />message.setText("Welcome to JavaMail");<br />// Send message<br />Transport.send(message);<br /></pre><br />由于建立邮件信息和发送邮件的过程中可能会抛出异常，所以我们需要将上面的代码放入到try-catch结构块中。<br /><br /><span style="COLOR: blue">2．接收邮件</span><br />为了在读取邮件，我们获得了session，并且连接到了邮箱的相应store，打开相应的Folder，然后得到我们想要的邮件，当然别忘记了在结束时关闭连接。<br /><pre class="overflow"><br />String host = ...;<br />String username = ...;<br />String password = ...;<br /><br />// Create empty properties<br />Properties props = new Properties();<br /><br />// Get session<br />Session session = Session.getDefaultInstance(props, null);<br /><br />// Get the store<br />Store store = session.getStore("pop3");<br />store.connect(host, username, password);<br /><br />// Get folder<br />Folder folder = store.getFolder("INBOX");<br />folder.open(Folder.READ_ONLY);<br /><br />// Get directory<br />Message message[] = folder.getMessages();<br /><br />for (int i=0, n=message.length; i&lt;n; i++) {<br />   System.out.println(i + ": " + message[i].getFrom()[0] <br />     + "\t" + message[i].getSubject());<br />}<br /><br />// Close connection <br />folder.close(false);<br />store.close();<br /></pre><br />上面的代码所作的是从邮箱中读取每个邮件，并且显示邮件的发信人地址和主题。从技术角度讲，这里存在着一个异常的可能：当发信人地址为空时，getFrom()[0]将抛出异常。<br /><br />下面的代码片断有效的说明了如何读取邮件内容，在显示每个邮件发信人和主题后，将出现用户提示从而得到用户是否读取该邮件的确认，如果输入YES的话，我们可用Message.writeTo(java.io.OutputStream os)方法将邮件内容输出到控制台上，关于Message.writeTo()的具体用法请看JavaMail API。<br /><pre class="overflow"><br />BufferedReader reader = new BufferedReader (<br />  new InputStreamReader(System.in));<br /><br />// Get directory<br />Message message[] = folder.getMessages();<br />for (int i=0, n=message.length; i&lt;n; i++) {<br />  System.out.println(i + ": " + message[i].getFrom()[0] <br />    + "\t" + message[i].getSubject());<br /><br />  System.out.println("Do you want to read message? " +<br />    "[YES to read/QUIT to end]");<br />  String line = reader.readLine();<br />  if ("YES".equals(line)) {<br />    message[i].writeTo(System.out);<br />  } else if ("QUIT".equals(line)) {<br />    break;<br />  }<br />}<br /></pre><br /><br /><span style="COLOR: blue">3．删除邮件和标志</span><br />设置与message相关的Flags是删除邮件的常用方法。这些Flags表示了一些系统定义和用户定义的不同状态。在Flags类的内部类Flag中预定义了一些标志：<br />Flags.Flag.ANSWERED<br />Flags.Flag.DELETED<br />Flags.Flag.DRAFT<br />Flags.Flag.FLAGGED<br />Flags.Flag.RECENT<br />Flags.Flag.SEEN<br />Flags.Flag.USER<br />但需要在使用时注意的：标志存在并非意味着这个标志被所有的邮件服务器所支持。例如，对于删除邮件的操作，POP协议不支持上面的任何一个。所以要确定哪些标志是被支持的——通过访问一个已经打开的Folder对象的getPermanetFlags()方法，它将返回当前被支持的Flags类对象。<br />删除邮件时，我们可以设置邮件的DELETED标志： <br /><pre class="overflow"><br />message.setFlag(Flags.Flag.DELETED, true);<br /></pre><br />但是首先要采用READ_WRITE的方式打开Folder：<br /><pre class="overflow"><br />folder.open(Folder.READ_WRITE);<br /></pre><br />在对邮件进行删除操作后关闭Folder时，需要传递一个true作为对删除邮件的擦除确认。<br /><pre class="overflow"><br />folder.close(true);<br /></pre><br />Folder类中另一种用于删除邮件的方法expunge()也同样可删除邮件，但是它并不为sun提供的POP3实现支持，而其它第三方提供的POP3实现支持或者并不支持这种方法。<br />另外，介绍一种检查某个标志是否被设置的方法：Message.isSet(Flags.Flag flag)方法，其中参数为被检查的标志。<br /><br /><span style="COLOR: blue">4．邮件认证</span><br />我们在前面已经学会了如何使用Authenticator类来代替直接使用用户名和密码这两字符串作为Session.getDefaultInstance()或者Session.getInstance()方法的参数。在前面的小试牛刀后，现在我们将了解到全面认识一下邮件认证。<br />我们在此取代了直接使用邮件服务器主机名、用户名、密码这三个字符串作为连接到POP3 Store的方式，使用存储了邮件服务器主机名信息的属性文件，并在获得Session时传入自定义的Authenticator实例：<br /><pre class="overflow"><br />// Setup properties<br />Properties props = System.getProperties();<br />props.put("mail.pop3.host", host);<br /><br />// Setup authentication, get session<br />Authenticator auth = new PopupAuthenticator();<br />Session session = Session.getDefaultInstance(props, auth);<br /><br />// Get the store<br />Store store = session.getStore("pop3");<br />store.connect();<br /></pre><br /><br />PopupAuthenticator类继承了抽象类Authenticator，并且通过重载Authenticator类的getPasswordAuthentication()方法返回PasswordAuthentication类对象。而getPasswordAuthentication()方法的参数param是以逗号分割的用户名、密码组成的字符串。<br /><pre class="overflow"><br />import javax.mail.*;<br />import java.util.*;<br /><br />public class PopupAuthenticator extends Authenticator {<br /><br />  public PasswordAuthentication getPasswordAuthentication(String param) {<br />    String username, password;<br /><br />    StringTokenizer st = new StringTokenizer(param, ",");<br />    username = st.nextToken();<br />    password = st.nextToken();<br /><br />    return new PasswordAuthentication(username, password);<br />  }<br /><br />}<br /></pre><br /><br /><span style="COLOR: blue">5．回复邮件</span><br />回复邮件的方法很简单：使用Message类的reply()方法，通过配置回复邮件的收件人地址和主题（如果没有提供主题的话，系统将默认将“Re：”作为邮件的主体），这里不需要设置任何的邮件内容，只要复制发信人或者reply-to到新的收件人。而reply()方法中的boolean参数表示是否将邮件回复给发送者（参数值为false），或是恢复给所有人（参数值为true）。<br />补充一下，reply-to地址需要在发信时使用setReplyTo()方法设置。<br /><pre class="overflow"><br />MimeMessage reply = (MimeMessage)message.reply(false);<br />reply.setFrom(new InternetAddress("president@whitehouse.gov"));<br />reply.setText("Thanks");<br />Transport.send(reply);<br /></pre><br /><br /><span style="COLOR: blue">6．转发邮件</span><br />转发邮件的过程不如前面的回复邮件那样简单，它将建立一个转发邮件，这并非一个方法就能做到。<br />每个邮件是由多个部分组成，每个部分称为一个邮件体部分，是一个BodyPart类对象，对于MIME类型邮件来讲就是MimeBodyPart类对象。这些邮件体包含在成为Multipart的容器中对于MIME类型邮件来讲就是MimeMultiPart类对象。在转发邮件时，我们建立一个文字邮件体部分和一个被转发的文字邮件体部分，然后将这两个邮件体放到一个Multipart中。说明一下，复制一个邮件内容到另一个邮件的方法是仅复制它的DataHandler（数据处理者）即可。这是由JavaBeans Activation Framework定义的一个类，它提供了对邮件内容的操作命令的访问、管理了邮件内容操作，是不同的数据源和数据格式之间的一致性接口。<br /><pre class="overflow"><br />// Create the message to forward<br />Message forward = new MimeMessage(session);<br /><br />// Fill in header<br />forward.setSubject("Fwd: " + message.getSubject());<br />forward.setFrom(new InternetAddress(from));<br />forward.addRecipient(Message.RecipientType.TO, <br />  new InternetAddress(to));<br /><br />// Create your new message part<br />BodyPart messageBodyPart = new MimeBodyPart();<br />messageBodyPart.setText(<br />  "Here you go with the original message:\n\n");<br /><br />// Create a multi-part to combine the parts<br />Multipart multipart = new MimeMultipart();<br />multipart.addBodyPart(messageBodyPart);<br /><br />// Create and fill part for the forwarded content<br />messageBodyPart = new MimeBodyPart();<br />messageBodyPart.setDataHandler(message.getDataHandler());<br /><br />// Add part to multi part<br />multipart.addBodyPart(messageBodyPart);<br /><br />// Associate multi-part with message<br />forward.setContent(multipart);<br /><br />// Send message<br />Transport.send(forward);<br /></pre><br /><br /><span style="COLOR: blue">7．使用附件</span><br />附件作为与邮件相关的资源经常以文本、表格、图片等格式出现，如流行的邮件客户端一样，我们可以用JavaMail API从邮件中获取附件或是发送带有附件的邮件。<br /><br /><span style="COLOR: green">A．发送带有附件的邮件</span><br />发送带有附件的邮件的过程有些类似转发邮件，我们需要建立一个完整邮件的各个邮件体部分，在第一个部分（即我们的邮件内容文字）后，增加一个具有DataHandler的附件而不是在转发邮件时那样复制第一个部分的DataHandler。<br /><br />如果我们将文件作为附件发送，那么要建立FileDataSource类型的对象作为附件数据源；如果从URL读取数据作为附件发送，那么将要建立URLDataSource类型的对象作为附件数据源。<br /><br />然后将这个数据源（FileDataSource或是URLDataSource）对象作为DataHandler类构造方法的参数传入，从而建立一个DataHandler对象作为数据源的DataHandler。<br /><br />接着将这个DataHandler设置为邮件体部分的DataHandler。这样就完成了邮件体与附件之间的关联工作，下面的工作就是BodyPart的setFileName()方法设置附件名为原文件名。<br /><br />最后将两个邮件体放入到Multipart中，设置邮件内容为这个容器Multipart，发送邮件。<br /><pre class="overflow"><br />// Define message<br />Message message = new MimeMessage(session);<br />message.setFrom(new InternetAddress(from));<br />message.addRecipient(Message.RecipientType.TO, <br />  new InternetAddress(to));<br />message.setSubject("Hello JavaMail Attachment");<br /><br />// Create the message part <br />BodyPart messageBodyPart = new MimeBodyPart();<br /><br />// Fill the message<br />messageBodyPart.setText("Pardon Ideas");<br /><br />Multipart multipart = new MimeMultipart();<br />multipart.addBodyPart(messageBodyPart);<br /><br />// Part two is attachment<br />messageBodyPart = new MimeBodyPart();<br />DataSource source = new FileDataSource(filename);<br />messageBodyPart.setDataHandler(new DataHandler(source));<br />messageBodyPart.setFileName(filename);<br />multipart.addBodyPart(messageBodyPart);<br /><br />// Put parts in message<br />message.setContent(multipart);<br /><br />// Send the message<br />Transport.send(message);<br /></pre><br />如果我们使用servlet实现发送带有附件的邮件，则必须上传附件给servlet，这时需要注意提交页面form中对编码类型的设置应为multipart/form-data。<br /><pre class="overflow"><br />&lt;FORM ENCTYPE="multipart/form-data" <br />    method=post action="/myservlet"&gt; <br />  &lt;INPUT TYPE="file" NAME="thefile"&gt;<br />  &lt;INPUT TYPE="submit" VALUE="Upload"&gt;<br />&lt;/FORM&gt;<br /></pre><br /><br /><span style="COLOR: green">B．读取邮件中的附件</span><br />读取邮件中的附件的过程要比发送它的过程复杂一点。因为带有附件的邮件是多部分组成的，我们必须处理每一个部分获得邮件的内容和附件。<br />但是如何辨别邮件信息内容和附件呢？Sun在Part类（BodyPart类实现的接口类）中提供了getDisposition()方法让开发者获得邮件体部分的部署类型，当该部分是附件时，其返回之将是Part.ATTACHMENT。但附件也可以没有部署类型的方式存在或者部署类型为Part.INLINE，无论部署类型为Part.ATTACHMENT还是Part.INLINE，我们都能把该邮件体部分导出保存。<br /><pre class="overflow"><br />Multipart mp = (Multipart)message.getContent();<br /><br />for (int i=0, n=multipart.getCount(); i&lt;n; i++) {<br />  Part part = multipart.getBodyPart(i));<br /><br />  String disposition = part.getDisposition();<br /><br />  if ((disposition != null) &amp;&amp; <br />      ((disposition.equals(Part.ATTACHMENT) || <br />       (disposition.equals(Part.INLINE))) {<br />    saveFile(part.getFileName(), part.getInputStream());<br />  }<br />}<br /></pre><br />下列代码中使用了saveFile方法是自定义的方法，它根据附件的文件名建立一个文件，如果本地磁盘上存在名为附件的文件，那么将在文件名后增加数字表示区别。然后从邮件体中读取数据写入到本地文件中（代码省略）。<br /><pre class="overflow"><br />// from saveFile()<br />File file = new File(filename);<br />for (int i=0; file.exists(); i++) {<br />  file = new File(filename+i);<br />}<br /></pre><br />以上是邮件体部分被正确设置的简单例子，如果邮件体部分的部署类型为null，那么我们通过获得邮件体部分的MIME类型来判断其类型作相应的处理，代码结构框架如下：<br /><pre class="overflow"><br />if (disposition == null) {<br />  // Check if plain<br />  MimeBodyPart mbp = (MimeBodyPart)part;<br />  if (mbp.isMimeType("text/plain")) {<br />    // Handle plain<br />  } else {<br />    // Special non-attachment cases here of <br />    // image/gif, text/html, ...<br />  }<br />...<br />}<br /></pre><br /><br /><span style="COLOR: blue">8．处理HTML邮件</span><br />前面的例子中发送的邮件都是以文本为内容的（除了附件），下面将介绍如何接收和发送基于HTML的邮件。<br /><span style="COLOR: green">A．发送HTML邮件</span><br />假如我们需要发送一个HTML文件作为邮件内容，并使邮件客户端在读取邮件时获取相关的图片或者文字的话，只要设置邮件内容为html代码，并设置内容类型为text/html即可：<br /><pre class="overflow"><br />String htmlText = "&lt;H1&gt;Hello&lt;/H1&gt;" + <br />  "&lt;img src=\"http://www.jguru.com/images/logo.gif\"&gt;";<br />message.setContent(htmlText, "text/html"));<br /></pre><br />请注意：这里的图片并不是在邮件中内嵌的，而是在URL中定义的。邮件接收者只有在线时才能看到。<br />在接收邮件时，如果我们使用JavaMail API接收邮件的话是无法实现以HTML方式显示邮件内容的。因为JavaMail API邮件内容视为二进制流。所以要显示HTML内容的邮件，我们必须使用JEditorPane或者第三方HTML展现组件。<br /><br />以下代码显示了如何使用JEditorPane显示邮件内容：<br /><pre class="overflow"><br />if (message.getContentType().equals("text/html")) {<br />  String content = (String)message.getContent();<br />  JFrame frame = new JFrame();<br />  JEditorPane text = new JEditorPane("text/html", content);<br />  text.setEditable(false);<br />  JScrollPane pane = new JScrollPane(text);<br />  frame.getContentPane().add(pane);<br />  frame.setSize(300, 300);<br />  frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);<br />  frame.show();<br />}<br /></pre><br /><br /><span style="COLOR: green">B．在邮件中包含图片</span><br />如果我们在邮件中使用HTML作为内容，那么最好将HTML中使用的图片作为邮件的一部分，这样无论是否在线都会正确的显示HTML中的图片。处理方法就是将HTML中用到的图片作为邮件附件并使用特殊的cid URL作为图片的引用，这个cid就是对图片附件的Content-ID头的引用。<br />处理内嵌图片就像向邮件中添加附件一样，不同之处在于我们必须通过设置图片附件所在的邮件体部分的header中Content-ID为一个随机字符串，并在HTML中img的src标记中设置为该字符串。这样就完成了图片附件与HTML的关联。<br /><pre class="overflow"><br />String file = ...;<br /><br />// Create the message<br />Message message = new MimeMessage(session);<br /><br />// Fill its headers<br />message.setSubject("Embedded Image");<br />message.setFrom(new InternetAddress(from));<br />message.addRecipient(Message.RecipientType.TO, <br />  new InternetAddress(to));<br /><br />// Create your new message part<br />BodyPart messageBodyPart = new MimeBodyPart();<br />String htmlText = "&lt;H1&gt;Hello&lt;/H1&gt;" + <br />  "&lt;img src=\"cid:memememe\"&gt;";<br />messageBodyPart.setContent(htmlText, "text/html");<br /><br />// Create a related multi-part to combine the parts<br />MimeMultipart multipart = new MimeMultipart("related");<br />multipart.addBodyPart(messageBodyPart);<br /><br />// Create part for the image<br />messageBodyPart = new MimeBodyPart();<br /><br />// Fetch the image and associate to part<br />DataSource fds = new FileDataSource(file);<br />messageBodyPart.setDataHandler(new DataHandler(fds));<br />messageBodyPart.setHeader("Content-ID","&lt;memememe&gt;");<br /><br />// Add part to multi-part<br />multipart.addBodyPart(messageBodyPart);<br /><br />// Associate multi-part with message<br />message.setContent(multipart);<br /></pre><br /><br /><span style="COLOR: blue">9．在邮件中搜索短语</span><br />JavaMail API提供了过滤器机制，它被用来建立搜索短语。这个短语由javax.mail.search包中的SearchTerm抽象类来定义，在定义后我们便可以使用Folder的Search()方法在Folder中查找邮件：<br /><pre class="overflow"><br />SearchTerm st = ...;<br />Message[] msgs = folder.search(st);<br /></pre><br />下面有22个不同的类（继承了SearchTerm类）供我们使用：<br />AND terms (class AndTerm)<br />OR terms (class OrTerm)<br />NOT terms (class NotTerm)<br />SENT DATE terms (class SentDateTerm)<br />CONTENT terms (class BodyTerm)<br />HEADER terms (FromTerm / FromStringTerm, RecipientTerm / RecipientStringTerm, SubjectTerm, etc.)<br />使用这些类定义的断语集合，我们可以构造一个逻辑表达式，并在Folder中进行搜索。下面是一个实例：在Folder中搜索邮件主题含有“ADV”字符串或者发信人地址为friend@public.com的邮件。<br /><pre class="overflow"><br />SearchTerm st = <br />  new OrTerm(<br />    new SubjectTerm("ADV:"), <br />    new FromStringTerm("friend@public.com"));<br />Message[] msgs = folder.search(st);<br /></pre><br /><br /><span style="COLOR: blue">六、参考资源</span><br /><a href="http://java.sun.com/products/javamail/index.jsp" target="_new"><font color="#002c99">JavaMail API Home</font></a><br /><a href="http://java.sun.com/developer/onlineTraining/JavaMail/contents.html" target="_new"><font color="#002c99">Sun’s JavaMail API基础</font></a><br /><a href="http://java.sun.com/products/javabeans/glasgow/jaf.html" target="_new"><font color="#002c99">JavaBeans Activation Framework Home</font></a><br /><a href="http://mail.java.sun.com/archives/javamail-interest.html" target="_new"><font color="#002c99">javamail-interest mailing list</font></a><br /><a href="http://java.sun.com/products/javamail/FAQ.html" target="_new"><font color="#002c99">Sun's JavaMail FAQ</font></a><br /><a href="http://www.jguru.com/faq/JavaMail" target="_new"><font color="#002c99">jGuru's JavaMail FAQ</font></a><br /><a href="http://java.sun.com/products/javamail/Third_Party.html" target="_new"><font color="#002c99">Third Party Products List</font></a><br /><br /><span style="COLOR: blue">七、代码下载</span><br /><a href="http://java.sun.com/developer/onlineTraining/JavaMail/exercises.html" target="_new"><font color="#002c99">http://java.sun.com/developer/onlineTraining/JavaMail/exercises.html</font></a></div><img src ="http://www.blogjava.net/TrampEagle/aggbug/48327.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-26 14:21 <a href="http://www.blogjava.net/TrampEagle/articles/48327.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Jakarta Commons项目组介绍</title><link>http://www.blogjava.net/TrampEagle/articles/47187.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 20 May 2006 09:21:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/47187.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/47187.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/47187.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/47187.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/47187.html</trackback:ping><description><![CDATA[原文引自：<a href="http://ijsp.net/2/2003-9/8/0000424.shtml">http://ijsp.net/2/2003-9/8/0000424.shtml</a><br /><br /><strong>Jakarta Commons:巧用类和组件<br /></strong>Jakarta Commons 是Jakarta 的子项目，它创建和维护着许多独立软件包，这些包一般与其他框架或产品<br />无关，其中收集了大量小型、实用的组件，大部分面向服务器端编程。<br />Commons的包分成两部分：Sandbox，Commons 代码库。Sandbox 是一个测试平台，用来检验各种设想、<br />计划。本文介绍的组件属于Commons代码库，文章将展示各个组件的功能、适用场合，并通过简单的例子<br />介绍其用法。<br />一、概述<br />可重用性是Jakarta Commons 项目的灵魂所在。这些包在设计阶段就已经考虑了可重用性问题。其中<br />一些包，例如Commons 里面用来记录日志的Logging包，最初是为其他项目设计的，例如Jakarta Struts<br />项目，当人们发现这些包对于其他项目也非常有用，能够极大地帮助其他项目的开发，他们决定为这些包<br />构造一个"公共"的存放位置，这就是Jakarta Commons项目。<br />为了真正提高可重用性，每一个包都必须不依赖于其他大型的框架或项目。因此，Commons项目的包<br />基本上都是独立的，不仅是相对于其他项目的独立，而且相对于Commons内部的大部分其他包独立。虽然<br />存在一些例外的情况，例如Betwixt 包要用到XML API，但绝大部分只使用最基本的API，其主要目的就<br />是要能够通过简单的接口方便地调用。<br />不过由于崇尚简洁，许多包的文档变得过于简陋，缺乏维护和支持，甚至有一部分还有错误的链接，<br />文档也少得可怜。大部分的包需要我们自己去找出其用法，甚至有时还需要我们自己去分析其适用场合。<br />本文将逐一介绍这些包，希望能够帮助你迅速掌握这一积累了许多人心血的免费代码库。<br />说明：Jakarta Commons 和Apache Commons 是不同的，后者是Apache Software Foundation的一个<br />顶层项目，前者则是Jakarta 项目的一个子项目，同是也是本文要讨论的主角。本文后面凡是提到Commons<br />的地方都是指Jakarta 的Commons。<br />为了便于说明，本文把Commons 项目十八个成品级的组件（排除了EL、Latka和Jexl）分成5类，<br />如下表所示。<br />必须指出的是，这种分类只是为了方便文章说明，Commons 项目里面实际上并不存在这种分类，同时<br />这些分类的边界有时也存在一定的重叠。<br />本文首先介绍Web 相关类和其他类里面的组件，下一篇文章将涉及XML 相关、包装这两类，最后一篇<br />文章专门介绍属于工具类的包。<br />二、其他类<br />CLI、Discovery、Lang 和Collections 包归入其他类，这是因为它们都各自针对某个明确、实用的<br />小目标，可谓专而精。<br />2.1 CLI<br />■ 概况：CLI 即Command Line Interface，也就是"命令行接口"，它为Java 程序访问和解析命令行<br />参数提供了一种统一的接口。<br />■ 官方资源：主页，二进制，源代码<br />■ 何时适用：当你需要以一种一致的、统一的方式访问命令行参数之时。<br />■ 示例应用：CLIDemo.java。CLASSPATH 中必须包含commons-cli-1.0.jar。<br />■ 说明：<br />有多少次你不得不为一个新的应用程序重新设计新的命令行参数处理方式？如果能够只用某个单一<br />的接口，统一完成诸如定义输入参数（是否为强制参数，数值还是字符串，等等）、根据一系列规则分析<br />参数、确定应用要采用的路径等任务，那该多好！答案就在CLI。<br />在CLI中，每一个想要在命令中指定的参数都是一个Option对象。首先创建一个Options 对象，将<br />各个Option对象加入Options对象，然后利用CLI提供的方法来解析用户的输入参数。Option对象可以<br />要求用户必须输入某个参数，例如必须在命令行提供文件名字。如果某个参数是必须的，创建Option 对<br />象的时候就要显式地指定。<br />下面是使用CLI 的步骤。<br />// …<br />// ① 创建一个Options：<br />Options options = new Options();<br />options.addOption("t", false, "current time");<br />// …<br />// ② 创建一个解析器，分析输入：<br />CommandLineParser parser = new BasicParser();<br />CommandLine cmd;<br />try {<br />cmd = parser.parse(options, args);<br />} catch (ParseException pe) {<br />usage(options);<br />return;<br />}<br />// …<br />// ③ 最后就可以根据用户的输入，采取相应的操作：<br />if (cmd.hasOption("n")) {<br />System.err.println("Nice to meet you: " +<br />cmd.getOptionValue('n'));<br />}<br />这就是使用CLI的完整过程了。当然，CLI 还提供了其他高级选项，例如控制格式和解析过程等，<br />但基本的使用思路仍是一致的。请参见本文最后提供的示例程序。<br />2.2 Discovery<br />■ 概况：Discovery 组件是发现模式（Discovery Pattern）的一个实现，它的目标是按照一种统一<br />的方式定位和实例化类以及其他资源。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你想用最佳的算法在Java程序中查找Java接口的各种实现之时。<br />■ 应用实例：DiscoveryDemo.java，MyInterface.java，MyImpl1.java，MyImpl2.java，MyInterface。<br />要求CLASSPATH 中必须包含commons-discovery.jar和commons-logging.jar。<br />■ 说明：<br />Discovery 的意思就是"发现"，它试图用最佳的算法查找某个接口的所有已知的实现。在使用服务的<br />场合，当我们想要查找某个服务的所有已知的提供者时，Discovery 组件尤其有用。<br />考虑一下这种情形：我们为某个特别复杂的任务编写了一个接口，所有该接口的实现都用各不相同的<br />方式来完成这个复杂任务，最终用户可以根据需要来选择完成任务的具体方式。那么，在这种情形下，<br />最终用户应该用什么办法来找出接口的所有可用实现（即可能的完成任务的方式）呢？<br />上面描述的情形就是所谓的服务-服务提供者体系。服务的功能由接口描述，服务提供者则提供具体<br />的实现。现在的问题是最终用户要用某种办法来寻找系统中已经安装了哪些服务提供者。在这种情形下，<br />Discovery 组件就很有用了，它不仅可以用来查找那些实现了特定接口的类，而且还可以用来查找资源，<br />例如图片或其他文件等。在执行这些操作时，Discovery遵从Sun的服务提供者体系所定义的规则。<br />由于这个原因，使用Discovery 组件确实带来许多方便。请读者参阅本文后面示例程序中的接口<br />MyInterface.java 和两个实现类MyImpl1.java、MyImple2.java，了解下面例子的细节。在使用Discovery<br />的时候要提供MyInterface 文件，把它放入META-INF/services目录，注意该文件的名字对应接口的完整<br />限定名称（Fully Qualified Name），如果接口属于某个包，该文件的名字也必须相应地改变。<br />// …<br />// ① 创建一个类装入器的实例。<br />ClassLoaders loaders =<br />ClassLoaders.getAppLoaders(MyInterface.class, getClass(), false);<br />// …<br />// ② 用DiscoverClass 的实例来查找实现类。<br />DiscoverClass discover = new DiscoverClass(loaders);<br />// …<br />// ③ 查找实现了指定接口的类：<br />Class implClass = discover.find(MyInterface.class);<br />System.err.println("Implementing Provider: " + implClass.getName());<br />运行上面的代码，就可以得到在MyInterface 文件中注册的类。再次提醒，如果你的实现是封装在包<br />里面的，在这里注册的名字也应该作相应地修改，如果该文件没有放在正确的位置，或者指定名字的实现<br />类不能找到或实例化，程序将抛出DiscoverException，表示找不到符合条件的实现。下面是MyInterface<br />文件内容的一个例子：MyImpl2 # Implementation 2。<br />当然，实现类的注册办法并非只有这么一种，否则的话Discovery 的实用性就要大打折扣了！实际上，<br />按照Discovery 内部的类查找机制，按照这种方法注册的类将是Discovery 最后找到的类。另一种常用的<br />注册方法是通过系统属性或用户定义的属性来传递实现类的名字，例如，放弃<br />META-INF/services 目录下<br />的文件，改为执行java -DMyInterface=MyImpl1 DiscoveryDemo命令来运行示例程序，这里的系统属性<br />是接口的名字，值是该接口的提供者，运行的结果是完全一样的。<br />Discovery 还可以用来创建服务提供者的(singleton)实例并调用其方法，语法如下：<br />((MyInterface)discover.newInstance(MyInterface.class)).myMethod();。注意在这个例子中，我们并<br />不知道到底哪一个服务提供者实现了myMethod，甚至我们根本不必关心这一点。具体的情形与运行这段<br />代码的方式以及运行环境中已经注册了什么服务提供者有关，在不同的环境下运行，实际得到的服务提供<br />者可能不同。<br />2.3 Lang<br />■ 概况：Lang是java.lang 的一个扩展包，增加了许多操作String的功能，另外还支持C 风格的枚举量。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当java.lang 包提供的方法未能满足需要，想要更多的功能来处理String、数值和<br />System 属性时；还有，当你想要使用C风格的枚举量时。<br />■ 示例应用：LangDemo.java，Mortgage.java，OnTV.java。CLASSPATH中必须包含commons-lang.jar。<br />■ 说明：<br />这个包提供了许多出于方便目的而提供的方法，它们中的大多数是静态的，简化了日常编码工作。<br />StringUtils类是其中的一个代表，它使得开发者能够超越标准的java.lang.String 包来处理字符串。<br />使用这些方法很简单，通常只要在调用静态方法时提供适当的参数就可以了。例如，如果要将某个单词<br />的首字符改为大写，只需调用：StringUtils.capitalise("name")，调用的输出结果是Name。请浏览<br />StringUtils API 文档了解其他静态方法，也许你会找到一些可以直接拿来使用的代码。本文提供的示例<br />程序示范了其中一些方法的使用。<br />另一个值得注意的类是RandomStringUtils，它提供了生成随机字符串的方法，用来创建随机密码实<br />在太方便了。<br />NumberUtils 类提供了处理数值数据的方法，许多方法值得一用，例如寻找最大、最小数的方法，将<br />String 转换成数值的方法，等等。NumberRange和CharRange类分别提供了创建和操作数值范围、字符范<br />围的方法。<br />Builder包里的类提供了一些特殊的方法，可用来构造类的toString、hashCode、compareTo 和equals<br />方法，其基本思路就是构造出类的高质量的toString、hashCode、compareTo 和equals 方法，从而免去<br />了用户自己定义这些方法之劳，只要调用一下Builder 包里面的方法就可以了。例如，我们可以用<br />ToStringBuilder 来构造出类的toString描述，如下例所示：<br />public class Mortgage {<br />private float rate;<br />private int years;<br />....<br />public String toString() {<br />return new ToStringBuilder(this).<br />append("rate", this.rate).<br />append("years", this.years).<br />toString();<br />}<br />}<br />使用这类方法有什么好处呢？显然，它使得我们有可能通过一种统一的方式处理所有数据类型。所有<br />Builder 方法的用法都和上例相似。<br />Java 没有C 风格的枚举量，为此，lang 包提供了一个类型安全的Enum 类型，填补了空白。Enum 类<br />是抽象的，如果你要创建枚举量，就要扩展Enum 类。下面的例子清楚地说明了Enum 的用法。<br />import org.apache.commons.lang.enum.Enum;<br />import java.util.Map;<br />import java.util.List;<br />import java.util.Iterator;<br />public final class OnTV extends Enum {<br />public static final OnTV IDOL=<br />new OnTV("Idol");<br />public static final OnTV SURVIVOR =<br />new OnTV("Survivor");<br />public static final OnTV SEINFELD =<br />new OnTV("Seinfeld");<br />private OnTV(String show) {<br />super(show);<br />}<br />public static OnTV getEnum(String show){<br />return (OnTV) getEnum(OnTV.class, show);<br />}<br />public static Map getEnumMap() {<br />return getEnumMap(OnTV.class);<br />}<br />public static List getEnumList() {<br />return getEnumList(OnTV.class);<br />}<br />public static Iterator iterator() {<br />return iterator(OnTV.class);<br />}<br />}<br />以后我们就可以按照下面的方式使用枚举变量：OnTV.getEnum("Idol")。该调用从前面创建的枚举数据<br />类型返回Idol。这个例子比较简单，实际上Enum类还提供了许多有用的方法，请参见本文后面提供的<br />完整实例。<br />2.4 Collections<br />■ 概况：扩展了Java Collection框架，增添了新的数据结构、迭代机制和比较操作符。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：几乎所有需要操作数据结构的重要Java开发项目都可以使用Collections API。和Java<br />的标准实现相比，Collections API 有着诸多优势。<br />■ 示例应用：CollectionsDemo.java。要求CLASSPATH 中包含commons-collections.jar。<br />■ 说明：<br />要在有限的文章篇幅之内详尽地介绍Collections API 实在是太困难了，不过这里仍将涵盖大多数<br />最重要的类，希望能够引起你的兴趣，认真了解一下其余的类。Collections 本身的文档也提供了许多资<br />料并解释了每一个类的用法。<br />Bag 接口扩展标准的Java Collection，允许生成计数器来跟踪Bag里面的所有元素。当你想要跟踪<br />进出某个集合的元素的总数时，Bag 是非常有用的。由于Bag本身是一个接口，所以实际使用<br />的应该是实<br />现了该接口的类，例如HashBag 或TreeBag--从这些类的名字也可以看出，HashBag 实现的是一个<br />HashMap的Bag，而TreeBag实现的是TreeMap的Bag。Bag 接口中两个最重要的方法是：<br />getCount(Object o)，<br />用来返回Bag 里面特定对象的出现次数；uniqueSet()，返回所有唯一元素。<br />Buffer接口允许按照预定义的次序删除集合中的对象，删除次序可以是LIFO（Last In First Out，<br />后进先出），或FIFO（First In First Out，先进先出），另外还可以是自定义的次序。下面来看看<br />如何实现一个Buffer，按照自然次序删除元素。<br />BinaryHeap 类实现了Buffer 接口，能够按照自然次序删除元素。如果要颠倒次序，则必须传入一个<br />false，告诉Heap 采用自然次序的逆序。<br />BinaryHeap heap = new BinaryHeap();<br />// …<br />// 将元素加入该Heap<br />heap.add(new Integer(-1));<br />heap.add(new Integer(-10));<br />heap.add(new Integer(0));<br />heap.add(new Integer(-3));<br />heap.add(new Integer(5));<br />//…<br />// 删除一个元素<br />heap.remove();<br />调用该Heap 的remove，按照自然次序，元素集合中的-10将被删除。如果我们要求按照逆序排序，<br />则被删除的将是5。<br />FastArrayList、FastHashMap 和FastTreeMap 类能够按照两种模式操作，超越了与它们对应的标准<br />Collection。第一种模式是"慢模式"，类的修改操作（添加、删除元素）是同步的。与此相对，另一种模式<br />是"快模式"，对这些类的访问假定为只读操作，因此不需要同步，速度较快。在快模式中，结构性的改动<br />通过下列方式完成：首先克隆现有的类，修改克隆得到的类，最后用克隆得到的类替换原有的类。<br />FastArrayList、FastHashMap和FastTreeMap 类特别适合于那种初始化之后大部分操作都是只读操作的<br />多线程环境。<br />iterators 包为各种集合和对象提供标准Java Collection 包没有提供的迭代器。本文的示例应用示范了<br />ArrayIterator，通过迭代方式访问Array的内容。iterators 包里面各种迭代器的用法基本上与标准<br />Java 迭代器一样。<br />最后，comparators 包提供了一些实用的比较符。所谓比较符其实也是一个类，它定义的是如何比较<br />两个属于同一类的对象，决定它们的排序次序。例如，在前面提到的Buffer 类中，我们可以定义自己的<br />比较符，用自定义的比较符来决定元素的排序次序，而不是采用元素的自然排序次序。下面来看看具体的<br />实现经过。<br />// …<br />// ① 创建一个BinaryHeap 类，但这一次参数中<br />// 指定NullComparator。NullComparator比较<br />// null与其他对象，根据nullsAreHigh 标记来<br />// 判断null 值比其他对象大还是小：如果<br />// nullsAreHigh的值是false，则认为null 要比<br />// 其他对象小。<br />BinaryHeap heap2 = new BinaryHeap<br />(new NullComparator(false));<br />// …<br />// ② 将一些数据（包括几个null 值）加入heap：<br />heap2.add(null);<br />heap2.add(new Integer("6"));<br />heap2.add(new Integer("-6"));<br />heap2.add(null);<br />// …<br />// ③ 最后删除一个元素，Bag 包含的null 将减少<br />// 一个，因为null 要比其他对象小。<br />heap2.remove();<br />有关其他类Commons 组件的介绍就到这里结束。如果你想了解更多细节信息，请参见API文档，最好<br />再看看这些包的源代码。<br />三、Web类<br />Web 类的组件用来执行与Web 相关的任务。<br />3.1 FileUpload<br />■ 概况：一个可以直接使用的文件上载组件。<br />■ 官方资源：主页。由于这个组件尚未正式发布，今年二月发布的Beta版又有许多BUG，所以建议<br />从nightly builds 下载最新的版本。<br />■ 何时适用：当你想要在Java 服务器环境中加入一个易用、高性能的文件上载组件之时。<br />■ 示例应用：fileuploaddemo.jsp，fileuploaddemo.htm，和msg.jsp。要求服务器端应用目录的<br />WEB-INF/lib下面有commons-fileupload-1.0-dev.jar。<br />■ 说明：<br />FileUpload 组件解决了常见的文件上载问题。它提供了一个易用的接口来管理上载到服务器的文件，<br />可用于JSP和Servlet 之中。FileUpload 组件遵从RFC1867，它分析输入请求，向应用程序提供一系列上<br />载到服务器的文件。上载的文件可以保留在内存中，也可以放入一个临时位置（允许配置一个表示文件大<br />小的参数，如果上载的文件超过了该参数指定的大小，则把文件写入一个临时位置）。另外还有一些参数<br />可供配置，包括可接受的最大文件、临时文件的位置等。<br />下面介绍一下使用FileUpload 组件的步骤。<br />首先创建一个HTML 页面。注意，凡是要上载文件的表单都必须设置enctype属性，且属性的值必须<br />是multipart/form-data，同时请求方法必须是POST。下面的表单除了上载两个文件，另外还有一个普通<br />的文本输入框：<br />&lt;form name="myform" action="fileuploaddemo.jsp"<br />method="post" enctype="multipart/form-data"&gt;<br />输入你的名字:&lt;br /&gt;<br />&lt;input type="text" name="name" size="15"/&gt;&lt;br /&gt;<br />图形:&lt;br /&gt;<br />&lt;input type="file" name="myimage"&gt;&lt;br/&gt;<br />文件:&lt;br /&gt;<br />&lt;input type="file" name="myfile"&gt;&lt;br /&gt;&lt;br /&gt;<br />&lt;input type="submit" name="Submit"<br />value="Submit your files"/&gt;<br />接下来创建JSP页面。<br />// …<br />// ① 检查输入请求是否为multipart的表单数据。<br />boolean isMultipart = FileUpload.<br />isMultipartContent(request);<br />// …<br />// ② 为该请求创建一个句柄，通过它来解析请求。执行<br />// 解析后，所有的表单项目都保存在一个List中。<br />DiskFileUpload upload = new DiskFileUpload();<br />// 通过句柄解析请求，解析得到的项目保存在一个List 中<br />List items = upload.parseRequest(request);<br />// …<br />// ③ 通过循环依次获得List里面的文件项目。要区分表示<br />// 文件的项目和普通的表单输入项目，使用isFormField()<br />// 方法。根据处理请求的要求，我们可以保存上载的文<br />// 件，或者一个字节一个字节地处理文件内容，或者打<br />// 开文件的输入流。<br />Iterator itr = items.iterator();<br />while(itr.hasNext()) {<br />FileItem item = (FileItem) itr.next();<br />// 检查当前的项目是普通的表单元素，还是一个上载的文件<br />if(item.isFormField()) {<br />// 获得表单域的名字<br />String fieldName = item.getFieldName();<br />// 如果表单域的名字是name…<br />if(fieldName.equals("name"))<br />request.setAttribute("msg",<br />"Thank You: " + item.getString());<br />} else {<br />// 该项目是一个上载的文件，把它保存到磁盘。<br />// 注意item.getName()<br />// 会返回上载文件在客户端的完整路径名称，这似乎是一个BUG。<br />// 为解决这个问题，这里使用了fullFile.getName()。<br />File fullFile = new File(item.getName());<br />File savedFile = new File<br />(getServletContext().getRealPath("/"),<br />fullFile.getName());<br />item.write(savedFile);<br />}<br />}<br />我们可以通过上载句柄的upload.setSizeMax 来限制上载文件的大小。当上载文件的大小超过允许的<br />值时，程序将遇到异常。在上面的例子中，文件大小的限制值是-1，表示允许上载任意大小的文件。<br />还有其他一些略有变化的使用形式，正如前面所指出的，我们可以在上载的文件上打开一个输入流，<br />或者让它们驻留在内存中直至空间占用达到一定的限制值，或者在判断文件类型的基础上，以String 或<br />Byte 数组的形式获取其内容，或者直接删除文件。这一切都只要使用FileItem 类提供的方法就可以方便<br />地做到（DefaultFileItem 是FileItem的一个实现）。<br />3.2 HttpClient<br />■ 概况：这个API 扩展了java.net包，提供了模拟浏览器的功能。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你要构造Web 浏览器的功能；当你的应用需要一种高效的办法进行HTTP/HTTPS通信时。<br />■ 示例应用：HttpClientDemo.java。要求CLASSPATH中有commons-httpclient.jar，<br />common-logging.jar。要求使用JDK 1.4 或更高版本。<br />■ 说明：<br />HttpClient 扩展和增强了标准java.net 包，是一个内容广泛的代码库，功能极其丰富，能够构造出<br />各种使用HTTP 协议的分布式应用，或者也可以嵌入到现有应用，为应用增加访问HTTP 协议的能力。<br />在Commons 稳定版中，HttpClient 的文档似乎要比其他包更完善一些，而且还带有几个实例。下面我们通过<br />一个简单的例子来了解如何提取一个Web 页面，HttpClient 文档中也有一个类似的例子，我们将扩充那<br />个例子使其支持SSL。注意本例需要JDK 1.4 支持，因为它要用到Java Secure Socket Connection库，<br />而这个库只有JDK 1.4 及更高的版本才提供。<br />① 首先确定一个可以通过HTTPS 下载的页面，本例使用的是https://www.paypal.com/。同时确保<br />%JAVA_HOME%/jre/lib/security/java.security文件包含了下面这行代码：<br />security.provider.2=com.sun.net.ssl.internal.ssl.Provider。<br />除了这些设置之外，HTTPS连接的处理方式没有其他特别的地方--至少对于本例来说如此。不过，如<br />果远程网站使用的根证书不被你使用的Java 认可，则首先必须导入它的证书。<br />② 创建一个HttpClient的实例。HttpClient 类可以看成是应用的主驱动程序，所有针对网络的功<br />能都依赖于它。HttpClient 类需要一个Connection Manager来管理连接。<br />HttpConnectionManager允许<br />我们创建自己的连接管理器，或者，我们也可以直接使用内建的SimpleHttpConnectionManager或<br />MultiThreadedHttpConnectionManager类。如果在创建HttpClient 时没有指定连接管理器，HttpClient<br />默认使用SimpleHttpConnectionManager。<br />// 创建一个HttpClient 的实例<br />HttpClient client = new HttpClient();<br />③ 创建一个HttpMethod的实例，即确定与远程服务器的通信要采用哪种传输方式，HTTP 允许采用<br />的传输方式包括：GET，POST，PUT，DELETE，HEAD，OPTIONS，以及TRACE。这些传输方式分别作为一个<br />独立的类实现，但所有这些类都实现HttpMethod接口。在本例中，我们使用的是GetMethod，创建GetMethod<br />实例时在参数中指定我们想要GET 的URL。<br />// 创建一个HttpMethod 的实例<br />HttpMethod method = new GetMethod(url);<br />④ 执行HttpMethod 定义的提取操作。执行完毕后，executeMethod方法将返回远程服务器报告的状态<br />代码。注意executeMethod属于HttpClient，而不是HttpMethod。<br />// 执行HttpMethod定义的提取操作<br />statusCode = client.executeMethod(method);<br />⑤ 读取服务器返回的应答。如果前面的连接操作失败，程序将遇到HttpException或IOException，<br />其中IOException 一般意味着网络出错，继续尝试也不太可能获得成功。服务器返回的应答可以按照多种<br />方式读取，例如作为一个字节数组，作为一个输入流，或者作为一个String。获得服务器返回的应答后，<br />我们就可以按照自己的需要任意处置它了。<br />byte[] responseBody = method.getResponseBody();<br />⑥ 最后要做的就是释放连接。<br />method.releaseConnection();<br />以上只是非常简单地介绍了一下HttpClient 库，HttpClient 实际的功能要比本文介绍的丰富得多，<br />不仅健壮而且高效，请参阅API 文档了解详情。<br />3.3 Net<br />■ 概况：一个用于操作Internet基础协议的底层API。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你想要访问各种Internet底层协议之时（Finger，Whois，TFTP，Telnet，POP3，FTP，NNTP，以及SMTP）。<br />■ 示例应用：NetDemo.java。要求CLASSPATH中包含commons-net-1.0.0.jar。<br />■ 说明：<br />Net 包是一个强大、专业的类库，类库里的类最初属于一个叫做NetComponents 的商业产品。<br />Net 包不仅支持对各种低层次协议的访问，而且还提供了一个高层的抽象。大多数情况下，Net包提<br />供的抽象已能满足一般需要，它使得开发者不再需要直接面对各种协议的Socket 级的低层命令。使用高<br />层抽象并不减少任何功能，Net API 在这方面做得很出色，既提供了足够的功能，又不至于在特色方面作<br />过多的妥协。<br />SocketClient 是支持所有协议的基础类，它是一个抽象类，聚合了各种协议都需要的公用功能。各种<br />不同协议的使用过程其实很相似，首先利用connect方法建立一个指向远程服务器的连接，执行必要的<br />操作，最后终止与服务器的连接。下面通过实例介绍具体的使用步骤。<br />// …<br />// ① 创建一个客户端。我们将用NNTPClient<br />// 从新闻服务器下载新闻组清单。<br />client = new NNTPClient();<br />// …<br />// ② 利用前面创建的客户端连接到新闻服务器。<br />// 这里选用的是一个新闻组较少的服务器。<br />client.connect("aurelia.deine.net");<br />// …<br />// ③ 提取新闻组清单。下面的命令将返回一个<br />// NewsGroupInfo 对象的数组。如果指定的服<br />// 务器上不包含新闻组，返回的数组将是空的，<br />// 如果遇到了错误，则返回值是null。<br />list = client.listNewsgroups();<br />//...<br />// ④ 最后终止与服务器的连接。<br />if (client.isConnected())<br />client.disconnect();<br />必须说明的是，listNewsgroups命令可能需要较长的时间才能返回，一方面是因为网络速度的影响，<br />另外也可能是由于新闻组清单往往是很庞大的。NewsGroupInfo对象包含有关新闻组的详细信息，并提供<br />了一些操作新闻组的命令，比如提取文章总数、最后发布的文章、发布文章的权限，等等。<br />其他客户端，例如FingerClient、POP3Client、TelnetClient 等，用法也差不多。<br />结束语：有关Web相关类和其他类的介绍就到此结束。在下一篇文章中，我们将探讨XML类和包装类，<br />最后一篇文章则介绍工具类。<br />希望读者有兴趣试试本文提供的程序实例。很多时候Jakarta Commons 给人以混乱的感觉，希望本文<br />使你加深了对Jakarta Commons 了解，或者至少引起了你对Commons 子项目以及它提供的各种实用API 和<br />库的兴趣。<br /><br /><b>第二部分XML 类和包装类</b><br />上一篇文章中，我们将Jakarta Commons的组件分成了五类，并介绍了其中的Web类和其他类，本文接着<br />介绍XML 类和包装类，接下来的最后一篇文章将介绍工具类。注意Commons本身并不进行这种分类，这里<br />进行分类纯粹是为组织方便起见。<br />一、包装类<br />这一类包含Codec 和Modeler 两个组件。<br />1.1 Codec<br />■ 概况：提供常用的编码器和解码器。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你需要Base64 和Hex编码功能的标准实现之时。<br />■ 示例应用：CodecDemo.java。要求CLASSPATH必须包含commons-codec-1.1.jar。<br />■ 说明：<br />Codec 里面的类分成两个包，其中一个包实现的是常用的Base64 和Hex 编码机制，另一个包是语言、<br />语音方面的编码。两个包的用法相似，鉴于语言、语音的编码并不是很常用，所以下面主要介绍第一个包。<br />Base64编码主要用于Email 传输。定义MIME 文档传输的RFC 规定了Base 64 编码，从而使得任何二进制<br />数据都可以转换成可打印的ASCII字符集安全地传输。例如，假设要通过Email 传输一个图形文件，<br />Email 客户端软件就会利用Base64 编码把图形文件的二进制数据转换成ASCII 码。在Base64编码中，<br />每三个8 位的字节被编码成一个4 个字符的组，每个字符包含原来24 位中的6 位，编码后的字符串大小是<br />原来的1.3倍，文件的末尾追加"="符号。除了MIME文档之外，Base64 编码技术还用于BASIC认证机制<br />中HTTP 认证头的"用户：密码"字符串。<br />Base64类的使用相当简单，最主要的两个静态方法是：Base64.encodeBase64(byte[] byteArray)，<br />用于对字节数组中指定的内容执行Base64 编码；Base64.decodeBase64(byte[] byteArray)，用于对字节<br />数组中指定的内容执行Base64解码。另外，Base64还有一个静态方法<br />Base64.isArrayByteBase64(byte[]<br />byteArray)，用于检测指定的字节数组是否可通过Base64 测试（即是否包含了经过Base64编码的数据，<br />如前所述，Base64 编码的结果只包含可打印的ASCII字符）。<br />byte[] encodedBytes=Base64.encodeBase64(testString.getBytes());<br />String decodedString=new String(Base64.decodeBase64(encodedBytes));<br />System.err.println("\'^\'是一个合法的Base64 字符吗？"<br />+ Base64.isArrayByteBase64(invalidBytes));<br />Hex 编码/解码就是执行字节数据和等价的十六进制表示形式之间的转换。Hex 编码的编码、解码过程<br />和Base64 相似，此处不再赘述。<br />1.2 Modeler<br />■ 概况：根据JMX（Java Management Extensions）规范的定义，支持对Model MBean（Managed Bean）<br />的配置和实例化。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你想要创建和管理Model MBean，以便利用标准的管理API来管理应用之时。<br />■ 示例应用：ModelerDemo.java，DemoManagedBean.java和mbeans-descriptors.xml。要求<br />CLASSPATH 中包含commons-modeler-1.0.jar、commons-logging.jar、<br />commons-digester.jar、<br />commons-collections.jar、commons-beanutils.jar，以及Sun的JMX参考实现jmxri.jar。<br />■ 说明：<br />下面的说明要求读者对JMX 有一定的了解。<br />Managed Bean 简称MBean，是一种关联到应用程序中被管理组件的Bean，是一种对资源抽象。Model<br />MBean 是一种特殊的MBean，具有高度动态和可配置的特点，但Model MBean 的这种能力是有代价的，<br />程序员需要设置大量的元信息来告诉JMX如何创建Model MBean，这些元信息包括组件的属性、操作和其它<br />信息。Modeler 的目的就是降低程序员实现Model MBean 的工作量，它提供的一组函数为处理元数据信息<br />带来了方便。另外，Modeler还提供了注册工具和一个基本的Model MBean。<br />Modeler 允许以XML文件的形式定义元数据信息，该XML文件应当遵从随同Modeler 提供的DTD 定义。<br />元数据信息用来在运行时创建注册信息，注册信息是所有Model MBean 的中心知识库，实际上相当于一个<br />创建这类Bean 的工厂。<br />下面我们首先为一个Managed Bean（DemoManagedBean）创建这个XML文件。DemoManagedBean有一<br />个name 属性，可读写。<br />&lt;?xml version="1.0" encoding="GB2312" ?&gt;<br />&lt;!DOCTYPE mbeans-descriptors PUBLIC<br />"-//Apache Software Foundation<br />//DTD Model MBeans Configuration File"<br />"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd"&gt;<br />&lt;!-- JMX MBean 的描述--&gt;<br />&lt;mbeans-descriptors&gt;<br />&lt;mbean name="ManagedBean" description="Example Managed Bean"<br />type="ManagedBean"&gt;<br />&lt;attribute name="name" description="Simple Name"<br />type="java.lang.String" /&gt;<br />&lt;constructor name="ManagedBean"/&gt;<br />&lt;/mbean&gt;<br />&lt;/mbeans-descriptors&gt;<br />可以看到，这个XML 文件提供了许多ManagedBean 的信息，包括它的属性、构造函数，另外还有它的<br />操作（不过本例没有显示），这就是所谓的元数据信息。如果你打算扩展随同Modeler 提供的标准MBean<br />（称为BaseModelMBean），可以在mbean元素中以属性的形式指定Model MBean的类名称：。在前面的<br />例子中，标准的Model MBean只是简单地把所有调用直接传递给ManagedBean 类。<br />接下来，我们要注册上述信息。注意通过描述文件装入注册信息之后，我们通过一个静态方法提取格<br />式化的注册信息：<br />// 创建一个Registry<br />Registry registry = null;<br />try {<br />URL url = ModelerDemo.class.getResource<br />("mbeans-descriptors.xml");<br />InputStream stream = url.openStream();<br />Registry.loadRegistry(stream);<br />stream.close();<br />registry = Registry.getRegistry();<br />} catch (Throwable t) {<br />t.printStackTrace(System.out);<br />System.exit(1);<br />}<br />创建好Registry之后，我们要创建一个Model MBean，并将它注册到默认的管理服务器。这样，任何<br />JMX 客户程序都可以通过Model MBean 调用Managed Bean 的功能了。<br />// 获得一个Managed Bean 实例的句柄<br />DemoManagedBean mBean = new DemoManagedBean();<br />// 创建一个Model MBean，并将它注册到MBean服务器<br />MBeanServer mServer = registry.getServer();<br />ManagedBean managed = registry.findManagedBean("ManagedBean");<br />try {<br />ModelMBean modelMBean = managed.createMBean(mBean);<br />String domain = mServer.getDefaultDomain();<br />ObjectName oName = new ObjectName(domain +<br />":type=ManagedBean");<br />mServer.registerMBean(modelMBean, oName);<br />} catch(Exception e) {<br />System.err.println(e);<br />System.exit(0);<br />}<br />try {<br />ObjectName name =<br />new ObjectName(mServer.getDefaultDomain() +<br />":type=ManagedBean");<br />ModelMBeanInfo info = (ModelMBeanInfo) mServer.<br />getMBeanInfo(name);<br />System.err.println(" className="+info.getClassName());<br />System.err.println(" description="+info.getDescription());<br />System.err.println(" mbeanDescriptor="+info.getMBeanDescriptor());<br />System.err.println("==== 测试====");<br />System.err.println("Name 的原始值: " +<br />mServer.getAttribute(name, "name"));<br />mServer.setAttribute(name, new Attribute("name", "Vikram"));<br />System.err.println("Name 的新值: " +<br />mServer.getAttribute(name, "name"));<br />} catch(Exception e) {<br />System.err.println(e);<br />System.exit(0);<br />}<br />虽然这个例子比较简单，但它仍旧清楚地说明了使用Modeler带来的方便，不妨将它与不使用Modeler<br />的情况下创建一个类似的Model MBean相比较。通过XML文件来描述ModelMBeanInfo不仅灵活方便，而<br />且也很容易扩展，比手工编写这类信息改进不少。<br />二、XML类<br />XML 类包含了与Java、XML技术相关的类，包括：Betwixt，Digester，Jelly，和JXPath。<br />2.1 Betwixt<br />■ 概况：实现XML 和JavaBean 的映射。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你想要以灵活的方式实现XML和Bean 的映射，需要一个数据绑定框架之时。<br />■示例应用：BetwixtDemo.java，Mortgage.java，mortgage.xml。要求CLASSPATH 中必须包含<br />commons-betwixt-1.0-alpha-1.jar、commons-logging.jar、commons-beanutils.jar、<br />commons-collections.jar、以及commons-digester.jar。<br />■ 说明：<br />如果你以前曾经用Castor绑定数据，一定会欣赏Betwixt的灵活性。Castor 适合在一个预定义模式<br />（Schema）的基础上执行Bean和XML 之间的转换；但如果你只想执行数据和XML之间的转换，最好的选<br />择就是Betwixt。Betwixt 的特点就是灵活，能够方便地将数据输出成为人类可阅读的XML。<br />Betwixt的用法相当简单。如果要把Bean 转换成XML，首先创建一个BeanWriter 的实例，设置其属性，<br />然后输出；如果要把XML 转换成Bean，首先创建一个BeanReader的实例，设置其属性，然后用Digester<br />执行转换。<br />将Bean转换成XML：<br />// 用Betwixt 将Bean转换成XML 必须有BeanWriter的实例。<br />// 由于BeanWriter的构造函数要求有一个写入器对象，<br />// 所以我们从创建一个StringWriter开始<br />StringWriter outputWriter = new StringWriter();<br />// 注意输出结果并不是格式良好的，所以需要在开始位置<br />// 写入下面的内容：<br />outputWriter.write("&lt;?xml version='1.0' ?&gt;");<br />// 创建一个BeanWriter<br />BeanWriter writer = new BeanWriter(outputWriter);<br />// 我们可以设置该写入器的各种属性。<br />// 下面的第一行禁止写入ID，<br />// 第二行允许格式化输出<br />writer.setWriteIDs(false);<br />writer.enablePrettyPrint();<br />// 创建一个Bean 并将其输出<br />Mortgage mortgage = new Mortgage(6.5f, 25);<br />// 将输出结果写入输出设备<br />try {<br />writer.write("mortgage", mortgage);<br />System.err.println(outputWriter.toString());<br />} catch(Exception e) {<br />System.err.println(e);<br />}<br />将XML 转换成Bean：<br />// 用Betwixt 来读取XML 数据并以此为基础创建<br />// Bean，必须用到BeanReader 类。注意BeanReader 类扩展了<br />// Digester包的Digester 类。<br />BeanReader reader = new BeanReader();<br />// 注册类<br />try {<br />reader.registerBeanClass(Mortgage.class);<br />// 并解析它…<br />Mortgage mortgageConverted =<br />(Mortgage)reader.parse(new File("mortgage.xml"));<br />// 检查转换得到的mortgage 是否包含文件中的值<br />System.err.println("Rate: " + mortgageConverted.getRate() +<br />", Years: " + mortgageConverted.getYears());<br />} catch(Exception ee) {<br />ee.printStackTrace();<br />}<br />注意，通过BeanReader 注册类时，如果顶层元素的名称和类的名称不同，必须用另一个方法注册并<br />指定准确的路径，如reader.registerBeanClass("toplevelelementname", Mortgage.class)。<br />2.2 Digester<br />■ 概况：提供友好的、事件驱动的高级XML 文档处理API。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你想要处理XML 文档，而且希望能够根据XML 文档中特定的模式所触发的一组规则<br />来执行某些操作时。<br />■ 示例应用：DigesterDemo.java、Employee.java、Company.java、rules.xml以及company.xml。<br />要求CLASSPATH 中必须包含commons-digester.jar、commons-logging.jar、<br />commons-beanutils.jar<br />以及commons-collections.jar。<br />■ 说明：<br />Digester 在解析配置文件的时候最为有用。实际上，Digester最初就是为读取Struts 配置文件而开<br />发的，后来才移到Commons 包。<br />Digester是一个强大的模式匹配工具，允许开发者在一个比SAX 或DOM API更高的层次上处理XML<br />文档，当找到特定的模式（或找不到模式）时能够触发一组规则。使用Digester 的基本思路是：首先创<br />建一个Digester 的实例，然后用它注册一系列模式和规则，最后将XML文档传递给它。此后，Digester<br />就会分析XML 文档，按照注册次序来触发规则。如果XML文档中的某个元素匹配一条以上的规则，所有<br />的规则会按照注册次序被依次触发。<br />Digester本身带有12条预定义的规则。当XML文档中找到一个特定的模式时，想要调用某个方法吗？<br />很简单，使用预定义的CallMethodRule！另外，你不一定要使用预定的规则，Digester 允许用户通过扩<br />展Rule 类定义自己的规则。<br />在指定模式时，元素必须用绝对名称给出。例如，根元素直接用名称指定，下一层元素则通过"/"符<br />号引出。例如，假设company是根元素，company/employee 就是匹配其中一个子元素的模式。<br />Digester允许使用通配符，例如*/employee 将匹配XML 文档内出现的所有employee元素。<br />找到匹配的模式时，关联到该匹配模式的规则内有四个回调方法会被调用，它们是：begin，end，body，<br />和finish。这些方法被调用的时刻正如其名字所示，例如调用begin 和end 的时刻分别是遇到元素的开<br />始标记和结束标记之时，body是在遇到了匹配模式之内的文本时被调用，finish 则是在全部对匹配模式<br />的处理工作结束后被调用。<br />最后，模式可以在一个外部的规则XML 文档内指定（利用digester-rules.dtd），或者在代码之内<br />指定，下面要使用的是第一种办法，因为这种办法比较常用。<br />使用Digester 之前要创建两个XML文档。第一个就是数据或配置文件，也就是我们准备对其应用规<br />则的文件。下面是一个例子（company.xml）<br />&lt;?xml version="1.0" encoding="gb2312"?&gt;<br />&lt;company&gt;<br />&lt;name&gt;我的公司&lt;/name&gt;<br />&lt;address&gt;中国浙江&lt;/address&gt;<br />&lt;employee&gt;<br />&lt;name&gt;孙悟空&lt;/name&gt;<br />&lt;employeeNo&gt;10000&lt;/employeeNo&gt;<br />&lt;/employee&gt;<br />&lt;employee&gt;<br />&lt;name&gt;猪八戒&lt;/name&gt;<br />&lt;employeeNo&gt;10001&lt;/employeeNo&gt;<br />&lt;/employee&gt;<br />&lt;/company&gt;<br />第二个文件是规则文件rules.xml。rules.xml 告诉Digester要在company.xml中查找什么、找到了<br />之后执行哪些操作：<br />&lt;?xml version="1.0" encoding="gb2312"?&gt;<br />&lt;digester-rules&gt;<br />&lt;!-- 创建顶层的Company对象--&gt;<br />&lt;object-create-rule pattern="company" classname="Company" /&gt;<br />&lt;call-method-rule pattern="company/name" methodname="setName"<br />paramcount="0" /&gt;<br />&lt;call-method-rule pattern="company/address"<br />methodname="setAddress" paramcount="0" /&gt;<br />&lt;pattern value="company/employee"&gt;<br />&lt;object-create-rule classname="Employee" /&gt;<br />&lt;call-method-rule pattern="name" methodname="setName"<br />paramcount="0" /&gt;<br />&lt;call-method-rule pattern="employeeNo" methodname=<br />"setEmployeeNo" paramcount="0" /&gt;<br />&lt;set-next-rule methodname="addEmployee" /&gt;<br />&lt;/pattern&gt;<br />&lt;/digester-rules&gt;<br />这个文件有哪些含义呢？第一条规则，&lt;object-create-rule pattern="company"<br />classname="Company" /&gt;，告诉Digester 如果遇到了模式company，则必须遵从object-create-rule，<br />也就是要创建一个类的实例！那么要创建的是哪一个类的实例呢？classname="Company"属性指定了类<br />的名称。因此，解析company.xml 的时候，当遇到顶级的company元素，等到object-create-rule规则执<br />行完毕，我们就拥有了一个Digester 创建的Company 类的实例。<br />现在要理解call-method-rule 规则也应该不那么困难了，这里call-method-rule 的功能是在遇到<br />company/name 或company/address 模式时调用一个方法（方法的名字通过methodname 属性指定）。<br />最后一个模式匹配值得注意，它把规则嵌套到了匹配模式之中。两种设定规则和模式的方式都是<br />Digester 接受的，我们可以根据自己的需要任意选择。在这个例子中，模式里面定义的规则在遇到<br />company/employee 模式时创建一个Employee 类的对象，设置其属性，最后用set-next-rule将这个雇员<br />加入到顶层的Company。<br />创建好上面两个XML 文件之后，只要用两行代码就可以调用Digester了：<br />Digester digester = DigesterLoader.createDigester(rules.toURL());<br />Company company = (Company)digester.parse(inputXMLFile);<br />第一行代码装入规则文件，创建一个Digester。第二行代码利用该Digester 来应用规则。请参见本文<br />后面提供的DigesterDemo.java 完整源代码。<br />2.3 Jelly<br />■ 概况：一种基于Java和XML 的脚本语言。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：简单地说，当你想要一种灵活的、可扩展的XML 脚本工具之时。<br />■ 示例应用：JellyDemo.java，jellydemo.xml以及TrivialTag.java。要求CLASSPATH 中必须有<br />commons-jelly-1.0-dev.jar、dom4j.jar、commons-logging.jar、commons-beanutils.jar以及<br />commons-collections.jar。<br />■ 说明：<br />要说清楚Jelly到底是什么以及它扮演着哪种角色是件很不容易的事情。Jelly 试图提供一个通用的<br />XML 脚本引擎，这种脚本引擎是可以由开发者通过定制动作和标记扩展的，XML文档之中的元素映射到<br />JavaBean，而XML 元素的属性映射到JavaBean的属性。从某种意义上说，Jelly是一种结合了Betwixt<br />和Digester的工具，但Jelly更强大，具有更好的可扩展性。<br />一个Jelly 系统由多个组件构成。第一个组件是Jelly 脚本，它是一种由Jelly引擎解析的XML文档，<br />经过解析的XML 文档元素被绑定到Jelly 标记动态处理。第二个组件是Jelly标记，它是一种实现了Jelly<br />的Tag 接口的JavaBean，凡是Jelly 标记都可以实现doTag 方法，这个doTag 方法就是当脚本引擎遇到<br />XML 文档中的特定元素时所执行的方法。Jelly正是通过这一机制实现动态的脚本处理能力，从某种意义<br />上看，有点类似于Digester 的工作机制。<br />Jelly 带有许多预定义的标记，其中部分标记提供核心Jelly支持，其他标记用来提供解析、循环、<br />条件执行代码等方面的支持。另外，Jelly 还为Ant任务提供了广泛的支持。<br />要在Java应用程序中使用Jelly，首先要创建一个JellyContext的实例，例如：JellyContext context<br />= new JellyContext();。我们可以把JellyContext对象看成是一个编译和运行Jelly脚本的运行环境。<br />有了JellyContext 就可以运行Jelly 脚本。JellyContext的输出实际上是一个XMLOutput类的实例：<br />context.runScript(new File("jellydemo.xml"), output);。<br />创建自定义标记时，我们既可以覆盖上面提到的doTag方法（如下面的例子所示），或者提供一个执<br />行方法，如invoke()或run()：<br />public void doTag(XMLOutput output) throws Exception {<br />// 在这里加入要执行的操作，<br />// 例如设置属性、访问文件系统等…<br />this.intProp = 3;<br />}<br />下面提供了一个定义Jelly 脚本的XML 文件示例：<br />&lt;j:jelly xmlns:j="jelly:core" xmlns:define="jelly:define"<br />xmlns:tr="trivialTag"&gt;<br />&lt;define:taglib uri="trivialTag"&gt;<br />&lt;define:jellybean name="trivial" className="TrivialTag" /&gt;<br />&lt;/define:taglib&gt;<br />&lt;tr:trivial intProp="1" stringProp="ball"&gt;Hello World&lt;/tr:trivial&gt;<br />&lt;/j:jelly&gt;<br />这个例子用到jelly:define 和jelly:core标记，以及一个trivialTag 标记。当遇到trivial标记<br />实例时，Jelly创建相应的JavaBean 的实例，执行doTag 方法（或者也可以是一个run 或invoke之类可<br />调用的方法）。<br />Jelly 还有许多其他功能，它既可以直接从命令行或Ant脚本运行，也可以嵌入到应用程序的代码之<br />内，请参见Jelly 文档了解详情。<br />2.4 JXPath<br />■ 概况：Java中的XPath 解释器。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你想要在JavaBean、DOM或其他对象构成的结构中应用XPath 查询之时。<br />■ 示例应用：JXPathDemo.java，Book.java，Author.java。要求CLASSPATH必须包含<br />commons-jxpath-1.1.jar。<br />■ 说明：<br />下面的说明要求读者已具备基本的XPath 知识。<br />XPath是一种查询XML文档的语言，JXPath将同一概念应用到了其他Java对象的查询，诸如JavaBean、<br />Collection、Array和Map 等。<br />JXPathContext是JXPath中的核心类，它利用一个工厂方法来定位和创建一个上下文的实例。由于<br />有了这一机制，必要时开发者可以插入一个新的JXPath 的实现。要使用JXPathContext，只要简单地向<br />它传递一个JavaBean、Collection 或Map，例如：JXPathContext context =<br />JXPathContext.newContext(book);。<br />利用JXPathContext 可执行许多任务。例如访问属性或嵌套属性，当然还可以设置属性：<br />System.err.println(context.getValue("title"));<br />System.err.println(context.getValue("author/authorId"));<br />context.setValue("author/authorId", "1001");<br />利用JXPath 还可以查找其他类型的对象，不过创建上下文对象的方式都一样，都是用上面介绍的静态<br />方法获得一个新的上下文，传入想要查询的对象。<br />结束语：有关包装类和XML 类的介绍就到这里结束。在下一篇也是最后一篇文章中，我们将了解工具<br />类的包。在这个系列文章的第一篇中，我们把Commons项目包含的组件分成了5类，介绍了Web类和其他<br />类。第二篇文章论及XML 类和包装类。这是最后一篇，探讨工具类的组件。注意Commons本身并不进行这<br />种分类，这里进行分类纯粹是为说明和组织方便起见。<br /><br /><b>第三部分、工具类</b><br />工具类包含BeanUtils、Logging、DBCP、Pool和Validator 这几个组件。<br />一、BeanUtils<br />■ 概况：提供了动态操作JavaBean 的工具。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你需要动态访问JavaBean，但对已编译好的accessor 和<br />modifier 一无所知之时。被动态访问的JavaBean 必须遵从JavaBeans<br />specification 定义的命名设计规范。<br />■ 示例应用：BeanUtilsDemo.java，AppLayer1Bean.java，<br />AppLayer2Bean.java，SubBean.java。要求CLASSPATH 中必须包含commons-beanutils.jar、<br />commons-logging.jar 以及commons-collections.jar。<br />■ 说明：<br />在动态Java应用程序设计环境中，我们不一定能够预先获知JavaBean 的各种set、get 方法。即使已经<br />知道了这些方法的名字，为Bean 的每个属性依次写出setXXX 或getXXX方法也是一件很麻烦的事情。考<br />虑一下这种情形：几个几乎完全相同的Bean 从应用的一个层传递到另一个层，你会为每一个属性调用<br />bean1.setXXX(bean2.getXXX())吗？虽然你可以这么做，但并非一定得这么做，因为你可以让BeanUtils<br />为你完成这些繁琐的操作！BeanUtils可以帮助开发者动态地创建、修改和复制JavaBean。<br />BeanUtils 能够操作符合下列条件的JavaBean：<br />⑴ JavaBean必须提供一个没有参数的构造函数。<br />⑵ JavaBean的属性必须能够通过getXXX和setXXX方法访问和修改。对于Boolean属性，也允许使用isXXX<br />和setXXX。JavaBean的属性可以是只读或只写的，也就是说，允许只提供属性的set或get方法。<br />⑶ 如果不采用传统的命名方式（即用get 和set），改用其它方式命名JavaBean 的accessor和modifier，<br />那么必须通过与JavaBean 关联的BeanInfo 类声明这一点。<br />下面来看一个简单的例子。<br />要获取和设置JavaBean 的简单属性，分别使用PropertyUtils.<br />getSimpleProperty(Object bean, String name)以及PropertyUtils.<br />setSimpleProperty(Object bean, String name, Object value)方法。如下面的例子所示，其中<br />AppLayer1Bean.java和AppLayer2Bean.java 定义了两个测试用的JavaBean。<br />PropertyUtils.setSimpleProperty(app1Bean,"intProp1", new Integer(10));<br />System.err.println("App1LayerBean, stringProp1: " +<br />PropertyUtils.getSimpleProperty(app1Bean, "stringProp1"));<br />既然我们可以通过直接调用Bean 的方法（app1Bean.getStringProp1()或<br />app1Bean.setIntProp1(10)）来获取或设置Bean 的属性，为什么还要使用setSimpleProperty、<br />getSimpleProperty方法呢？这是因为，我们不一定能够预先知道JavaBean 属性的名字，因此也不一定<br />知道要调用哪些方法才能获取/设置对应的属性。这些属性的名字可能来自其他过程或外部应用程序设置<br />的变量。因此，一旦搞清楚了JavaBean的属性的名字并把它保存到一个变量，你就可以将变量传递给<br />PropertyUtils，再也不必依靠其他开发者才能预先得知正确的方法名字。<br />那么，如果JavaBean 的属性不是简单数据类型，又该怎么办呢？例如，JavaBean的属性可能是一个<br />Collection，也可能是一个Map。在这种情况下，我们要改用PropertyUtils.getIndexedProperty或<br />PropertyUtils.getMappedProperty。对于集合类属性值，我们必须指定一个索引值，规定待提取或设置<br />的值在集合中的位置；对于Map 类属性，我们必须指定一个键，表示要提取的是哪一个值。下面是两个例子：<br />PropertyUtils.setIndexedProperty(<br />app1Bean, "listProp1[1]", "新字符串1");<br />System.err.println("App1LayerBean, listProp1[1]: " +<br />PropertyUtils.getIndexedProperty(app1Bean,<br />"listProp1[1]"));<br />请注意，对于可索引的属性，索引值是通过方括号传递的。例如上面的例子中，我们把JavaBean<br />（app1Bean）的List中索引为1 的值设置成了"新字符串1"，后面的一行代码又从索引1的位置提取同<br />一个值。还有另一种方式也可以达到同样的目标，即使用<br />PropertyUtils.setIndexedProperty(Object<br />bean, String name, int index, Object value)和PropertyUtils.getIndexedProperty(Object bean,<br />String name, int index)方法，在这两个方法中索引值作为方法的参数传递。对于Map类属性，也有类<br />似的方法，只要改用键（而不是索引）来获取或设置指定的值。<br />最后，Bean的属性可能也是一个Bean。那么，怎样来获取或设置那些以属性的形式从属于主Bean 的<br />属性Bean 呢？只要使用PropertyUtils.getNestedProperty(Object bean, String name)和<br />PropertyUtils.setNestedProperty(Object bean, String name, Object value)方法就可以了。下面提<br />供了一个例子。<br />// 访问和设置嵌套的属性<br />PropertyUtils.setNestedProperty(app1Bean, "subBean.stringProp",<br />"来自SubBean 的信息，通过setNestedProperty 设置。");<br />System.err.println(<br />PropertyUtils.getNestedProperty(app1Bean,"subBean.stringProp"));<br />通过上面的例子可以看出，从属Bean 的属性是通过一个句点符号访问的。<br />上述几种访问属性的方式可以结合在一起使用，嵌套深度不受限制。具体要用到的两个方法是<br />PropertyUtils.getProperty(Object bean, String name)和PropertyUtils.setProperty(Object bean,<br />String name, Object value)。例如：PropertyUtils.setProperty(app1Bean, "subBean.listProp[0]",<br />"属性的值");。<br />这个例子是把嵌套Bean 对象和可索引属性结合在一起访问。<br />BeanUtils经常用于动态访问Web 应用中的请求参数。实际上，正是BeanUtils触发了Struts 项目中<br />把请求参数动态转换成系统JavaBean 的灵感：利用代码把用户填写的表单转换成一个Map，其中参数的<br />名字变成Map中的键，参数的值则来自于用户在表单中输入的数据，然后由一个简单的<br />BeanUtils.populate调用把这些值转换成一个系统Bean。<br />最后，BeanUtils 提供了一个一步到位的方法把数据从一个Bean 复制到另一个Bean：<br />// 把app1Bean 的数据复制到app2Bean<br />BeanUtils.copyProperties(app2Bean, app1Bean);<br />BeanUtils 还有一些这里尚未提及的实用方法。不过不必担心，BeanUtils 是Commons 中文档较为完善的<br />组件之一，建议读者参阅BeanUtils 包的JavaDoc 文档了解其余方法的相关信息。<br />二、Logging<br />■ 概况：一个封装了许多流行日志工具的代码库，并提供统一的日志访问接口。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你的应用需要一种以上的日志工具之时，或者预期以后会有这种需要之时。<br />■ 示例应用：LoggingDemo.java，commons-logging.properties。要求CLASSPATH中必须包含<br />commons-logging.jar，有时还需要log4j.jar。<br />■ 说明：<br />日志（Logging）使得我们能够调试和跟踪应用程序任意时刻的行为和状态。在任何规模较大的应用中，<br />Logging 都是不可或缺的组成部分，因此现在已经有许多第三方Logging工具，它们免去了开发者自己编<br />写Logging API 之劳。实际上，即使JDK 也带有构造好了的Logging API。既然已经有这么多选择（log4j，<br />JDK，Logkit，等等），通常我们总是可以找到最适合自己应用要求的现成API。<br />不过也有可能出现例外的情形，例如一个熟悉的Logging API 不能和当前的应用程序兼容，或者是由于某<br />种硬性规定，或者是由于应用的体系结构方面的原因。Commons项目Logging 组件的办法是将记录日志的<br />功能封装为一组标准的API，但其底层实现却可以任意修改和变换。开发者利用这个API来执行记录日志<br />信息的命令，由API 来决定把这些命令传递给适当的底层句柄。因此，对于开发者来说，Logging组件对<br />于任何具体的底层实现都是中立的。<br />如果你熟悉log4j，使用Commons 的Logging API 应该不会有什么问题。即使你不熟悉log4j，只要知道<br />使用Logging必须导入两个类、创建一个Log 的静态实例，下面显示了这部分操作的代码：<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br />public class LoggingDemo {<br />private static Log log = LogFactory.getLog<br />(LoggingDemo.class);<br />// ...<br />}<br />有必要详细说明一下调用LogFactory.getLog()时发生的事情。调用该函数会启动一个发现过程，即<br />找出必需的底层日志记录功能的实现，具体的发现过程在下面列出。注意，不管底层的日志工具是怎么找<br />到的，它都必须是一个实现了Log 接口的类，且必须在CLASSPATH 之中。Commons LoggingAPI直接提供<br />对下列底层日志记录工具的支持：Jdk14Logger，Log4JLogger，LogKitLogger，NoOpLogger（直接丢弃<br />所有日志信息），还有一个SimpleLog。<br />⑴ Commons 的Logging 首先在CLASSPATH 中寻找一个commons-logging.properties 文件。这个属性<br />文件至少必须定义org.apache.commons.logging.Log 属性，它的值应该是上述任意Log 接口实现的完整<br />限定名称。<br />⑵ 如果上面的步骤失败，Commons 的Logging接着检查系统属性<br />org.apache.commons.logging.Log。<br />⑶ 如果找不到org.apache.commons.logging.Log系统属性，Logging接着在CLASSPATH中寻找log4j<br />的类。如果找到了，Logging就假定应用要使用的是log4j。不过这时log4j 本身的属性仍要通过<br />log4j.properties 文件正确配置。<br />⑷ 如果上述查找均不能找到适当的Logging API，但应用程序正运行在JRE 1.4 或更高版本上，则<br />默认使用JRE 1.4 的日志记录功能。<br />⑸ 最后，如果上述操作都失败，则应用将使用内建的SimpleLog。SimpleLog 把所有日志信息直接输<br />出到System.err。<br />获得适当的底层日志记录工具之后，接下来就可以开始记录日志信息。作为一种标准的API，Commons<br />Logging API 主要的好处是在底层日志机制的基础上建立了一个抽象层，通过抽象层把调用转换成与具体<br />实现有关的日志记录命令。<br />本文提供的示例程序会输出一个提示信息，告诉你当前正在使用哪一种底层的日志工具。请试着在不<br />同的环境配置下运行这个程序，例如，在不指定任何属性的情况下运行这个程序，这时默认将使用<br />Jdk14Logger；然后指定系统属性-Jorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog <br />再运行程序，这时日志记录工具将是SimpleLog；最后，把Log4J的类放入<br />CLASSPATH，只要正确设置了log4j 的log4j.properties 配置文件，就可以得到Log4JLogger输出的信息。<br />三、Pool<br />■ 概况：用来管理对象池的代码库。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：当你需要管理一个对象实例池之时。<br />■ 示例应用：PoolDemo.java 和MyObjectFactory.java。要求CLASSPATH中必须有commons-pool.jar<br />和commons-collections.jar。<br />■ 说明：<br />Pool 组件定义了一组用于对象池的接口，另外还提供了几个通用的对象池实现，以及<br />一些帮助开发者自己创建对象池的基础类。<br />对于大多数开发者来说，对象池应该不算什么新概念了。也许许多读者已经在访问数据<br />库的时候使用过数据库连接池，对象池的概念其实也相似。对象池允许开发者在缓冲区中创<br />建一组对象（创建对象的操作可以通过应用的配置文件完成，或者也可以在应用的启动阶段<br />完成），当应用程序需要用到对象时就可以很快获得相响应。如果应用程序不再需要对象，<br />它仍旧把对象返回给缓冲池，下次需要使用对象时再从缓冲池提取。<br />Pool 组件允许我们创建对象（实例）池，但不限制我们一定要使用某个具体的实现。<br />Pool 组件本身提供了几种实现，必要时我们还可以创建自己的实现。<br />Pool 组件包含三个基本的类：ObjectPool，这是一个定义和维护对象池的接口；<br />ObjectPoolFactory，负责创建ObjectPool 的实例；还有一个PoolableObjectFacotry，它<br />为那些用于ObjectPool 之内的实例定义了一组生命周期方法。<br />如前面指出的，Pool组件包含几种通用的实现，其中一个就是GenericObjectPool，下<br />面通过一个实例来看看它的用法。<br />① 创建一个PoolableObjectFactory。这个工厂类定义对象如何被创建、拆除和验证。<br />import org.apache.commons.pool.PoolableObjectFactory;<br />public class MyObjectFactory implements<br />PoolableObjectFactory {<br />private static int counter;<br />// 返回一个新的字符串<br />public Object makeObject() {<br />return String.valueOf(counter++);<br />}<br />public void destroyObject(Object obj) {}<br />public boolean validateObject(Object obj)<br />{ return true; }<br />public void activateObject(Object obj) {}<br />public void passivateObject(Object obj) {}<br />}<br />本例创建了一个序号不断增加的String 对象的池，验证操作（validateObject）总是<br />返回true。<br />② 利用PoolableObjectFactory 创建一个GenericObjectPool，maxActive、maxIdle<br />等选项都采用默认值。<br />GenericObjectPool pool = new GenericObjectPool<br />(new MyObjectFactory());<br />③ 从对象池"借用"一个对象。<br />System.err.println("Borrowed: " + pool.borrowObject());<br />④ 把对象返回给对象池。<br />pool.returnObject("0");<br />对象池的状态可以通过多种方法获知，例如：<br />// 有多少对象已经激活（已被借用）？<br />System.err.println("当前活动的对象数量: " + pool.getNumActive());<br />本文后面提供的PoolDemo.java 提供了完整的源代码。<br />四、DBCP<br />■ 概况：数据库连接池。建立在Pool 组件的基础上。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：需要访问关系数据库之时。<br />■ 示例应用：DBCPDemo.java。要求CLASSPATH中必须有commons-dbcp.jar、<br />commons-pool.jar 以及commons-collections.jar。另外还要能够访问数据库，配置适合该<br />数据库的JDBC 驱动程序。示例应用测试的是一个MySQL数据库连接，驱动程序是MySQL JDBC<br />driver。注意运行这个程序需要二进制文件的nightly 版，当前的正式发行版缺少某些必需的类。<br />最后，运行这个示例程序时，应当确保已经为JDBC 驱动程序设置了系统属性（-Djdbc.drivers=com.mysql.jdbc.Driver）。<br />■ 说明：<br />DBCP 建立在Pool组件的基础上，提供了数据库连接缓冲池机制。与常规的连接池相比，<br />DBCP 的使用要稍微复杂一点，因为它的思路是以伪JDBC 驱动程序的形式提供一个通用的体<br />系。不过，前面我们已经了解了Pool组件的基本知识，现在要理解DBCP 的用法应该也很简单了。<br />// ...<br />// ① 创建一个GenericObjectPool 类的实例。<br />GenericObjectPool pool = new GenericObjectPool(null);<br />// ...<br />// ② 在前面讨论Pool 组件时提到GenericObjectPool<br />// 要求有一个PoolableObjectFactory 来创建需<br />// 要缓冲的Object 的实例，对于DBCP 来说，<br />// 这一功能现在由PoolableConnectionFactory提<br />// 供，如下面的例子所示：<br />DriverManagerConnectionFactory cf =<br />new DriverManagerConnectionFactory(<br />"jdbc:mysql://host/db", "username", "password");<br />PoolableConnectionFactory pcf = new PoolableConnectionFactory(<br />CF, pool, null, "SELECT * FROM mysql.db", false, true);<br />// ...<br />// ③ 现在，我们只要创建并注册PoolingDriver：<br />new PoolingDriver().registerPool("myPool", pool);<br />接下来就可以从这个连接池提取连接了。注意创建这个连接池时采用了maxActive、<br />maxIdle 等选项的默认值，如有必要，你可以在前面步骤1创建GenericObjectPool 类的实<br />例时自定义这些值。DBCPDemo.java 提供了一个完整的实例。<br />五、Validator<br />■ 概况：一个收集了常见用户输入验证功能的API。<br />■ 官方资源：主页，二进制，源代码。<br />■ 何时适用：对JavaBean 执行常规验证操作之时。<br />■ 示例应用：ValidatorDemo.java，MyValidator.java，MyFormBean.java，<br />validation.xml。要求CLASSPATH 中必须有commons-validator.jar，<br />commons-beanutils.jar，commons-collections.jar，commons-digester.jar，以及<br />commons-logging.jar。<br />■ 说明：<br />如果你曾经用Struts 开发过Web 应用，那么应该已经用过Validator包了。Validator<br />包极大地简化了用户输入数据的检验。不过，Validator 并不局限于Web应用，它还可以方<br />便地用于其它使用了JavaBean的场合。<br />Validator 允许为用户输入域定义验证条件，支持错误信息国际化，允许创建自定义的<br />验证器，此外，Validator 包还提供了一些预定义的可以直接使用的验证器。<br />验证规则和验证方法用XML文件定义（可以用一个或者多个XML 文件定义，但通常而言，<br />把它们分开比较好）。验证方法文件定义了要用到的验证器，指定各个实际实现验证器的<br />Java 类（不要求这些类实现某些特定的接口，也不要求这些类必须从特定的类派生，只需<br />要遵从方法定义文件中声明的定义就可以了）。<br />下面我们就来构造一个自定义的验证器，它的功能是检查Bean的一个String属性是否<br />包含特定的字符（"*"）。<br />import org.apache.commons.validator.*;<br />public class MyValidator {<br />public static boolean validateContainsChar(<br />Object bean, Field field) {<br />// 首先获得Bean 的属性（即一个String 值）<br />String val = ValidatorUtil.getValueAsString<br />(bean, field.getProperty());<br />// 根据属性中是否包含"*"字符，返回true 或false。<br />return ((val.indexOf('*') == -1)?false:true);<br />}<br />}<br />ValidatorUtil类提供了许多实用方法，例如ValidatorUtil.getValueAsString用来<br />提取Bean的属性值并返回一个String。现在我们要在XML文件中声明MyValidator验证器。<br />&lt;!-- 定义验证器方法--&gt;<br />&lt;global&gt;<br />&lt;validator name="containsStar"<br />classname="MyValidator"<br />method="validateContainsChar"<br />methodParams="java.lang.Object,<br />org.apache.commons.validator.Field" /&gt;<br />&lt;/global&gt;<br />可以看到，XML 文件详细地定义了验证方法的特征，包括该方法的输入参数。下面来看<br />看使用这个验证器的步骤。<br />① 在上面的XML文件中加入我们要实现的验证规则。<br />&lt;!-- 定义验证规则--&gt;<br />&lt;formset&gt;<br />&lt;!-- 检查Bean的name 属性是否能够通过<br />containsStar测试--&gt;<br />&lt;form name="myFormBean"&gt;<br />&lt;field property="name" depends="containsStar"&gt;<br />&lt;arg0 key="myFormBean.name" /&gt;<br />&lt;/field&gt;<br />&lt;/form&gt;<br />&lt;/formset&gt;<br />可以看到，所有验证规则都在formset 元素之内声明。formset 元素之内首先声明要验<br />证的表单，表单之内列出了要验证的输入域及其验证条件。在本例中，我们希望验证<br />myFormBean的name属性，检查该属性是否能够通过containsStar的验证（也即name 属性<br />的值是否包含"*"字符）。<br />② 以XML文件为基础，创建一个Validator 实例并予以初始化。<br />// 装入验证器XML 文件<br />InputStream in = getClass().getResourceAsStream<br />("validator.xml");<br />// 创建一个ValidatorResources<br />ValidatorResources resources = new ValidatorResources();<br />// 初始化验证器资源<br />ValidatorResourcesInitializer.initialize(resources, in);<br />// 创建Validator<br />Validator validator = new Validator(resources, "myFormBean");<br />validator.addResource(Validator.BEAN_KEY, bean);<br />③ 验证Bean。验证的结果是一个ValidatorResults，其中包含了各个要求验证的属性<br />按照各自的验证条件执行验证的结果。<br />// 执行验证<br />ValidatorResults results = validator.validate();<br />④ 处理ValidationResults。<br />//验证结果对象ValidationResults 可能还包含了验证其他表单属性的结果，<br />//对于每一个属性，我们都可以单独提取其验证结果。<br />ValidatorResult result = results.getValidatorResult("name");<br />// 对于每一个属性，我们可以分别检查各个验证条件的检查结果。<br />// 例如，name 属性通过了containsStar 验证吗？<br />System.err.println("name 属性包含"*"字符的测试结果：" +<br />result.isValid("containsStar"));<br />对于每一个ValidationResult 的实例，我们可以查询它是否通过了某项特定的检查。<br />例如，在上面的代码中，我们用result.isValid('containsStart')表达式来检查name 属<br />性的ValidatorResult 实例，看看它是否通过了containsStar 验证。<br />对于Web 应用来说，Validator是一个相当有用的组件，它提供了一组预定义的验证器，<br />极大地方便了用户输入合法性的验证。预定义的验证器可以用来（但不限于）检查输入值的<br />范围、数据类型、长度，以及email 地址和地理位置检查。此外，我们还可以自己定义验证<br />器并将它加入到Validator框架之中。<br />结束语：第三篇（也是最后一篇）介绍Jakarta Commons 的文章就到这里结束。虽然<br />这个系列的文章只涉及了各个组件的基础知识，但希望它们已经足以让你开始下一步的深入研究。<br /><img src ="http://www.blogjava.net/TrampEagle/aggbug/47187.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-20 17:21 <a href="http://www.blogjava.net/TrampEagle/articles/47187.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java.util包(转)</title><link>http://www.blogjava.net/TrampEagle/articles/46598.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 17 May 2006 03:51:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/46598.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/46598.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/46598.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/46598.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/46598.html</trackback:ping><description><![CDATA[
		<div class="postcontent">ArrayList是List接口的一个可变长数组实现。实现了所有List接口的操作，并允许存储null值。除了没有进行同步，ArrayList基本等同于Vector。在Vector中几乎对所有的方法都进行了同步，但ArrayList仅对writeObject和readObject进行了同步，其它比如add(Object)、remove(int)等都没有同步。<br /><br />1.存储<br />ArrayList使用一个Object的数组存储元素。<br />private transient Object elementData[];<br />ArrayList实现了java.io.Serializable接口，这儿的transient标示这个属性不需要自动序列化。下面会在writeObject()方法中详细讲解为什么要这样作。<br /><br />2.add和remove<br /><br /><br />    public boolean add(Object o) { <br />    ensureCapacity(size + 1);  // Increments modCount!! <br />    elementData[size++] = o; <br />    return true; <br />    } <br /><br />注意这儿的ensureCapacity()方法，它的作用是保证elementData数组的长度可以容纳一个新元素。在“自动变长机制”中将详细讲解。<br /><br />    public Object remove(int index) { <br />    RangeCheck(index); <br />    modCount++; <br />    Object oldValue = elementData[index]; <br />    int numMoved = size - index - 1; <br />    if (numMoved &gt; 0) <br />        System.arraycopy(elementData, index+1, elementData, index, <br />                 numMoved); <br />    elementData[--size] = null; // Let gc do its work <br />    return oldValue; <br />    } <br /><br />RangeCheck()的作用是进行边界检查。由于ArrayList采用一个对象数组存储元素，所以在删除一个元素时需要把后面的元素前移。删除一个元素时只是把该元素在elementData数组中的引用置为null，具体的对象的销毁由垃圾收集器负责。<br />modCount的作用将在下面的“iterator()中的同步”中说明。<br />注：在前移时使用了System提供的一个实用方法：arraycopy()，在本例中可以看出System.arraycopy()方法可以对同一个数组进行操作，这个方法是一个native方法，如果对同一个数组进行操作时，会首先把从源部分拷贝到一个临时数组，在把临时数组的元素拷贝到目标位置。<br /><br />3.自动变长机制<br />在实例化一个ArrayList时，你可以指定一个初始容量。这个容量就是elementData数组的初始长度。如果你使用：<br /><br />    ArrayList list = new ArrayList(); <br /><br />则使用缺省的容量：10。<br /><br />    public ArrayList() { <br />    this(10); <br />    } <br /><br />ArrayList提供了四种add()方法，<br /><br />public boolean add(Object o)<br /><br />public void add(int index, Object element)<br /><br />public boolean addAll(Collection c)<br /><br />public boolean addAll(int index, Collection c)<br /><br />在每一种add()方法中，都首先调用了一个ensureCapacity(int miniCapacity)方法，这个方法保证elementData数组的长度不小于miniCapacity。ArrayList的自动变长机制就是在这个方法中实现的。<br /><br />    public void ensureCapacity(int minCapacity) { <br />    modCount++; <br />    int oldCapacity = elementData.length; <br />    if (minCapacity &gt; oldCapacity) { <br />        Object oldData[] = elementData; <br />        int newCapacity = (oldCapacity * 3)/2 + 1; <br />            if (newCapacity &lt; minCapacity) <br />        newCapacity = minCapacity; <br />        elementData = new Object[newCapacity]; <br />        System.arraycopy(oldData, 0, elementData, 0, size); <br />    } <br />    } <br /><br />从这个方法实现中可以看出ArrayList每次扩容，都扩大到原来大小的1.5倍。<br />每种add()方法的实现都大同小异，下面给出add(Object)方法的实现：<br /><br />    public boolean add(Object o) { <br />    ensureCapacity(size + 1);  // Increments modCount!! <br />    elementData[size++] = o; <br />    return true; <br />    } <br /><br /><br />4.iterator()中的同步<br />在父类AbstractList中定义了一个int型的属性：modCount，记录了ArrayList结构性变化的次数。<br /><br />    protected transient int modCount = 0; <br /><br />在ArrayList的所有涉及结构变化的方法中都增加modCount的值，包括：add()、remove()、addAll()、removeRange()及clear()方法。这些方法每调用一次，modCount的值就加1。<br />注：add()及addAll()方法的modCount的值是在其中调用的ensureCapacity()方法中增加的。<br /><br />AbstractList中的iterator()方法（ArrayList直接继承了这个方法）使用了一个私有内部成员类Itr，生成一个Itr对象（Iterator接口）返回：<br /><br />    public Iterator iterator() { <br />    return new Itr(); <br />    } <br /><br />Itr实现了Iterator()接口，其中也定义了一个int型的属性：expectedModCount，这个属性在Itr类初始化时被赋予ArrayList对象的modCount属性的值。<br /><br />    int expectedModCount = modCount; <br /><br />注：内部成员类Itr也是ArrayList类的一个成员，它可以访问所有的AbstractList的属性和方法。理解了这一点，Itr类的实现就容易理解了。<br /><br />在Itr.hasNext()方法中：<br /><br />    public boolean hasNext() { <br />        return cursor != size(); <br />    } <br /><br />调用了AbstractList的size()方法，比较当前光标位置是否越界。<br /><br />在Itr.next()方法中，Itr也调用了定义在AbstractList中的get(int)方法，返回当前光标处的元素：<br /><br />    public Object next() { <br />        try { <br />        Object next = get(cursor); <br />        checkForComodification(); <br />        lastRet = cursor++; <br />        return next; <br />        } catch(IndexOutOfBoundsException e) { <br />        checkForComodification(); <br />        throw new NoSuchElementException(); <br />        } <br />    } <br /><br />注意，在next()方法中调用了checkForComodification()方法，进行对修改的同步检查：<br /><br />    final void checkForComodification() { <br />        if (modCount != expectedModCount) <br />        throw new ConcurrentModificationException(); <br />    } <br /><br />现在对modCount和expectedModCount的作用应该非常清楚了。在对一个集合对象进行跌代操作的同时，并不限制对集合对象的元素进行操作，这些操作包括一些可能引起跌代错误的add()或remove()等危险操作。在AbstractList中，使用了一个简单的机制来规避这些风险。这就是modCount和expectedModCount的作用所在。<br /><br />5.序列化支持<br />ArrayList实现了java.io.Serializable接口，所以ArrayList对象可以序列化到持久存储介质中。ArrayList的主要属性定义如下：<br /><br />private static final long serialVersionUID = 8683452581122892189L;<br /><br />private transient Object elementData[];<br /><br />private int size;<br /><br />可以看出serialVersionUID和size都将自动序列化到介质中，但elementData数组对象却定义为transient了。也就是说ArrayList中的所有这些元素都不会自动系列化到介质中。为什么要这样实现？因为elementData数组中存储的“元素”其实仅是对这些元素的一个引用，并不是真正的对象，序列化一个对象的引用是毫无意义的，因为序列化是为了反序列化，当你反序列化时，这些对象的引用已经不可能指向原来的对象了。所以在这儿需要手工的对ArrayList的元素进行序列化操作。这就是writeObject()的作用。<br /><br />    private synchronized void writeObject(java.io.ObjectOutputStream s) <br />        throws java.io.IOException{ <br />    // Write out element count, and any hidden stuff <br />    s.defaultWriteObject(); <br />   // Write out array length <br />    s.writeInt(elementData.length); <br />    // Write out all elements in the proper order. <br />    for (int i=0; i&lt;size; i++) <br />            s.writeObject(elementData[i]); <br />    } <br /><br />这样元素数组elementData中的所以元素对象就可以正确地序列化到存储介质了。<br />对应的readObject()也按照writeObject()方法的顺序从输入流中读取：<br /><br />    private synchronized void readObject(java.io.ObjectInputStream s) <br />        throws java.io.IOException, ClassNotFoundException { <br />    // Read in size, and any hidden stuff <br />    s.defaultReadObject(); <br />    // Read in array length and allocate array <br />    int arrayLength = s.readInt(); <br />    elementData = new Object[arrayLength]; <br />    // Read in all elements in the proper order. <br />    for (int i=0; i&lt;size; i++) <br />            elementData[i] = s.readObject(); <br />    } <br /><br />本章介绍Java的实用工具类库java.util包。在这个包中，Java提供了一些实用的方法和数据结构。例如，Java提供日期(Data)类、日历(Calendar)类来产生和获取日期及时间，提供随机数(Random)类产生各种类型的随机数，还提供了堆栈(Stack)、向量(Vector) 、位集合(Bitset)以及哈希表(Hashtable)等类来表示相应的数据结构。<br />　　图1.1给出了java.util包的基本层次结构图。下面我们将具体介绍其中几个重要的类。<br />　　　　　　　　　　　┌java.util.BitSet<br />　　　　　　　　　　　│java.util.Calendar<br />　　　　　　　　　　　│　　　　　　└java.util.GregorianCalendar<br />　　　　　　　　　　　│java.util.Date<br />　　　　　　　　　　　│java.util.Dictionary<br />　　　　　　　　　　　│　　　　　　└java.util.Hashtable<br />　　　　　　　　　　　│　　　　　　　　　　　　　└java.util.Properties<br />　　　　　　　　　　　│java.util.EventObject<br />　　　　　　　　　　　│java.util.ResourceBundle<br />　　　　　　　┌普通类┤　　　　　　├java.util.ListResourceBundle<br />　　　　　　　│　　　│　　　　　　└java.util.PropertyResourceBundle<br />　　　　　　　│　　　│java.util.Local<br />　　　　　　　│　　　│java.util.Observable<br />　　　　　　　│　　　│java.util.Random<br />　　　　　　　│　　　│java.util.StringTokenizer<br />　　　　　　　│　　　│java.util.Vector<br />　　　　　　　│　　　│　　　　　　└java.util.Stack<br />　　Java.util┤　　　└java.util.TimeZone<br />　　　　　　　│　　　　　　　　　　└java.util.SimpleTimeZone<br />　　　　　　　│　　　┌java.util.Enumeration<br />　　　　　　　├接　口┤java.util.EventListener<br />　　　　　　　│　　　└java.util.Observer<br />　　　　　　　│　　　┌java.util.EmptyStackException<br />　　　　　　　└异常类┤java.util.MissingResourceException<br />　　　　　　　　　　　│java.util.NoSuchElementException<br />　　　　　　　　　　　└java.util.TooManyListenersException<br />　　　　　　　图1.1 java.util包的基本层次结构<br /><br /><br />1.2 日期类Date<br /><br />　　Java在日期类中封装了有关日期和时间的信息，用户可以通过调用相应的方法来获取系统时间或设置日期和时间。Date类中有很多方法在JDK1.0公布后已经过时了，在8.3中我们将介绍JDK1.0中新加的用于替代Date的功能的其它类。<br />　　在日期类中共定义了六种构造函数。<br />　　(1)public Date()<br />　　创建的日期类对象的日期时间被设置成创建时刻相对应的日期时间。<br />　　例 Date today=new Date()；//today被设置成创建时刻相对应的日期时间。<br />　　(2)public Date (long date)<br />　　long 型的参数date可以通过调用Date类中的static方法parse(String s)来获得。<br />　　例 long l=Date.parse("Mon 6 Jan 1997 13:3:00");<br />　　　　Date day=new Date(l);<br />　　//day中时间为1997年 1月6号星期一，13:3:00。<br />　　(3)public Date(String s)<br />　　按字符串s产生一日期对象。s的格式与方法parse中字符串参数的模式相同。<br />　　例 Date day=new Date("Mon 6 Jan 1997 13:3:00");<br />　　//day 中时间为1997年1月6号星期一，13:3:00.<br />　　(4)public Date(int year,int month,int date)<br />　　(5)public Date(int year,int month,int date,int hrs,int min)<br />　　(6)public Date(int year,int month,int date,int hrs,int min,int sec)<br />　　按给定的参数创建一日期对象。<br />　　参数说明：<br />　　year的值为：需设定的年份-1900。例如需设定的年份是1997则year的值应为97，即1997-1900的结果。所以Date中可设定的年份最小为1900；<br />　　month的值域为0～11，0代表1月，11表代表12月；<br />　　date的值域在1～31之间；<br />　　hrs的值域在0～23之间。从午夜到次日凌晨1点间hrs=0，从中午到下午1点间hrs=12；<br />　　min和sec的值域在0～59之间。<br />　　例 Date day=new Date(11,3,4);<br />　　//day中的时间为：04-Apr-11 12:00:00 AM<br />另外，还可以给出不正确的参数。<br />　　例　设定时间为1910年2月30日，它将被解释成3月2日。<br />　　Date day=new Date(10,1,30,10,12,34);<br />　　System.out.println("Day's date is:"+day);<br />　　//打印结果为：Day's date is:Web Mar 02 10:13:34 GMT+08:00 1910<br />　　下面我们给出一些Date类中常用方法。<br />　　(1)public static long UTC(int year,int month,int date,int hrs. int min,int sec)<br />　　该方法将利用给定参数计算UTC值。UTC是一种计时体制，与GMT(格林威治时间)的计时体系略有差别。UTC计时体系是基于原子时钟的，而GTMT计时体系是基于天文学观测的。计算中使用的一般为GMT计时体系。<br />　　(2)public static long parse(String s)<br />　　该方法将字符串s转换成一个long型的日期。在介绍构造方法Date(long date)时曾使用过这个方法。<br />　　字符串s有一定的格式，一般为：<br />　　(星期 日 年 时间GMT+时区)<br />　　若不注明时区，则为本地时区。<br />　　(3)public void setMonth(int month)<br />　　(4)public int getMonth()<br />　　这两个方法分别为设定和获取月份值。<br />　　获取的月份的值域为0～11，0代表1月，11代表12月。<br />　　(5)public String toString()<br />　　(6)public String toLocalString()<br />　　(7)public String toGMTString()<br />　　将给定日期对象转换成不同格式的字符串。它们对应的具体的格式可参看例子8.1。<br />　　(8)public int getTimezoneOffset()<br />　　该方法用于获取日期对象的时区偏移量。<br />　　例8.1中对上面介绍的Date类中的基本方法进行了具体的应用，并打印了相应的结果。由于使用了一些过时的方法，所以编译时会有警告信息。另外，由于本例中的时间表示与平台有关，不同的JDK版本对此处理不完全相同，因此不同版本的JDK执行本例的结果可能有细微差异。<br />　　例1.1 DateApp.java<br />　　import java.lang.System;<br />　　import java.util.Date;<br />　　public class DateApp{<br />　　　public static void main(String args[]){<br />　　　　Date today=new Date();<br />　　　　//today中的日期被设成创建时刻的日期和时间，假设创建时刻为1997年3月<br />　　　　//23日17时51分54秒。<br />　　　　System.out.println("Today's date is "+today);<br />　　　　//返回一般的时间表示法，本例中结果为<br />　　　　//Today's date is Fri May 23 17:51:54 1997<br />　　　　System.out.println("Today's date(Internet GMT)is:"<br />　　　　　+today.toGMTString());<br />　　　　//返回结果为GMT时间表示法，本例中结果为<br />　　　　//Today's date(Internet GMT)is: 23 May 1997 09:51:54:GMT<br />　　　　System.out.println("Today's date(Locale) is:"<br />　　　　　+today.toLocaleString());<br />　　　　//返回结果为本地习惯的时间表示法，结果为<br />　　　　//Today's date(Locale)is:05/23/97 17:51:54<br />　　　　System.out.println("Today's year is: "+today.getYear());<br />　　　　System.out.println("Today's month is: "+(today.getMonth()+1));<br />　　　　System.out.println("Today's date is: "+today.getDate());<br />　　　　//调用Date类中方法，获取年月日的值。<br />　　　　//下面调用了不同的构造方法来创建Date类的对象。<br />　　　　Date day1=new Date(100,1,23,10,12,34);<br />　　　　System.out.println("Day1's date is: "+day1);<br />　　　　Date day2=new Date("Sat 12 Aug 1996 13:3:00");<br />　　　　System.out.println("Day2's date is: "+day2);<br />　　　　long l= Date.parse("Sat 5 Aug 1996 13:3:00 GMT+0800");<br />　　　　Date day3= new Date(l);<br />　　　　System.out.println("Day3's date(GMT)is: "+day3.toGMTString());<br />　　　　System.out.println("Day3's date(Locale)is: "<br />　　　　　+day3.toLocaleString());<br />　　　　System.out.println("Day3's time zone offset is:"<br />　　　　　+day3.getTimezoneOffset());<br />　　　}<br />　　}<br /><br />　　运行结果(JDK1.3版，与原文不同，原文是JDK1.0版)：<br />　　E:\java\tutorial\java01&gt;java DateApp<br />　　Today's date is Thu Dec 27 17:58:16 CST 2001<br />　　Today's date(Internet GMT)is:27 Dec 2001 09:58:16 GMT<br />　　Today's date(Locale) is:2001-12-27 17:58:16<br />　　Today's year is: 101<br />　　Today's month is: 12<br />　　Today's date is: 27<br />　　Day1's date is: Wed Feb 23 10:12:34 CST 2000<br />　　Day2's date is: Fri Aug 12 13:03:00 CST 1996<br />　　Day3's date(GMT)is: 5 Aug 1996 05:03:00 GMT<br />　　Day3's date(Locale)is: 1996-8-5 13:03:00<br />　　Day3's time zone offset is:-480<br /><br />　　E:\java\tutorial\java01&gt;<br /><br />1.3 日历类Calendar<br /><br />　　在早期的JDK版本中，日期(Date)类附有两大功能：(1)允许用年、月、日、时、分、秒来解释日期：(2)允许对表示日期的字符串进行格式化和句法分析。在JDK1.1中提供了类Calendar来完成第一种功能，类DateFormat来完成第二项功能。dateFormat是java.text包中的一个类。与Date类有所不同的是，DateFormat类接受用各种语言和不同习惯表示的日期字符串。本节将介绍java.util包中的类Calendar及其它新增加的相关的类。<br />　　类Calendar是一个抽象类，它完成日期(Date)类和普通日期表示法(即用一组整型域如YEAR，MONTH，DAY，HOUR表示日期)之间的转换。<br />　　由于所使用的规则不同，不同的日历系统对同一个日期的解释有所不同。在JDK1.1中提供了Calendar类一个子类GregorianCalendar??它实现了世界上普遍使用的公历系统。当然用户也可以通过继承Calendar类，并增加所需规则，以实现不同的日历系统。<br />　　第GregorianCalendar继承了Calendar类。本节将在介绍类GregorianCalendar的同时顺带介绍Calendar类中的相关方法。<br />　　类GregorianCalendar提供了七种构造函数：<br />　　(1)public GregorianCalendar()<br />　　创建的对象中的相关值被设置成指定时区，缺省地点的当前时间，即程序运行时所处的时区、地点的当前时间。<br />　　(2)public GregorianCalendar(TimeZone zone)<br />　　创建的对象中的相关值被设置成指定时区zone，缺省地点的当前时间。<br />　　(3)public GregorianCalendar(Locale aLocale)<br />　　创建的对象中的相关值被设置成缺省时区，指定地点aLocale的当前时间。<br />　　(4)public GregorianCalendar(TimeZone zone,Local aLocale)<br />　　创建的对象中的相关值被设置成指定时区，指定地点的当前时间。<br />　　上面使用到的类TimeZone的性质如下：<br />　　TimeZone是java.util包中的一个类，其中封装了有关时区的信息。每一个时区对应一组ID。类TimeZone提供了一些方法完成时区与对应ID两者之间的转换。<br />　　(Ⅰ)已知某个特定的ID，可以调用方法<br />　　public static synchronized TimeZone getTimeZone(String ID)<br />来获取对应的时区对象。<br />　　例 太平洋时区的ID为PST，用下面的方法可获取对应于太平洋时区的时区对象：<br />　　TimeZone tz=TimeZone.getTimeZone("PST");<br />　　调用方法getDefault()可以获取主机所处时区的对象。<br />　　TimeZone tz=TimeZone.getDefault();<br />　　(Ⅱ)调用以下方法可以获取时区的ID<br />　　■public static synchronized String[] getavailableIDs(int rawOffset)<br />　　根据给定时区偏移值获取ID数组。同一时区的不同地区的ID可能不同，这是由于不同地区对是否实施夏时制意见不统一而造成的。<br />　　例String s[]=TimeZone.getAvailableIDs(-7*60*60*1000);<br />　　打印s，结果为s[0]=PNT，s[1]=MST<br />　　■public static synchronized String[] getAvailableIDs()<br />　　获取提供的所有支持的ID。<br />　　■public String getID()<br />　　获取特定时区对象的ID。<br />　　例 TimeZone tz=TimeZone.getDefault();<br />　　String s=tz.getID();<br />　　打印s，结果为s=CTT。<br />　　上面使用类的对象代表了一个特定的地理、政治或文化区域。Locale只是一种机制，它用来标识一类对象，Local本身并不包含此类对象。<br />　　要获取一个Locale的对象有两种方法：<br />　　(Ⅰ)调用Locale类的构造方法<br />　　Locale(String language,String country)<br />　　Locale(String language,String country,String variant)<br />　　参数说明：language??在ISO-639中定义的代码，由两个小写字母组成。<br />　　　　　　　country??在ISO-3166中定义的代码，由两个大写字母组成。<br />　　　　　　　variant??售货商以及特定浏览器的代码，例如使用WIN代表Windows。<br />　　(Ⅱ)调用Locale类中定义的常量<br />　　Local类提供了大量的常量供用户创建Locale对象。<br />　　例 Locale.CHINA<br />　　　　为中国创建一个Locale的对象。<br />　　类TimeZone和类Locale中的其它方法，读者可查阅API。<br />　　(5)public GregorianCalendar(int year,int month,int date)<br />　　(6)public GregorianCalendar(int year,int month,int date,int hour,int minute)<br />　　(7)public GregorianCalendar(int year,int month,int date,int hour,int minute,int second)<br />　　用给定的日期和时间创建一个GregorianCalendar的对象。<br />　　参数说明：<br />　　year-设定日历对象的变量YEAR；month-设定日历对象的变量MONTH；<br />　　date-设定日历对象的变量DATE；hour-设定日历对象的变量HOUR_OF_DAY；<br />　　minute-设定日历对象的变量MINUTE；second-设定日历对象的变量SECOND。<br />　　与Date类中不同的是year的值没有1900这个下限，而且year的值代表实际的年份。month的含义与Date类相同，0代表1月，11代表12月。<br />　　例 GregorianCalendar cal=new GregorianCalendar(1991,2,4)<br />　　cal的日期为1991年3月4号。<br />　　除了与Date中类似的方法外，Calendar类还提供了有关方法对日历进行滚动计算和数学计算。计算规则由给定的日历系统决定。进行日期计算时，有时会遇到信息不足或信息不实等特殊情况。Calendar采取了相应的方法解决这些问题。当信息不足时将采用缺省设置，在GregorianCalendar类中缺省设置一般为YEAR=1970,MONTH=JANUARY,DATE=1。<br />　　当信息不实时，Calendar将按下面的次序优先选择相应的Calendar的变量组合，并将其它有冲突的信息丢弃。<br />　　MONTH+DAY_OF_MONTH<br />　　MONTH+WEEK_OF_MONTH+DAY_OF_WEEK<br />　　MONTH+DAY_OF_WEEK_OF_MONTH+DAY_OF_WEEK<br />　　DAY_OF+YEAR<br />　　DAY_OF_WEEK_WEEK_OF_YEAR<br />　　HOUR_OF_DAY<br /><br />1.4 随机数类Random<br /><br />　　Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处，后者只产生double型的随机数。<br />　　类Random中的方法十分简单，它只有两个构造方法和六个普通方法。<br />　　构造方法：<br />　　(1)public Random()<br />　　(2)public Random(long seed)<br />　　Java产生随机数需要有一个基值seed，在第一种方法中基值缺省，则将系统时间作为seed。<br />　　普通方法：<br />　　(1)public synonronized void setSeed(long seed)<br />　　该方法是设定基值seed。<br />　　(2)public int nextInt()<br />　　该方法是产生一个整型随机数。<br />　　(3)public long nextLong()<br />　　该方法是产生一个long型随机数。<br />　　(4)public float nextFloat()<br />　　该方法是产生一个Float型随机数。<br />　　(5)public double nextDouble()<br />　　该方法是产生一个Double型随机数。<br />　　(6)public synchronized double nextGoussian()<br />　　该方法是产生一个double型的Goussian随机数。<br />　　例1.2 RandomApp.java。<br />　　//import java.lang.*;<br />　　import java.util.Random;<br /><br />　　public class RandomApp{<br />　　　public static void main(String args[]){<br />　　　　Random ran1=new Random();<br />　　　　Random ran2=new Random(12345);<br />　　　　//创建了两个类Random的对象。<br />　　　　System.out.println("The 1st set of random numbers:");<br />　　　　System.out.println("\t Integer:"+ran1.nextInt());<br />　　　　System.out.println("\t Long:"+ran1.nextLong());<br />　　　　System.out.println("\t Float:"+ran1.nextFloat());<br />　　　　System.out.println("\t Double:"+ran1.nextDouble());<br />　　　　System.out.println("\t Gaussian:"+ran1.nextGaussian());<br />　　　　//产生各种类型的随机数<br />　　　　System.out.print("The 2nd set of random numbers:");<br />　　　　for(int i=0;i&lt;5;i++){<br />　　　　　System.out.println(ran2.nextInt()+" ");<br />　　　　　if(i==2) System.out.println();<br />　　　　　//产生同种类型的不同的随机数。<br />　　　　　System.out.println();//原文如此<br />　　　　}<br />　　　}<br />　　}<br /><br />　　运行结果：<br />　　E:\java01&gt;java RandomApp<br />　　The 1st set of random numbers:<br />　　　　Integer:-173899656<br />　　　　Long:8056223819738127077<br />　　　　Float:0.6293638<br />　　　　Double:0.7888394520265607<br />　　　　Gaussian:0.5015701094568733<br />　　The 2nd set of random numbers:1553932502<br />　　-2090749135<br />　　-287790814<br />　　-355989640<br />　　-716867186<br />　　E:\java01&gt;<br /><br />1.5 向量类Vector<br /><br />　　Java.util.Vector提供了向量(Vector)类以实现类似动态数组的功能。在Java语言中。正如在一开始就提到过，是没有指针概念的，但如果能正确灵活地使用指针又确实可以大大提高程序的质量，比如在C、C++中所谓“动态数组”一般都由指针来实现。为了弥补这点缺陷，Java提供了丰富的类库来方便编程者使用，Vector类便是其中之一。事实上，灵活使用数组也可完成向量类的功能，但向量类中提供的大量方法大大方便了用户的使用。<br />　　创建了一个向量类的对象后，可以往其中随意地插入不同的类的对象，既不需顾及类型也不需预先选定向量的容量，并可方便地进行查找。对于预先不知或不愿预先定义数组大小，并需频繁进行查找、插入和删除工作的情况，可以考虑使用向量类。<br />　　向量类提供了三种构造方法：<br />　　public vector()<br />　　public vector(int initialcapacity,int capacityIncrement)<br />　　public vector(int initialcapacity)<br />　　使用第一种方法，系统会自动对向量对象进行管理。若使用后两种方法，则系统将根据参数initialcapacity设定向量对象的容量(即向量对象可存储数据的大小)，当真正存放的数据个数超过容量时，系统会扩充向量对象的存储容量。参数capacityIncrement给定了每次扩充的扩充值。当capacityIncrement为0时，则每次扩充一倍。利用这个功能可以优化存储。<br />　　在Vector类中提供了各种方法方便用户使用：<br />　　■插入功能<br />　　(1)public final synchronized void addElement(Object obj)<br />　　将obj插入向量的尾部。obj可以是任何类的对象。对同一个向量对象，可在其中插入不同类的对象。但插入的应是对象而不是数值，所以插入数值时要注意将数值转换成相应的对象。<br />　　例 要插入一个整数1时，不要直接调用v1.addElement(1)，正确的方法为：<br />　　Vector v1=new Vector();<br />　　Integer integer1=new Integer(1);<br />　　v1.addElement(integer1);<br />　　(2)public final synchronized void setElementAt(object obj,int index)<br />　　将index处的对象设成obj，原来的对象将被覆盖。<br />　　(3)public final synchronized void insertElementAt(Object obj,int index)<br />　　在index指定的位置插入obj，原来对象以及此后的对象依次往后顺延。<br />　　■删除功能<br />　　(1)public final synchronized void removeElement(Object obj)<br />　　从向量中删除obj。若有多个存在，则从向量头开始试，删除找到的第一个与obj相同的向量成员。<br />　　(2)public final synchronized void removeAllElement()<br />　　删除向量中所有的对象。<br />　　(3)public final synchronized void removeElementlAt(int index)<br />　　删除index所指的地方的对象。<br />　　■查询搜索功能<br />　　(1)public final int indexOf(Object obj)<br />　　从向量头开始搜索obj ,返回所遇到的第一个obj对应的下标，若不存在此obj，返回-1。<br />　　(2)public final synchronized int indexOf(Object obj,int index)<br />　　从index所表示的下标处开始搜索obj。<br />　　(3)public final int lastIndexOf(Object obj)<br />　　从向量尾部开始逆向搜索obj。<br />　　(4)public final synchronized int lastIndexOf(Object obj,int index)<br />　　从index所表示的下标处由尾至头逆向搜索obj。<br />　　(5)public final synchronized Object firstElement()<br />　　获取向量对象中的首个obj。<br />　　(6)public final synchronized Object lastelement()<br />　　获取向量对象中的最后一个obj。<br />　　了解了向量的最基本的方法后，我们来看一下例8.3VectorApp.java。<br />　　例1.3 VectorApp.java。<br />　　import java.util.Vector;<br />　　import java.lang.*;//这一句不应该要，但原文如此<br />　　import java.util.Enumeration;<br />　　public class VectorApp{<br />　　　public static void main(String[] args){<br />　　　　Vector v1=new Vector();<br />　　　　Integer integer1=new Integer(1);<br />　　　　v1.addElement("one");<br />　　　　//加入的为字符串对象<br />　　　　v1.addElement(integer1);<br />　　　　v1.addElement(integer1);<br />　　　　//加入的为Integer的对象<br />　　　　v1.addElement("two");<br />　　　　v1.addElement(new Integer(2));<br />　　　　v1.addElement(integer1);<br />　　　　v1.addElement(integer1);<br />　　　　System.out.println("The vector v1 is:\n\t"+v1);<br />　　　　//将v1转换成字符串并打印<br />　　　　v1.insertElementAt("three",2);<br />　　　　v1.insertElementAt(new Float(3.9),3);<br />　　　　System.out.println("The vector v1(used method insertElementAt()) is:\n\t "+v1);<br />　　　　//往指定位置插入新的对象，指定位置后的对象依次往后顺延<br />　　　　v1.setElementAt("four",2);<br />　　　　System.out.println("The vector v1(used method setElementAt()) is:\n\t "+v1);<br />　　　　//将指定位置的对象设置为新的对象<br />　　　　v1.removeElement(integer1);<br />　　　　//从向量对象v1中删除对象integer1由于存在多个integer1所以从头开始<br />　　　　//找，删除找到的第一个integer1<br />　　　　Enumeration enum=v1.elements();<br />　　　　System.out.print("The vector v1(used method removeElement())is:");<br />　　　　while(enum.hasMoreElements())<br />　　　　System.out.print(enum.nextElement()+" ");<br />　　　　System.out.println();<br />　　　　//使用枚举类(Enumeration)的方法来获取向量对象的每个元素<br />　　　　System.out.println("The position of object 1(top-to-bottom):"<br />　　　　　+ v1.indexOf(integer1));<br />　　　　System.out.println("The position of object 1(tottom-to-top):"<br />　　　　　+v1.lastIndexOf(integer1));<br />　　　　//按不同的方向查找对象integer1所处的位置<br />　　　　v1.setSize(4);<br />　　　　System.out.println("The new vector(resized the vector)is:"+v1);<br />　　　　//重新设置v1的大小，多余的元素被行弃<br />　　　}<br />　　}<br />　　运行结果：<br />　　E:\java01&gt;java VectorApp<br />　　The vector v1 is:<br />　　　　　[one, 1, 1, two, 2, 1, 1]<br />　　The vector v1(used method insertElementAt()) is:<br />　　　　　[one, 1, three, 3.9, 1, two, 2, 1, 1]<br />　　The vector v1(used method setElementAt()) is:<br />　　　　　[one, 1, four, 3.9, 1, two, 2, 1, 1]<br />　　The vector v1(used method removeElement())is:one four 3.9 1 two 2 1 1<br />　　The position of object 1(top-to-bottom):3<br />　　The position of object 1(tottom-to-top):7<br />　　The new vector(resized the vector)is:[one, four, 3.9, 1]<br />　　E:\java01&gt;<br />　　从例1.3运行的结果中可以清楚地了解上面各种方法的作用，另外还有几点需解释。<br />　　(1)类Vector定义了方法<br />　　public final int size()<br />　　此方法用于获取向量元素的个数。它的返回值是向是中实际存在的元素个数，而非向量容量。可以调用方法capactly()来获取容量值。<br />　　方法：<br />　　public final synchronized void setsize(int newsize)<br />　　此方法用来定义向量大小。若向量对象现有成员个数已超过了newsize的值，则超过部分的多余元素会丢失。<br />　　(2)程序中定义了Enumeration类的一个对象<br />　　Enumeration是java.util中的一个接口类，在Enumeration中封装了有关枚举数据集合的方法。<br />　　在Enumeration中提供了方法hawMoreElement()来判断集合中是束还有其它元素和方法nextElement()来获取下一个元素。利用这两个方法可以依次获得集合中元素。<br />　　Vector中提供方法：<br />　　public final synchronized Enumeration elements()<br />　　此方法将向量对象对应到一个枚举类型。java.util包中的其它类中也大都有这类方法，以便于用户获取对应的枚举类型。<br /><br />1.6 栈类Stack<br /><br />　　Stack类是Vector类的子类。它向用户提供了堆栈这种高级的数据结构。栈的基本特性就是先进后出。即先放入栈中的元素将后被推出。Stack类中提供了相应方法完成栈的有关操作。<br />　　基本方法：<br />　　public Object push(Object Hem)<br />　　将Hem压入栈中，Hem可以是任何类的对象。<br />　　public Object pop()<br />　　弹出一个对象。<br />　　public Object peek()<br />　　返回栈顶元素，但不弹出此元素。<br />　　public int search(Object obj)<br />　　搜索对象obj,返回它所处的位置。<br />　　public boolean empty()<br />　　判别栈是否为空。<br />　　例1.4 StackApp.java使用了上面的各种方法。<br />　　例1.4 StackApp.java。<br />　　import java.lang.*;<br />　　import java.util.*;<br />　　public class StackApp{<br />　　　public static void main(String args[]){<br />　　　　Stack sta=new Stack();<br />　　　　sta.push("Apple");<br />　　　　sta.push("banana");<br />　　　　sta.push("Cherry");<br />　　　　//压入的为字符串对象<br />　　　　sta.push(new Integer(2));<br />　　　　//压入的为Integer的对象，值为2<br />　　　　sta.push(new Float(3.5));<br />　　　　//压入的为Float的对象，值为3.5<br />　　　　System.out.println("The stack is,"+sta);<br />　　　　//对应栈sta<br />　　　　System.out.println("The top of stack is:"+sta.peek());<br />　　　　//对应栈顶元素，但不将此元素弹出<br />　　　　System.out.println("The position of object Cherry is:"<br />　　　　+sta.search("cherry"));<br />　　　　//打印对象Cherry所处的位置<br />　　　　System.out.print("Pop the element of the stack:");<br />　　　　while(!sta.empty())<br />　　　　System.out.print(sta.pop()+" ");<br />　　　　System.out.println();<br />　　　　//将栈中的元素依次弹出并打印。与第一次打印的sta的结果比较，可看出栈<br />　　　　//先进后出的特点<br />　　　}<br />　　}<br />　　运行结果(略)<br /><br /><br />1.7 哈希表类Hashtable<br /><br />　　哈希表是一种重要的存储方式，也是一种常见的检索方法。其基本思想是将关系码的值作为自变量，通过一定的函数关系计算出对应的函数值，把这个数值解释为结点的存储地址，将结点存入计算得到存储地址所对应的存储单元。检索时采用检索关键码的方法。现在哈希表有一套完整的算法来进行插入、删除和解决冲突。在Java中哈希表用于存储对象，实现快速检索。<br />　　Java.util.Hashtable提供了种方法让用户使用哈希表，而不需要考虑其哈希表真正如何工作。<br />　　哈希表类中提供了三种构造方法，分别是：<br />　　public Hashtable()<br />　　public Hashtable(int initialcapacity)<br />　　public Hashtable(int initialCapacity,float loadFactor)<br />　　参数initialCapacity是Hashtable的初始容量，它的值应大于0。loadFactor又称装载因子，是一个0.0到0.1之间的float型的浮点数。它是一个百分比，表明了哈希表何时需要扩充，例如，有一哈希表，容量为100，而装载因子为0.9，那么当哈希表90%的容量已被使用时，此哈希表会自动扩充成一个更大的哈希表。如果用户不赋这些参数，系统会自动进行处理，而不需要用户操心。<br />　　Hashtable提供了基本的插入、检索等方法。<br />　　■插入<br />　　public synchronized void put(Object key,Object value)<br />给对象value设定一关键字key,并将其加到Hashtable中。若此关键字已经存在，则将此关键字对应的旧对象更新为新的对象Value。这表明在哈希表中相同的关键字不可能对应不同的对象(从哈希表的基本思想来看，这也是显而易见的)。<br />　　■检索<br />　　public synchronized Object get(Object key)<br />　　根据给定关键字key获取相对应的对象。<br />　　public synchronized boolean containsKey(Object key)<br />　　判断哈希表中是否包含关键字key。<br />　　public synchronized boolean contains(Object value)<br />　　判断value是否是哈希表中的一个元素。<br />　　■删除<br />　　public synchronized object remove(object key)<br />　　从哈希表中删除关键字key所对应的对象。<br />　　public synchronized void clear()<br />　　清除哈希表<br />　　另外，Hashtalbe还提供方法获取相对应的枚举集合：<br />　　public synchronized Enumeration keys()<br />　　返回关键字对应的枚举对象。<br />　　public synchronized Enumeration elements()<br />　　返回元素对应的枚举对象。<br />　　例1.5 Hashtable.java给出了使用Hashtable的例子。<br />　　例1.5 Hashtalbe.java。<br />　　//import java.lang.*;<br />　　import java.util.Hashtable;<br />　　import java.util.Enumeration;<br />　　public class HashApp{<br />　　　public static void main(String args[]){<br />　　　　Hashtable hash=new Hashtable(2,(float)0.8);<br />　　　　//创建了一个哈希表的对象hash，初始容量为2，装载因子为0.8<br /><br />　　　　hash.put("Jiangsu","Nanjing");<br />　　　　//将字符串对象“Jiangsu”给定一关键字“Nanjing”,并将它加入hash<br />　　　　hash.put("Beijing","Beijing");<br />　　　　hash.put("Zhejiang","Hangzhou");<br /><br />　　　　System.out.println("The hashtable hash1 is: "+hash);<br />　　　　System.out.println("The size of this hash table is "+hash.size());<br />　　　　//打印hash的内容和大小<br /><br />　　　　Enumeration enum1=hash.elements();<br />　　　　System.out.print("The element of hash is: ");<br />　　　　while(enum1.hasMoreElements())<br />　　　　　System.out.print(enum1.nextElement()+" ");<br />　　　　System.out.println();<br />　　　　//依次打印hash中的内容<br />　　　　if(hash.containsKey("Jiangsu"))<br />　　　　　System.out.println("The capatial of Jiangsu is "+hash.get("Jiangsu"));<br />　　　　hash.remove("Beijing");<br />　　　　//删除关键字Beijing对应对象<br />　　　　System.out.println("The hashtable hash2 is: "+hash);<br />　　　　System.out.println("The size of this hash table is "+hash.size());<br />　　　}<br />　　}<br /><br />　　运行结果：<br />　　The hashtable hash1 is: {Beijing=Beijing, Zhejiang=Hangzhou, Jiangsu=Nanjing}<br />　　The size of this hash table is 3<br />　　The element of hash is: Beijing Hangzhou Nanjing<br />　　The capatial of Jiangsu is Nanjing<br />　　The hashtable hash2 is: {Zhejiang=Hangzhou, Jiangsu=Nanjing}<br />　　The size of this hash table is 2<br /><br />　　Hashtable是Dictionary(字典)类的子类。在字典类中就把关键字对应到数据值。字典类是一个抽象类。在java.util中还有一个类Properties，它是Hashtable的子类。用它可以进行与对象属性相关的操作。<br /><br />1.8 位集合类BitSet<br /><br />　　位集合类中封装了有关一组二进制数据的操作。<br />　　我们先来看一下例8.6 BitSetApp.java。<br />　　例8.6 BitSetApp.java<br />　　//import java.lang.*;<br />　　import java.util.BitSet;<br />　　public class BitSetApp{<br />　　　private static int n=5;<br />　　　public static void main(String[] args){<br />　　　　BitSet set1=new BitSet(n);<br />　　　　for(int i=0;i&lt;n;i++) set1.set(i);<br />　　　　//将set1的各位赋1，即各位均为true<br />　　　　BitSet set2= new BitSet();<br />　　　　set2=(BitSet)set1.clone();<br />　　　　//set2为set1的拷贝<br />　　　　set1.clear(0);<br />　　　　set2.clear(2);<br />　　　　//将set1的第0位set2的第2位清零<br />　　　　System.out.println("The set1 is: "+set1);<br />　　　　//直接将set1转换成字符串输出，输出的内容是set1中值true所处的位置<br />　　　　//打印结果为The set1 is:{1,2,3,4}<br />　　　　System.out.println("The hash code of set2 is: "+set2.hashCode());<br />　　　　//打印set2的hashCode<br />　　　　printbit("set1",set1);<br />　　　　printbit("set2",set2);<br />　　　　//调用打印程序printbit(),打印对象中的每一个元素<br />　　　　//打印set1的结果为The bit set1 is: false true true true true<br />　　　　set1.and(set2);<br />　　　　printbit("set1 and set2",set1);<br />　　　　//完成set1 and set2,并打印结果<br />　　　　set1.or(set2);<br />　　　　printbit("set1 or set2",set1);<br />　　　　//完成set1 or set2,并打印结果<br />　　　　set1.xor(set2);<br />　　　　printbit("set1 xor set2",set1);<br />　　　　//完成set1 xor set2,并打印结果<br />　　　}<br />　　　//打印BitSet对象中的内容<br />　　　public static void printbit(String name,BitSet set){<br />　　　　System.out.print("The bit "+name+" is: ");<br />　　　　for(int i=0;i&lt;n;i++)<br />　　　　　System.out.print(set.get(i)+" ");<br />　　　　System.out.println();<br />　　　}<br />　　}<br /><br />　　运行结果：<br />　　The set1 is: {1, 2, 3, 4}<br />　　The hash code of set2 is: 1225<br />　　The bit set1 is: false true true true true<br />　　The bit set2 is: true true false true true<br />　　The bit set1 and set2 is: false true false true true<br />　　The bit set1 or set2 is: true true false true true<br />　　The bit set1 xor set2 is: false false false false false<br /><br />　　程序中使用了BitSet类提供的两种构造方法：<br />　　　　public BitSet();<br />　　　　public BitSet(int n);<br />　　参数n代表所创建的BitSet类的对象的大小。BitSet类的对象的大小在必要时会由系统自动扩充。<br />　　其它方法：<br />　　public void set(int n)<br />　　将BitSet对象的第n位设置成1。<br />　　public void clear(int n)<br />　　将BitSet对象的第n位清零。<br />　　public boolean get(int n)<br />　　读取位集合对象的第n位的值，它获取的是一个布尔值。当第n位为1时，返回true；第n位为0时，返回false。<br />　　另外，如在程序中所示，当把一BitSet类的对象转换成字符串输出时，输出的内容是此对象中true所处的位置。<br />　　在BitSet中提供了一组位操作，分别是：<br />　　public void and(BitSet set)<br />　　public void or(BitSet set)<br />　　public void xor(BitSet set)<br />利用它们可以完成两个位集合之间的与、或、异或操作。<br />　　BitSet类中有一方法public int size()来取得位集合的大小，它的返回值与初始化时设定的位集合大小n不一样，一般为64。<br /><br />小结<br /><br />　　本章我们介绍了Java的实用工具类库java.util中一些常用的类。java.util包中还有其它一些类。它们的具体用法用户可以自行查阅API。 <br /></div>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/46598.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-17 11:51 <a href="http://www.blogjava.net/TrampEagle/articles/46598.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>移动视频: QuickTime for Java API 入门</title><link>http://www.blogjava.net/TrampEagle/articles/46374.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 16 May 2006 04:41:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/46374.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/46374.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/46374.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/46374.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/46374.html</trackback:ping><description><![CDATA[原文引自：<a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/">http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/</a><br /><br /><blockquote>最新一代的 iPod 媒体播放器在移动视频编程领域开辟了一个新机会 —— 但是如何入门呢？本文介绍将 QuickTime for Java™ 库，以编程方式为 iPod 媒体播放器创建视频内容时需要这个库。</blockquote><!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES--><p>Apple iPod 是当今市场上的几个媒体播放器之一，它能播放多种媒体，其中包括音频、视频和图片。在美国和其他地方，iPod 引领着便携媒体播放器市场，从诞生至今，已经售出 3千万套 iPod ，这占据了整个媒体播放器市场 78％ 以上的份额。虽然 iPod 的成功显然是受 MP3 流行的推动，而且手机用户拍摄和交易数字图片到现在也有了一段时间，但移动视频的市场仍然在形成中。早期进入移动视频市场的潜在收益是显而易见的，但对许多开发人员来说，他们要解决的问题是最好地应用这项新技术的方式和地方。</p><p>在这篇文章中，我首先将推荐一些移动视频的实际（可能也是非常流行的）应用，然后演示两个程序，让您开始使用 QuickTime for Java API 为 iPod 创建视频内容。这两个程序可以让您很容易地给现有的视频文件添加标题，并将传统的视频文件转换成与 iPod 兼容的格式。在文章末尾，将留给您一些示例代码，可以用它们学习使用 QuickTime for Java API 进行视频操作的更多内容。</p><p><a name="IDABD0CB"><span class="atitle"><font face="Arial" size="4">为什么要用移动视频？</font></span></a></p><br /><a name="figure1"><b>图 1. iPod 视频播放器</b></a><br /><img height="428" alt="iPod 视频播放器" src="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/ipod_pic.gif" width="359" /><br /><p>便携媒体播放器在特定人群（即年龄在 18 到 38 岁之间，具有一定的可支配收入的消费者）中特别流行。因为移动视频对于便携媒体播放器来是相对较新的东西，所以对大多数开发者来说，遇到的第一个问题就是： <i>为什么要创建移动视频内容</i>？除了移动媒体最明显的娱乐要求之外，还有许多实际的使用情况：移动媒体既可以促进销售，也可以给便携媒体播放器和内容带来一类新用户。请想想以下这些情况：</p><ul><li>房地产经纪人可以创建一段视频，将视频做成房产可用属性和用录制好的配音说明的一些功能的虚拟漫步。然后，潜在的住房买主可能会用便携媒体播放器查看中意的属性。在没有时间寻找所有符合自己需求的房产的住宅搜索客户中，这种极为节约时间的方式会变得非常流行。 
</li><li>汽车厂商和经销商可以使用移动视频发布所销售的最新车型的视频目录。潜在客户就会更熟悉中意的车型的特性和好处，而不必亲自去经销商那里。 
</li><li>学院和大学的官员可以用移动视频格式向学生提供提供演讲，辅助学生学习。 
</li><li>生产商可以为汽车、书架、玩具（等等）提供移动视频格式的安装说明。</li></ul><p>显然，这只是创建移动视频内容的商业驱动力的几个示例。这些示例中的共同之处就是向新市场介绍便携媒体的潜力。现在，我们来看看允许用编程方式创建和编辑视频文件的 Java API。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="IDA3D0CB"><span class="atitle"><font face="Arial" size="4">QuickTime for Java API</font></span></a></p><p>对于 Java 开发人员来说，支持数字媒体的创建、修改和回放的能力最强、功能最丰富的 API 就是 QuickTime for Java API。QuickTime for Java 最初是为 Macintosh 平台创建的，但过去这几年时间里，Windows 和 Mac 用户也可以随意使用它。当然，如果曾经用 Java 语言做过一些编程，那么您可能会问：为什么不可以只使用 Java 媒体框架 (JMF) API。</p><p>最初创建 JMF 是为了给 Java V1.0.2 的开发人员提供处理各种媒体的能力。但是，它的应用并不广，它支持一些过时的媒体格式和编码方式，例如 MPEG-2、AU、HotMedia 和 Flash 2。另一方面，QuickTime for Java 支持 QuickTime 可以播放的所有媒体格式和编码方式。使用 QuickTime for Java API 可以访问更新的媒体格式，其中包括 MPEG-4、MP3 音频、H.264 和 Flash 5。实际上，下面就是 QuickTime 支持的视频编码方式的完整列表： </p><ul><li>动画 
</li><li>Apple BMP 
</li><li>Apple Pixlet 
</li><li>Apple Video 
</li><li>Cinepak 
</li><li>Component video 
</li><li>DV and DVC Pro NTSC 
</li><li>DV PAL 
</li><li>DVC Pro PAL 
</li><li>Graphics 
</li><li>H.261 
</li><li>H.263 
</li><li>H.264 
</li><li>JPEG 2000 
</li><li>Microsoft® OLE 
</li><li>Microsoft Video 1 
</li><li>Motion JPEG A 
</li><li>Motion JPEG B 
</li><li>MPEG-4 (Part 2) 
</li><li>Photo JPEG 
</li><li>Planar RGB 
</li><li>PNG 
</li><li>Sorenson Video 2 
</li><li>Sorenson Video 3 
</li><li>TGA 
</li><li>TIFF </li></ul><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="IDACF0CB"><span class="atitle"><font face="Arial" size="4">向视频添加标题</font></span></a></p><p>如果是初次接触 QuickTime for Java API 或者是初次处理数字媒体，那么向视频片断添加标题是一个好的起点。<a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#listing1"><font color="#996699">清单 1</font></a> 显示的 <code>CaptionAdder.java</code> 是一个简单的程序，它接受视频文件，然后向视频添加一个文本字符串作为标题。</p><br /><a name="listing1"><b>清单 1. CaptionAdder.java</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">import quicktime.*;
import quicktime.io.*;
import quicktime.std.image.*;
import quicktime.std.movies.media.*;
import quicktime.std.movies.*;
import quicktime.std.*;
import quicktime.qd.*;
import quicktime.util.QTPointer;


import java.io.*;

public class CaptionAdder {

 public static void main (String args[]) {

  if (args.length != 1){

    System.out.println("Usage: java CaptionAdder [input_filename]");

  } else {
      
    try {
     QTSession.open();     

     System.out.println ("QuickTime version: " + QTSession.getMajorVersion() + "." + 
        QTSession.getMinorVersion());

     QTFile qtfile = new QTFile(new File(args[0]));
     DataRef urlMovie = new DataRef ("file://" + qtfile.getPath());
     Movie movie = Movie.fromDataRef (urlMovie,StdQTConstants.newMovieActive);
    
     float  textTrackHeight = 32;

     QDRect movieBounds = movie.getNaturalBoundsRect();
     float  movieWidth  = movieBounds.getWidthF();
     float  movieHeight = movieBounds.getHeightF();

     Track  textTrack = movie.addTrack(movieWidth, textTrackHeight, 0);

     Matrix textTrackMatrix = textTrack.getMatrix();
     textTrackMatrix.translate (0, movieHeight - textTrackHeight);
     textTrack.setMatrix (textTrackMatrix);

     textTrack.setEnabled (true);

     int movieTimeScale = movie.getTimeScale();
     TextMedia textMedia = new TextMedia (textTrack, movieTimeScale);

       QDRect textBounds = new QDRect (movieWidth, movieHeight);

     textMedia.beginEdits();

      TimeInfo sampleTime = new TimeInfo (0, movie.getDuration()/2);
    
      <span class="boldcode"><strong>String text = new String ("1234 Main St. - Listing price: $164,000");</strong></span>
      TextMediaHandler textMediaHandler = textMedia.getTextHandler();
      QTPointer textPointer = new QTPointer ( text.length() + 1, true );
      textPointer.copyFromArray ( 0, text.getBytes(), 0, text.length() );
      textMediaHandler.addTextSample (
        textPointer,
        QDFont.getFNum("Times"), 
        16, 
        0,
        QDColor.white,
        QDColor.black,
        QDConstants.teCenter,
        textBounds,
        StdQTConstants.dfClipToTextBox | StdQTConstants.dfKeyedText,
        0,0,0,
        null,
        sampleTime.duration );
      
     textMedia.endEdits();

     textTrack.insertMedia (sampleTime.time, 0, sampleTime.duration, 1 );
     OpenMovieFile outStream = OpenMovieFile.asWrite (qtfile); 

     movie.updateResource (outStream, StdQTConstants.movieInDataForkResID, 
        qtfile.getName());
    } catch (Exception e) {
         e.printStackTrace();
             QTSession.close();
             System.exit(0);
    }
        
               QTSession.close();

  // end else
  }

  System.out.println ("complete.");

 //end method
 }
  

}
</font></code></pre></td></tr></tbody></table><br /><p><a name="IDA2F0CB"><span class="smalltitle"><strong><font face="Arial">关于代码</font></strong></span></a></p><p>几乎每个 QuickTime for Java 应用程序都一样，所有的实际动作都发生在 <code>QTSession.open()</code> 和 <code>QTSession.close()</code> 语句之间。在调用 <code>QTSession.open()</code> 时，QuickTime 引擎可以初始化其自身。如果没有先调用 <code>QTSession.open()</code>，就想调用其他 QuickTime for Java 类，那么您会获得抛出到命令行中的非常难看的堆栈跟踪。</p><p>QuickTime for Java API 中使用的核心对象之一是 <code>quicktime.std.movies.Movie</code> 对象。如果初次使用 QuickTime for Java，那么需要认识到，在 <code>quicktime.std.movies.Movie</code> 对象和硬盘上可能存在的 <code>file_name.mov</code> 之间有一点细微的区别。重要的是，可以创建 <code>quicktime.std.movies.Movie</code> 对象，并且无需要在硬盘上创建 <code>file_name.mov</code> 文件该对象就可以存在。 </p><p><code>quicktime.std.movies.Movie</code> 对象可由多个<i>轨道</i> 组成，这些轨道是独立的媒体源（例如音频、视频、静态图片或文本）。在 <code>CaptionAdder.java()</code> 中，在创建了 <code>quicktime.std.movies.Track</code> 对象后，我在 <code>textMedia.beginEdits()</code> 语句和 <code>textMedia.endEdits()</code> 语句之间添加了文本标题。在这个示例中，我用 <code>quicktime.std.movies.TimeInfo</code> 对象设置标题在视频中显示的时间长度，以及什么时候开始显示标题。我想让文本标题在视频的前半段显示，所以我把 <code>TimeInfo</code> 设为从 0 开始，并通过 movie 的持续时间将它设置为运行到一半时结束。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="IDALQEDB"><span class="atitle"><font face="Arial" size="4">使用 CaptionAdder.java</font></span></a></p><p>那么该如何使用 <code>CaptionAdder.java</code> 呢？首先，调用 <code>CaptionAdder.java</code> 非常简单直接。在向类路径中添加了 QuickTime for Java 库之后，需要做的就是提供想要编辑的文件的名称。例如，假设想向 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#figure2"><font color="#996699">图 2</font></a> 所示的房地产清单视频添加标题。</p><br /><a name="figure2"><b>图 2. 没有标题的房地产视频示例截图</b></a><br /><img height="239" alt="没有标题的房地产视频示例截图" src="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/house_old.gif" width="317" /><br /><p>要向这个文件添加标题，只需如 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#listing2"><font color="#996699">清单 2 </font></a>中所示那样运行 CaptionAdder.java 程序即可。</p><br /><a name="listing2"><b>清单 2. 使用 CaptionAdder.java</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">java CaptionAdder listing22345.mov 
</font></code></pre></td></tr></tbody></table><br /><p>结果显示在 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#figure3"><font color="#996699">图 3</font></a> 中。</p><br /><a name="figure3"><b>图 3. 添加了标题的房地产视频示例</b></a><br /><img height="236" alt="虚拟的房地产清单视频在运行了 CaptionAdder.java 之后的截图" src="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/house_new.gif" width="316" /><br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="IDANSEDB"><span class="atitle"><font face="Arial" size="4">转换成 iPod 格式</font></span></a></p><p>在有了使用 QuickTime for Java API 创建和操作视频内容的基础之后，下一个主要障碍就是把内容转换成与 iPod 兼容的格式。不论使用摄像机、从活动视频源录制，还是创建动画序列，视频内容都不会立即与 iPod 视频播放器兼容。iPod 对于它播放的媒体的类型有点挑剔。<a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#table1"><font color="#996699">表 1</font></a> 显示了 iPod 环境支持的视频格式和设置。</p><br /><a name="table1"><b>表 1. iPod 兼容内容的视频设置</b></a><br /><p></p><table cellspacing="2" cellpadding="2" border="1"><tbody><tr><td><br /><b>视频格式</b></td><td><br /><b>数据率</b></td><td><br /><b>尺寸</b></td><td><br /><b>帧率</b></td></tr><tr><td>H.264 视频</td><td>最高 768 Kbps</td><td>320 x 240 像素</td><td>30 fps</td></tr><tr><td>MPEG-4 视频</td><td>最高 2.5 Mbps</td><td>480 x 480 像素</td><td>30 fps</td></tr></tbody></table><p>让问题更复杂的是，对于视频中的音轨，它只支持高级音频编码 (AAC)，所以如果想在 iPod 上播放视频，不仅仅要转换传统视频内容。幸运的是，可以求助于 <code>MovieConverter.java</code>，它是转换传统视频内容到 iPod 兼容格式的一个方便工具。<code>MovieConverter.java</code> 如 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#listing3"><font color="#996699">清单 3</font></a> 中所示。 </p><br /><a name="listing3"><b>清单 3. MovieConverter.java</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.io.IOException;

import quicktime.*;
import quicktime.io.*;
import quicktime.qd.*;
import quicktime.std.*;
import quicktime.std.clocks.*;
import quicktime.std.StdQTConstants;
import quicktime.std.image.*;
import quicktime.std.movies.*;
import quicktime.std.movies.media.*;
import quicktime.std.qtcomponents.*;
import quicktime.util.*;

import quicktime.app.view.*;

public class MovieConverter extends JFrame implements Errors, ActionListener{
	
  QTComponent component = null;  
  JPanel jpanel = null;
  Button selectButton = null;

  public static void main (String args[]) {
    try{
      new MovieConverter();
    } catch(Exception e){
      System.out.println(e);
    }
  }

  public MovieConverter(){

    super("Movie Converter");

    try { 
      QTSession.open();
    } catch (Exception e) {
      e.printStackTrace();
      QTSession.close();
    }

    jpanel = new JPanel();

    this.setContentPane(jpanel);
    selectButton = new Button ("Select a File to Convert");
    selectButton.addActionListener(this);

    jpanel.add (selectButton);

    addWindowListener(new WindowAdapter () {
      public void windowClosing (WindowEvent e) {
        QTSession.close();
        dispose();
      }

      public void windowClosed (WindowEvent e) { 
        System.exit(0);
      }
    });     

    this.pack();
    this.setVisible(true);

  }

  public void actionPerformed (ActionEvent event) {
    exportMovie();    
  }


  void displayMovie (Movie m) throws QTException {
    component = QTFactory.makeQTComponent (new MovieController (m));
    jpanel.add ((Component)component);
    jpanel.remove(selectButton);
    pack();
  }
    
  void exportMovie () {
    try{
      FileDialog fileDialog = new FileDialog (this, 
          "Choose Movie to Export...", FileDialog.LOAD);
      fileDialog.show();
      if (fileDialog.getFile() == null)
        return;
      QTFile movieFile = new QTFile (fileDialog.getDirectory() 
         + fileDialog.getFile());
      
      Movie movie = Movie.fromFile (OpenMovieFile.asRead(movieFile));
    
      if (component != null) {
        component.setMovieController(new MovieController(movie));
      } else {
        displayMovie (movie);
      }
    
      new Thread (new Exporter(movie)).start();
    
    } catch (QTException err) {
      err.printStackTrace();
    }
  }
  

}
</font></code></pre></td></tr></tbody></table><br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><font face="Lucida Console"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></font></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><font face="Lucida Console"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /></font><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><font face="Lucida Console"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></font></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="IDAQUEDB"><span class="atitle"><font face="Arial" size="4">使用 MovieConverter.java</font></span></a></p><p>使用 <code>MovieConverter.java</code> 非常简单。在 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#figure4"><font color="#996699">图 4</font></a> 中，可以看到一个关于如何使用 Internet 的虚拟学术培训视频的截屏（从 Google 的主页开始）。实际的影片文件是 AVI 格式，并且还包括授课录音的音轨。 </p><br /><a name="figure4"><b>图 4. 示例学术培训视频的截屏</b></a><br /><img height="475" alt="示例学术培训视频的截屏" src="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/web_class_fig1.gif" width="423" /><br /><p>用 <code>MovieConverter.java</code> 程序将这个视频文件转换成 iPod 兼容的格式很容易。在启动 <code>MovieConverter.java</code> 之后，首先选择要转换的文件。然后指定新创建的文件的名称，如 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#figure5"><font color="#996699">图 5</font></a> 中所示。 </p><br /><a name="figure5"><b>图 5. MovieConverter.java 请求新文件的名称</b></a><br /><img height="232" alt="MovieConverter.java 请求新文件的名称" src="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/options.gif" width="560" /><br /><p>在指定了新视频文件的名称之后，应用程序显示一个影片设置总结屏幕，如 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#figure6"><font color="#996699">图 6</font></a> 中所示。在这个屏幕上，有修改编码方式、压缩算法的选项，甚至可以对媒体应用视频过滤器。</p><br /><a name="figure6"><b>图 6. 影片设置总结</b></a><br /><img height="416" alt="影片设置总结" src="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/settings.gif" width="340" /><br /><p>在 <a href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#figure7"><font color="#996699">图 7</font></a> 中，可以看到 MovieConverter 的视频设置窗口，在这个窗口中，可以选择视频编码方式，而提供了转换后的视频预览。</p><br /><a name="figure7"><b>图 7. 视频设置窗口</b></a><br /><img height="434" alt="MovieConverter.java 的视频设置窗口" src="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/video_settings.gif" width="575" /><br /><p>设置好之后，MovieConverter 生成可以在 iPod 视频播放器上播放的新视频文件。还要注意的是，<code>MovieConverter.java</code> 是个功能丰富且强大的应用程序。不但可以用它生成能够在 iPod 媒体播放器上播放的视频内容，还能用它把传统视频转换成 QuickTime 支持的其他视频格式。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="IDATXEDB"><span class="atitle"><font face="Arial" size="4">结束语</font></span></a></p><p>在数年之后，iPod 很有可能会领导便携媒体播放器市场。目前这代 iPod 媒体播放器中添加了视频内容，这开辟了许多令人兴奋的营销和商业机会。在这篇文章中，我提供了移动视频内容的一些实际和商业上的应用，介绍了 QuickTime for Java API，并演示了如何用编程方式操纵移动视频文件，并将它转换成与视频 iPod 兼容的格式。 </p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="resources"><span class="atitle"><font face="Arial" size="4">参考资料 </font></span></a></p><b>学习</b><br /><ul><li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www-128.ibm.com/developerworks/wireless/library/wi-mvideo/" target="_blank"><font color="#5c81a7">英文原文</font></a>。<br /><br /></li><li>“<a href="http://www.ibm.com/developerworks/wireless/library/wi-elite14.html"><font color="#5c81a7">Secrets of the wireless elite: Video for handsets</font></a>”（developerWorks，2003 年 6 月）：创建移动视频的技巧。<br /><br /></li><li><a href="http://www.ibm.com/developerworks/blogs/dw_blog_comments.jspa?blog=319&amp;entry=93641&amp;roll=-5"><font color="#5c81a7">No joy in stickville</font></a> （developerWorks blogs，2005 年 8 月）：一名 Java 开发人员对 QuickTime for Java 的开创性尝试。<br /><br /></li><li><a href="http://www.ibm.com/developerworks/wireless/"><font color="#5c81a7">developerWorks 无线技术专区</font></a>：专门介绍无线技术解决方案。</li></ul><br /><b>获得产品和技术</b><br /><ul><li><a href="http://developer.apple.com/quicktime/qtjava/"><font color="#5c81a7">QuickTime for Java</font></a>：请从 Apple Developer Connection 下载它。<br /><br /></li><li><a href="http://www.alphaworks.ibm.com/tech/tk4mpeg4"><font color="#5c81a7">IBM Toolkit for MPEG-4</font></a>：一套 Java 类和 API，用来生成供 MPEG-4 兼容设备使用的 MPEG-4 内容。<br /><br /></li><li><a href="http://www.ibm.com/software/wireless/weme/"><font color="#5c81a7">WebSphere® Everyplace Micro Environment 6.0</font></a>：支持为 PocketPC 平台创建移动视频内容。<br /><br /></li><li><a href="http://www.javabluetooth.com/articles/ipod/java_ipod.zip"><font color="#5c81a7">CaptionAdder and MovieConverter</font></a>：下载本文使用的样本影片文件。<br /><br /></li></ul><br /><b>讨论</b><br /><ul><li><a href="http://www.ibm.com/developerworks/blogs/"><font color="#5c81a7">developerWorks blogs</font></a>：加入 developerWorks 社区！<br /><br /></li></ul><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/wi-mvideo/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="author"><span class="atitle"><font face="Arial" size="4">关于作者</font></span></a></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td colspan="3"><font face="Arial" size="4"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /></font></td></tr><tr valign="top" align="left"><td><p><font face="Arial" size="4"><img height="80" alt="" src="http://www.ibm.com/developerworks/i/p-bhopkins.jpg" width="64" align="left" /></font></p></td><td><font face="Arial" size="4"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" /></font></td><td width="100%"><p>Bruce Hopkins 是 <i>Bluetooth for Java</i> (Apress) 一书的作者，也是 JB-22 开发包的制作者。他毕业于底特律的 Wayne 州立大学，拥有电子和计算机工程的学士学位。他目前是 Gestalt LLC 的技术架构师，专攻分布式计算、Web 服务和无线技术。您可以通过他的邮箱 <a href="mailto:bhopkins@gestalt-llc.com"><font color="#5c81a7">bhopkins@gestalt-llc.com</font></a> 与他联系。</p></td></tr></tbody></table><img src ="http://www.blogjava.net/TrampEagle/aggbug/46374.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-16 12:41 <a href="http://www.blogjava.net/TrampEagle/articles/46374.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简化JavaMail：小巧 Jakarta Commons-Email 简单教程</title><link>http://www.blogjava.net/TrampEagle/articles/45294.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 09 May 2006 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/45294.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/45294.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/45294.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/45294.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/45294.html</trackback:ping><description><![CDATA[原文引自：<a href="/martinx/archive/2005/09/29/14386.html">http://www.blogjava.net/martinx/archive/2005/09/29/14386.html</a><br /><br /><div class="postcontent">Jakarta发布了Commons Emails 1.0 released 版本，目的是为了简化JavaMail。<br /><br />知道有它几个class吗？你一定想不到，只有8个！<br /><br />好了，开始我们的jakarta commons emails 之旅：）<br /><br />一：Quick Start<br />通过SimpleEmail发送邮件<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">java.lang.Object<br /></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />  org.apache.commons.mail.Email<br /></span><span style="COLOR: #008080">3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />      org.apache.commons.mail.SimpleEmail</span></div><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">SimpleEmail email </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SimpleEmail();<br /></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setHostName(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">mail.4ya.cn</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setAuthentication(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;username&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;password&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br /></span><span style="COLOR: #008080">4</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.addTo(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin.xus@gmail.com</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setFrom(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin@4ya.cn</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">6</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setSubject(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">测试主题</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">7</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setMsg(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">这里是邮件内容</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">8</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.send();</span></div><br />就如代码里字面上的意思一样简单:<br />1：创建以SimpleEmail对象<br />2：设定发送信件的smtp服务器，如果没有设定，会寻找系统变量中mail.host值。<br />3：设定smtp的用户和密码<br />4：收件人<br />5：发件人<br />6：主题<br />7：内容<br />8：发送<br /><br />二：发送带附件的邮件<br />我们可以发送本机的附件，当然我们也可以发送非本机的附件，如果发送的是一个存在网络上的附件的url,则邮件发送的时候会自动下载，添加到附件中。<br /><br />   1：）发送本地附件：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">EmailAttachment attachment </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> EmailAttachment();<br /></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setPath(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">test/test.rar</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setDisposition(EmailAttachment.ATTACHMENT);<br /></span><span style="COLOR: #008080">4</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setDescription(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">python resource</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setName(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">resource</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);</span></div><br />   2：）发送不存在本地的附件<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">EmailAttachment attachment </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> EmailAttachment();<br /></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setURL(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> URL(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">http://www.smilinglibrary.org/sldoc/pics/index03.jpg</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">));<br /></span><span style="COLOR: #008080">3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setDisposition(EmailAttachment.ATTACHMENT);<br /></span><span style="COLOR: #008080">4</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setDescription(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">微笑图书馆</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />attachment.setName(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">微笑图书馆</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);</span></div><br /><br />next,添加附件到我们的邮件中<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080"> 1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">MultiPartEmail email </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> MultiPartEmail();<br /></span><span style="COLOR: #008080"> 2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setHostName(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">mail.4ya.cn</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /> 3    email.setAuthentication(<span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;username&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;password&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)</span><br /></span><span style="COLOR: #008080"> 4</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.addTo(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin.xus@gmail.com</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> 5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setFrom(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin@4ya.cn</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> 6</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setSubject(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">邮件主题</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> 7</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setMsg(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">邮件内容</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /></span><span style="COLOR: #008080"> 8</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">添加附件</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #008080"> 9</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">email.attach(attachment);<br /></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">发送邮件</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #008080">12</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">email.send();</span></div><br />如果需要发送多个附件，只需创建多个EmailAttachement,即可<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">email.attach(attachment1)<br /></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.attach(attachment2)</span></div><br />三：发送html格式的邮件<br />通过HtmlEmail我们可以发送Html格式的邮件：<br /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">java.lang.Object<br /></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />  org.apache.commons.mail.Email<br /></span><span style="COLOR: #008080">3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />      org.apache.commons.mail.MultiPartEmail<br /></span><span style="COLOR: #008080">4</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />          org.apache.commons.mail.HtmlEmail<br /></span><span style="COLOR: #008080">5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span></div><br />如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080"> 1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #008000">//</span><span style="COLOR: #008000">HtmlEmail!</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #008080"> 2</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">HtmlEmail email </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> HtmlEmail();<br /></span><span style="COLOR: #008080"> 3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setHostName(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">mail.4ya.cn</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /> 3   email.setAuthentication(<span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;username&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;password&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)</span><br /></span><span style="COLOR: #008080"> 5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.addTo("martin@4ya.cn</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> 6</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setFrom("martin.xus@gmail.com</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">martin</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> 7</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setSubject(</span><span style="COLOR: #000000">"主题：该邮件包括html格式内容</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080"> </span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /></span><span style="COLOR: #008080"> 8</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> embed the image and get the content id<br /></span><span style="COLOR: #008080"> 9</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> 注意这里：embed 将帮助我们创建标签如：cid:xxx url</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #008080">10</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">URL url </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> URL(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">http://www.apache.org/images/asf_logo_wide.gif</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />String cid </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> email.embed(url, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Apache logo</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img id="Codehighlighter1_419_557_Open_Image" onclick="this.style.display='none'; Codehighlighter1_419_557_Open_Text.style.display='none'; Codehighlighter1_419_557_Closed_Image.style.display='inline'; Codehighlighter1_419_557_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_419_557_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_419_557_Closed_Text.style.display='none'; Codehighlighter1_419_557_Open_Image.style.display='inline'; Codehighlighter1_419_557_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_419_557_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_419_557_Open_Text"><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #008080">14</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />set the html message<br /></span><span style="COLOR: #008080">15</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />我们看到HtmlEmail extends Email的，它依然有setMsg()，但是这里发送的邮件包括了插入在邮件内容中的图片，所以不能在使用了setMsg(),而要以setHtmlMsg 或setTextMsg代码<br /></span><span style="COLOR: #008080">16</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />*</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />email.setHtmlMsg(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;html&gt;The apache logo - &lt;img src=\</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">cid:</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">+cid+</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">\</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&gt;&lt;/html&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> set the alternative message</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #008080">20</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">email.setTextMsg(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Your email client does not support HTML messages</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /></span><span style="COLOR: #008080">22</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">set mail</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #008080">23</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">email.send();<br /></span><span style="COLOR: #008080">24</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span></div><br />四：最后一步 <br /><font face="Courier New">如果需要实现更复杂authenticator 你可以extends javax.mail.Authenticator</font> ,实现你自己的东西，然后调用<font face="Courier">Email.setAuthenticator(javax.mail.Authenticator newAuthenticator)即可<br /><br />这一点jakarta也做了，给我们提供了一个defaultAuthenticator<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">java.lang.Object<br /></span><span style="COLOR: #008080">2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />  javax.mail.Authenticator<br /></span><span style="COLOR: #008080">3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />      org.apache.commons.mail.DefaultAuthenticator</span></div><br />覆盖掉该方法，实现你自己的东东 o_o<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> javax.mail.PasswordAuthentication getPasswordAuthentication()</span></div><br /><br />五：any more?<br />当然有了 o_o 以后再写.<br /></font></div><img src ="http://www.blogjava.net/TrampEagle/aggbug/45294.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-09 22:53 <a href="http://www.blogjava.net/TrampEagle/articles/45294.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Transaction API概述(3)</title><link>http://www.blogjava.net/TrampEagle/articles/31647.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 20 Feb 2006 06:46:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/31647.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/31647.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/31647.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/31647.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/31647.html</trackback:ping><description><![CDATA[引自：<a href="http://act.it.sohu.com/book/chapter.php?id=387&amp;volume=4&amp;chapter=3">http://act.it.sohu.com/book/chapter.php?id=387&amp;volume=4&amp;chapter=3</a><br /><br /><strong>JDBC驱动程序和XAResource</strong><br /><br />　　为了简化XAResource的说明，这些例子说明了一个应用程序在不包含应用程序服务器和事项管理程序的情况下应该如何使用JTA。 基本上，这些例子中的应用程序也担任应用程序服务器和事项管理程序的任务。大部分的企业使用事务管理程序和应用程序服务器，因为它们能够比一个应用程序更能够高效地管理分布式事务。 然而遵循这些例子，一个应用程序开发者可以测试在JDBC驱动程序中的JTA支持的健壮性。而且有一些例子可能不是工作在某个特定的数据库上，这是因为关联在数据库上的一些内在的问题。<br /><br />　　在使用JTA之前，你必须首先实现一个Xid类用来标识事务（在普通情况下这将由事务管理程序来处理）。Xid包含三个元素：formatID、gtrid（全局事务标识符）和bqual（分支修饰词标识符）。 <br /><br />　　formatID通常是零，这意味着你将使用OSI CCR（Open Systems Interconnection Commitment, Concurrency和Recovery 标准）来命名。如果你要是用另外一种格式，那么formatID应该大于零。-1值意味着Xid为无效。<br /><br />　　gtrid和bqual可以包含64个字节二进制码来分别标识全局事务和分支事务。唯一的要求是gtrid和bqual必须是全局唯一的。此外，这可以通过使用指定在OSI CCR中的命名规则规范来完成。<br /><br />　　下面的例子说明Xid的实现：<br /><br /><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>import javax.transaction.xa.*; <br />public class MyXid implements Xid <br />{ <br />　protected int formatId; <br />　protected byte gtrid[]; <br />　protected byte bqual[]; <br />　public MyXid() <br />　{ <br />　} <br />　public MyXid(int formatId, byte gtrid[], byte bqual[]) <br />　{ <br />　　this.formatId = formatId; <br />　　this.gtrid = gtrid; <br />　　this.bqual = bqual; <br />　} <br /><br />　public int getFormatId() <br />　{ <br />　　return formatId; <br />　} <br /><br />　public byte[] getBranchQualifier() <br />　{ <br />　　return bqual; <br />　} <br /><br />　public byte[] getGlobalTransactionId() <br />　{ <br />　　return gtrid; <br />　} <br /><br />} </p></td></tr></tbody></table><p><br /><br />　　其次，你需要创建一个你要使用的数据库的数据源：<br /><br /></p><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>public DataSource getDataSource() <br />　throws SQLException <br />　{ <br />　　SQLServerDataSource xaDS = new <br />　　com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource(); <br />　　xaDS.setDataSourceName("SQLServer"); <br />　　xaDS.setServerName("server"); <br />　　xaDS.setPortNumber(1433); <br />　　xaDS.setSelectMethod("cursor"); <br />　　return xaDS; <br />} </p></td></tr></tbody></table><p><br />　　例1—这个例子是用“两步提交协议”来提交一个事务分支：<br /><br /></p><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>XADataSource xaDS; <br />XAConnection xaCon; <br />XAResource xaRes; <br />Xid xid; <br />Connection con; <br />Statement stmt; <br />int ret; <br />xaDS = getDataSource(); <br />xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password"); <br />xaRes = xaCon.getXAResource(); <br />con = xaCon.getConnection(); <br />stmt = con.createStatement(); <br />xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); <br />try { <br />　　xaRes.start(xid, XAResource.TMNOFLAGS); <br />　　stmt.executeUpdate("insert into test_table values (100)"); <br />　　xaRes.end(xid, XAResource.TMSUCCESS); <br />　　ret = xaRes.prepare(xid); <br />　　if (ret == XAResource.XA_OK) { <br />　　　　xaRes.commit(xid, false); <br />　　　} <br />} <br />catch (XAException e) { <br />　e.printStackTrace(); <br />} <br />finally { <br />　stmt.close(); <br />　con.close(); <br />　xaCon.close(); <br />} </p></td></tr></tbody></table><p><br />　　因为所有这些例子中的初始化代码相同或者非常相似，仅仅是一些重要的地方的代码由不同。<br /><br />　　例2—这个例子，与例1相似，说明了一个返回过程：<br /><br /></p><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>xaRes.start(xid, XAResource.TMNOFLAGS); <br />stmt.executeUpdate("insert into test_table values (100)"); <br />xaRes.end(xid, XAResource.TMSUCCESS); <br />ret = xaRes.prepare(xid); <br />if (ret == XAResource.XA_OK) { <br />　xaRes.rollback(xid); <br />} </p></td></tr></tbody></table><p><br />　　例3—这个例子说明一个分布式事务分支如何中止，让相同的连接做本地事务处理，以及它们稍后该如何继续这个分支。 分布式事务的两步提交作用不影响本地事务。<br /><br /></p><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); <br />xaRes.start(xid, XAResource.TMNOFLAGS); <br />stmt.executeUpdate("insert into test_table values (100)"); <br />xaRes.end(xid, XAResource.TMSUSPEND); <br />∥这个更新在事务范围之外完成，所以它不受XA返回影响。 <br /><br />stmt.executeUpdate("insert into test_table2 values (111)"); <br />xaRes.start(xid, XAResource.TMRESUME); <br />stmt.executeUpdate("insert into test_table values (200)"); <br />xaRes.end(xid, XAResource.TMSUCCESS); <br />ret = xaRes.prepare(xid); <br /><br />if (ret == XAResource.XA_OK) { <br /><br />xaRes.rollback(xid); <br /><br />} </p></td></tr></tbody></table><p><br />　　例4—这个例子说明一个XA资源如何分担不同的事务。 创建了两个事务分支，但是它们不属于相同的分布式事务。 JTA允许XA资源在第一个分支上做一个两步提交，虽然这个资源仍然与第二个分支相关联。<br /><br /></p><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); <br />xid2 = new MyXid(100, new byte[]{0x11}, new byte[]{0x22}); <br />xaRes.start(xid1, XAResource.TMNOFLAGS); <br />stmt.executeUpdate("insert into test_table1 values (100)"); <br />xaRes.end(xid1, XAResource.TMSUCCESS); <br />xaRes.start(xid2, XAResource.TMNOFLAGS); <br />ret = xaRes.prepare(xid1); <br />if (ret == XAResource.XA_OK) { <br />　xaRes.commit(xid2, false); <br />} <br />stmt.executeUpdate("insert into test_table2 values (200)"); <br />xaRes.end(xid2, XAResource.TMSUCCESS); <br />ret = xaRes.prepare(xid2); <br />if (ret == XAResource.XA_OK) { <br />　xaRes.rollback(xid2); <br />} </p></td></tr></tbody></table><p><br />　　例5—这个例子说明不同的连接上的事务分支如何连接成为一个单独的分支，如果它们连接到相同的资源管理程序。这个特点改善了分布式事务的效率，因为它减少了两步提交处理的数目。两个连接到数据库服务器上的XA将被创建。每个连接创建它自己的XA资源，正规的JDBC连接和语句。在第二个XA资源开始一个事务分支之前，它将察看是否使用和第一个XA资源使用的是同一个资源管理程序。如果这是实例，它将加入在第一个XA连接上创建的第一个分支，而不是创建一个新的分支。 稍后，这个事务分支使用XA资源来准备和提交。<br /><br /></p><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>xaDS = getDataSource(); <br />xaCon1 = xaDS.getXAConnection("jdbc_user", "jdbc_password"); <br />xaRes1 = xaCon1.getXAResource(); <br />con1 = xaCon1.getConnection(); <br />stmt1 = con1.createStatement(); <br /><br />xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); <br />xaRes1.start(xid1, XAResource.TMNOFLAGS); <br />stmt1.executeUpdate("insert into test_table1 values (100)"); <br />xaRes1.end(xid, XAResource.TMSUCCESS); <br />xaCon2 = xaDS.getXAConnection("jdbc_user", "jdbc_password"); <br />xaRes2 = xaCon1.getXAResource(); <br />con2 = xaCon1.getConnection(); <br />stmt2 = con1.createStatement(); <br /><br />if (xaRes2.isSameRM(xaRes1)) { <br />　xaRes2.start(xid1, XAResource.TMJOIN); <br />　stmt2.executeUpdate("insert into test_table2 values (100)"); <br />　xaRes2.end(xid1, XAResource.TMSUCCESS); <br />} <br />else { <br />　xid2 = new MyXid(100, new byte[]{0x01}, new byte[]{0x03}); <br />　xaRes2.start(xid2, XAResource.TMNOFLAGS); <br />　stmt2.executeUpdate("insert into test_table2 values (100)"); <br />　xaRes2.end(xid2, XAResource.TMSUCCESS); <br />　ret = xaRes2.prepare(xid2); <br />　if (ret == XAResource.XA_OK) { <br />　　xaRes2.commit(xid2, false); <br />　} <br />} <br />ret = xaRes1.prepare(xid1); <br />if (ret == XAResource.XA_OK) { <br />　xaRes1.commit(xid1, false); <br />} <br /></p></td></tr></tbody></table><p><br />　　例6—这个例子说明在错误恢复的阶段，如何恢复准备好的或者快要完成的事务分支。 它首先试图返回每个分支；如果它失败了，它尝试着让资源管理程序丢掉关于事务的消息。<br /><br /></p><table cellpadding="0" width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>MyXid[] xids; <br />xids = xaRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); <br />for (int i=0; xids!=null &amp;&amp; i<xids.length; i++)="" {=""><br /> 　try { <br />　　xaRes.rollback(xids[i]); <br />　} <br />　catch (XAException ex) { <br />　　try { <br />　　　xaRes.forget(xids[i]); <br />　　} <br />　catch (XAException ex1) { <br />　　System.out.println("rollback/forget failed: " + ex1.errorCode); <br />　} <br />} <br /><br />} <br /></xids.length;></p></td></tr></tbody></table><img src ="http://www.blogjava.net/TrampEagle/aggbug/31647.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-20 14:46 <a href="http://www.blogjava.net/TrampEagle/articles/31647.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Transaction API概述(2)</title><link>http://www.blogjava.net/TrampEagle/articles/31646.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 20 Feb 2006 06:45:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/31646.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/31646.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/31646.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/31646.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/31646.html</trackback:ping><description><![CDATA[引自：<A href="http://act.it.sohu.com/book/chapter.php?id=387&amp;volume=4&amp;chapter=2">http://act.it.sohu.com/book/chapter.php?id=387&amp;volume=4&amp;chapter=2</A><BR><BR>
<P><STRONG>应用程序服务器</STRONG><BR><BR>　　应用程序服务器是事务处理操作的另一个组件。应用程序服务器处理大部分的应用程序操作并且获得最终用户应用程序的一些负载。基于前面的例子，我们可以看出应用程序服务器在事务处理上添加了另一个操作层:<BR><BR></P>
<TABLE cellPadding=0 width="100%" border=0>
<TBODY>
<TR align=middle>
<TD><IMG src="http://act.it.sohu.com/book/images/upload/1100141791-1.jpg" align=baseline border=0></TD></TR></TBODY></TABLE><BR>　　到目前为止，我们的例子说明了单个的本地事务处理，并且描述了分布式事务处理模型的五个组件中的四个。第五个组件，事务管理程序只有当事务将要被分配的时候才会开始被考虑。 <BR><BR>　　<B>分布式事务处理和事务管理程序 </B><BR><BR>　　像我们前面所提到的，一个分布式事务处理是一个在两个或更多网络资源上访问和更新数据的事务处理。 <BR><BR>　　这些资源可以由好几个位于一个单独服务器上的不同的关系型数据库管理系统组成，比如说Oracle、SQL Server和Sybase；它们也可以包含存在于若干不同的服务器上的同一种数据库的若干个实例。在任何情况下，一个分布式事务处理包括各种的资源管理程序之间的协同作用。这个协同作用是事务管理函数。<BR><BR>　　事务管理程序负责作出要么提交(commit)要么退回(rollback)任何分布式事务处理的决定。一个提交决定应该导致一个成功的事务处理；而退回操作则是保持数据库中的数据不变。 JTA指定一个分布式事务处理中的事务管理程序和另一个组件之间的标准Java接口：应用程序，应用程序服务器和资源管理程序。 这个关系被显示在下面的图表中：<BR><BR>
<TABLE cellPadding=0 width="100%" border=0>
<TBODY>
<TR align=middle>
<TD><IMG src="http://act.it.sohu.com/book/images/upload/1100141797-2.jpg" align=baseline border=0></TD></TR></TBODY></TABLE><BR><BR>　　在事务管理程序周围的数字框框相应于JTA的三个接口部分：<BR><BR>　　1—UserTransaction—javax.transaction.UserTransaction接口提供能够编程地控制事务处理范围的应用程序。 javax.transaction.UserTransaction方法开启一个全局事务并且使用调用线程与事务处理关联。<BR><BR>　　2—Transaction Manager—javax.transaction.TransactionManager接口允许应用程序服务器来控制代表正在管理的应用程序的事务范围。<BR><BR>　　3—XAResource—javax.transaction.xa.XAResource接口是一个基于X/Open CAE Specification的行业标准XA接口的Java映射。<BR><BR>　　注意，一个限制性环节是通过JDBC驱动程序的XAResource接口的支持。JDBC驱动程序必须支持两个正常的JDBC交互作用：应用程序和/或应用程序服务器，而且以及JTA的XAResource部分。<BR><BR>　　编写应用程序水平代码的开发者不会关心分布式事务处理管理的细节。 这是分布式事务处理基本结构的工作—应用程序服务器、事务管理程序和JDBC驱动程序。应用程序代码中唯一的需要注意的就是当连接处于一个分布式事务范围内的时候，不应该调用一个会影响事务边界的方法。特别的是，一个应用程序不应该调用Connection方法commit、rollback和setAutoCommit(true)，因为它们将破坏分布式事务的基本结构管理。<BR><BR>　　<B>分布式事务处理</B><BR><BR>　　事务管理程序是分布式事务基本结构的基本组件；然而JDBC驱动程序和应用程序服务器组件应该具备下面的特征：<BR><BR>　　驱动程序应该实现JDBC 2.0应用程序接口，包括Optional Package接口XADataSource和XAConnection以及JTA接口XAResource。<BR><BR>　　应用程序服务器应该提供一个DataSource类，用来实现与分布式事务基本结的交互以及一个连接池模块（用于改善性能）。<BR><BR>　　分布式事务处理的第一步就是应用程序要发送一个事务请求到事务管理程序。虽然最后的commit/rollback决定把事务作为一个简单的逻辑单元来对待，但是仍然可能会包括许多事务分支。一个事务分支与一个到包含在分布式事务中的每个资源管理程序相关联。因此，到三个不同的关系数据库管理的请求需要三个事务分支。每个事务分支必须由本地资源管理程序提交或者返回。事务管理程序控制事务的边界，并且负责最后决定应该提交或者返回的全部事务。 这个决定由两个步骤组成，称为Two - Phase Commit Protocol。<BR><BR>　　在第一步骤中，事务管理程序轮询所有包含在分布式事务中的资源管理程序（关系数据库管理）来看看哪个可以准备提交。如果一个资源管理程序不能提交，它将不响应，并且把事务的特定部分返回，以便数据不被修改。<BR><BR>　　在第二步骤中，事务管理程序判断否定响应的资源管理程序中是否有能够返回整个事务的。如果没有否定响应的话，翻译管理程序提交整个事务并且返回结果到应用程序中。<BR><BR>　　开发事项管理程序代码的开发者必须与所有三个JTA接口有关：UserTransaction、TransactionManager和XAResource，这三个接口都被描述在 <BR><BR>　　Sun JTA specification中。JDBC驱动程序开发者只需要关心XAResource接口。这个接口是允许一个资源管理程序参与事务的行业标准X/Open XA协议的Java映射。连接XAResource接口的驱动程序组件负责在事务管理程序和资源管理程序之间担任"翻译"的任务。下面的章节提供了XAResource调用的例子。<BR><img src ="http://www.blogjava.net/TrampEagle/aggbug/31646.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-20 14:45 <a href="http://www.blogjava.net/TrampEagle/articles/31646.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Transaction API概述(1)</title><link>http://www.blogjava.net/TrampEagle/articles/31644.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 20 Feb 2006 06:43:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/31644.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/31644.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/31644.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/31644.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/31644.html</trackback:ping><description><![CDATA[引自：<A href="http://act.it.sohu.com/book/chapter.php?id=387&amp;volume=4&amp;chapter=1">http://act.it.sohu.com/book/chapter.php?id=387&amp;volume=4&amp;chapter=1</A><BR><BR>
<P><STRONG>引言</STRONG><BR><BR>　　JTA(Java Transaction API)允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。<BR><BR>　　本文的目的是要提供一个关于的Java事务处理API（JTA）的高级的概述，以及与分布式事务相关的内容。一个事务处理定义了一个工作逻辑单元，要么彻底成功要么不产生任何结果。 一个分布式事务处理只是一个在两个或更多网络资源上访问和更新数据的事务处理，因此它在那些资源之间必然是等价的。在本文中，我们主要关心的是如何处理关系数据库系统。<BR><BR>　　我们要讨论的分布式事务处理（DTP）模型中包含的组件是：<BR><BR>　　　 应用程序<BR><BR>　　　 应用程序服务器<BR><BR>　　　 事务管理程序<BR><BR>　　　 资源适配器<BR><BR>　　　 资源管理程序<BR><BR>　　在以后的内容中，我们将描述这些组件以及它们与JTA和数据库访问的关系。<BR><BR><STRONG>　　访问数据库 <BR></STRONG><BR>　　最好把分布式事务处理中包含的组件看作是独立的过程，而不是考虑它们在一个特定的电脑中的位置。这些组件中的一些可以保存在单机中，或者也可在好几台机器之间分布。 下面例子中的图表可以显示在一台特定的电脑上的组件，但是这些操作之间的关系是必须首要考虑的。<BR><BR>　　最简单的例子：用于本地数据库事务处理的应用程序 <BR><BR>　　关系数据库访问的最简单的形式仅仅包括应用程序、资源管理程序和资源适配器。应用程序只不过是发送请求到数据库并且从数据库中获取数据的最终用户访问点<BR><BR>　　我们讨论的资源管理程序是一个关系数据库管理系统（RDBMS），比如Oracle或者SQL Server。所有的实际数据库管理都是由这个组件处理的。<BR><BR>　　资源适配器是外部空间之间的通信管道组件，或者是请求翻译器，在本例中，是应用程序和资源管理程序。在我们的讨论中，这是一个JDBC驱动程序。<BR><BR>　　下面的描述是资源管理程序本地事务处理的一个描述，也就是说，一个事务处理被被限制在一个特定的企业数据库。<BR><BR>　　应用程序发送一个用于JDBC驱动程序数据的请求，然后翻译这个请求并把它通过网络发送到数据库中。 数据库把数据发送回驱动程序，然后把翻译的结果发送回应用程序，如下图所示：<BR><BR></P>
<TABLE cellPadding=0 width="100%" border=0>
<TBODY>
<TR align=middle>
<TD>
<P><IMG src="http://act.it.sohu.com/book/images/upload/1100141726-1.jpg" align=baseline border=0></P></TD></TR></TBODY></TABLE>
<P><BR>　　这个例子说明了在一个简化的系统中的基本的信息流；然而，今天的企业使用的应用程序服务器都添加了其他的组件到这个过程处理中。</P><img src ="http://www.blogjava.net/TrampEagle/aggbug/31644.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-20 14:43 <a href="http://www.blogjava.net/TrampEagle/articles/31644.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA缓存应用：OSCache使用指南</title><link>http://www.blogjava.net/TrampEagle/articles/31640.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 20 Feb 2006 06:28:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/31640.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/31640.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/31640.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/31640.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/31640.html</trackback:ping><description><![CDATA[<P>引自：<A href="http://www.gamvan.com/developer/java/opener/2005/11/739.html">http://www.gamvan.com/developer/java/opener/2005/11/739.html</A><BR><BR>OSCache是当前运用最广的缓存方案，JBoss,Hibernate,Spring等都对其有支持，下面简单介绍一下OSCache的配置和使用过程。<BR></P>
<DIV class=content>1.安装过程<BR>从http://www.opensymphony.com/oscache/download.html下载合适的OSCache版本，<BR>我下载的是oscache-2.0.2-full版本。<BR>解压缩下载的文件到指定目录<BR><BR>从解压缩目录取得oscache.jar &nbsp;文件放到 &nbsp;/WEB-INF/lib &nbsp;或相应类库目录 &nbsp;目录中，<BR>jar文件名可能含有版本号和该版本的发布日期信息等，如oscache-2.0.2-22Jan04.jar<BR><BR>如果你的jdk版本为1.3.x,建议在lib中加入Apache &nbsp;Common &nbsp;Lib &nbsp;的commons-collections.jar包。<BR>如jdk是1.4以上则不必<BR><BR>从src或etc目录取得oscache.properties &nbsp;文件，放入src根目录或发布环境的/WEB-INF/classes &nbsp;目录<BR>如你需要建立磁盘缓存，须修改oscache.properties &nbsp;中的cache.path信息 &nbsp;(去掉前面的#注释)。<BR>win类路径类似为c:\app\cache<BR>unix类路径类似为/opt/myapp/cache<BR><BR>拷贝OSCache标签库文件oscache.tld到/WEB-INF/classes目录。<BR><BR>现在你的应用目录类似如下：<BR>$WEB_APPLICATIONWEB-INFliboscache.jar<BR>$WEB_APPLICATIONWEB-INFclassesoscache.properties<BR>$WEB_APPLICATIONWEB-INFclassesoscache.tld<BR><BR>将下列代码加入web.xml文件中<BR>
<TABLE cellSpacing=1 cellPadding=4 width="98%" align=center bgColor=#bad5ef border=0>
<FORM>
<TBODY>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#e6eef7 height=25>程序代码：</TD></TR>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#ffffff>&lt;taglib&gt;<BR>&lt;taglib-uri&gt;oscache&lt;/taglib-uri&gt;<BR>&lt;taglib-location&gt;/WEB-INF/classes/oscache.tld&lt;/taglib-location&gt;<BR>&lt;/taglib&gt;&nbsp;</TD></TR></FORM></TBODY></TABLE><BR><BR>为了便于调试日志输出，须加入commons-logging.jar和log4j-1.2.8.jar到当前类库路径中<BR><BR>在src目录加入下面两个日志输出配置文件：<BR><BR>log4j.properties &nbsp;文件内容为：<BR>
<TABLE cellSpacing=1 cellPadding=4 width="98%" align=center bgColor=#bad5ef border=0>
<FORM>
<TBODY>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#e6eef7 height=25>程序代码：</TD></TR>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#ffffff>log4j.rootLogger=DEBUG,stdout,file<BR><BR>log4j.appender.stdout=org.apache.log4j.ConsoleAppender<BR>log4j.appender.stdout.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.stdout.layout.ConversionPattern=[start]%d{yyyy/MM/dd/&nbsp;HH:mm:ss}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]&nbsp;n%c[CATEGORY]%n%m[MESSAGE]%n%n<BR><BR>log4j.appender.file=org.apache.log4j.RollingFileAppender<BR>log4j.appender.file.File=oscache.log<BR>log4j.appender.file.MaxFileSize=100KB<BR>log4j.appender.file.MaxBackupIndex=5<BR>log4j.appender.file.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.file.layout.ConversionPattern=[start]%d{yyyy/MM/dd/&nbsp;HH:mm:ss}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]&nbsp;n%c[CATEGORY]%n%m[MESSAGE]%n%n<BR><BR>log4j.logger.org.apache.commons=ERROR<BR>log4j.logger.com.opensymphony.oscache.base=INFO<BR><BR>commons-logging.properties&nbsp;文件内容为<BR>org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JCategoryLog</TD></TR></FORM></TBODY></TABLE><BR><BR>2.oscache.properties &nbsp;文件配置向导 &nbsp;<BR><BR>cache.memory<BR>值为true &nbsp;或 &nbsp;false &nbsp;，默认为在内存中作缓存，<BR>如设置为false，那cache只能缓存到<A href="http://www.gamvan.com/database/" target=_blank><FONT color=#002c99>数据库</FONT></A>或硬盘中，那cache还有什么意义：）<BR><BR>cache.capacity<BR>缓存元素个数<BR><BR>cache.persistence.class<BR>持久化缓存类，如此类打开，则必须设置cache.path信息<BR><BR>cache.cluster &nbsp;相关<BR>为集群设置信息。<BR>如<BR>cache.cluster.multicast.ip为广播IP地址<BR>cache.cluster.properties为集群属性<BR><BR><BR>3.OSCache的基本用法<BR><BR>cache1.jsp &nbsp;内容如下<BR><BR>
<TABLE cellSpacing=1 cellPadding=4 width="98%" align=center bgColor=#bad5ef border=0>
<FORM>
<TBODY>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#e6eef7 height=25>程序代码：</TD></TR>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#ffffff>&lt;%@&nbsp;page&nbsp;import=<SPAN style="COLOR: #2a00ff">"java.util.*"</SPAN>&nbsp;%&gt;<BR>&lt;%@&nbsp;taglib&nbsp;uri=<SPAN style="COLOR: #2a00ff">"oscache"</SPAN>&nbsp;prefix=<SPAN style="COLOR: #2a00ff">"cache"</SPAN>&nbsp;%&gt;<BR><BR>&lt;html&gt;<BR>&lt;body&gt;<BR><BR>没有缓存的日期:&nbsp;&lt;%=&nbsp;<SPAN style="FONT-WEIGHT: bold; COLOR: #7f0055">new&nbsp;</SPAN>Date()&nbsp;%&gt;&lt;p&gt;<BR>&lt;!--自动刷新--&gt;<BR>&lt;cache:cache&nbsp;time=<SPAN style="COLOR: #2a00ff">"30"</SPAN>&gt;<BR>每30秒刷新缓存一次的日期:&nbsp;&lt;%=&nbsp;<SPAN style="FONT-WEIGHT: bold; COLOR: #7f0055">new&nbsp;</SPAN>Date()&nbsp;%&gt;&nbsp;<BR>&lt;/cache:cache&gt;<BR>&lt;!--手动刷新--&gt;<BR>&lt;cache:cache&nbsp;key=<SPAN style="COLOR: #2a00ff">"testcache"</SPAN>&gt;<BR>手动刷新缓存的日期:&nbsp;&lt;%=&nbsp;<SPAN style="FONT-WEIGHT: bold; COLOR: #7f0055">new&nbsp;</SPAN>Date()&nbsp;%&gt;&nbsp;&lt;p&gt;<BR>&lt;/cache:cache&gt;<BR>&lt;a&nbsp;href=<SPAN style="COLOR: #2a00ff">"cache2.jsp"</SPAN>&gt;手动刷新&lt;/a&gt;<BR><BR>&lt;/body&gt;<BR>&lt;/html&gt;</TD></TR></FORM></TBODY></TABLE><BR><BR>cache2.jsp &nbsp;执行手动刷新页面如下<BR>&lt; &nbsp;%@ &nbsp;taglib &nbsp;uri="oscache" &nbsp;prefix="cache" &nbsp;% &nbsp;&gt;<BR><BR>&lt;html&gt;<BR>&lt;body&gt;<BR><BR>缓存已刷新...&lt;p&gt;<BR><BR>&lt;cache:flush &nbsp;key="testcache" &nbsp;scope="application"/&gt;<BR><BR>&lt;a &nbsp;href="cache1.jsp"&gt;返回&lt;/a&gt;<BR><BR>&lt;/body&gt;<BR>&lt;/html&gt;<BR><BR><BR>你也可以通过下面语句定义Cache的有效范围,如不定义scope,scope默认为Applcation<BR>
<TABLE cellSpacing=1 cellPadding=4 width="98%" align=center bgColor=#bad5ef border=0>
<FORM>
<TBODY>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#e6eef7 height=25>程序代码：</TD></TR>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#ffffff>&lt;cache:cache&nbsp;time=<SPAN style="COLOR: #2a00ff">"30"</SPAN>&nbsp;scope=<SPAN style="COLOR: #2a00ff">"session"</SPAN>&gt;<BR>...<BR>&lt;/cache:cache&gt;</TD></TR></FORM></TBODY></TABLE><BR><BR>4. &nbsp;缓存过滤器 &nbsp;CacheFilter &nbsp;<BR><BR>你可以在web.xml中定义缓存过滤器，定义特定资源的缓存。<BR>
<TABLE cellSpacing=1 cellPadding=4 width="98%" align=center bgColor=#bad5ef border=0>
<FORM>
<TBODY>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#e6eef7 height=25>程序代码：</TD></TR>
<TR>
<TD style="FONT-SIZE: 12px" bgColor=#ffffff>&lt;filter&gt;<BR>&lt;filter-name&gt;CacheFilter&lt;/filter-name&gt;<BR>&lt;filter-class&gt;com.opensymphony.oscache.web.filter.CacheFilter&lt;/filter-class&gt;<BR>&lt;init-param&gt;<BR>&lt;param-name&gt;time&lt;/param-name&gt;<BR>&lt;param-value&gt;60&lt;/param-value&gt;<BR>&lt;/init-param&gt;<BR>&lt;init-param&gt;<BR>&lt;param-name&gt;scope&lt;/param-name&gt;<BR>&lt;param-value&gt;session&lt;/param-value&gt;<BR>&lt;/init-param&gt;<BR>&lt;/filter&gt;<BR>&lt;filter-mapping&gt;<BR>&lt;filter-name&gt;CacheFilter&lt;/filter-name&gt;<BR>&lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;<BR>&lt;/filter-mapping&gt;</TD></TR></FORM></TBODY></TABLE><BR><BR>上面定义将缓存所有.jsp页面，缓存刷新时间为60秒，缓存作用域为Session<BR><BR>注意，CacheFilter只捕获Http头为200的页面请求，即只对无错误请求作缓存，<BR>而不对其他请求（如500,404,400）作缓存处理</DIV><img src ="http://www.blogjava.net/TrampEagle/aggbug/31640.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-20 14:28 <a href="http://www.blogjava.net/TrampEagle/articles/31640.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[Image] AWT使用ImageProducer/ImagConsumer模式加载和显示图像的原理 </title><link>http://www.blogjava.net/TrampEagle/articles/30832.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 15 Feb 2006 08:39:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30832.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30832.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30832.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30832.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30832.html</trackback:ping><description><![CDATA[
		<div class="postTitle">原文引自：<a href="/leon/">http://www.blogjava.net/leon/</a><a class="postTitle2" id="_6049e06f9ec9025_HomePageDays_DaysList__ctl5_DayItem_DayList__ctl0_TitleUrl" href="/leon/archive/2005/10/11/15217.html"><font color="#1a8bc8"><br /><br />[Image] AWT使用ImageProducer/ImagConsumer模式加载和显示图像的原理</font></a></div>
		<em>以前写过2篇关于AWT中图像加载显示方法的文章，最近又多了一些对于 ImageProducer / ImagConsumer 模式的一些理解，尝试着用文字总结了一下，接着还想再写一篇介绍 AWT 中图像过滤的原理和方法。你可能认为现在学习 AWT 中的成像方法对于开发中已经没有太大的意义，因为已经有了 Java 2D 和 JAI ，但是我在实际工作中感到还是无法完全离开 AWT，特别是在一些基本的应用上。而且我觉得理解 AWT 的 Producer / Consumer (push) model 对于理解 Java 2D 的 Immediate mode model 和 JAI 的 Pipeline (pull) model 的都是有好处的。<br /><br />因为水平有限，这方面学习资料相对也不丰富，我也不知道我的理解或想法是否完全正确或者表述清楚，感兴趣的朋友可以当作学习参考，希望能够和我联系进行进一步的讨论。</em>
		<br />
		<br />
		<br />
		<p>AWT 使用 ImageProducer / ImagConsumer 模式，支持加载和显示 GIF 图像文件格式和 JPEG 图像文件格式。因为图像的加载和显示是异步方式进行的，所以有大量加载和显示的技术。<br /><br />在 AWT 中，提供了一个 java.awt.Image 类。java.awt.Image 类代表一个图像对象被作为参数传递给其他用来显示和处理图像的其他 AWT 对象使用。例如，通过调用 Graphics.drawImage(java.awt.Image, int, int, ImageObserver) 方法，可以在组件中画出图像。<br /><br />java.awt.Image 是一个定义方法的抽象类，它定义的方法提供的对图像信息的访问。而创建和处理图像的基本结构则在 java.awt.image 包中。注意，这里不要和 java.awt.Image 发生混淆。<br /><br />AWT  加载和显示图像使用的是 ImageProducer / ImagConsumer 模式，我们必须了解3个术语，ImageProducer（图像生产者），ImageConsumer（图像消费者）和ImageObserver（图像观察者）。<br /><br />ImageProducer 负责生产图像的位，ImagConsumer 接受图像的位，ImageObserver 监视 ImageProducer 的图像生产过程。ImageProducer 生产传递给 ImagConsumer 与图像相关的位。因为图像生产过程是异步进行的，并不是一次性生产所有图像位，所以当 ImageProducer 加载图像时，ImageObserver 用来监视它的进展情况。因为 java.awt.Component 实现了 ImageObserver 接口，所以 AWT 中的每个组件都可以是ImageObserver，当一个给定的 ImageProducer 进行异步操作时，这个 ImageObserver 可以选择是否被更新。java.awt.image 包为 ImageProducer，ImagConsumer 和 ImageObserver 都定义了接口。<br /><br /><strong>ImageProducer</strong><br />和图像相关的位并不存储在 java.awt.Image 中，每个图像都维护一个和一个 ImageProducer 的关联。这个 ImageProducer 的责任是生产图像的位并将它们传送给 ImagConsumer，用于过滤该图像。<br /><br />java.awt.image软件包中，FilteredImageSource（被过滤的图像源）和 MemoryImageSource（内存的图像源）实现了 ImageProducer  接口，是 ImageProducer 。<br /><br /><br /><strong>ImagConsumer</strong><br />java.awt.image软件包中，ImageFilter（图像过滤器）和 PixelGrabber（像素抓取器）实现了 ImagConsumer 接口，是 ImagConsumer。<br /><br /><br />ImageProducer 和 ImagConsumer 的详细介绍请阅读 <u><font color="#0000ff">使用 ImageProducer  / ImagConsumer 进行图像过滤</font></u><br /><br /><br /><strong>ImageObserver</strong><br />ImageObserver接口中，定义了一个常数集合和一个方法：<br /><br /><em>public boolean imageUpdate(image img, int flags, int x, int y, int width, int height);</em><br /><br /></p>
		<table id="table1" width="60%" border="1">
				<tbody>
						<tr>
								<td align="middle" colspan="2">
										<span lang="en-us">ImageObserver</span>的常数</td>
						</tr>
						<tr>
								<td align="middle">标志</td>
								<td align="middle">含义</td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">ABORT</span>
								</td>
								<td>图像加载被中断</td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">ALLBITS</span>
								</td>
								<td>所有的位都已加载给图像</td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">ERROR</span>
								</td>
								<td>在加载过程中发生错误</td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">FRAMEBITS</span>
								</td>
								<td>多帧图像的一个帧被传送，一般用于<span lang="en-us">GIF</span></td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">HEIGHT</span>
								</td>
								<td>图像的高度已经可用</td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">PROPERTIES</span>
								</td>
								<td>图像的属性已经可用</td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">SOMEBITS</span>
								</td>
								<td>图像的缩放变体的多个位已经可用</td>
						</tr>
						<tr>
								<td>
										<span lang="en-us">WIDTH</span>
								</td>
								<td>图像的宽度已经可用</td>
						</tr>
				</tbody>
		</table>
		<br />参数 flags 的作用是通知图像观察者图像生产的进展情况。这些常数代表传递给 ImageObserver.imageUpdate() 的 flags 参数中的位。<br /><br />当 Component 作为 ImageObserver 时，一旦图像有新的部分被加载，就会调用 Component.imageUpdate() 方法，imageUpdate() 再调用 repaint() 重新绘制图像。repaint() 将先调用 update() 方法清除组件背景，再由 update() 方法调用 paint() 方法绘制图像。我们可以通过重载 imageUpdate() 方法控制组件何时被更新，重载 update() 方法控制是否每次重绘都要清除背景图像（每次重绘都清除背景图像会造成画面闪烁）。<br /><br /><br />为了更好的理解，下面我们来看几个样例程序：<br /><br /><font color="#000000">例1，图像位在需要之前不被生产</font><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.net.URL;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.applet.Applet;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Graphics;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Image;</span><span style="COLOR: #008000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000"><br /><img id="Codehighlighter1_221_556_Open_Image" onclick="this.style.display='none'; Codehighlighter1_221_556_Open_Text.style.display='none'; Codehighlighter1_221_556_Closed_Image.style.display='inline'; Codehighlighter1_221_556_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_221_556_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_221_556_Closed_Text.style.display='none'; Codehighlighter1_221_556_Open_Image.style.display='inline'; Codehighlighter1_221_556_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> ImageTestAppletSimple </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> Applet</span><span id="Codehighlighter1_221_556_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_221_556_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> Image im;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_263_489_Open_Image" onclick="this.style.display='none'; Codehighlighter1_263_489_Open_Text.style.display='none'; Codehighlighter1_263_489_Closed_Image.style.display='inline'; Codehighlighter1_263_489_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_263_489_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_263_489_Closed_Text.style.display='none'; Codehighlighter1_263_489_Open_Image.style.display='inline'; Codehighlighter1_263_489_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> init()</span><span id="Codehighlighter1_263_489_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_263_489_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        URL codebase </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getCodeBase();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(codebase);<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" />        im </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getImage(codebase,</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">flower.jpg</span><span style="COLOR: #000000">"</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" />        System.out.print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Image width = </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> im.getWidth(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(</span><span style="COLOR: #000000">" </span><span style="COLOR: #000000">hight = </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> im.getHeight(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_523_554_Open_Image" onclick="this.style.display='none'; Codehighlighter1_523_554_Open_Text.style.display='none'; Codehighlighter1_523_554_Closed_Image.style.display='inline'; Codehighlighter1_523_554_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_523_554_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_523_554_Closed_Text.style.display='none'; Codehighlighter1_523_554_Open_Image.style.display='inline'; Codehighlighter1_523_554_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> paint(Graphics g)</span><span id="Codehighlighter1_523_554_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_523_554_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        g.drawImage(im,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span></div><p>输出结果：<br />image width = -1 height = -1<br /><br />图像的高度和宽度只有在图像被完全加载后才是有效的，输出结果说明 java.awt.image 相关的图像位在需要之前不被生产。<br /><br /><br />例2，图像异步生产</p><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.net.URL;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.applet.Applet;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Graphics;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Image;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /><img id="Codehighlighter1_148_480_Open_Image" onclick="this.style.display='none'; Codehighlighter1_148_480_Open_Text.style.display='none'; Codehighlighter1_148_480_Closed_Image.style.display='inline'; Codehighlighter1_148_480_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_148_480_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_148_480_Closed_Text.style.display='none'; Codehighlighter1_148_480_Open_Image.style.display='inline'; Codehighlighter1_148_480_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> ImageTestAppletSimple2 </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> Applet</span><span id="Codehighlighter1_148_480_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_148_480_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> Image im;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_190_238_Open_Image" onclick="this.style.display='none'; Codehighlighter1_190_238_Open_Text.style.display='none'; Codehighlighter1_190_238_Closed_Image.style.display='inline'; Codehighlighter1_190_238_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_190_238_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_190_238_Closed_Text.style.display='none'; Codehighlighter1_190_238_Open_Image.style.display='inline'; Codehighlighter1_190_238_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> init()</span><span id="Codehighlighter1_190_238_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_190_238_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        im </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getImage(getCodeBase(),</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">flower.jpg</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_272_478_Open_Image" onclick="this.style.display='none'; Codehighlighter1_272_478_Open_Text.style.display='none'; Codehighlighter1_272_478_Closed_Image.style.display='inline'; Codehighlighter1_272_478_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_272_478_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_272_478_Closed_Text.style.display='none'; Codehighlighter1_272_478_Open_Image.style.display='inline'; Codehighlighter1_272_478_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> paint(Graphics g)</span><span id="Codehighlighter1_272_478_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_272_478_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">drawing image...</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(g.drawImage(im,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">));</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span></div><p><span style="COLOR: #000000">输出结果：<br />drawing image...<br /></span><span style="COLOR: #000000">false<br /></span><span style="COLOR: #000000">drawing image...<br /></span><span style="COLOR: #000000">false<br /></span><span style="COLOR: #000000">drawing image...<br /></span><span style="COLOR: #000000">false<br /></span><span style="COLOR: #000000">drawing image...<br /></span><span style="COLOR: #000000">true</span><br /><br />输出结果说明组件作为 ImageObserver ，监视 ImageProducer 异步生产图像，一旦有新的图像位被生产时就重绘图像，图像完全加载后 drawImage() 方法返回 true。<br /><br /><br />例3，重载 ImageObserver 的 imageUpdate() 方法，在图像完全加载前不调用 repaint()</p><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.applet.Applet;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Graphics;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Image;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /><img id="Codehighlighter1_130_660_Open_Image" onclick="this.style.display='none'; Codehighlighter1_130_660_Open_Text.style.display='none'; Codehighlighter1_130_660_Closed_Image.style.display='inline'; Codehighlighter1_130_660_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_130_660_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_130_660_Closed_Text.style.display='none'; Codehighlighter1_130_660_Open_Image.style.display='inline'; Codehighlighter1_130_660_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> ImageTestAppletWithUpdate </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> Applet</span><span id="Codehighlighter1_130_660_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_130_660_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> Image im;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_172_336_Open_Image" onclick="this.style.display='none'; Codehighlighter1_172_336_Open_Text.style.display='none'; Codehighlighter1_172_336_Closed_Image.style.display='inline'; Codehighlighter1_172_336_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_172_336_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_172_336_Closed_Text.style.display='none'; Codehighlighter1_172_336_Open_Image.style.display='inline'; Codehighlighter1_172_336_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> init()</span><span id="Codehighlighter1_172_336_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_172_336_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        im </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getImage(getCodeBase(),</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">flower.jpg</span><span style="COLOR: #000000">"</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" />        System.out.print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Image width = </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> im.getWidth(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">hight = </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> im.getHeight(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_370_401_Open_Image" onclick="this.style.display='none'; Codehighlighter1_370_401_Open_Text.style.display='none'; Codehighlighter1_370_401_Closed_Image.style.display='inline'; Codehighlighter1_370_401_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_370_401_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_370_401_Closed_Text.style.display='none'; Codehighlighter1_370_401_Open_Image.style.display='inline'; Codehighlighter1_370_401_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> paint(Graphics g)</span><span id="Codehighlighter1_370_401_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_370_401_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        g.drawImage(im,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_479_658_Open_Image" onclick="this.style.display='none'; Codehighlighter1_479_658_Open_Text.style.display='none'; Codehighlighter1_479_658_Closed_Image.style.display='inline'; Codehighlighter1_479_658_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_479_658_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_479_658_Closed_Text.style.display='none'; Codehighlighter1_479_658_Open_Image.style.display='inline'; Codehighlighter1_479_658_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">boolean</span><span style="COLOR: #000000"> imageUpdate(Image image,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> flags,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> x,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> y,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> w,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> h)</span><span id="Codehighlighter1_479_658_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_479_658_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">imageUpdate():x=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> x </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,y=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> y </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,w=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> w </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,h=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> h);<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" />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">((flags </span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000"> ALLBITS) </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br /><img id="Codehighlighter1_620_655_Open_Image" onclick="this.style.display='none'; Codehighlighter1_620_655_Open_Text.style.display='none'; Codehighlighter1_620_655_Closed_Image.style.display='inline'; Codehighlighter1_620_655_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_620_655_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_620_655_Closed_Text.style.display='none'; Codehighlighter1_620_655_Open_Image.style.display='inline'; Codehighlighter1_620_655_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />        </span><span id="Codehighlighter1_620_655_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_620_655_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            repaint();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />        }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span></div><p><br /><br />例4，重载  Component.update() 方法，被调用时不清除背景图像，直接调用 paint() 方法绘制图像，消除闪烁</p><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.applet.Applet;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Graphics;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.Image;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /><img id="Codehighlighter1_143_713_Open_Image" onclick="this.style.display='none'; Codehighlighter1_143_713_Open_Text.style.display='none'; Codehighlighter1_143_713_Closed_Image.style.display='inline'; Codehighlighter1_143_713_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_143_713_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_143_713_Closed_Text.style.display='none'; Codehighlighter1_143_713_Open_Image.style.display='inline'; Codehighlighter1_143_713_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> ImageTestAppletWithSmoothDynamicUpdate </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> Applet</span><span id="Codehighlighter1_143_713_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_143_713_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> Image im;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_185_345_Open_Image" onclick="this.style.display='none'; Codehighlighter1_185_345_Open_Text.style.display='none'; Codehighlighter1_185_345_Closed_Image.style.display='inline'; Codehighlighter1_185_345_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_185_345_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_185_345_Closed_Text.style.display='none'; Codehighlighter1_185_345_Open_Image.style.display='inline'; Codehighlighter1_185_345_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> init()</span><span id="Codehighlighter1_185_345_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_185_345_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        im </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getImage(getCodeBase(),</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">hl.jpg</span><span style="COLOR: #000000">"</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" />        System.out.print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Image width = </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> im.getWidth(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">hight = </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> im.getHeight(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_379_410_Open_Image" onclick="this.style.display='none'; Codehighlighter1_379_410_Open_Text.style.display='none'; Codehighlighter1_379_410_Closed_Image.style.display='inline'; Codehighlighter1_379_410_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_379_410_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_379_410_Closed_Text.style.display='none'; Codehighlighter1_379_410_Open_Image.style.display='inline'; Codehighlighter1_379_410_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> paint(Graphics g)</span><span id="Codehighlighter1_379_410_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_379_410_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        g.drawImage(im,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_488_661_Open_Image" onclick="this.style.display='none'; Codehighlighter1_488_661_Open_Text.style.display='none'; Codehighlighter1_488_661_Closed_Image.style.display='inline'; Codehighlighter1_488_661_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_488_661_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_488_661_Closed_Text.style.display='none'; Codehighlighter1_488_661_Open_Image.style.display='inline'; Codehighlighter1_488_661_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">boolean</span><span style="COLOR: #000000"> imageUpdate(Image image,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> flags,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> x,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> y,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> w,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> h)</span><span id="Codehighlighter1_488_661_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_488_661_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">imageUpdate():x=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> x </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,y=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> y </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,w=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> w </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,h=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> h);<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" />        repaint();<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" />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">((flags </span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000"> ALLBITS) </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img id="Codehighlighter1_696_711_Open_Image" onclick="this.style.display='none'; Codehighlighter1_696_711_Open_Text.style.display='none'; Codehighlighter1_696_711_Closed_Image.style.display='inline'; Codehighlighter1_696_711_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_696_711_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_696_711_Closed_Text.style.display='none'; Codehighlighter1_696_711_Open_Image.style.display='inline'; Codehighlighter1_696_711_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> update(Graphics g)</span><span id="Codehighlighter1_696_711_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_696_711_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        paint(g);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span></div><img src ="http://www.blogjava.net/TrampEagle/aggbug/30832.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-15 16:39 <a href="http://www.blogjava.net/TrampEagle/articles/30832.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[Swing]布局管理器 - OverlayLayout </title><link>http://www.blogjava.net/TrampEagle/articles/30821.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 15 Feb 2006 07:37:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30821.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30821.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30821.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30821.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30821.html</trackback:ping><description><![CDATA[引自：<a href="/leon/">http://www.blogjava.net/leon/</a><br /><br />OverlayLayout是用于排列重叠组件的布局管理器。它的用途是以一些对齐的点为基准将一些组件层叠的放置在布局容器中。<br /><br />组件的横轴和纵轴的对齐点介于0.0和1.0之间。横轴（X轴）上0.0代表组件的左侧面，1.0代表组件的右侧面；纵轴（Y轴）上0.0和1.0分别代表组件的顶部和底部。<br /><br />构造函数<br /><em><strong>public OverlayLayout(Container target)<br /></strong></em><br />因为构造函数不会为target对象安装结果布局管理器，所以我们还必须调用setLayout()来完成此功能。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">JPanel p1 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JPanel();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />OverlayLayout overlay </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> OverlayLayout(p1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />p1.setLayout(overlay); </span></div><br /><strong><u>在OverlayLayout布局管理器中，每个组件都有一对横纵坐标值，每个组件的位置只和它本身的横纵坐标值有关，换句话说就是组件以他自己的位置作为基准，横轴上0.0和1.0分别代表组件的左侧面和右侧面；纵轴上0.0和1.0分别代表组件的顶部和底部，和容器位置无关。如果一个组件的alignmentX属性设置为0.5，原本左侧面的位置对应0.0，现在变成了0.5，那么，现在组件的位置就要向左移动width/2的距离，使左侧面的位置对应现在的0.0。纵轴亦是如此，明白了吗？</u></strong><br /><br />为了容易理解，我们来看《Java Swing》中关于OverlayLayout的一段样例程序，它可以编译运行。如图，你可以在输入框中调节容器中3个按钮的X，Y轴的值来看他们在容器中的位置是怎样改变的，多试几次，你就可以完全理解OverlayLayout。<br /><br /><img height="301" alt="OverlayTest.jpg" src="http://www.blogjava.net/images/blogjava_net/leon/program/OverlayTest.jpg" width="502" border="0" /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> OverlayTest.java<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> A test of the OverlayLayout manager allowing experimentation.<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> 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" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> java.awt.event.</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> javax.swing.</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 id="Codehighlighter1_196_4146_Open_Image" onclick="this.style.display='none'; Codehighlighter1_196_4146_Open_Text.style.display='none'; Codehighlighter1_196_4146_Closed_Image.style.display='inline'; Codehighlighter1_196_4146_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_196_4146_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_196_4146_Closed_Text.style.display='none'; Codehighlighter1_196_4146_Open_Image.style.display='inline'; Codehighlighter1_196_4146_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> OverlayTest </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> JFrame </span><span id="Codehighlighter1_196_4146_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_196_4146_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img id="Codehighlighter1_224_3482_Open_Image" onclick="this.style.display='none'; Codehighlighter1_224_3482_Open_Text.style.display='none'; Codehighlighter1_224_3482_Closed_Image.style.display='inline'; Codehighlighter1_224_3482_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_224_3482_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_224_3482_Closed_Text.style.display='none'; Codehighlighter1_224_3482_Open_Image.style.display='inline'; Codehighlighter1_224_3482_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> OverlayTest() </span><span id="Codehighlighter1_224_3482_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_224_3482_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">super</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">OverlayLayout Test</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        setSize(</span><span style="COLOR: #000000">500</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">300</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        setDefaultCloseOperation(EXIT_ON_CLOSE);<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" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> Container c </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getContentPane();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        c.setLayout(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> GridBagLayout());<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" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JPanel p1 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> GridPanel();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> OverlayLayout overlay </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> OverlayLayout(p1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p1.setLayout(overlay);<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" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JButton jb1 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JButton(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JButton jb2 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JButton(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B2</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JButton jb3 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JButton(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B3</span><span style="COLOR: #000000">"</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" />        Dimension b1 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Dimension(</span><span style="COLOR: #000000">60</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">50</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        Dimension b2 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Dimension(</span><span style="COLOR: #000000">80</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">40</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        Dimension b3 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Dimension(</span><span style="COLOR: #000000">100</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">60</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" />        jb1.setMinimumSize(b1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb1.setMaximumSize(b1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb1.setPreferredSize(b1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb2.setMinimumSize(b2);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb2.setMaximumSize(b2);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb2.setPreferredSize(b2);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb3.setMinimumSize(b3);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb3.setMaximumSize(b3);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb3.setPreferredSize(b3);<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" />        SimpleReporter reporter </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SimpleReporter();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb1.addActionListener(reporter);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb2.addActionListener(reporter);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        jb3.addActionListener(reporter);<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" />        p1.add(jb1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p1.add(jb2);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p1.add(jb3);<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" />        JPanel p2 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JPanel();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.setLayout(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> GridLayout(</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">6</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JLabel(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B1 X</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, JLabel.CENTER));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JLabel(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B1 Y</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, JLabel.CENTER));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JLabel(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B2 X</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, JLabel.CENTER));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JLabel(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B2 Y</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, JLabel.CENTER));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JLabel(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B3 X</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, JLabel.CENTER));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JLabel(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B3 Y</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, JLabel.CENTER));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JLabel(</span><span style="COLOR: #000000">""</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" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JTextField x1 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JTextField(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">0.0</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">); </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> Button1 x alignment</span><span style="COLOR: #008000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #000000">        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JTextField y1 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JTextField(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">0.0</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">); </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> Button1 y alignment</span><span style="COLOR: #008000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #000000">        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JTextField x2 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JTextField(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">0.0</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">); <br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JTextField y2 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JTextField(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">0.0</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">); <br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JTextField x3 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JTextField(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">0.0</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">); <br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> JTextField y3 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JTextField(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">0.0</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">4</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" />        p2.add(x1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(y1);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(x2);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(y2);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(x3);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        p2.add(y3);<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" /><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        GridBagConstraints constraints </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> GridBagConstraints();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        c.add(p1, constraints);<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" />        constraints.gridx </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        JButton updateButton </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> JButton(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Update</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img id="Codehighlighter1_2556_3302_Open_Image" onclick="this.style.display='none'; Codehighlighter1_2556_3302_Open_Text.style.display='none'; Codehighlighter1_2556_3302_Closed_Image.style.display='inline'; Codehighlighter1_2556_3302_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_2556_3302_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2556_3302_Closed_Text.style.display='none'; Codehighlighter1_2556_3302_Open_Image.style.display='inline'; Codehighlighter1_2556_3302_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />        updateButton.addActionListener(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ActionListener() </span><span id="Codehighlighter1_2556_3302_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_2556_3302_Open_Text"><span style="COLOR: #000000">{<br /><img id="Codehighlighter1_2614_3292_Open_Image" onclick="this.style.display='none'; Codehighlighter1_2614_3292_Open_Text.style.display='none'; Codehighlighter1_2614_3292_Closed_Image.style.display='inline'; Codehighlighter1_2614_3292_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_2614_3292_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2614_3292_Closed_Text.style.display='none'; Codehighlighter1_2614_3292_Open_Image.style.display='inline'; Codehighlighter1_2614_3292_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> actionPerformed(ActionEvent ae) </span><span id="Codehighlighter1_2614_3292_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_2614_3292_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                jb1.setAlignmentX(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                    Float.valueOf(x1.getText().trim()).floatValue());<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                jb1.setAlignmentY(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                    Float.valueOf(y1.getText().trim()).floatValue());<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                jb2.setAlignmentX(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                    Float.valueOf(x2.getText().trim()).floatValue());<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                jb2.setAlignmentY(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                    Float.valueOf(y2.getText().trim()).floatValue());<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                jb3.setAlignmentX(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                    Float.valueOf(x3.getText().trim()).floatValue());<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                jb3.setAlignmentY(<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                    Float.valueOf(y3.getText().trim()).floatValue());<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" />                p1.revalidate();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />            }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />        }</span></span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        c.add(updateButton, constraints);<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" />        constraints.gridx </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        constraints.gridy </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        constraints.gridwidth </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        c.add(p2, constraints);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img id="Codehighlighter1_3528_3607_Open_Image" onclick="this.style.display='none'; Codehighlighter1_3528_3607_Open_Text.style.display='none'; Codehighlighter1_3528_3607_Closed_Image.style.display='inline'; Codehighlighter1_3528_3607_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_3528_3607_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_3528_3607_Closed_Text.style.display='none'; Codehighlighter1_3528_3607_Open_Image.style.display='inline'; Codehighlighter1_3528_3607_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> main(String args[]) </span><span id="Codehighlighter1_3528_3607_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_3528_3607_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        OverlayTest ot </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> OverlayTest();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        ot.setVisible(</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img id="Codehighlighter1_3668_3793_Open_Image" onclick="this.style.display='none'; Codehighlighter1_3668_3793_Open_Text.style.display='none'; Codehighlighter1_3668_3793_Closed_Image.style.display='inline'; Codehighlighter1_3668_3793_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_3668_3793_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_3668_3793_Closed_Text.style.display='none'; Codehighlighter1_3668_3793_Open_Image.style.display='inline'; Codehighlighter1_3668_3793_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> SimpleReporter </span><span style="COLOR: #0000ff">implements</span><span style="COLOR: #000000"> ActionListener </span><span id="Codehighlighter1_3668_3793_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_3668_3793_Open_Text"><span style="COLOR: #000000">{<br /><img id="Codehighlighter1_3722_3787_Open_Image" onclick="this.style.display='none'; Codehighlighter1_3722_3787_Open_Text.style.display='none'; Codehighlighter1_3722_3787_Closed_Image.style.display='inline'; Codehighlighter1_3722_3787_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_3722_3787_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_3722_3787_Closed_Text.style.display='none'; Codehighlighter1_3722_3787_Open_Image.style.display='inline'; Codehighlighter1_3722_3787_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> actionPerformed(ActionEvent ae) </span><span id="Codehighlighter1_3722_3787_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_3722_3787_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            System.out.println(ae.getActionCommand());<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />        }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img id="Codehighlighter1_3838_4144_Open_Image" onclick="this.style.display='none'; Codehighlighter1_3838_4144_Open_Text.style.display='none'; Codehighlighter1_3838_4144_Closed_Image.style.display='inline'; Codehighlighter1_3838_4144_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_3838_4144_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_3838_4144_Closed_Text.style.display='none'; Codehighlighter1_3838_4144_Open_Image.style.display='inline'; Codehighlighter1_3838_4144_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> GridPanel </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> JPanel </span><span id="Codehighlighter1_3838_4144_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_3838_4144_Open_Text"><span style="COLOR: #000000">{<br /><img id="Codehighlighter1_3878_4138_Open_Image" onclick="this.style.display='none'; Codehighlighter1_3878_4138_Open_Text.style.display='none'; Codehighlighter1_3878_4138_Closed_Image.style.display='inline'; Codehighlighter1_3878_4138_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_3878_4138_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_3878_4138_Closed_Text.style.display='none'; Codehighlighter1_3878_4138_Open_Image.style.display='inline'; Codehighlighter1_3878_4138_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> paint(Graphics g) </span><span id="Codehighlighter1_3878_4138_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_3878_4138_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">super</span><span style="COLOR: #000000">.paint(g);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> w </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getSize().width;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> h </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getSize().height;<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" />            g.setColor(Color.red);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            g.drawRect(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,w</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,h</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            g.drawLine(w</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,w</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">,h);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            g.drawLine(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,h</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">,w,h</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />        }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</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><br /><br />最后提醒，使用OverlayLayout布局管理器关键要记住X，Y轴对应组件位置，和容器没有关系。只要明白这一点，使用还是很简单方便的，我用OverlayLayout布局管理器clone了一个PhotoShop的工具面板。<img height="19" src="http://www.blogjava.net/Emoticons/red_smile.gif" width="19" border="0" /><br /><img height="264" alt="ToolWidget.jpg" src="http://www.blogjava.net/images/blogjava_net/leon/program/ToolWidget.jpg" width="60" border="0" /> <img src ="http://www.blogjava.net/TrampEagle/aggbug/30821.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-15 15:37 <a href="http://www.blogjava.net/TrampEagle/articles/30821.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 理论与实践: 用弱引用堵住内存泄漏</title><link>http://www.blogjava.net/TrampEagle/articles/30266.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 11 Feb 2006 07:21:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30266.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30266.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30266.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30266.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30266.html</trackback:ping><description><![CDATA[原文引自：<a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/">http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/</a><br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr valign="top"><td width="100%"><h1><span style="COLOR: #999999">Java 理论与实践: </span>用弱引用堵住内存泄漏</h1><p id="subtitle">弱引用使得表达对象生命周期关系变得容易了</p><img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" /></td><td class="no-print" width="192"><img height="18" alt="developerWorks" src="http://www-128.ibm.com/developerworks/cn/i/dw.gif" width="192" /></td></tr></tbody></table><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr valign="top"><td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td><td width="100%"><table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0"><tbody><tr><td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td><td><table cellspacing="0" cellpadding="0" width="150" border="0"><tbody><tr><td class="v14-header-1-small">文档选项</td></tr></tbody></table><table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="no-padding" width="150"><table cellspacing="0" cellpadding="0" width="143" border="0"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /><form name="email" action="https://www-130.ibm.com/developerworks/secure/email-it.jsp"><input type="hidden" value="虽然用 Java 语言编写的程序在理论上是不会出现“内存泄漏”的，但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集。本月，清洁大师 Brian Goetz 探讨了无意识的对象保留的常见原因，并展示了如何用弱引用堵住泄漏。&#xD;&#xA;" name="body" /><input type="hidden" value="Java 理论与实践: 用弱引用堵住内存泄漏" name="subject" /><input type="hidden" value="cn" name="lang" /><script language="JavaScript" type="text/javascript"><!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//--></script><tbody><tr valign="top"><td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td><td width="16"><img height="16" alt="将此页作为电子邮件发送" src="http://www.ibm.com/i/v14/icons/em.gif" width="16" vspace="3" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b><font face="Verdana" color="#5c81a7" size="2">将此页作为电子邮件发送</font></b></a></p></td></tr><noscript><tr valign="top"><td width="8"><img height="1" alt="" src="//www.ibm.com/i/c.gif" width="8" /></td><td width="16"><img height="16" alt="" src="//www.ibm.com/i/c.gif" width="16" /></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></noscript></tbody></form><tr valign="top"><td width="8"><font face="Verdana" color="#5c81a7" size="2"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></font></td><td width="16"><font face="Verdana" color="#5c81a7" size="2"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/fw_bold.gif" width="16" vspace="3" border="0" /></font></td><td width="122"><p><a class="smallplainlink" href="javascript:void forumWindow()"><b><font face="Verdana" color="#5c81a7" size="2">讨论</font></b></a></p></td></tr></table></td></tr></tbody></table></td></tr></tbody></table><br /><table cellspacing="0" cellpadding="0" width="150" border="0"><tbody><tr><td class="v14-header-1-small">对此页的评价</td></tr></tbody></table><table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="no-padding" width="150"><table cellspacing="0" cellpadding="0" width="143" border="0"><tbody><tr valign="top"><td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td><td><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width="16" vspace="3" border="0" /></td><td width="125"><p><a class="smallplainlink" href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#rate"><b><font face="Verdana" color="#996699" size="2">帮助我们改进这些内容</font></b></a></p></td></tr></tbody></table></td></tr></tbody></table><br /></td></tr></tbody></table><p>级别: 中级</p><p><a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#author"><font color="#996699">Brian Goetz </font></a>, 首席顾问, Quiotix<br /></p><p>2005 年 12 月 19 日</p><blockquote>虽然用 Java™ 语言编写的程序在理论上是不会出现“内存泄漏”的，但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集。本月，负责保障应用程序健康的工程师 Brian Goetz 探讨了无意识的对象保留的常见原因，并展示了如何用弱引用堵住泄漏。 </blockquote><p>要让垃圾收集（GC）回收程序不再使用的对象，对象的<i>逻辑</i> 生命周期（应用程序使用它的时间）和对该对象拥有的引用的<i>实际</i> 生命周期必须是相同的。在大多数时候，好的软件工程技术保证这是自动实现的，不用我们对对象生命周期问题花费过多心思。但是偶尔我们会创建一个引用，它在内存中包含对象的时间比我们预期的要长得多，这种情况称为<i>无意识的对象保留（unintentional object retention）</i>。 </p><p><a name="1.0"><span class="atitle"><strong><font size="4">全局 Map 造成的内存泄漏</font></strong></span></a></p><p>无意识对象保留最常见的原因是使用 <code><font face="Courier" size="2">Map</font></code> 将元数据与临时对象（transient object）相关联。假定一个对象具有中等生命周期，比分配它的那个方法调用的生命周期长，但是比应用程序的生命周期短，如客户机的套接字连接。需要将一些元数据与这个套接字关联，如生成连接的用户的标识。在创建 <code><font face="Courier" size="2">Socket</font></code> 时是不知道这些信息的，并且不能将数据添加到 <code><font face="Courier" size="2">Socket</font></code> 对象上，因为不能控制 <code><font face="Courier" size="2">Socket</font></code> 类或者它的子类。这时，典型的方法就是在一个全局 <code><font face="Courier" size="2">Map</font></code> 中存储这些信息，如清单 1 中的 <code><font face="Courier" size="2">SocketManager</font></code> 类所示： </p><br /><a name="listing1"><b>清单 1. 使用一个全局 Map 将元数据关联到一个对象</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">public class SocketManager {
    private Map&lt;Socket,User&gt; m = new HashMap&lt;Socket,User&gt;();
    
    public void setUser(Socket s, User u) {
        m.put(s, u);
    }
    public User getUser(Socket s) {
        return m.get(s);
    }
    public void removeUser(Socket s) {
        m.remove(s);
    }
}

SocketManager socketManager;
...
socketManager.setUser(socket, user);
</font></code></pre></td></tr></tbody></table><br /><p>这种方法的问题是元数据的生命周期需要与套接字的生命周期挂钩，但是除非准确地知道什么时候程序不再需要这个套接字，并记住从 <code><font face="Courier" size="2">Map</font></code> 中删除相应的映射，否则，<code><font face="Courier" size="2">Socket</font></code> 和 <code><font face="Courier" size="2">User</font></code> 对象将会永远留在 <code><font face="Courier" size="2">Map</font></code> 中，远远超过响应了请求和关闭套接字的时间。这会阻止 <code><font face="Courier" size="2">Socket</font></code> 和 <code><font face="Courier" size="2">User</font></code> 对象被垃圾收集，即使应用程序不会再使用它们。这些对象留下来不受控制，很容易造成程序在长时间运行后内存爆满。除了最简单的情况，在几乎所有情况下找出什么时候 <code><font face="Courier" size="2">Socket</font></code> 不再被程序使用是一件很烦人和容易出错的任务，需要人工对内存进行管理。 </p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="2.0"><span class="atitle"><strong><font size="4">找出内存泄漏</font></strong></span></a></p><p>程序有内存泄漏的第一个迹象通常是它抛出一个 <code><font face="Courier" size="2">OutOfMemoryError</font></code>，或者因为频繁的垃圾收集而表现出糟糕的性能。幸运的是，垃圾收集可以提供能够用来诊断内存泄漏的大量信息。如果以 <code><font face="Courier" size="2">-verbose:gc</font></code> 或者 <code><font face="Courier" size="2">-Xloggc</font></code> 选项调用 JVM，那么每次 GC 运行时在控制台上或者日志文件中会打印出一个诊断信息，包括它所花费的时间、当前堆使用情况以及恢复了多少内存。记录 GC 使用情况并不具有干扰性，因此如果需要分析内存问题或者调优垃圾收集器，在生产环境中默认启用 GC 日志是值得的。 </p><p>有工具可以利用 GC 日志输出并以图形方式将它显示出来，JTune 就是这样的一种工具（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#resources"><font color="#996699">参考资料</font></a>）。观察 GC 之后堆大小的图，可以看到程序内存使用的趋势。对于大多数程序来说，可以将内存使用分为两部分：<i>baseline</i> 使用和 <i>current load</i> 使用。对于服务器应用程序，baseline 使用就是应用程序在没有任何负荷、但是已经准备好接受请求时的内存使用，current load 使用是在处理请求过程中使用的、但是在请求处理完成后会释放的内存。只要负荷大体上是恒定的，应用程序通常会很快达到一个稳定的内存使用水平。如果在应用程序已经完成了其初始化并且负荷没有增加的情况下，内存使用持续增加，那么程序就可能在处理前面的请求时保留了生成的对象。 </p><p>清单 2 展示了一个有内存泄漏的程序。<code><font face="Courier" size="2">MapLeaker</font></code> 在线程池中处理任务，并在一个 <code><font face="Courier" size="2">Map</font></code> 中记录每一项任务的状态。不幸的是，在任务完成后它不会删除那一项，因此状态项和任务对象（以及它们的内部状态）会不断地积累。 </p><br /><a name="listing2"><b>清单 2. 具有基于 Map 的内存泄漏的程序</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">public class MapLeaker {
    public ExecutorService exec = Executors.newFixedThreadPool(5);
    public Map&lt;Task, TaskStatus&gt; taskStatus 
        = Collections.synchronizedMap(new HashMap&lt;Task, TaskStatus&gt;());
    private Random random = new Random();

    private enum TaskStatus { NOT_STARTED, STARTED, FINISHED };

    private class Task implements Runnable {
        private int[] numbers = new int[random.nextInt(200)];

        public void run() {
            int[] temp = new int[random.nextInt(10000)];
            taskStatus.put(this, TaskStatus.STARTED);
            doSomeWork();
            taskStatus.put(this, TaskStatus.FINISHED);
        }
    }

    public Task newTask() {
        Task t = new Task();
        taskStatus.put(t, TaskStatus.NOT_STARTED);
        exec.execute(t);
        return t;
    }
}
</font></code></pre></td></tr></tbody></table><br /><p>图 1 显示 <code><font face="Courier" size="2">MapLeaker</font></code> GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。（在真实的应用程序中，坡度不会这么大，但是在收集了足够长时间的 GC 数据后，上升趋势通常会表现得很明显。） </p><br /><a name="figure1"><b>图 1. 持续上升的内存使用趋势</b></a><br /><img alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/heapsize.gif" /><br /><p>确信有了内存泄漏后，下一步就是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具，但是找出内存泄漏不一定要花钱买这些工具 —— 内置的 <code><font face="Courier" size="2">hprof</font></code> 工具也可完成这项工作。要使用 <code><font face="Courier" size="2">hprof</font></code> 并让它跟踪内存使用，需要以 <code><font face="Courier" size="2">-Xrunhprof:heap=sites</font></code> 选项调用 JVM。 </p><p>清单 3 显示分解了应用程序内存使用的 <code><font face="Courier" size="2">hprof</font></code> 输出的相关部分。（<code><font face="Courier" size="2">hprof</font></code> 工具在应用程序退出时，或者用 <code><font face="Courier" size="2">kill -3</font></code> 或在 Windows 中按 Ctrl+Break 时生成使用分解。）注意两次快照相比，<code><font face="Courier" size="2">Map.Entry</font></code>、<code><font face="Courier" size="2">Task</font></code> 和 <code><font face="Courier" size="2">int[]</font></code> 对象有了显著增加。 </p><p>请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/sidefile1.html"><font color="#5c81a7">清单 3</font></a>。</p><p>清单 4 展示了 <code><font face="Courier" size="2">hprof</font></code> 输出的另一部分，给出了 <code><font face="Courier" size="2">Map.Entry</font></code> 对象的分配点的调用堆栈信息。这个输出告诉我们哪些调用链生成了 <code><font face="Courier" size="2">Map.Entry</font></code> 对象，并带有一些程序分析，找出内存泄漏来源一般来说是相当容易的。 </p><br /><a name="listing4"><b>清单 4. HPROF 输出，显示 Map.Entry 对象的分配点</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">TRACE 300446:
	java.util.HashMap$Entry.&lt;init&gt;(&lt;Unknown Source&gt;:Unknown line)
	java.util.HashMap.addEntry(&lt;Unknown Source&gt;:Unknown line)
	java.util.HashMap.put(&lt;Unknown Source&gt;:Unknown line)
	java.util.Collections$SynchronizedMap.put(&lt;Unknown Source&gt;:Unknown line)
	com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48)
	com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64)
</font></code></pre></td></tr></tbody></table><br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><font face="Lucida Console"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></font></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><font face="Lucida Console"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></font></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="3.0"><span class="atitle"><strong><font size="4">弱引用来救援了</font></strong></span></a></p><p><code><font face="Courier" size="2">SocketManager</font></code> 的问题是 <code><font face="Courier" size="2">Socket</font></code>-<code><font face="Courier" size="2">User</font></code> 映射的生命周期应当与 <code><font face="Courier" size="2">Socket</font></code> 的生命周期相匹配，但是语言没有提供任何容易的方法实施这项规则。这使得程序不得不使用人工内存管理的老技术。幸运的是，从 JDK 1.2 开始，垃圾收集器提供了一种声明这种对象生命周期依赖性的方法，这样垃圾收集器就可以帮助我们防止这种内存泄漏 —— 利用<i>弱引用</i>。 </p><p>弱引用是对一个对象（称为 <i>referent</i>）的引用的持有者。使用弱引用后，可以维持对 referent 的引用，而不会阻止它被垃圾收集。当垃圾收集器跟踪堆的时候，如果对一个对象的引用只有弱引用，那么这个 referent 就会成为垃圾收集的候选对象，就像没有任何剩余的引用一样，而且所有剩余的弱引用都被<i>清除</i>。（只有弱引用的对象称为<i>弱可及（weakly reachable）</i>。） </p><p><code><font face="Courier" size="2">WeakReference</font></code> 的 referent 是在构造时设置的，在没有被清除之前，可以用 <code><font face="Courier" size="2">get()</font></code> 获取它的值。如果弱引用被清除了（不管是 referent 已经被垃圾收集了，还是有人调用了 <code><font face="Courier" size="2">WeakReference.clear()</font></code>），<code><font face="Courier" size="2">get()</font></code> 会返回 <code><font face="Courier" size="2">null</font></code>。相应地，在使用其结果之前，应当总是检查 <code><font face="Courier" size="2">get()</font></code> 是否返回一个非 null 值，因为 referent 最终总是会被垃圾收集的。 </p><p>用一个普通的（强）引用拷贝一个对象引用时，限制 referent 的生命周期至少与被拷贝的引用的生命周期一样长。如果不小心，那么它可能就与程序的生命周期一样 —— 如果将一个对象放入一个全局集合中的话。另一方面，在创建对一个对象的弱引用时，完全没有扩展 referent 的生命周期，只是在<i>对象仍然存活的时候</i>，保持另一种到达它的方法。 </p><p>弱引用对于构造弱集合最有用，如那些在应用程序的其余部分使用对象期间存储关于这些对象的元数据的集合 —— 这就是 <code><font face="Courier" size="2">SocketManager</font></code> 类所要做的工作。因为这是弱引用最常见的用法，<code><font face="Courier" size="2">WeakHashMap</font></code> 也被添加到 JDK 1.2 的类库中，它对键（而不是对值）使用弱引用。如果在一个普通 <code><font face="Courier" size="2">HashMap</font></code> 中用一个对象作为键，那么这个对象在映射从 <code><font face="Courier" size="2">Map</font></code> 中删除之前不能被回收，<code><font face="Courier" size="2">WeakHashMap</font></code> 使您可以用一个对象作为 <code><font face="Courier" size="2">Map</font></code> 键，同时不会阻止这个对象被垃圾收集。清单 5 给出了 <code><font face="Courier" size="2">WeakHashMap</font></code> 的 <code><font face="Courier" size="2">get()</font></code> 方法的一种可能实现，它展示了弱引用的使用： </p><br /><a name="listing5"><b>清单 5. WeakReference.get() 的一种可能实现</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">public class WeakHashMap&lt;K,V&gt; implements Map&lt;K,V&gt; {

    private static class Entry&lt;K,V&gt; extends WeakReference&lt;K&gt; 
      implements Map.Entry&lt;K,V&gt; {
        private V value;
        private final int hash;
        private Entry&lt;K,V&gt; next;
        ...
    }

    public V get(Object key) {
        int hash = getHash(key);
        Entry&lt;K,V&gt; e = getChain(hash);
        while (e != null) {
            K eKey= e.get();
            if (e.hash == hash &amp;&amp; (key == eKey || key.equals(eKey)))
                return e.value;
            e = e.next;
        }
        return null;
    }
</font></code></pre></td></tr></tbody></table><br /><p>调用 <code><font face="Courier" size="2">WeakReference.get()</font></code> 时，它返回一个对 referent 的强引用（如果它仍然存活的话），因此不需要担心映射在 <code><font face="Courier" size="2">while</font></code> 循环体中消失，因为强引用会防止它被垃圾收集。<code><font face="Courier" size="2">WeakHashMap</font></code> 的实现展示了弱引用的一种常见用法 —— 一些内部对象扩展 <code><font face="Courier" size="2">WeakReference</font></code>。其原因在下面一节讨论引用队列时会得到解释。</p><p>在向 <code><font face="Courier" size="2">WeakHashMap</font></code> 中添加映射时，请记住映射可能会在以后“脱离”，因为键被垃圾收集了。在这种情况下，<code><font face="Courier" size="2">get()</font></code> 返回 <code><font face="Courier" size="2">null</font></code>，这使得测试 <code><font face="Courier" size="2">get()</font></code> 的返回值是否为 <code><font face="Courier" size="2">null</font></code> 变得比平时更重要了。 </p><p><a name="3.1"><span class="smalltitle"><strong>用 WeakHashMap 堵住泄漏</strong></span></a></p><p>在 <code><font face="Courier" size="2">SocketManager</font></code> 中防止泄漏很容易，只要用 <code><font face="Courier" size="2">WeakHashMap</font></code> 代替 <code><font face="Courier" size="2">HashMap</font></code> 就行了，如清单 6 所示。（如果 <code><font face="Courier" size="2">SocketManager</font></code> 需要线程安全，那么可以用 <code><font face="Courier" size="2">Collections.synchronizedMap()</font></code> 包装 <code><font face="Courier" size="2">WeakHashMap</font></code>）。当映射的生命周期必须与键的生命周期联系在一起时，可以使用这种方法。不过，应当小心不滥用这种技术，大多数时候还是应当使用普通的 <code><font face="Courier" size="2">HashMap</font></code> 作为 <code><font face="Courier" size="2">Map</font></code> 的实现。 </p><br /><a name="listing6"><b>清单 6. 用 WeakHashMap 修复 SocketManager</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">public class SocketManager {
    private Map&lt;Socket,User&gt; m = new WeakHashMap&lt;Socket,User&gt;();
    
    public void setUser(Socket s, User u) {
        m.put(s, u);
    }
    public User getUser(Socket s) {
        return m.get(s);
    }
}
</font></code></pre></td></tr></tbody></table><br /><p><a name="3.2"><span class="smalltitle"><strong>引用队列</strong></span></a></p><p><code><font face="Courier" size="2">WeakHashMap</font></code> 用弱引用承载映射键，这使得应用程序不再使用键对象时它们可以被垃圾收集，<code><font face="Courier" size="2">get()</font></code> 实现可以根据 <code><font face="Courier" size="2">WeakReference.get()</font></code> 是否返回 <code><font face="Courier" size="2">null</font></code> 来区分死的映射和活的映射。但是这只是防止 <code><font face="Courier" size="2">Map</font></code> 的内存消耗在应用程序的生命周期中不断增加所需要做的工作的一半，还需要做一些工作以便在键对象被收集后从 <code><font face="Courier" size="2">Map</font></code> 中删除死项。否则，<code><font face="Courier" size="2">Map</font></code> 会充满对应于死键的项。虽然这对于应用程序是不可见的，但是它仍然会造成应用程序耗尽内存，因为即使键被收集了，<code><font face="Courier" size="2">Map.Entry</font></code> 和值对象也不会被收集。 </p><p>可以通过周期性地扫描 <code><font face="Courier" size="2">Map</font></code>，对每一个弱引用调用 <code><font face="Courier" size="2">get()</font></code>，并在 get() 返回 <code><font face="Courier" size="2">null</font></code> 时删除那个映射而消除死映射。但是如果 <code><font face="Courier" size="2">Map</font></code> 有许多活的项，那么这种方法的效率很低。如果有一种方法可以在弱引用的 referent 被垃圾收集时发出通知就好了，这就是<i>引用队列</i> 的作用。 </p><p>引用队列是垃圾收集器向应用程序返回关于对象生命周期的信息的主要方法。弱引用有两个构造函数：一个只取 referent 作为参数，另一个还取引用队列作为参数。如果用关联的引用队列创建弱引用，在 referent 成为 GC 候选对象时，这个引用对象（不是 referent）就在引用清除后<i>加入</i> 到引用队列中。之后，应用程序从引用队列提取引用并了解到它的 referent 已被收集，因此可以进行相应的清理活动，如去掉已不在弱集合中的对象的项。（引用队列提供了与 <code><font face="Courier" size="2">BlockingQueue</font></code> 同样的出列模式 —— polled、timed blocking 和 untimed blocking。） </p><p><code><font face="Courier" size="2">WeakHashMap</font></code> 有一个名为 <code><font face="Courier" size="2">expungeStaleEntries()</font></code> 的私有方法，大多数 <code><font face="Courier" size="2">Map</font></code> 操作中会调用它，它去掉引用队列中所有失效的引用，并删除关联的映射。清单 7 展示了 <code><font face="Courier" size="2">expungeStaleEntries()</font></code> 的一种可能实现。用于存储键-值映射的 <code><font face="Courier" size="2">Entry</font></code> 类型扩展了 <code><font face="Courier" size="2">WeakReference</font></code>，因此当 <code><font face="Courier" size="2">expungeStaleEntries()</font></code> 要求下一个失效的弱引用时，它得到一个 <code><font face="Courier" size="2">Entry</font></code>。用引用队列代替定期扫描内容的方法来清理 <code><font face="Courier" size="2">Map</font></code> 更有效，因为清理过程不会触及活的项，只有在有实际加入队列的引用时它才工作。 </p><br /><a name="listing7"><b>清单 7. WeakHashMap.expungeStaleEntries() 的可能实现</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">    private void expungeStaleEntries() {
	Entry&lt;K,V&gt; e;
        while ( (e = (Entry&lt;K,V&gt;) queue.poll()) != null) {
            int hash = e.hash;

            Entry&lt;K,V&gt; prev = getChain(hash);
            Entry&lt;K,V&gt; cur = prev;
            while (cur != null) {
                Entry&lt;K,V&gt; next = cur.next;
                if (cur == e) {
                    if (prev == e)
                        setChain(hash, next);
                    else
                        prev.next = next;
                    break;
                }
                prev = cur;
                cur = next;
            }
        }
    }
</font></code></pre></td></tr></tbody></table><br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><font face="Lucida Console"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></font></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><font face="Lucida Console"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></font></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="4.0"><span class="atitle"><strong><font size="4">结束语</font></strong></span></a></p><p>弱引用和弱集合是对堆进行管理的强大工具，使得应用程序可以使用更复杂的可及性方案，而不只是由普通（强）引用所提供的“要么全部要么没有”可及性。下个月，我们将分析与弱引用有关的<i>软引用</i>，将分析在使用弱引用和软引用时，垃圾收集器的行为。 </p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="resources"><span class="atitle"><strong><font size="4">参考资料 </font></strong></span></a></p><strong>学习</strong><br /><ul><li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www-128.ibm.com/developerworks/java/library/j-jtp11225/" target="_blank"><font color="#5c81a7">英文原文</font></a><br /><br /></li><li>“<a href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/"><font color="#5c81a7">关注性能: 调优垃圾收集</font></a>”：Kirk Pepperdine 和 Jack Shirazi 展示了缓慢的内存泄漏最终对于垃圾收集器造成了无法承受的压力。<br /><br /></li><li>“<a href="http://java.sun.com/developer/technicalArticles/Programming/HPROF.html"><font color="#5c81a7">HPROF</font></a>”：Sun 的这一篇文章描述了如何使用内置的 HPROF 分析工具。<br /><br /></li><li><a href="http://java.sun.com/developer/technicalArticles/ALT/RefObj/"><font color="#5c81a7">Reference objects and garbage collection</font></a>：Sun 的这篇文章是在 Reference 对象刚加入类库不久写的，描述了垃圾收集器如何处理 Reference 对象。<br /><br /></li><li><a href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=Java+%E7%90%86%E8%AE%BA%E4%B8%8E%E5%AE%9E%E8%B7%B5%EF%BC%9A"><i><font color="#5c81a7">Java 理论与实践</font></i></a>：Brian Goetz 所写的全部系列文章。<br /><br /></li><li><a href="http://www.ibm.com/developerworks/cn/java/"><font color="#5c81a7">Java 技术专区</font></a>：数百篇关于 Java 编程各个方面的文章。<br /><br /></li></ul><br /><b>获得产品和技术</b><br /><ul><li><a href="http://www.hp.com/products1/unix/java/java2/hpjtune/"><font color="#5c81a7">JTune</font></a>：免费 JTune 工具，可以使用 GC 日志并以图形方式显示堆大小、GC 持续时间和其他有用的内存管理数据。<br /><br /></li></ul><br /><b>讨论</b><br /><ul><li>加入本文的<a href="javascript:void forumWindow()"><font color="#5c81a7">论坛</font></a> 。(您也可以通过点击文章顶部或者底部的论坛链接参加讨论。)<br /><br /></li><li><a href="http://www.ibm.com/developerworks/blogs/"><font color="#5c81a7">developerWorks blogs</font></a>：参与 developerWorks 社区。</li></ul><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11225/#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="author"><span class="atitle"><strong><font size="4">关于作者</font></strong></span></a></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td colspan="2"><strong><font size="4"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /></font></strong></td></tr><tr valign="top" align="left"><td><p><strong><font size="4"></font></strong></p></td><td><p><a href="mailto:brian@quiotix.com"><font color="#5c81a7">Brian Goetz</font></a> 成为专业软件开发人员已经超过 18 年了。他是 <a href="http://www.quiotix.com/"><font color="#5c81a7">Quiotix</font></a> 的首席顾问，该公司是位于加利福尼亚 Los Altos 的软件开发和咨询公司。他参加了几个 JCP 专家组。Brian 的 <a href="http://www.amazon.com/exec/obidos/ASIN/0321349601/ref=nosim/none0b69"><i><font color="#5c81a7">Java Concurrency In Practice</font></i></a> 一书将于 2005 年末由 Addison-Wesley 出版。请在业界流行的出版物上查阅 Brian <a href="http://www.briangoetz.com/pubs.html"><font color="#5c81a7">已发表的和即将发表的文章</font></a>。 </p></td></tr></tbody></table><br /><img src ="http://www.blogjava.net/TrampEagle/aggbug/30266.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-11 15:21 <a href="http://www.blogjava.net/TrampEagle/articles/30266.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java EE5到底有什么系列 – Java Persistence API 1.0( EJB3 Entity Bean)</title><link>http://www.blogjava.net/TrampEagle/articles/30265.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 11 Feb 2006 07:13:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30265.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30265.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30265.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30265.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30265.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.redsoftfactory.com/chinese/resources/jee5jpa.html">http://www.redsoftfactory.com/chinese/resources/jee5jpa.html</A><BR><BR><BR>作者：黄海波(Charles Huang) <BR><BR>Java EE5作为新一代Java企业开发平台的规范，从开始设计就引来了整个java开发社区的注目，引起无数的辩论和带来了众多的期盼。Java EE5作为J2EE平台诞生几近6年后的第4代规范重点关注的是目前java开发的几个热点：开发效率，运行效率和企业应用整合。目标也是让J2EE开发简单，简单再简单。那我们就看看J2EE5规范到底有什么，是否真的能给开发者/企业带来真正的实惠？<BR><BR>Java EE5规范是一个所谓的雨伞规范(Umbrella)，在其下是一系列的子规范,主要包括： <BR><BR>
<DIV style="BORDER-RIGHT: 1px solid; BORDER-TOP: 1px solid; BORDER-LEFT: 1px solid; BORDER-BOTTOM: 1px solid">
<TABLE class=txt>
<TBODY>
<TR>
<TD vAlign=top>EJB 3.0 (JSR 220)<BR>Java Persistence API 1.0 (JSR 220)<BR>JSP 2.1 (JSR 245)<BR>JSF 1.2 (JSR 252) <BR>JAX-WS 2.0 (JSR 224)<BR>StAX 1.0 (JSR 173)<BR>JAXB 2.0 (JSR 222)<BR>Web Services Annotations 1.0 (JSR 181)<BR>Common Annotations 1.0 (JSR 250)<BR>SAAJ 1.3 maintenance</TD>
<TD vAlign=top>JTA 1.1 maintenance<BR>JavaMail 1.4 &amp; JAF 1.1 maintenance<BR>JSTL 1.2 maintenance<BR>Java EE Mgmt maintenance<BR>JACC maintenance<BR>Servlet maintenance<BR>Java EE Deployment maintenance<BR>WSEE maintenance</TD></TR></TBODY></TABLE></DIV><BR><BR>Java Persistence API 1.0( EJB3 Entity Bean) 在Java EE5中, Entity Bean做为EJB规范中负责持久化的组件将逐渐成为一个历史名词了，作为J2EE 4规范中最为人所垢病的Entity Bean在Java EE5中被推到重来，取而代之的是java开发的通用持久化规范Java Persistence API 1.0, 其实就是完全重新定义了的Entity Bean规范（目前在很多场合中，由于历史原因我们仍然使用ejb3持久化来称呼这个规范）。JPA作为java中负责关系数据持久化的组件已经完全独立出来成为一个单独的规范，而不再属于Enterprise Java Bean的范畴(EJB更多的是指Stateless/Stateful session bean和Message Driven Bean)。<BR><BR>Java Persistence AP(JPA)可以说是java持久化技术的一个集大成者，它吸取了Hiberante,JDO,TopLink等优秀技术和框架，将这几年发展成熟起来的基于POJO模型的O/R Mapping技术标准化，成为在J2EE和J2SE环境中通用的java持久化API。值得注意的是Java Persistence API并不是J2EE环境专用，而是在java中的通用API。意味着我们可以在任何需要访问关系数据库的地方使用JPA，甚至包括swing开发的桌面应用。JPA也不要求一定在J2EE容器中才能运行，而是任何有JVM的环境都可以运用。 这就使得我们可以很容易的把JPA作为一个持久化组件自由的和各种容器／框架（EJB3容器, Spring等等）组合。 <BR><BR>JPA如何简化原来EJB2中Entity Bean的开发，看一个简单对比：<BR>
<TABLE class=txt border=1>
<TBODY>
<TR>
<TD vAlign=top></TD>
<TD vAlign=top>EJB2.0</TD>
<TD>EJB3.0(JPA)</TD></TR>
<TR>
<TD>Business Interface</TD>
<TD><PRE>public inerface HelloWold extends EJBLocalObject{
    Public String getResult();
}
						</PRE></TD>
<TD>无需定义接口</TD></TR>
<TR>
<TD>映射配置文件</TD>
<TD>编写EJB3 Deployment descriptor</TD>
<TD>可选</TD></TR>
<TR>
<TD>EJB实现</TD>
<TD vAlign=top><PRE>public class HelloWorldEntityBean 
         implements HelloWold, EntityBean{
    private int id;
    private String result;
    private EntityContext txt;
   
    public HelloWorldEntityBean(){}
    public void setEntityContext( EntityContext text ){ 
        txt = text;
    }
   
    public String getResult(){ 
        Return result; 
    }
        
    public int getId(){
        return id; 
    }
    
    public void setResult( String result ){ 
        this.result = result;
    }
    
    public String cretaeByName( String name ) throws EJBException{
        .....
    }
   
 }						
						</PRE></TD>
<TD vAlign=top><PRE>@Entity
@Table(name=”hellotable”)
public class HelloWoldEntity{   
    @Id
    private int id; p
    private String result; 
    
    public HelloWoldEntity(){} 
    
    public String getResult(){ 
        return result;
    }
    
    public int getId(){
        return id;
    }  
    
    public void setResult( String result ){ 
        this.result = result;
    }
}						
						</PRE></TD></TR></TBODY></TABLE><BR>在JPA 中，ejb3的Entity Bean就是一个简单的java bean，即POJO( Plain Old Java Object)。不象EJB2中的EntityBean需要跟容器有密切的关联（EJB2中必须有EntityContext），EJB3 中的entityBean和容器无关，事实上在JPA中，EntityBean也不再称为EntityBean,而是Entity，和Session Bean/Message Driven Bean的仍然存在的EJB区别开来。 <BR><BR>为了简化O/R Mapping的配置，JPA大量采用JDK1.5的最重要的新特性annotaion直接在java代码中进行配置的标注。 采用annotation标注O/R Mapping配置可以大幅度减少以往使用xml配置O/R Mapping工作量，提高效率和可维护性。 <BR><BR>下面是一个最简单的一对一关联关系采用annotation和xml的配置比较。<BR><IMG src="http://www.redsoftfactory.com/chinese/images/one2one.jpg"> 
<TABLE class=txt border=1>
<TBODY>
<TR>
<TD></TD>
<TD>Java Persistence API(EJB3 Persistence)</TD>
<TD>Hiberante</TD></TR>
<TR>
<TD>配置文件</TD>
<TD>可选</TD>
<TD>需要</TD></TR>
<TR>
<TD>One-To-One配置</TD>
<TD>可选</TD>
<TD><PRE>&lt;one-to-one        
    name="address"        
    class="com.foo.Address"        
    cascade="All"        
    lazy="false"/&gt;
</PRE></TD></TR>
<TR>
<TD>Java代码</TD>
<TD><PRE>public class Order{     
	@OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZYL)    
	Address address;
    ......	
}
						</PRE></TD>
<TD><PRE>public class Order{    
    Address address;
    ......   
}
</PRE></TD></TR></TBODY></TABLE>采用annotation的优势在于： 
<UL>
<LI>减少了配置文件的数量，特别是在实体(Entity)比较多的系统中，维护大量的O/R Mapping xml配置文件是不少的工作量。 
<LI>减少了配置需要标注的内容。由于annotation由java compiler来编译解析，很多需要在xml配置中显式声明的内容不再需要（比如变量名称，类型，集合中的对象类型等）。 
<LI>Annotation的编译期检查可以避免xml文件中容易出现的配置语法错误，在IDE中及时发现和纠正。 
<LI>无需在xml配置文件和java代码中切换，较少思维的跳跃，提高了开发效率。 
<LI>annotation被编译到java bytecode中，省略了xml的解析过程，极大的提升应用的启动速度和内存占用（特别是Entity多的情况）。 </LI></UL>JPA在启动上做了很多程度的简化，使我们能够很容易地在容器内(container)和J2SE环境中使用JPA。JPA拥有一个最基本的工厂类EntityManagerFactory。通过调用这个工厂类的createEntityManager()方法获得EntityManager。所有对实体(Entity)的操作包括持久化，查询，删除等等操作都都定义EntityManager上。 
<DIV style="BORDER-RIGHT: 1px solid; BORDER-TOP: 1px solid; BORDER-LEFT: 1px solid; BORDER-BOTTOM: 1px solid"><PRE>public interface EntityManager {
    public void persist(Object entity);
    public <T> T merge(T entity);
    public void remove(Object entity);
    public <T> T find(Class<T> entityClass, Object primaryKey);
    public <T> T getReference(Class<T> entityClass, Object primaryKey);
    public void flush();
    public void setFlushMode(FlushModeType flushMode);
    public FlushModeType getFlushMode();
    public void lock(Object entity, LockModeType lockMode);
    public void refresh(Object entity);
    public void clear();
    public boolean contains(Object entity);
    public Query createQuery(String ejbqlString);
    public Query createNamedQuery(String name);
    public Query createNativeQuery(String sqlString);
    public Query createNativeQuery(String sqlString, Class result-
    Class);
    public Query createNativeQuery(String sqlString, String result-
    SetMapping);
    public void close();
    public boolean isOpen();
    public EntityTransaction getTransaction();
}
				
				</PRE></DIV><BR>那又如何获得EntityManagerFactory呢？不管是在J2EE或者J2SE中，都需要通过一个persistence.xml配置文件对EntityMangaerFactory进行配置。下面是一个最简单的persistence.xml的范例。 <BR>
<DIV style="BORDER-RIGHT: 1px solid; BORDER-TOP: 1px solid; BORDER-LEFT: 1px solid; BORDER-BOTTOM: 1px solid"><PRE>&lt;entity-manager&gt;
    &lt;name&gt;myEntityManager&gt;/name&gt;
    &lt;provider&gt;com.redsoft.ejb3.PersistenceProviderImpl&gt;/provider&gt;
    &lt;class&gt;com.redsoft.samples.HelloEntityBean&gt;/class&gt;
    &lt;properties&gt;
        &lt;property name="ConnectionDriverName" value="com.mysql.jdbc.Driver"/&gt;
        &lt;property name="ConnectionURL" value="jdbc:mysql://localhost/EJB3Test"/&gt;
        &lt;property name="ConnectionUserName" value="ejb3"/&gt;
        &lt;property name="ConnectionPassword" value="ejb3"/&gt;
   &gt;/properties&gt;
&lt;/entity-manager&gt;

}
				
				</PRE></DIV><BR>name – 定了当前这个EntityMangaerFactory的名字，我们可以在一个persistence.xml中定义多个EntityManagerFactory。 <BR><BR>Provider – 定了提供EntityManagerFactory的具体实现类。这个实现类由不同的持久化产品开发商提供。例子中采用的是国产红工场的ejb3持久化实现的 EntityManagerFactory实现类。如如果我们需要更换成其他厂商的产品，就需要更换具体的实现类。 <BR><BR>class – 列出所有需要被JPA管理的实体类。为了保证在J2SE/J2EE中的通用性和可移植性，JPA要求这里必须列出所有被JPA管理的实体类。 <BR><BR>properties – 由持久化厂商自行定义的属性。<BR><BR>如果使用JTA事务，也可以使用 <JTA-DATA-SOURCE>myDataSource</ jta-data-source>来定义。<BR><BR>在J2EE容器环境中和J2SE环境中，都是通过读取这个配置文件来初始化EntityMangaerFactory。在J2EE容器环境下，ejb3容器负责读取persistence.xml并初始化EntityManagerFactory，并将EntityManagerFactory帮定到JDNI中，这样我们就可以通过访问JNDI获得EntityManagerFactory, 进而获得EntityManager。由于EJB3容器支持IOC模式，我们也可以通过IOC将EntityMangerFactory直接注射给需要的使用JPA持久化的java类。通过IOC注射的方式获得EntityManagerFactory或者EntityManager是更方便，合理和推荐的方式。 <BR><BR>而在J2SE环境中，我们可以通过标准的javax.persistence.Persistence类来获得EntityManagerFactory。Javax.persistence.Persistence会在当前classpath或者jar包的META-INF/下搜索并读persistence.xml后初始化EntityManagerFactory。 <BR><BR>下面是一个简单的示例如何在J2SE环境中获得EntityManagerFactory并获得EntityManager,运用EntityManager持久化HelloWorldEntityBean. 
<DIV style="BORDER-RIGHT: 1px solid; BORDER-TOP: 1px solid; BORDER-LEFT: 1px solid; BORDER-BOTTOM: 1px solid"><PRE>public class HelloWorld {

    public static void main( final String[] args ){

        /*
         * Obtain an EJB3 entity manager
         */
        final EntityManagerFactory emf = Persistence.createEntityManagerFactory();
        final EntityManager entityManager = emf.createEntityManager();

        // Construct a HelloEntityBean
        final HelloEntityBean hello = new HelloEntityBean( 1, "foo" );
        EntityTransaction trans = entityManager.getTransaction();
        trans.begin();
        entityManager.persist( hello );
        trans.commit();
        System.out.println( "Successfully persist hello: " + hello );

        // Look up the HelloEntityBean by primarky key
        final HelloEntityBean anotherHello = entityManager.find( HelloEntityBean.class, new Integer( hello.getId() ) );
        System.out.println( "Found hello: " + anotherHello );

        // close the EntityManager
        entityManager.close();
        emf.close();
    }
}
				</PRE></DIV>事实上不管是在J2SE还是J2EE中我们都可以这样通过javax.persistence.Persistence来初始化EntityManagerFactory。 <BR><BR>在上面HelloWorld的例子中我们需要显式调用javax.persistence.Persistence.createEntityManagerFactory, 并且显式地开始事务和关闭事务。在今天大量使用IOC托管容器的时代，这样的编码已经显得落后。 <BR><BR>作为J2EE一个部分的JPA自然可以利用EJB3的IOC容器托管事务和注射资源，同样的也可以使用开源IOC容器spring来托管事务和注射资源。红工场也提供了一个开源的<A href="http://sourceforge.net/projects/ejb3daosupport">spring DAO扩展 http://sourceforge.net/projects/ejb3daosupport </A>是来支持JPA和Spring的结合。 <BR><BR>下面是一个如何在Spring中托管事务和在DAO中注入EntityManager的配置范例：<BR>
<DIV style="BORDER-RIGHT: 1px solid; BORDER-TOP: 1px solid; BORDER-LEFT: 1px solid; BORDER-BOTTOM: 1px solid"><PRE>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "file://spring-beans.dtd"&gt;

&lt;beans&gt;
	&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt;
		&lt;property name="driverClassName"&gt;&lt;value&gt;com.mysql.jdbc.Driver&lt;/value&gt;&lt;/property&gt;
		&lt;property name="url"&gt;&lt;value&gt;jdbc:mysql://localhost/EJB3Test&lt;/value&gt;&lt;/property&gt;
		&lt;property name="username"&gt;&lt;value&gt;ejb3&lt;/value&gt;&lt;/property&gt;
		&lt;property name="password"&gt;&lt;value&gt;ejb3&lt;/value&gt;&lt;/property&gt;
	&lt;/bean&gt;
	&lt;bean id="entityManagerFactory"
		class="org.springframework.orm.ejb3.LocalEntityManagerFactoryBean"&gt;
		&lt;property name="persistenceInfo"&gt;&lt;ref local="persistenceInfo"/&gt;&lt;/property&gt;
		
	&lt;/bean&gt;
	&lt;bean id="persistenceInfo" class="com.redsoft.ejb3.PersistenceInfoImpl"&gt;
		&lt;property name="nonJtaDataSource"&gt;&lt;ref local="dataSource"/&gt;&lt;/property&gt;
		&lt;property name="entityManagerName"&gt;&lt;value&gt;myEntityManager&lt;/value&gt;&lt;/property&gt;
		&lt;property name="persistenceProviderClassName"&gt;
			&lt;value&gt;
				com.redsoft.ejb3.PersistenceProviderImpl
			&lt;/value&gt;
		&lt;/property&gt;
		&lt;property name="entityClassNames"&gt;
		&lt;list&gt;
			&lt;value&gt;com.redsoft.ejb3.spring.Child&lt;/value&gt;
			&lt;value&gt;com.redsoft.ejb3.spring.Father&lt;/value&gt;
		&lt;/list&gt;
		&lt;/property&gt;
		&lt;property name="properties"&gt;
			&lt;props&gt;
			&lt;prop key="javax.jdo.PersistenceManagerFactoryClass"&gt;
				com.redsoft.jdo.PersistenceManagerFactoryImpl
			&lt;/prop&gt;
			&lt;/props&gt;
		&lt;/property&gt;
	&lt;/bean&gt;
	&lt;bean id="transactionManager" class="org.springframework.orm.ejb3.EJB3TransactionManager"
		singleton="true"&gt;
		&lt;property name="entityManagerFactory"&gt;
			&lt;ref local="entityManagerFactory" /&gt;
		&lt;/property&gt;
	&lt;/bean&gt;
	
	&lt;bean id="dao"
		class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
		singleton="true"&gt;
		&lt;property name="transactionManager"&gt;
			&lt;ref local="transactionManager" /&gt;
		&lt;/property&gt;
		&lt;property name="target"&gt;
			&lt;bean class="com.redsoft.ejb3.spring.DAOImpl"&gt;
				&lt;property name="entityManagerFactory"&gt;
					&lt;ref local="entityManagerFactory" /&gt;
				&lt;/property&gt;
			&lt;/bean&gt;
		&lt;/property&gt;
		&lt;property name="transactionAttributes"&gt;
			&lt;props&gt;
				&lt;prop key="save*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
				&lt;prop key="remove*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
				&lt;prop key="del*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
				&lt;prop key="update*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
				&lt;prop key="get*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;
				&lt;prop key="find*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;
				&lt;prop key="query*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;
			&lt;/props&gt;
		&lt;/property&gt;
	&lt;/bean&gt;
&lt;/beans&gt;

				</PRE></DIV>文中的例子可以从红工场主页下载 <A href="http://www.redsoftfactory.com/chinese/index.html">http://www.redsoftfactory.com/chinese/index.html</A><BR><img src ="http://www.blogjava.net/TrampEagle/aggbug/30265.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-11 15:13 <a href="http://www.blogjava.net/TrampEagle/articles/30265.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 Groovy 生成器作标记</title><link>http://www.blogjava.net/TrampEagle/articles/30035.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 09 Feb 2006 06:51:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30035.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30035.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30035.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30035.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30035.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr valign="top">
								<td width="100%">
										<h1>
												<span style="COLOR: #999999">
														<font size="4">引自：</font>
														<a href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/">
																<font size="4">http://www-128.ibm.com/developerworks/cn/java/j-pg04125/</font>
														</a>
												</span>
										</h1>
										<h1>
												<span style="COLOR: #999999">
												</span> </h1>
										<h1>
												<span style="COLOR: #999999">实战 Groovy: </span>用 Groovy 生成器作标记</h1>
										<p id="subtitle">抛开标记语言的细节，聚焦应用程序的内容</p>
										<img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" />
								</td>
								<td class="no-print" width="192">
										<img height="18" alt="developerWorks" src="http://www-128.ibm.com/developerworks/i/dw.gif" width="192" />
								</td>
						</tr>
				</tbody>
		</table>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr valign="top">
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
								</td>
								<td width="100%">
										<table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0">
												<tbody>
														<tr>
																<td width="10">
																		<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
																</td>
																<td>
																		<table cellspacing="0" cellpadding="0" width="150" border="0">
																				<tbody>
																						<tr>
																								<td class="v14-header-1-small">文档选项</td>
																						</tr>
																				</tbody>
																		</table>
																		<table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td class="no-padding" width="150">
																										<table cellspacing="0" cellpadding="0" width="143" border="0">
																												<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" />
																												<form name="email" action="https://www-130.ibm.com/developerworks/secure/email-it.jsp">
																														<input type="hidden" value="Groovy 生成器让您能够利用诸如 Swing 这样的框架来模拟标记语言（如 XML、HTML、Ant） 任务以及 GUI。它们对于快速原型化非常有&#xD;&#xA;用，并且正像 Andrew Glover 这个月在“实战 Groovy”专栏中向您展示的那样，当您马上需要可消费的标记时，它们是数据绑定框架的一种便利的替代方案。" name="body" />
																														<input type="hidden" value="实战 Groovy: 用 Groovy 生成器作标记" name="subject" />
																														<input type="hidden" value="cn" name="lang" />
																														<script language="JavaScript" type="text/javascript">
																																<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
																														</script>
																														<tbody>
																																<tr valign="top">
																																		<td width="8">
																																				<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" />
																																		</td>
																																		<td width="16">
																																				<img height="16" alt="将此页作为电子邮件发送" src="http://www.ibm.com/i/v14/icons/em.gif" width="16" vspace="3" />
																																		</td>
																																		<td width="122">
																																				<p>
																																						<a class="smallplainlink" href="javascript:document.email.submit();">
																																								<b>
																																										<font color="#5c81a7" size="2">将此页作为电子邮件发送</font>
																																								</b>
																																						</a>
																																				</p>
																																		</td>
																																</tr>
																																<noscript>
																																		<tr valign="top">
																																				<td width="8">
																																						<img height="1" alt="" src="//www.ibm.com/i/c.gif" width="8" />
																																				</td>
																																				<td width="16">
																																						<img height="16" alt="" src="//www.ibm.com/i/c.gif" width="16" />
																																				</td>
																																				<td class="small" width="122">
																																						<p>
																																								<span class="ast">未显示需要 JavaScript 的文档选项</span>
																																						</p>
																																				</td>
																																		</tr>
																																</noscript>
																														</tbody>
																												</form>
																										</table>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<table cellspacing="0" cellpadding="0" width="150" border="0">
												<tbody>
														<tr>
																<td class="v14-header-1-small">对此页的评价</td>
														</tr>
												</tbody>
										</table>
										<table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td class="no-padding" width="150">
																		<table cellspacing="0" cellpadding="0" width="143" border="0">
																				<tbody>
																						<tr valign="top">
																								<td width="8">
																										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" />
																								</td>
																								<td>
																										<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width="16" vspace="3" border="0" />
																								</td>
																								<td width="125">
																										<p>
																												<a class="smallplainlink" href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/#rate">
																														<b>
																																<font color="#996699" size="2">帮助我们改进这些内容</font>
																														</b>
																												</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<p>级别: 初级</p>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/#author">
						<font color="#996699">Andrew Glover</font>
				</a>, CTO, Vanward Technologies<br /></p>
		<p>2005 年 4 月 12 日</p>
		<blockquote>Groovy 生成器让您能够利用诸如 Swing 这样的框架来模拟标记语言（如 XML、HTML、Ant） 任务以及 GUI。它们对于快速原型化非常有用，并且正像 Andrew Glover 这个月在“<i>实战 Groovy</i>”专栏中向您展示的那样，当您马上需要可消费的标记时，它们是数据绑定框架的一种便利的替代方案。</blockquote>
		<p>几个月前，当我最初撰写有关 <a href="http://www-128.ibm.com/developerworks/cn/java/j-pg12144.html"><font color="#5c81a7">实战 Groovy: 用 Groovy 进行 Ant 脚本编程</font></a> 的文章时，我提及了 Groovy 中的<i>生成器</i> 概念。在那篇文章里，我向您展示了，使用一个叫做 <code>AntBuilder</code> 的 Groovy 类，构建富有表现力的 Ant 构建文件是多么容易。本文中，我将深入 Groovy 生成器的世界，向您展示您还能用这些强大的类做些什么。</p>
		<p>
				<a name="N10060">
						<span class="atitle">
								<font face="Arial" size="4">用生成器进行构建</font>
						</span>
				</a>
		</p>
		<p>Groovy 生成器让您能够利用诸如 Swing 这样的框架来模拟标记语言（如 XML、HTML、Ant） 任务以及 GUI。使用生成器，您可以迅速地创建复杂的标记（如 XML），而无须理会 XML 本身。</p>
		<p>生成器的范例非常简单。生成器的实例的方法表示该标记（如 HTML 中的 <code>&lt;body&gt;</code> 标签）的元素。方法的创建于闭包中的对象表示子节点（例如，<code>&lt;body&gt;</code> 标签中所包含的 <code>&lt;p&gt;</code> 标签）。</p>
		<p>为了便于您查看这一过程，我将创建一个简单的生成器，以程序方式来表示一个具有清单 1 所示结构的 XML 文档。</p>
		<br />
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
				<tbody>
						<tr>
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
								</td>
								<td>
										<table cellspacing="0" cellpadding="5" width="100%" border="1">
												<tbody>
														<tr>
																<td bgcolor="#eeeeee">
																		<a name="N10083">
																				<b>关于本系列文章</b>
																		</a>
																		<br />
																		<p>在开发实践中采用任何工具的关键是，了解何时使用这些工具，何时将其弃而不用。脚本语言可能是对工具包的一个功能强大的扩充，但是只有在正确应用于适当的环境时才是如此。总之，<a href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=%E5%AE%9E%E6%88%98+Groovy%3A"><font color="#5c81a7"><i>实战 Groovy</i></font></a>系列文章旨在展示 Groovy 的实际使用，以及何时和如何成功应用它。</p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<a name="code1">
				<b>清单 1. 简单 XML 结构</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="600" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console"> &lt;person&gt;
   &lt;name first="Megan" last="Smith"&gt;
     &lt;age&gt;32&lt;/age&gt;
     &lt;gender&gt;female&lt;/gender&gt;
   &lt;/name&gt;
   &lt;friends&gt;
     &lt;friend&gt;Julie&lt;/friend&gt;
     &lt;friend&gt;Joe&lt;/friend&gt;
     &lt;friend&gt;Hannah&lt;/friend&gt;
   &lt;/friends&gt;
 &lt;/person&gt;
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>要表示这个结构非常简单。首先将 <code>person</code> 方法连接到生成器实例，现在它表示 XML 的根节点，即 <code>&lt;person&gt;</code>。要创建子节点，我创建一个闭包并声明一个名叫 <code>name</code> 的新对象（它接收 <code>map</code> 形式的参数。顺便说一下，这些参数是元素的属性的基础。</p>
		<p>接下来，在 <code>name</code> 对象中，将两个附加对象连接到闭包，一个对象是 <code>age</code>，另一个是 <code>gender</code>，它们对应于 <code>&lt;name&gt;</code> 的类似子元素。您明白其中的诀窍了么？确实很简单。</p>
		<p>
				<code>&lt;friends&gt;</code> 元素是 <code>&lt;person&gt;</code> 的兄弟元素，于是我跳出这个闭包，声明了一个 <code>friends</code> 对象，当然，还附加了一个集合了多个 <code>friend</code> 元素的闭包，如清单 2 所示。</p>
		<br />
		<a name="code1">
				<b>清单 2. 生成器是如此的简单</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">import groovy.xml.*
import java.io.*
class XMLBuilder{
  
  static void main(args) {
  
    writer = new StringWriter()	 	
    builder = new MarkupBuilder(writer)
    friendnames = [ "Julie", "Joey", "Hannah"]
    
	builder.person() {
       name(first:"Megan", last:"Smith") {
         age("33")
         gender("female")
       }
       friends() {
         for (e in friendnames) { friend(e) }
       }
    }
    println writer.toString()
  }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如您所见，这里的 Groovy 表示非常优雅，且易于映射到相应的标记表示。在底层，Groovy 显然在处理烦人的标记元素（如 &lt; and &gt;），使我们可以将更多精力放在内容上，而不必过分在意结构的细节。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100E6">
						<span class="atitle">
								<font face="Arial" size="4">显示 HTML</font>
						</span>
				</a>
		</p>
		<p>生成器也可以有助于构建 HTML，这在开发 Groovlet 时可以派上用场。如同小菜一碟，假设我要创建一个如清单 3 所示的 HTML 页面。</p>
		<br />
		<a name="code1">
				<b>清单 3. HTML 101</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console"> &lt;html&gt;
  &lt;head&gt;
   &lt;title&gt;Groov'n with Builders&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
   &lt;p&gt;Welcome to Builders 101. As you can see this Groovlet is fairly simple.&lt;/p&gt;
  &lt;/body&gt;
 &lt;/html&gt;
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>我可以轻易地将它编码在 Groovy 中，清单 4 所示。</p>
		<br />
		<a name="code1">
				<b>清单 4. HTML in Groovy 101 </b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">import groovy.xml.*
import java.io.*
class HTMLBuilderExample{
  
  static void main(args) {
   writer = new StringWriter()	 	
   builder = new MarkupBuilder(writer)
  
   builder.html(){
     head(){
       title("Groov'n with Builders"){}
     }
     body(){
       p("Welcome to Builders 101. As you can see " +
         "this Groovlet is fairly simple.") 
     }
   }
   println writer.toString()
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>来点有意思的，让我们再看看用生成器建立一个成熟的 GUI 有多么容易。前面我曾提到过，Groovy 的 <code>SwingBuilder</code> 使它能够以一种极为简单的方式构造 GUI。您可以查阅清单 5 中 <code>SwingBuilder</code> 是如何工作的。</p>
		<br />
		<a name="code1">
				<b>清单 5. Groovy 中的 GUI 生成器真的很“GROOVY”（很“棒”）</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">import java.awt.FlowLayout
import javax.swing.*
import groovy.swing.SwingBuilder
class SwingExample{
  
  static void main(args) {
    swinger = new SwingBuilder()
    langs = ["Groovy", "Ruby", "Python", "Pnuts"]
  
	gui = swinger.frame(title:'Swinging with Groovy!', size:[290,100]) {
      panel(layout:new FlowLayout()) {
        panel(layout:new FlowLayout()) {
          for (lang in langs) {
            checkBox(text:lang)
          }
        }
        button(text:'Groovy Button', actionPerformed:{ 
          swinger.optionPane(message:'Indubitably Groovy!').createDialog(null, 'Zen Message').show()
        })
        button(text:'Groovy Quit', actionPerformed:{ System.exit(0)})
     }
   }
   gui.show()
   }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>图 1 显示了上面的结果，还不错吧？</p>
		<br />
		<a name="fig1">
				<b>图 1. Groovy 中神奇的 GUI 编程</b>
		</a>
		<br />
		<img height="101" alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/sgui1.gif" width="294" border="0" />
		<br />
		<p>可以想像，对于原型化，<code>SwingBuilder</code> 是一个多么强大的工具，不是么？</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N1013A">
						<span class="atitle">
								<font face="Arial" size="4">一些事实</font>
						</span>
				</a>
		</p>
		<p>这些例子虽然琐碎，却也有趣。我希望我能让您明白，Groovy 的生成器可以让您避免特定语言（如 XML）中的底层标记。显然，有时避免 XML 或 HTML 会更好，并且，那些标记协助器（facilitator）对 Java 平台来说并不陌生。例如，我最喜欢的 XML 协助框架是 JiBX。</p>
		<p>使用 JiBX，您可以轻易地将 XML 结构映射到对象模型，<i>反之亦然</i>。绑定是个强大的范例，有不计其数的类似工具拥有此功能，如 JAXB、 Castor 和 Zeus 等。</p>
		<p>绑定框架的惟一缺点是，它们<i>恐怕</i> 要耗费不少时间。幸运的是，您可以使用 Groovy 的生成器作为一个<i>较简单的</i> 解决方案，这在某些情况下是有效的。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10152">
						<span class="atitle">
								<font face="Arial" size="4">用生成器进行伪绑定</font>
						</span>
				</a>
		</p>
		<p>假设有一个英文词典的简单数据库。有一个表用于 <code>word</code>，另一个表用于 <code>definition </code>，最后还有一个表用于 <code>synonym</code>。图 2 是这个数据库的简单表示。</p>
		<br />
		<a name="fig2">
				<b>图 2. 词典数据库</b>
		</a>
		<br />
		<img height="191" alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/words-db.jpg" width="463" border="0" />
		<br />
		<p>如您所见，这个数据库非常直观：<code>word</code> 与 <code>definition</code> 和 <code>synonym</code> 具有一对多的关系。</p>
		<p>词典数据库拥有一个消费者，他在寻求一种表示数据库内容的关键方面的 XML 结构。所寻求的 XML 结构如清单 6 所示。</p>
		<br />
		<a name="code1">
				<b>清单 6. 可采用的词典 XML</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">&lt;words&gt;
  &lt;word spelling="glib" partofspeech="adjective"&gt;
    &lt;defintions&gt;
      &lt;defintion&gt;Performed with a natural, offhand ease.&lt;/defintion&gt;
      &lt;defintion&gt;Marked by ease and fluency of speech or writing that often suggests 
	  or stems from insincerity, superficiality, or deceitfulness&lt;/defintion&gt;
    &lt;/defintions&gt;
    &lt;synonyms&gt;
      &lt;synonym spelling="artful"/&gt; 
      &lt;synonym spelling="urbane"/&gt; 
    &lt;/synonyms&gt;
  &lt;/word&gt;	
&lt;/words&gt;
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如果选择使用 JiBX 这样的绑定框架来解决这个问题，则很可能需要创建一些中间对象模型，以从关系模型到达最终的 XML 模型。然后必须将数据库内容读取到对象模型中，并请求底层框架将其内部的结构编组为 XML 格式。</p>
		<p>这一过程内含了将对象结构映射到 XML 格式的步骤（使用所需的框架过程）。某些框架，如 JAXB，实际上是从 XML 和其他框架（如 JiBX ）生成 Java 对象，允许您自定义自己的 Java 对象到 XML 格式的映射。总之，这都需要大量的工作。</p>
		<p>并且，这是一项宏伟的计划。我并不提倡避免使用绑定框架。这里，我要声明：我已经预先警告过您。我计划向您展示的是一个生成 XML 的便捷方式。</p>
		<p>
				<a name="N1019F">
						<span class="smalltitle">
								<strong>
										<font face="Arial">可消费的 XML 很简单</font>
								</strong>
						</span>
				</a>
		</p>
		<p>使用 Groovy 的 <code>MarkupBuilder</code>，结合新的数据库访问框架 GroovySql，您可以轻易地生成可消费的 XML。您所要做的只是计算出所需的查询，并将结果映射到生成器实例 —— 然后，您马上就可以得到表示词典数据库内容的 XML 文档。</p>
		<p>让我们逐步来了解这一过程。首先，创建一个生成器实例，在本例中是 <code>MarkupBuilder</code>，因为您想要生成 XML。最外面的 XML 元素（也就是“根”）是 <code>words</code>，这样就创建了一个 <code>words</code> 方法。在闭包里，调用第一个查询，并在迭代中将查询结果映射到 <code>word</code> 子节点。</p>
		<p>接着，通过两个新的查询，创建 <code>word</code> 的两个子节点。创建一个 <code>definitions</code> 对象，并在迭代中映射它，接着用同样的方法处理 <code>synonyms</code>。</p>
		<br />
		<a name="code1">
				<b>清单 7. 用生成器集合所有元素</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">import groovy.sql.Sql
import groovy.xml.MarkupBuilder
import java.io.File
import java.io.StringWriter
class WordsDbReader{
  static void main(args) {
    sql = Sql.newInstance("jdbc:mysql://localhost/words", 
      "words", "words", "org.gjt.mm.mysql.Driver")
    writer = new StringWriter()	 	
    builder = new MarkupBuilder(writer)
    builder.words() {
      sql.eachRow("select word_id, spelling, part_of_speech from word"){ row |
        builder.word(spelling:row.spelling, partofspeech:row.part_of_speech){
         
		 builder.definitions(){
            sql.eachRow("select definition from definition where word_id = ${row.word_id}"){ defrow |
              builder.definition(defrow.definition)
            }
         }
         
		 builder.synonyms(){  		       		
            sql.eachRow("select spelling from synonym where word_id = ${row.word_id}"){ synrow |
              builder.synonym(synrow.spelling)
            }		       					       		
         }
       }
      }
    }
   new File("dbouuput.xml").withPrintWriter{ pwriter |
      pwriter.println writer.toString()
   }
 }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N101D9">
						<span class="smalltitle">
								<strong>
										<font face="Arial">结束语</font>
								</strong>
						</span>
				</a>
		</p>
		<p>这里，我向您展示的绑定解决方案似乎简单得让人难以置信，特别是以 Java 纯化论者的观点看来更是如此。尽管该解决方案不比使用绑定框架（如 JABX 和 JiBX） <i>更好</i>，但它确实更快一些 —— 而且，我主张使用这样较简单的方法。是不是我在<i>简单的</i> Java 代码中做一些类似的事情？是的，但我敢肯定，某些时候我也不得不处理 XML。</p>
		<p>用 Groovy 生成器进行开发的速度和简易性，在调用标记的时候可大显神威。例如，就像在第二个例子里展示的那样，我可以马上加快数据库的 XML 表示。对于原型化，或者当需要以最少的开发时间和精力来产生可工作的解决方案时，生成器也是一个不错的选择。</p>
		<p>在下个月的<i>实战 Groovy</i> 中我会讲些什么呢？哦，当然是在 Java 语言中使用 Groovy！</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">
								<font face="Arial" size="4">参考资料 </font>
						</span>
				</a>
		</p>
		<ul>
				<li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www-128.ibm.com/developerworks/java/library/j-pg04125/" target="_blank"><font color="#5c81a7">英文原文</font></a>。<br /><br /></li>
				<li>请阅读整套 <a href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=%E5%AE%9E%E6%88%98+Groovy%3A"><font color="#5c81a7"><i>实战 Groovy</i></font></a>文章，这是一套相互关联的系列文集。相关讨论有：<br /><br /><ul><li><a href="http://www-128.ibm.com/developerworks/cn/java/j-pg12144.html"><font color="#5c81a7">用 Groovy 进行 Ant 脚本编程</font></a>（2004 年 12 月），介绍了 Groovy 生成器和 Groovy 标记工具。 
</li><li><a href="http://www-128.ibm.com/developerworks/cn/java/j-pg03155"><font color="#5c81a7">用 Groovy 打造服务器端</font></a>（2005 年 1 月），介绍了 GroovyMarkup。 
</li><li><a href="http://www-128.ibm.com/developerworks/cn/java/j-pg01115.html"><font color="#5c81a7">用 Groovy 进行 JDBC 编程</font></a>（2005 年 1 月），解释了为什么 Groovysql 应该成为您最新喜欢的数据库访问框架。 </li></ul><br /><br /><br /></li>
				<li>您可以阅读 Dennis Sosnoski 关于 <a href="http://www.ibm.com/developerworks/library/x-databdopt/index.html"><font color="#5c81a7">XML 和 Java 技术</font></a> 的系列文章（developerWorks，2003 年 1 月），了解更多关于数据绑定框架（包括 JiBX）的知识。<br /><br /></li>
				<li>您可以从 <a href="http://groovy.codehaus.org/"><font color="#5c81a7">Groovy 开放源代码项目页面</font></a> 下载 Groovy。<br /><br /></li>
				<li>您可以在 developerWorks 的 <a href="http://www.ibm.com/developerworks/cn/java/"><font color="#5c81a7">Java 技术专区</font></a> 找到有关 Java 编程各个方面的文章。<br /><br /></li>
				<li>还请参阅 <a href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/java-onlinecourse-bytitle?OpenView&amp;Count=500"><font color="#5c81a7">Java 技术专区教程页面</font></a>，获得 <a href="http://www.ibm.com/developerworks/cn/"><font color="#5c81a7">developerWorks</font></a> 上聚焦于 Java 免费教程的完整列表。<br /><br /></li>
				<li>通过参与 <a href="http://www.ibm.com/developerworks/blogs/"><font color="#5c81a7">developerWorks blogs</font></a> 加入 developerWorks 社区。<br /></li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-pg04125/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">
								<font face="Arial" size="4">关于作者</font>
						</span>
				</a>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="2">
										<font face="Arial" size="4">
												<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										</font>
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
								</td>
								<td>
										<p>Andrew Glover 是 <a href="http://www.vanwardtechnologies.com/"><font color="#5c81a7">Vanward Technologies</font></a> 的 CTO，该公司位于华盛顿特区的大都会地区，公司的专业领域是自动化测试工具和框架的构造，该框架可以减少软件 bug 数量，缩短集成和测试的时间，提高代码的整体稳定性。他是 <i><a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=047144846X"><font color="#5c81a7">Java Testing Patterns</font></a></i>一书的合著者（Wiley，2004 年 9 月）。</p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/30035.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-09 14:51 <a href="http://www.blogjava.net/TrampEagle/articles/30035.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>感受 Groovy</title><link>http://www.blogjava.net/TrampEagle/articles/30034.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 09 Feb 2006 06:49:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30034.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30034.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30034.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30034.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30034.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1><SPAN style="COLOR: #999999"><FONT size=3>原文引自：</FONT><A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/"><FONT size=3>http://www-128.ibm.com/developerworks/cn/java/j-alj08034/</FONT></A></SPAN></H1>
<H1><SPAN style="COLOR: #999999"></SPAN>&nbsp;</H1>
<H1><SPAN style="COLOR: #999999">alt.lang.jre: </SPAN>感受 Groovy</H1>
<P id=subtitle>介绍 Java 平台的一种新标准语言</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD></TD></TR></TBODY></TABLE>
<P>级别: 初级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#author"><FONT color=#996699>Andrew Glover</FONT></A>, CTO, Vanward Technologies<BR></P>
<P>2004 年 8 月 03 日</P>
<BLOCKQUOTE>虽然 Java 语言因其严密性和扩展性的承诺而在整整一代程序员中胜出，但是 Groovy 预示了 Java 平台上的一个编程新时代，这种语言是以方便性、适宜性和敏捷性为出发点定义的。在新的 <I>alt.lang.jre</I>专栏的第二期文章中，Andrew Glover 对提议添加到 Java 平台的标准编程语言作了非正式的介绍。 </BLOCKQUOTE>
<P>如果您在使用 Java 平台（block），不管时间长短，您都有可能听说过 Groovy。Groovy 是超级明星开发人员 James Strachan 和 Bob McWhirter 发明的，它是一种敏捷开发语言，完全以 Java 编程 API 为基础。Groovy 当前正处于 Java Specification Request 的开始阶段，它于 2004 年 3 月底获得批准。Groovy 还是一种脚本语言，有些人说它会永久性地改变您看待和使用 Java 平台的方式。</P>
<P>在其对 JSR 241 （请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#resources"><FONT color=#996699>参考资料</FONT></A>）的开放评论中，Groovy 的共同规范领导者 Richard Monson-Haefel 说他对 Groovy 的支持是建立在总有一天 Java 平台要包括一种敏捷开发语言这一信念上。与许多移植到 Java 平台的脚本语言不同，Groovy 是 <I>为</I>JRE 而写的。在规范请求中（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#resources"><FONT color=#996699>参考资料</FONT></A>），Groovy 的制造者提出了“Java 不仅是一种编程语言，更是一个健壮的平台，可以有多种语言在上面运行和共存”（Monson-Haefel 语）的思想。 </P>
<P>新 <I>alt.lang.jre</I>专栏的这第二期文章的目的是让读者了解 Groovy。我首先回答关于这种新语言的一些最显然的问题（为什么需要它？），然后以代码为基础概述 Groovy 最令人兴奋的功能。 </P>
<P><A name=N10062><SPAN class=atitle><FONT face=Arial size=4>为什么需要另一种语言？</FONT></SPAN></A></P>
<P>正如在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj07064/"><FONT color=#5c81a7>上个月的专栏</FONT></A>中介绍的，Groovy 不是与 JRE 兼容的惟一脚本语言。Python、Ruby 和 Smalltalk 就是成功地移植到 Java 平台上的三种脚本语言。对于一些开发人员，这带来了问题：为什么要另一种语言？毕竟，我们许多人已经将 Java 代码与 Jython 或者 JRuby 结合来快速开发应用程序，为什么还要学习另一种语言？回答是 <I>您不一定要学习一种新语言以用 Groovy 编码</I>。Groovy 与其他 JRE 兼容脚本语言的不同在于它的语法以及重用 Java 库。Jython 和 JRuby 共享它们前身（分别是 Python 和 Ruby）的外观，Groovy 让人觉得就像是 Java 语言，不过限制要少得多。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=N10075><B>关于本系列</B></A><BR>
<P>虽然本系列的大多数读者熟悉 Java 语言以及它是如何在跨平台虚拟机上运行的，但是只有少数人知道 Java Runtime Environment 可以承载 Java 语言之外的语言。本系列文章对 JRE 的多种替代语言进行了综述。这里讨论的大多数语言是开放源代码的，许多是免费使用的，有少数是商业产品，必须购买。在 <A href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;sort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=alt.lang.jre"><FONT color=#5c81a7><I>alt.lang.jre</I> </FONT></A>系列中介绍的所有语言都得到了 JRE 支持，并且作者相信它们增强了 Java 平台的动态性和灵活性特征。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>像 Jython 这样的语言是在它们的父语言库上建立的，而 Groovy 使用了 Java 开发人员最熟悉的功能和库 <I>—— 但是将它们放到了一个敏捷开发框架中</I>。敏捷开发的基本宗旨是代码应该很好地适合范围广泛的任务，并可以不同的方式应用。Groovy 通过以下方式落实了这些宗旨： </P>
<UL>
<LI>使开发人员不用编译。 
<LI>允许动态类型。 
<LI>使合成结构容易。 
<LI>使其脚本可以在普通 Java 应用程序中使用。 
<LI>提供一个 shell 解析器。 </LI></UL>
<P>这些特性使 Groovy 成为一种特别容易学习和使用的语言，不管您是有经验的 Java 开发人员还是刚接触 Java 平台。在下面几节中，我将详细讨论上述特性。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N100A1><SPAN class=atitle><FONT face=Arial size=4>看呀，没有 javac！</FONT></SPAN></A></P>
<P>像许多脚本语言一样，Groovy 不用为运行时编译。这意味着 Groovy 脚本是 <I>在它们运行时</I>解释的，就像 JavaScript 是在观看 Web 页时由浏览器解释的一样。运行时判断会有执行速度的代价，这有可能使脚本语言不能用于对性能有要求的项目，但是无编译的代码在构建-运行周期中可以提供很多好处。运行时编译使 Groovy 成为快速原型设计、构建不同的实用程序和测试框架的理想平台。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=N100B0><B>脚本的能力</B></A><BR>
<P>脚本语言很流行，因为它们容易学习并且为开发人员设置的限制较少。脚本语言通常使用简单的、相当简洁的语法，这使开发人员可以用比大多数编程语言所需要的更少的代码创建真实世界的应用程序。像 Perl、Python、Ruby 和现在的 Groovy，用其敏捷方式编写代码而使编程工作达到一个新的效率水平。这种提高的敏捷性通常会使开发人员的效率提高。脚本语言的成功表明脚本不是一种小范围内使用的技术或者黑客的娱乐工具，而是一种由像 Google、Yahoo 和 IBM 这样的世界级公司所使用的切实可行的技术。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>例如，运行脚本 Emailer.groovyin Groovy 就是在命令行键入 <CODE>groovy Emailer.groovy</CODE> 这么容易。如果希望运行同样的 Java 文件（Emailer.java），显然必须键入额外的命令： <CODE>javac Emailer.java</CODE> ，然后是 <CODE>java Emailer</CODE> 。虽然这看起来可能有些微不足道，但是可以容易设想运行时编译在应用程序开发的更大上下文中的好处。 </P>
<P>可以在稍后看到，Groovy 还允许脚本放弃 main 方法以静态地运行一个关联的应用程序。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N100CC><SPAN class=atitle><FONT face=Arial size=4>动态 dynamo</FONT></SPAN></A></P>
<P>像其他主流脚本语言一样，Groovy 不需要像 C++ 和 Java 语言这样的正式语言的显式类型。在 Groovy 中，一个对象的类型是在运行时动态发现的，这极大地减少了要编写的代码数量。首先可以通过分析清单 1 和 2 中的简单例子看到这一点。</P>
<P>清单 1 显示了在 Java 语言中如何将一个本地变量声明为 <CODE>String</CODE> 。注意必须声明类型、名和值。 </P><BR><A name=code1><B>清单 1. Java 静态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">String myStr = "Hello World";
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在清单 2 中，您看到同样的声明，但是不需要声明变量类型。 </P><BR><A name=code2><B>清单 2. Groovy 动态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">myStr = "Hello World"
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>您可能还注意到了，在清单 2 中我可以去掉声明中的分号。在定义方法及其相关的参数时动态类型有戏剧性的后果：多态具有了全新的意义！事实上，使用动态类型， <I>不使用</I>继承就可以得到多态的全部能力。在清单 3 中，可以真正开始看到动态类型在 Groovy 的灵活性方面所起的作用。 </P><BR><A name=code3><B>清单 3. 更多 Groovy 动态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">class Song{
  length
  name
}

class Book{
  name
  author
}

def doSomething(thing){
  println "going to do something with a thing named = " + thing.name
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这里，我定义了两个 Groovy 类， <CODE>Song</CODE> 和 <CODE>Book</CODE> ，我将在后面对它们进一步讨论。这两个类都包含一个 <CODE>name</CODE> 属性。我还定义了一个函数 <CODE>doSomething</CODE> ，它以一个 <CODE>thing</CODE> 为参数，并试图打印这个对象的 <CODE>name</CODE> 属性。 </P>
<P>因为 <CODE>doSomething</CODE> 函数没有定义其输入参数的类型，只要对象包含一个 <CODE>name</CODE> 属性，那么它就可以工作。因此，在清单 4 中，可以看到在使用 <CODE>Song</CODE> 和 <CODE>Book</CODE> 的实例作为 <CODE>doSomething</CODE> 的输入时会有什么现象。 </P><BR><A name=code4><B>清单 4. 试验动态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">mySong = new Song(length:90, name:"Burning Down the House")
myBook = new Book(name:"One Duck Stuck", author:"Phyllis Root")

doSomething(mySong) //prints Burning Down the House
doSomething(myBook) //prints One Duck Stuck

anotherSomething = doSomething

anotherSomething(myBook) //prints One Duck Stuck
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>除了展示 Groovy 中的动态类型，清单 4 的最后两行还揭示了创建对一个函数的引用有多容易。这是因为在 Groovy 中 <I>所有东西</I>都是对象，包括函数。 </P>
<P>关于 Groovy 的动态类型声明最后要注意的是，它会导致更少的 <CODE>import</CODE> 语句。尽管 Groovy 需要 import 以显式使用类型，但是这些 import 可以使用别名以提供更短的名字。 </P>
<P><A name=N1014C><SPAN class=smalltitle><STRONG><FONT face=Arial>动态类型综述</FONT></STRONG></SPAN></A></P>
<P>下面两个例子将到目前为止讨论过的 Groovy 中的动态类型的内容放到一起。下面的 Java 代码组和 Groovy 代码组利用了 Freemarker（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#resources"><FONT color=#996699>参考资料</FONT></A>），这是一个开放源代码模板引擎。这两组代码都只是简单地用一个目录和文件名创建一个 <CODE>Template</CODE> 对象，然后将相应对象的内容打印到标准输出，当然，不同之处是每一组代码处理这项任务所需要的代码量。 </P><BR><A name=code5><B>清单 5. 简单的 TemplateReader Java 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import java.io.File;
import java.io.IOException;

import freemarker.template.Configuration;
import freemarker.template.Template;

public class TemplateReader {

  public static void main(String[] args) {
    try{
	  Configuration cfg = Configuration.getDefaultConfiguration();
	  cfg.setDirectoryForTemplateLoading(
	       new File("C:\\dev\\projects\\http-tester\\src\\conf"));
		    
	  Template temp = cfg.getTemplate("vendor-request.tmpl");
		
  	  System.out.println(temp.toString());
      }catch(IOException e){
        e.printStackTrace();
      }
  }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>初看之下，清单 5 中的 Java 代码相当简单 —— 特别是如果以前从来没见过脚本代码时。幸运的是，有清单 6 中的 Groovy 作为对比。现在这段代码很简单！</P><BR><A name=code6><B>清单 6. 用 Groovy 编写的更简单的 TemplateReader</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import freemarker.template.Configuration as tconf
import java.io.File

cfg = tconf.getDefaultConfiguration()

cfg.setDirectoryForTemplateLoading(
  new File("C:\\dev\\projects\\http-tester\\src\\conf"))
  
temp = cfg.getTemplate("vendor-request.tmpl")

println temp.toString()
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>Groovy 代码只有 Java 代码的一半那么长，下面是原因：</P>
<UL>
<LI>Groovy 代码只需要一半的 <CODE>import</CODE> 语句。还要注意， <CODE>freemarker.template.Configuration</CODE> 使用了别名 <CODE>tconf</CODE> ，使得语法更短。 <BR><BR>
<LI>Groovy 允许类型为 <CODE>Template</CODE> 的变量 <CODE>tmpl</CODE> 不声明其类型。 <BR><BR>
<LI>Groovy 不需要 <CODE>class</CODE> 声明或者 <CODE>main</CODE> 方法。 <BR><BR>
<LI>Groovy 不关心任何相应异常，使您可以不用导入 Java 代码中需要的 <CODE>IOException</CODE> 。 </LI></UL>
<P>现在，在继续之前，想一下您所编写的最后一个 Java 类。您可能不得不编写很多 import 并声明类型，并在后面加上同样数量的分号。考虑用 Groovy 编写同样的代码会是什么情况。可以使用简练得多的语法，不需要遵守这么多的规则，并且得到完全相同的行为。</P>
<P>想一下，如果您正好是刚刚开始……</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N101B8><SPAN class=atitle><FONT face=Arial size=4>特别灵活的语法</FONT></SPAN></A></P>
<P>谈到语法，灵活性是更有效地开发代码的主要因素。很像其有影响的对手（Python、Ruby 和 Smalltalk），Groovy 极大地简化了核心库的使用和它所模拟的语言（在这里是 Java 语言）的构造。为了让您对 Groovy 语法的灵活性有一个大体概念，我将展示它的一些主要结构，即类、函数（通过 <CODE>def</CODE> 关键词）、闭包、集合、范围、映射和迭代器。 </P>
<P><A name=N101C5><SPAN class=smalltitle><STRONG><FONT face=Arial>类</FONT></STRONG></SPAN></A></P>
<P>在字节码水平，Groovy 类是真正的 Java 类。不同之处在于 Groovy 将类中定义的所有内容都默认为 <CODE>public</CODE> ，除非定义了特定的访问修饰符。而且，动态类型应用到字段和方法，不需要 <CODE>return</CODE> 语句。 </P>
<P>在清单 7 中可以看到 Groovy 中类定义的例子，其中类 <CODE>Dog</CODE> 有一个 <CODE>getFullName</CODE> 方法，它实际上返回一个表示 <CODE>Dog</CODE> 的全名的 <CODE>String</CODE> 。并且所有方法都隐式地为 <CODE>public</CODE> 。 </P><BR><A name=code7><B>清单 7. 示例 Groovy 类：Dog</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">class Dog{
  name

  bark(){
    println "RUFF! RUFF!"
  }
  
  getFullName(master){
    name + " " + master.lname
  }
  
  obeyMaster(){
    println "I hear you and will not obey."
  }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在清单 8 中，推广到有两个属性 —— <CODE>fname</CODE> 和 <CODE>lname</CODE> —— 的类 <CODE>DogOwner</CODE> ，就是这么简单！ </P><BR><A name=code8><B>清单 8. 示例 Groovy 类：DogOwner</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">class DogOwner{
  fname
  lname

  trainPet(pet){
    pet.obeyMaster()
  }
  
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在清单 9 中，用 Groovy 设置属性并对 <CODE>Dog</CODE> 和 <CODE>DogOwner</CODE> 实例调用方法。现在很明显，使用 Groovy 类比 Java 类要容易得多。虽然需要 <CODE>new</CODE> 关键词，但是类型是可选的，且设置属性（它隐式为 public）是相当轻松的。 </P><BR><A name=code9><B>清单 9. 使用 Groovy 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">myDog = new Dog()
myDog.name = "Mollie"

myDog.bark()
myDog.obeyMaster() 

me = new DogOwner()
me.fname = "Ralf"
me.lname = "Waldo"

me.trainPet(myDog)

str = myDog.getFullName(me)
println str  // prints Mollie Waldo

</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>注意在 <CODE>Dog</CODE> 类中定义的 <CODE>getFullName</CODE> 方法返回一个 <CODE>String</CODE> 对象，在这里它是 “ <CODE>Mollie Waldo</CODE> ”。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=N1023F><B>第一类对象</B></A><BR>
<P><I>第一类对象</I> 是可以在运行时用数据创建并使用的对象。第一类对象还可以传递给函数和由函数输出、作为变量存储、由其他对象返回。Java 语言自带的基本数据类型，如 <CODE>int</CODE> 和 <CODE>boolean</CODE> ，不认为是第一类对象。许多面向对象纯粹论者哀叹这个小细节，一些人据此提出 Java 语言是否是真正的面向对象语言。Groovy 通过将所有内容声明为对象而解决了这一问题。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A name=N10254><SPAN class=smalltitle><STRONG><FONT face=Arial>Def</FONT></STRONG></SPAN></A></P>
<P>除了像许多脚本语言那样将所有对象指定为第一类对象（见侧栏），Groovy 还让您创建 <I>第一类函数</I>，它本身实质上就是对象。它们是用 <CODE>def</CODE> 关键词定义的并在类定义之外。您实际上在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#code3"><FONT color=#996699>清单 3</FONT></A> 中已经看到了如何用 <CODE>def</CODE> 关键词定义第一类函数，并在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#code4"><FONT color=#996699>清单 4</FONT></A>中看到使用了一个函数。Groovy 的第一类函数定义简单脚本时特别有用。 </P>
<P><A name=N10270><SPAN class=smalltitle><STRONG><FONT face=Arial>闭包</FONT></STRONG></SPAN></A></P>
<P>Groovy 中最令人兴奋和最强大的功能是支持闭包。 <I>闭包（Closure）</I>是第一类对象，它类似于 Java 语言中的匿名内部类。闭包和匿名内部类都是可执行的一段代码，不过这两者之间有一些细微的不同。状态是自动传入传出闭包的。闭包可以有名字。它们可以重复使用。而且，最重要且对 Groovy 同样成立的是，闭包远比匿名内部类要灵活得多！ </P>
<P>清单 10 展示了闭包的强大。清单中新的和改进的 <CODE>Dog</CODE> 类包括一个 <CODE>train</CODE> 方法，它实际上执行创建了 <CODE>Dog</CODE> 实例的闭包。 </P><BR><A name=code10><B>清单 10. 使用闭包</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">class Dog{  
  action

  train(){
    action.call()
  }
}

sit = { println "Sit, Sit! Sit! Good dog"}
down = { println "Down! DOWN!" }


myDog = new Dog(action:sit)
myDog.train()  // prints Sit, Sit! Sit! Good dog

mollie = new Dog(action:down)
mollie.train() // prints Down! DOWN!
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>而且，闭包还可以接收参数。如清单 11 所示， <CODE>postRequest</CODE> 闭包接收两个参数（ <CODE>location</CODE> 和 <CODE>xml</CODE> ），并使用 Jakarta Commons HttpClient 库（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#resources"><FONT color=#996699>参考资料</FONT></A>）将一个 XML 文档发送给指定位置。然后这个闭包返回一个表示响应的 <CODE>String</CODE> 。闭包定义下面是一个使用闭包的例子。事实上，调用闭包就像调用函数一样。 </P><BR><A name=code11><B>清单 11. 使用带参数的闭包</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.methods.PostMethod

postRequest = { location, xml |

  clint = new HttpClient()
  mthd = new PostMethod(location)  	
  mthd.setRequestBody(xml)
  mthd.setRequestContentLength(xml.length())
  mthd.setRequestHeader("Content-type", 
     "text/xml; charset=ISO-8859-1")
	
  statusCode = clint.executeMethod(mthd)
  responseBody = mthd.getResponseBody()
  mthd.releaseConnection()	
  return new String(responseBody)	  
}

loc = "http://localhost:8080/simulator/AcceptServlet/"
vxml = "&lt;test&gt;&lt;data&gt;blah blah blah&lt;/data&gt;&lt;/test&gt;"

str = postRequest(loc, vxml)
println str
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></FONT></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=N102B9><B>自动装箱</B></A><BR>
<P>自动装箱或者装箱转换是一个自动将像 <CODE>int</CODE> 、 <CODE>double</CODE> 和 <CODE>boolean</CODE> 这样的基本数据类型自动转换为可以在 <CODE>java.lang</CODE> 包中找到的它们的相应包装类型的过程。这一功能出现在 J2SE 1.5 中，使开发人员不必在源代码中手工编写转换代码。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A name=N102D3><SPAN class=smalltitle><STRONG><FONT face=Arial>集合</FONT></STRONG></SPAN></A></P>
<P>将对象组织到像列表和映射这样的数据结构中是一项基本的编码任务，是我们大多数人每天要做的工作。像大多数语言一样，Groovy 定义了一个丰富的库以管理这些类型的集合。如果曾经涉足 Python 或者 Ruby，那么应该熟悉 Groovy 的集合语法。如清单 12 所示，创建一个列表与在 Java 语言中创建一个数组很类似。（注意，列表的第二项自动装箱为一个 <CODE>Integer</CODE> 类型。) </P><BR><A name=code12><B>清单 12. 使用集合</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">collect = ['groovy', 29, 'here', 'groovy']
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>除了使列表更容易处理，Groovy 还为集合增加了几个新方法。这些方法使得，如 <CODE>统计</CODE> 值出现的次数、将整个列表 <CODE>结合</CODE> 到一起、对列表 <CODE>排序</CODE> 变得非常容易。可以在清单 13 中看到这些集合方法的使用。 </P><BR><A name=code13><B>清单 13. 使用 Groovy 集合</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">aCollect = [5, 9, 2, 2, 4, 5, 6] 

println aCollect.join(' - ')  // prints 5 - 9 - 2 - 2 - 4 - 5 - 6
println aCollect.count(2)     // prints 2
println aCollect.sort()       // prints [2, 2, 4, 5, 5, 6, 9]
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><B>Maps</B> <BR><BR>像列表一样，映射也是一种在 Groovy 中非常容易处理的数据结构。清单 14 中的映射包含两个对象，键是 <CODE>name</CODE> 和 <CODE>date</CODE> 。注意可以用不同的方式取得值。 </P><BR><A name=code14><B>清单 14. 处理映射</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">myMap = ["name" : "Groovy", "date" : new Date()]

println myMap["date"]

println myMap.date
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><B>范围</B> <BR><BR>在处理集合时，很可能会大量使用 <CODE>范围（Range）</CODE> 。 <CODE>范围</CODE> 实际上是一个很直观的概念，并且容易理解，利用它可以包含地或者排除地创建一组有序值。使用两个点 （ <CODE>..</CODE> ） 声明一个包含范围，用三个点 （ <CODE>...</CODE> ） 声明一个排除范围，如清单 15 所示。 </P><BR><A name=code15><B>清单 15. 处理范围</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">myRange = 29...32
myInclusiveRange = 2..5

println myRange.size() // prints 3
println myRange[0]   // prints 29
println myRange.contains(32) //prints false

println myInclusiveRange.contains(5) //prints true
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><B>用范围实现循环</B> <BR><BR>在循环结构中，范围可以实现相当巧妙的想法。在清单 16 中，将 <CODE>aRange</CODE> 定义为一个排除范围，循环打印 a、b、c 和 d。 </P><BR><A name=code16><B>清单 16. 用范围实现循环</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">aRange = 'a'...'e'

for (i in aRange){
  println i
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><B>集合的其他功能</B> <BR><BR>如果不熟悉 Python 和其他脚本语言，那么您在 Groovy 集合中发现的一些其他功能会让您印象深刻。例如，创建了集合后，可以用负数在列表中反向计数，如清单 17 所示。 </P><BR><A name=code17><B>清单 17. 负索引</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">aList = ['python', 'ruby', 'groovy']

println aList[-1] // prints groovy
println aList[-3] // prints python
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>Groovy 还让您可以用范围分割列表。分割可获得列表的准确子集，如清单 18 所示。 </P><BR><A name=code18><B>清单 18. 用范围分割</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">fullName = "Andrew James Glover"

mName = fullName[7...13]

print "middle name: " + mName // prints James
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><B>集合类似于 Ruby</B> <BR><BR>如果愿意的话，还可以将 Groovy 集合作为 Ruby 集合。可以用类似 Ruby 的语法，以 <CODE>&lt;&lt;</CODE> 语法附加元素、用 <CODE>+</CODE> 串接和用 <CODE>-</CODE> 对集合取差，甚至还可以用 <CODE>*</CODE> 语法处理集合的重复，如清单 19 所示。注意，还可以用 <CODE>==</CODE> 比较集合。 </P><BR><A name=code19><B>清单 19. Ruby 风格的集合</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">collec = [1, 2, 3, 4, 5]
collec &lt;&lt; 6 //appended 6 to collec

acol = ['a','b','c'] * 3 //acol now has 9 elements

coll =  [10, 11]
coll2 = [12, 13]

coll3 = coll + coll2 //10,11,12,13

difCol = [1,2,3] - [1,2] //difCol is 3

assert [1, 2, 3] == [1, 2, 3] //true
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=N103A4><SPAN class=smalltitle><STRONG><FONT face=Arial>迭代器</FONT></STRONG></SPAN></A></P>
<P>在 Groovy 中，迭代任何序列都相当容易。迭代字符序列所需要的就是一个简单的 <CODE>for</CODE> 循环，如清单 20 所示。（正如您现在可能注意到的，Groovy 提供了比 Java 1.5 以前的传统语法更自然的 <CODE>for</CODE> 循环语法。） </P><BR><A name=code20><B>清单 20. 迭代器示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">str = "uncle man, uncle man"

for (ch in str){
  println ch
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>Groovy 中的大多数对象具有像 <CODE>each</CODE> 和 <CODE>find</CODE> 这样的以闭包为参数的方法。用闭包来迭代对象会产生几种令人兴奋的可能性，如清单 21 所示。 </P><BR><A name=code21><B>清单 21. 带有迭代器的闭包</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">[1, 2, 3].each {  
  val = it 
  val += val
  println val
}

[2, 4, 6, 8, 3].find { x |
     if (x == 3){
       println x
     }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在清单 21 中，方法 <CODE>each</CODE> 作为迭代器。在这里，闭包添加元素的值，完成时 <CODE>val</CODE> 的值为 6。 <CODE>find</CODE> 方法也是相当简单的。每一次迭代传递进元素。在这里，只是测试值是否为 3。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N103E3><SPAN class=atitle><FONT face=Arial size=4>Groovy 的高级内容</FONT></SPAN></A></P>
<P>到目前为止，我着重讲述的都是使用 Groovy 的基本方面，但是这种语言有比基本内容多得多的内容！我将以分析 Groovy 提供的一些高级开发功能作为结束，包括 Groovy 样式的 JavaBeans 组件、文件 IO、正则表达式和用 <CODE>groovyc</CODE> 编译。 </P>
<P><A name=N103F0><SPAN class=smalltitle><STRONG><FONT face=Arial>GroovyBean！</FONT></STRONG></SPAN></A></P>
<P>永远不变的是，应用程序最后要使用类似 struct 的对象表示真实世界的实体。在 Java 平台上，称这些对象为 JavaBean 组件，它们通常用于表示订单、客户、资源等的业务对象。Groovy 由于其方便的简写语法，以及在定义了所需 bean 的属性后自动提供构造函数，而简化了 JavaBean 组件的编写。结果自然就是极大地减少了代码，正如您可以自己从清单 22 和 23 中看到的。</P>
<P>在清单 22 中，可看到一个简单的 JavaBean 组件，它是用 Java 语言定义的。</P><BR><A name=code22><B>清单 22. 一个简单的 JavaBean 组件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public class LavaLamp {
  private Long model;
  private String baseColor;
  private String liquidColor;
  private String lavaColor;

  public String getBaseColor() {
    return baseColor;
  }

  public void setBaseColor(String baseColor) {
    this.baseColor = baseColor;
  }

  public String getLavaColor() {
    return lavaColor;
  }

  public void setLavaColor(String lavaColor) {
    this.lavaColor = lavaColor;
  }

  public String getLiquidColor() {
    return liquidColor;
  }

  public void setLiquidColor(String liquidColor) {
    this.liquidColor = liquidColor;
  }

  public Long getModel() {
    return model;
  }

  public void setModel(Long model) {
    this.model = model;
  }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在清单 23 中，可以看到用 Groovy 写这个 bean 时所发生的事。所要做的就是定义属性，Groovy 会自动给您一个很好的构造函数以供使用。Groovy 还使您在操纵 <CODE>LavaLamp</CODE> 的实例时有相当大的灵活性。例如，我们可以使用 Groovy 的简写语法 <I>或者</I>传统的冗长的 Java 语言语法操纵 bean 的属性。 </P><BR><A name=code23><B>清单 23. 用 Groovy 编写的 JavaBeans 组件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">class LavaLamp{
  model
  baseColor
  liquidColor
  lavaColor
}

llamp = new LavaLamp(model:1341, baseColor:"Black", 
  liquidColor:"Clear", lavaColor:"Green")

println llamp.baseColor
println "Lava Lamp model ${llamp.model}"

myLamp = new LavaLamp()
myLamp.baseColor = "Silver"
myLamp.setLavaColor("Red")

println "My Lamp has a ${myLamp.baseColor} base"
println "My Lava is " + myLamp.getLavaColor()
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=N1041A><SPAN class=smalltitle><STRONG><FONT face=Arial>轻松的 IO</FONT></STRONG></SPAN></A></P>
<P>Groovy IO 操作很轻松，特别是与迭代器和闭包结合时。Groovy 使用标准 Java 对象如 <CODE>File</CODE> 、 <CODE>Reader</CODE> 和 <CODE>Writer</CODE> ，并用接收闭包作参数的额外方法增强了它们。如在清单 24 中，可以看到传统的 <CODE>java.io.File</CODE> ，但是带有额外的、方便的 <CODE>eachLine</CODE> 方法。 </P><BR><A name=code24><B>清单 24. Groovy IO</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import java.io.File

new File("File-IO-Example.txt").eachLine{ line |
 println "read the following line -&gt; " + line
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>因为文件实质上是一系列行、字符等，所以可以相当简单地迭代它们。 <CODE>eachLine</CODE> 方法接收一个闭包并迭代文件的每一行，在这里是 <CODE>File-IO-Example.txt</CODE> 。 以这种方式使用闭包是相当强大的，因为 Groovy 保证所有文件资源都是关闭的，不考虑任何异常。这意味着无需大量 <CODE>try</CODE> / <CODE>catch</CODE> / <CODE>finally</CODE> 子句就可以进行文件 IO！ </P>
<P><A name=N10458><SPAN class=smalltitle><STRONG><FONT face=Arial>高级编译</FONT></STRONG></SPAN></A></P>
<P>Groovy 脚本实际上是字节码级别的 Java 类。因此，可以容易地用 <CODE>groovyc</CODE> 编译 Groovy 脚本。可以通过命令行或者 <CODE>Ant</CODE> 使用 <CODE>groovyc</CODE> 以生成脚本的类文件。这些类可以用普通 <CODE>java</CODE> 命令运行，只要 classpath 包括 <CODE>groovy.jar</CODE> 和 <CODE>asm.jar</CODE> ，这是 ObjectWeb 的字节码操纵框架。要了解更多编译 Groovy 的内容，请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#resources"><FONT color=#996699>参考资料</FONT></A>。 </P>
<P><A name=N1047D><SPAN class=smalltitle><STRONG><FONT face=Arial>最大 RegEx</FONT></STRONG></SPAN></A></P>
<P>如果一种语言没有正则表达式处理，则它是没价值的。Groovy 使用 Java 平台的 <CODE>java.util.regex</CODE> 库 —— 但是做了少量基本的改变。例如，Groovy 使您可以用 <CODE>~</CODE> 表达式创建 <CODE>Pattern</CODE> 对象，用 <CODE>=~</CODE> 表达式创建 <CODE>Matcher</CODE> 对象，如清单 25 所示。 </P><BR><A name=code25><B>清单 25. Groovy RegEx</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">str =  "Water, water, every where,
        And all the boards did shrink;
        Water, water, every where,
        Nor any drop to drink."

if (str =~ 'water'){
  println 'found a match'
}

ptrn = ~"every where"

nStr = (str =~ 'every where').replaceAll('nowhere')

println nStr
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>您可能已经注意到了，可以在上述清单中定义 <CODE>String</CODE> 、 <CODE>str</CODE> ，而无需为每一新行添加结束引号和 <CODE>+</CODE> 。这是因为 Groovy 放松了要求字符串串接的普通 Java 约束。运行这段 Groovy 脚本会对匹配 <CODE>water</CODE> 的情况打印出 <CODE>true</CODE> ，然后打印出一节诗，其中所有出现 “ <CODE>every where</CODE> ”的地方都替换为 “ <CODE>nowhere</CODE> ”。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=N104C6><B>关于 （band）shell</B></A><BR>
<P>Groovy 提供了两种不同的解释器，使所有有效的 Groovy 表达式可以交互地执行。这些 shell 是特别强大的机制，可以用它们迅速学习 Groovy。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N104D0><SPAN class=atitle><FONT face=Arial size=4>结束语</FONT></SPAN></A></P>
<P>像所有婴儿期的项目一样，Groovy 是正在发展的语言。习惯于使用 Ruby 和 Python （或者 Jython）的开发人员可能会怀念 mixins、脚本导入（尽管可以将所需要的可导入脚本编译为相应的 Java 类）和方法调用的命名参数等这些功能的方便性。 但是 Groovy 绝对是一种发展中的语言。随着其开发人员数量的增加，它很有可能结合这些功能及更多功能。</P>
<P>同时，Groovy 有很多优点。它很好地融合了 Ruby、Python 和 Smalltalk 的一些最有用的功能，同时保留了基于 Java 语言的核心语法。对于熟悉 Java 平台的开发人员，Groovy 提供了更简单的替代语言，且几乎不需要学习时间。对于刚接触 Java 平台的开发人员，它可以作为有更复杂语法和要求的 Java 语言的一个容易的入口点。</P>
<P>像在本系统讨论的其他语言一样，Groovy 不是要替代 Java 语言，而是作为它的另一种选择。与这里讨论的其他语言不一样，Groovy 遵循 Java 规范，这意味着它有可能与 Java 平台上的 Java 语言具有同等重要的作用。</P>
<P>在本月这一期 <I>alt.lang.jre</I>文章中，介绍了 Groovy 的基本框架和语法，以及它的一些高级编程功能。下个月将介绍在 Java 开发人员中最受欢迎的脚本语言： JRuby。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>参考资料 </FONT></SPAN></A></P>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/library/j-alj08034.html"><FONT color=#5c81a7>英文原文</FONT></A>. <BR><BR>
<LI>新 <I>alt.lang.jre</I>系列是上个月开始的，首先是 Barry Feigenbaum 的文章 “ <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj07064/"><FONT color=#5c81a7>Get to know Jython</FONT></A>”（ <I>developerWorks</I>，2004 年 7 月）。 <BR><BR>
<LI>从 <A href="http://groovy.codehaus.org/"><FONT color=#5c81a7>Groovy 开放源代码项目页</FONT></A>下载 Groovy，在这里还可以学习更多有关编译、单元测试、正则表达式等之类的内容。 <BR><BR>
<LI>可以在 <A href="http://www.jcp.org/en/jsr/detail?id=241"><FONT color=#5c81a7>Java Community Process 主页</FONT></A>找到“JSR 241: The Groovy programming language”。 <BR><BR>
<LI>阅读 James Strachan 的“ <A href="http://radio.weblogs.com/0112098/2003/08/29.html#a399"><FONT color=#5c81a7>Groovy -- the birth of a new dynamic language for the Java platform</FONT></A>”（Radio Userland，James Strachan 的 Weblog，2003 年 8 月），对 Groovy 背后的思路有一个概念。 <BR><BR>
<LI>在 <A href="http://weblogs.java.net/pub/wlg/1125"><FONT color=#5c81a7>java.net weblogs 页</FONT></A>上阅读更多 Richard Monson-Haefel 对 Groovy 的思想。 <BR><BR>
<LI>Groovy 的一个最强大的功能是它的敏捷性。通过 Roy Miller 的“ <A href="http://www-128.ibm.com/developerworks/cn/java/j-xp0813/"><FONT color=#5c81a7>揭开极端编程的神秘面纱：重访“XP 精华”，第 1 部分</FONT></A>”（ <I>developerWorks</I>，2002 年 8 月）学习有关敏捷开发（或者 XP） 的更多底层原理。 <BR><BR>
<LI>Richard Hightower 和 Nicholas Lesiecki 的 <I>Java tools for extreme programming</I>（摘自 <I>developerWorks</I>，2002 年 7 月）是在 Java 平台上进行敏捷开发的从业者指导，包括关于“ <A href="http://www.ibm.com/developerworks/java/library/j-tools4xp.html"><FONT color=#5c81a7>Building Java applications with Ant</FONT></A>”的一章。 <BR><BR>
<LI>通过 Malcolm Davis 的 “ <A href="http://www-128.ibm.com/developerworks/cn/java/j-ant/"><FONT color=#5c81a7>利用 Ant 和 JUnit 进行增量开发</FONT></A>” （ <I>developerWorks</I>，2000 年 11 月），学习有关用 Ant 构建 Java（因而也包括 Groovy） 应用程序的内容。 <BR><BR>
<LI>在 “ <A href="http://www-128.ibm.com/developerworks/cn/java/j-junitmail/"><FONT color=#5c81a7>让编译和测试过程自动化</FONT></A>” （ <I>developerWorks</I>，2001 年 8 月）中，Erik Hatcher 为您展示了如何结合 Ant 和 JUnit 以朝 XP 天堂更进一步。 <BR><BR>
<LI>Maven 是 Ant 的替代物，它可以很好地完成项目管理任务。通过 Charles Chan 的“ <A href="http://www-128.ibm.com/developerworks/cn/java/j-maven/"><FONT color=#5c81a7>项目管理：Maven 让事情变得简单</FONT></A>” （ <I>developerWorks</I>，2003 年 4 月），学习有关 Maven 的更多内容。 <BR><BR>
<LI>面向方面的程序设计是一种构建高度去耦和可扩展的企业系统的敏捷开发技术。通过 Andrew Glover 的 “ <A href="http://www-128.ibm.com/developerworks/cn/java/j-aopsc/"><FONT color=#5c81a7>AOP 解决紧密耦合的难题</FONT></A>” （ <I>developerWorks</I>，2004 年 2 月），学习有关 AOP 的更多内容。 <BR><BR>
<LI><A href="http://justgroovy.org/"><FONT color=#5c81a7>JustGroovy</FONT></A>是一个专门针对 Groovy 的 Web 网站。 <BR><BR>
<LI>在清单 6 中，开放源代码模板引擎 <A href="http://freemarker.sourceforge.net/"><FONT color=#5c81a7>Freemarker</FONT></A>结合了 Java 代码和 Groovy 代码段。 <BR><BR>
<LI>清单 11 中使用了 <A href="http://jakarta.apache.org/commons/httpclient/"><FONT color=#5c81a7>Jakarta Commons HttpClient 库</FONT></A>。 <BR><BR>
<LI>Groovy 如果没有像 <A href="http://www.python.org/"><FONT color=#996699>Python</FONT></A>和 <A href="http://www.ruby-lang.org/en/"><FONT color=#5c81a7>Ruby</FONT></A>这样的语言的强大影响是不会变成今天这样的。 <BR><BR>
<LI>可以在 <A href="http://www-128.ibm.com/developerworks/cn/java/"><FONT color=#5c81a7><I>developerWorks</I>Java 技术专区 </FONT></A>找到关于 Java 编程各个方面的文章。. <BR><BR>
<LI>访问 <A href="http://devworks.krcinfo.com/"><FONT color=#5c81a7>Developer Bookstore</FONT></A>，获取技术书籍的完整列表，其中包括数百本 <A href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200&amp;p=Java"><FONT color=#5c81a7>Java 相关主题</FONT></A>的书籍。 <BR><BR>
<LI>还请参阅 <A href="http://www.ibm.com/developerworks/cn/cnedu.nsf/java-onlinecourse-bytitle?OpenView"><FONT color=#5c81a7>Java 技术专区教程页</FONT></A>以获得 <I>developerWorks</I>上关于 Java 的免费教程的完整清单。 <BR><BR>
<LI>是否对无需通常的高成本入口点（entry point ）或短期评估许可证的 IBM 测试产品感兴趣？ <A href="http://www-128.ibm.com/developerworks/cn/subscription/"><FONT color=#5c81a7>developerWorks Subscription</FONT></A>针对 WebSphere ®、DB2 ®、Lotus ®、Rational ®和 Tivoli ®产品提供了低成本的 12 个月单用户许可证，包括基于 Eclipse 的 WebSphere Studio IDE，用于开发、测试、评估和展示您的应用程序。 <BR></LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>关于作者</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4></FONT></P></TD>
<TD>
<P>Andrew Glover 是 <A href="http://www.vanwardtechnologies.com/welcome.php"><FONT color=#5c81a7>Vanward Technologies</FONT></A>的 CTO，这是华盛顿特区中心的一家专门从事构建自动测试框架的公司，这种框架可以降低软件 bug 数量、减少集成和测试次数，并改进代码整体稳定性。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/30034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-09 14:49 <a href="http://www.blogjava.net/TrampEagle/articles/30034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Java文件路径问题</title><link>http://www.blogjava.net/TrampEagle/articles/28741.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 20 Jan 2006 03:17:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/28741.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/28741.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/28741.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/28741.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/28741.html</trackback:ping><description><![CDATA[引自：<A href="http://www.matrix.org.cn/resource/article/44/44113_java.html">http://www.matrix.org.cn/resource/article/44/44113_java.html</A><BR><BR>1.如何获得当前文件路径<BR><BR>常用：<BR><BR>字符串类型：System.getProperty("user.dir");<BR><BR>综合：<BR><BR><PRE class=overflow>package com.zcjl.test.base;<BR>import java.io.File;<BR>public class Test {<BR>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) throws Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.currentThread().getContextClassLoader().getResource(""));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Test.class.getClassLoader().getResource(""));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(ClassLoader.getSystemResource(""));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Test.class.getResource(""));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Test.class.getResource("/"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(new File("").getAbsolutePath());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(System.getProperty("user.dir"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</PRE><BR><BR>2.Web服务中<BR><BR>(1).Weblogic<BR><BR>WebApplication的系统文件根目录是你的weblogic安装所在根目录。<BR>例如：如果你的weblogic安装在c:\bea\weblogic700.....<BR>那么，你的文件根路径就是c:\.<BR>所以，有两种方式能够让你访问你的服务器端的文件：<BR>a.使用绝对路径：<BR>比如将你的参数文件放在c:\yourconfig\yourconf.properties，<BR>直接使用 new FileInputStream("yourconfig/yourconf.properties");<BR>b.使用相对路径：<BR>相对路径的根目录就是你的webapplication的根路径，即WEB-INF的上一级目录，将你的参数文件放在yourwebapp\yourconfig\yourconf.properties，<BR>这样使用：<BR>new FileInputStream("./yourconfig/yourconf.properties");<BR>这两种方式均可，自己选择。<BR><BR>(2).Tomcat<BR><BR>在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin<BR><BR>(3).Resin<BR><BR>不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET<BR>的路径为根.比如用新建文件法测试File f = new File("a.htm");<BR>这个a.htm在resin的安装目录下 <BR><BR>(4).如何读相对路径哪？<BR><BR>在Java文件中getResource或getResourceAsStream均可<BR><BR>例：getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web发布根路径下WEB-INF/classes<BR><BR>(5).获得文件真实路径<BR><BR>string&nbsp;&nbsp;file_real_path=request.getRealPath("mypath/filename");&nbsp;&nbsp;<BR><BR>通常使用request.getRealPath("/");&nbsp;&nbsp;<BR><BR>3.文件操作的类<BR><BR><PRE class=overflow>import java.io.*;<BR>import java.net.*;<BR>import java.util.*;<BR>//import javax.swing.filechooser.*;<BR>//import org.jr.swing.filter.*;<BR><BR>/**<BR>* 此类中封装一些常用的文件操作。<BR>* 所有方法都是静态方法，不需要生成此类的实例，<BR>* 为避免生成此类的实例，构造方法被申明为private类型的。<BR>* @since&nbsp;&nbsp;0.1<BR>*/<BR><BR>public class FileUtil {<BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 私有构造方法，防止类的实例化，因为工具类不需要实例化。<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;private FileUtil() {<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 修改文件的最后访问时间。<BR>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<BR>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考<BR><BR>虑中。&lt;/b&gt;<BR>&nbsp;&nbsp; * @param file 需要修改最后访问时间的文件。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static void touch(File file) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;long currentTime = System.currentTimeMillis();<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (!file.exists()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println("file not found:" + file.getName());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println("Create a new file:" + file.getName());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (file.createNewFile()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.out.println("Succeeded!");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.err.println("Create file failed!");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (IOException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.err.println("Create file failed!");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;boolean result = file.setLastModified(currentTime);<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (!result) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.err.println("touch failed: " + file.getName());<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 修改文件的最后访问时间。<BR>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<BR>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考<BR><BR>虑中。&lt;/b&gt;<BR>&nbsp;&nbsp; * @param fileName 需要修改最后访问时间的文件的文件名。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static void touch(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;touch(file);<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 修改文件的最后访问时间。<BR>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<BR>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考<BR><BR>虑中。&lt;/b&gt;<BR>&nbsp;&nbsp; * @param files 需要修改最后访问时间的文件数组。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static void touch(File[] files) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; files.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;touch(files);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 修改文件的最后访问时间。<BR>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<BR>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考<BR><BR>虑中。&lt;/b&gt;<BR>&nbsp;&nbsp; * @param fileNames 需要修改最后访问时间的文件名数组。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static void touch(String[] fileNames) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File[] files = new File[fileNames.length];<BR>&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; fileNames.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files = new File(fileNames);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;touch(files);<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 判断指定的文件是否存在。<BR>&nbsp;&nbsp; * @param fileName 要判断的文件的文件名<BR>&nbsp;&nbsp; * @return 存在时返回true，否则返回false。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static boolean isFileExist(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;return new File(fileName).isFile();<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 创建指定的目录。<BR>&nbsp;&nbsp; * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<BR>&nbsp;&nbsp; * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;/b&gt;<BR>&nbsp;&nbsp; * @param file 要创建的目录<BR>&nbsp;&nbsp; * @return 完全创建成功时返回true，否则返回false。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static boolean makeDirectory(File file) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File parent = file.getParentFile();<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (parent != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return parent.mkdirs();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;return false;<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 创建指定的目录。<BR>&nbsp;&nbsp; * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<BR>&nbsp;&nbsp; * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;/b&gt;<BR>&nbsp;&nbsp; * @param fileName 要创建的目录的目录名<BR>&nbsp;&nbsp; * @return 完全创建成功时返回true，否则返回false。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static boolean makeDirectory(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return makeDirectory(file);<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 清空指定目录中的文件。<BR>&nbsp;&nbsp; * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。<BR>&nbsp;&nbsp; * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<BR>&nbsp;&nbsp; * @param directory 要清空的目录<BR>&nbsp;&nbsp; * @return 目录下的所有文件都被成功删除时返回true，否则返回false.<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static boolean emptyDirectory(File directory) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;boolean result = false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;File[] entries = directory.listFiles();<BR>&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; entries.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!entries.delete()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result = false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;return true;<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 清空指定目录中的文件。<BR>&nbsp;&nbsp; * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。<BR>&nbsp;&nbsp; * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<BR>&nbsp;&nbsp; * @param directoryName 要清空的目录的目录名<BR>&nbsp;&nbsp; * @return 目录下的所有文件都被成功删除时返回true，否则返回false。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static boolean emptyDirectory(String directoryName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File dir = new File(directoryName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return emptyDirectory(dir);<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 删除指定目录及其中的所有内容。<BR>&nbsp;&nbsp; * @param dirName 要删除的目录的目录名<BR>&nbsp;&nbsp; * @return 删除成功时返回true，否则返回false。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static boolean deleteDirectory(String dirName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;return deleteDirectory(new File(dirName));<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 删除指定目录及其中的所有内容。<BR>&nbsp;&nbsp; * @param dir 要删除的目录<BR>&nbsp;&nbsp; * @return 删除成功时返回true，否则返回false。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static boolean deleteDirectory(File dir) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;if ( (dir == null) || !dir.isDirectory()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new IllegalArgumentException("Argument " + dir +<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; " is not a directory. ");<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;File[] entries = dir.listFiles();<BR>&nbsp;&nbsp;&nbsp;&nbsp;int sz = entries.length;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; sz; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (entries.isDirectory()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!deleteDirectory(entries)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!entries.delete()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if (!dir.delete()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;return true;<BR>&nbsp;&nbsp;}<BR><BR><BR><BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 返回文件的URL地址。<BR>&nbsp;&nbsp; * @param file 文件<BR>&nbsp;&nbsp; * @return 文件对应的的URL地址<BR>&nbsp;&nbsp; * @throws MalformedURLException<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<BR>&nbsp;&nbsp; * @deprecated 在实现的时候没有注意到File类本身带一个toURL方法将文件路径转换为URL。<BR>&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请使用File.toURL方法。<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static URL getURL(File file) throws MalformedURLException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;String fileURL = "file:/" + file.getAbsolutePath();<BR>&nbsp;&nbsp;&nbsp;&nbsp;URL url = new URL(fileURL);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return url;<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 从文件路径得到文件名。<BR>&nbsp;&nbsp; * @param filePath 文件的路径，可以是相对路径也可以是绝对路径<BR>&nbsp;&nbsp; * @return 对应的文件名<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getFileName(String filePath) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(filePath);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return file.getName();<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 从文件名得到文件绝对路径。<BR>&nbsp;&nbsp; * @param fileName 文件名<BR>&nbsp;&nbsp; * @return 对应的文件路径<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getFilePath(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return file.getAbsolutePath();<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。<BR>&nbsp;&nbsp; * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，<BR>&nbsp;&nbsp; * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。<BR>&nbsp;&nbsp; * @param filePath 转换前的路径<BR>&nbsp;&nbsp; * @return 转换后的路径<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String toUNIXpath(String filePath) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;return filePath.replace('\\', '/');<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 从文件名得到UNIX风格的文件绝对路径。<BR>&nbsp;&nbsp; * @param fileName 文件名<BR>&nbsp;&nbsp; * @return 对应的UNIX风格的文件路径<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<BR>&nbsp;&nbsp; * @see #toUNIXpath(String filePath) toUNIXpath<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getUNIXfilePath(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return toUNIXpath(file.getAbsolutePath());<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到文件的类型。<BR>&nbsp;&nbsp; * 实际上就是得到文件名中最后一个“.”后面的部分。<BR>&nbsp;&nbsp; * @param fileName 文件名<BR>&nbsp;&nbsp; * @return 文件名中的类型部分<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getTypePart(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.lastIndexOf('.');<BR>&nbsp;&nbsp;&nbsp;&nbsp;int length = fileName.length();<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1 || point == length - 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "";<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(point + 1, length);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到文件的类型。<BR>&nbsp;&nbsp; * 实际上就是得到文件名中最后一个“.”后面的部分。<BR>&nbsp;&nbsp; * @param file 文件<BR>&nbsp;&nbsp; * @return 文件名中的类型部分<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getFileType(File file) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;return getTypePart(file.getName());<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到文件的名字部分。<BR>&nbsp;&nbsp; * 实际上就是路径中的最后一个路径分隔符后的部分。<BR>&nbsp;&nbsp; * @param fileName 文件名<BR>&nbsp;&nbsp; * @return 文件名中的名字部分<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getNamePart(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int point = getPathLsatIndex(fileName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;int length = fileName.length();<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;else if (point == length - 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int secondPoint = getPathLsatIndex(fileName, point - 1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (secondPoint == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (length == 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(0, point);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(secondPoint + 1, point);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(point + 1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到文件名中的父路径部分。<BR>&nbsp;&nbsp; * 对两种路径分隔符都有效。<BR>&nbsp;&nbsp; * 不存在时返回""。<BR>&nbsp;&nbsp; * 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"/path/"返回""。<BR>&nbsp;&nbsp; * @param fileName 文件名<BR>&nbsp;&nbsp; * @return 父路径，不存在或者已经是父目录时返回""<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getPathPart(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int point = getPathLsatIndex(fileName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;int length = fileName.length();<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "";<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;else if (point == length - 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int secondPoint = getPathLsatIndex(fileName, point - 1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (secondPoint == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(0, secondPoint);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(0, point);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到路径分隔符在文件路径中首次出现的位置。<BR>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<BR>&nbsp;&nbsp; * @param fileName 文件路径<BR>&nbsp;&nbsp; * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static int getPathIndex(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.indexOf('/');<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.indexOf('\\');<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;return point;<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到路径分隔符在文件路径中指定位置后首次出现的位置。<BR>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<BR>&nbsp;&nbsp; * @param fileName 文件路径<BR>&nbsp;&nbsp; * @param fromIndex 开始查找的位置<BR>&nbsp;&nbsp; * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回-1。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static int getPathIndex(String fileName, int fromIndex) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.indexOf('/', fromIndex);<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.indexOf('\\', fromIndex);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;return point;<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到路径分隔符在文件路径中最后出现的位置。<BR>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<BR>&nbsp;&nbsp; * @param fileName 文件路径<BR>&nbsp;&nbsp; * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static int getPathLsatIndex(String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.lastIndexOf('/');<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.lastIndexOf('\\');<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;return point;<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到路径分隔符在文件路径中指定位置前最后出现的位置。<BR>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<BR>&nbsp;&nbsp; * @param fileName 文件路径<BR>&nbsp;&nbsp; * @param fromIndex 开始查找的位置<BR>&nbsp;&nbsp; * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static int getPathLsatIndex(String fileName, int fromIndex) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.lastIndexOf('/', fromIndex);<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.lastIndexOf('\\', fromIndex);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;return point;<BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 将文件名中的类型部分去掉。<BR>&nbsp;&nbsp; * @param filename 文件名<BR>&nbsp;&nbsp; * @return 去掉类型部分的结果<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String trimType(String filename) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int index = filename.lastIndexOf(".");<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (index != -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return filename.substring(0, index);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return filename;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;/**<BR>&nbsp;&nbsp; * 得到相对路径。<BR>&nbsp;&nbsp; * 文件名不是目录名的子节点时返回文件名。<BR>&nbsp;&nbsp; * @param pathName 目录名<BR>&nbsp;&nbsp; * @param fileName 文件名<BR>&nbsp;&nbsp; * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件名<BR>&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<BR>&nbsp;&nbsp; */<BR>&nbsp;&nbsp;public static String getSubpath(String pathName,String fileName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;int index = fileName.indexOf(pathName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (index != -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(index + pathName.length() + 1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;}<BR><BR>}</PRE><BR><BR>4.遗留问题<BR><BR>目前new FileInputStream()只会使用绝对路径，相对没用过，因为要相对于web服务器地址，比较麻烦<BR><BR>还不如写个配置文件来的快哪<BR><BR>5.按Java文件类型分类读取配置文件<BR><BR>配置文件是应用系统中不可缺少的，可以增加程序的灵活性。java.util.Properties是从jdk1.2就有的类，一直到现在都支持load()方法，jdk1.4以后save(output,string) -&gt;store(output,string)。如果只是单纯的读，根本不存在烦恼的问题。web层可以通过Thread.currentThread().getContextClassLoader().<BR>getResourceAsStream("xx.properties")获取；Application可以通过new FileInputStream("xx.properties");直接在classes一级获取。关键是有时我们需要通过web修改配置文件，我们不能将路径写死了。经过测试觉得有以下心得：<BR><BR>1.servlet中读写。如果运用Struts或者Servlet可以直接在初始化参数中配置，调用时根据servlet的getRealPath("/")获取真实路径，再根据String file = this.servlet.getInitParameter("abc");获取相对的WEB-INF的相对路径。<BR>例：<BR><PRE class=overflow>InputStream input = Thread.currentThread().getContextClassLoader().<BR>getResourceAsStream("abc.properties");<BR>Properties prop = new Properties();<BR>prop.load(input);<BR>input.close();<BR>OutputStream out = new FileOutputStream(path);<BR>prop.setProperty("abc", “test");<BR>prop.store(out, “–test–");<BR>out.close();</PRE><BR><BR>2.直接在jsp中操作，通过jsp内置对象获取可操作的绝对地址。<BR>例：<BR><PRE class=overflow>// jsp页面<BR>String path = pageContext.getServletContext().getRealPath("/");<BR>String realPath = path+"/WEB-INF/classes/abc.properties";<BR><BR>//java 程序<BR>InputStream in = getClass().getClassLoader().getResourceAsStream("abc.properties"); // abc.properties放在webroot/WEB-INF/classes/目录下<BR>prop.load(in);<BR>in.close();<BR><BR>OutputStream out = new FileOutputStream(path); // path为通过页面传入的路径<BR>prop.setProperty("abc", “abcccccc");<BR>prop.store(out, “–test–");<BR>out.close();</PRE><BR><BR>3.只通过Java程序操作资源文件<BR><PRE class=overflow>InputStream in = new FileInputStream("abc.properties"); // 放在classes同级<BR><BR>OutputStream out = new FileOutputStream("abc.properties");</PRE><BR><img src ="http://www.blogjava.net/TrampEagle/aggbug/28741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-01-20 11:17 <a href="http://www.blogjava.net/TrampEagle/articles/28741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>