<?xml version="1.0" encoding="gb2312"?>

<!-- RSS generated by oioj.net on 4/16/2004 ; 感谢LeXRus提供 RSS 2.0 文档; 此文件可自由使用，但请保留此行信息 --> 
<!-- Source download URL: http://blogger.org.cn/blog/rss2.asp       -->
<rss version="2.0">

<channel>
<title>巴斯光年的博客</title>
<link>http://blogger.org.cn/blog/blog.asp?name=yug001</link>
<description>巴斯光年的博客</description>
<copyright>blogger.org.cn</copyright>
<generator>W3CHINA Blog</generator>
<webMaster>webmaster@blogger.org.cn</webMaster>
<item>
<title><![CDATA[20060725面试总结]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=yug001&amp;id=16926</link>
<author>yug001</author>
<pubDate>2006/7/25 22:23:11</pubDate>
<description><![CDATA[哎，今天面试收获了不少，还是自己懂的太少了，要努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊努力啊]]></description>
</item><item>
<title><![CDATA[20060725面试之layout模式]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=yug001&amp;id=16925</link>
<author>yug001</author>
<pubDate>2006/7/25 22:16:46</pubDate>
<description><![CDATA[<P>哎，被人问到layout采用什么模式，回答他装饰模式，真是笨啊。</P>
<H2>从Java类库看设计模式 (Composite,Strategy,Iterato</H2>
<P><BR><FONT size=2>NetReptile推荐&nbsp;[2005-8-8]</FONT><BR><FONT size=2>出处：IBM DW</FONT><BR><FONT size=2>作者：刘武东</FONT><BR><FONT size=2>&nbsp;<FONT size=+0><BR><BR></P>
<P>
<DIV align=center>
<P align=left><FONT size=2>本文除了还会介绍一个结构型的Composite模式之外，还会有两个行为模式登场。实际上在前面的内容中，我们已经接触到行为模式了：Observer和Command就是两个典型的行为模式。行为模式更多的注重于算法和对象建间职责的分配，也就是说，它会更多的关注于这个模式系统之类的各对象协作间的语义，以及在对象间进行通讯的流控制。<BR><BR><B>Composite模式&nbsp;<BR></B><BR>毫无疑问的，AWT中的Component-Container体系就是一个很好的Composite模式的例子。Container继承于Component，而Container中有可以包含有多个Component，因为Container实际上也是Component，因而Container也可以包含Container。这样通过Component-Container结构的对象组合，形成一个树状的层次结构。这也就是Composite模式所要做的。<BR><BR>Composite模式是为了简化编程而提出的，一般的在编程的时候，如果严格的区分Component和Container的话，有时候会带来许多不便，而且这些往往是没有必要的。比如，我要在一个Container中放置一个Component，我并不需要知道这个Component到底是一个Container，或者就是一个一般的Component，在父级容器中所要做的，只是记录一个Component的引用，在需要的时候调用Component的绘制方法来显示这个Component。当这个Component确实是一个Container的时候，它可以通过Container重载后的绘制方法，完成对这个容器的显示，并把绘制消息传递给到它的子对象去。也就是说，对一个父级容器而言，它并不不关心，其子对象到底是一个Component，还是一个Container。它需要将Component和Container统一对待。<BR><BR><IMG height=250 src="http://www.sawin.cn/doc/share/882774.gif" width=373 border=0></FONT></P></DIV>
<DIV align=center>
<P align=left><FONT size=2><BR>图十一：Composite模式的类图&nbsp;<BR><BR>Composite模式比较简单，实现起来也不复杂，但是有一定的局限性。比如，在处理树的时候，我们往往需要处理三类对象：子树，页节点和非页节点。而在Composite模式中对于子树和非叶节点的区分并不明显，而是把他们合成为一个Composite对象了。而且在GOF给出的Composite的模式中，对于添加，删除子节点等属于Composite对象的的方法，是放在了Component对象中的，这虽然在实现的时候可以区分开来，但容易造成一些概念上的误解。<BR><BR>由上所叙，我们可以提出一个改进了的Composite模式，引入子树对象，从而将子树和非叶节点分开，如下图所示：&nbsp;<BR><BR><IMG height=218 src="http://www.sawin.cn/doc/share/883014.gif" width=456 border=0><BR><BR><BR><BR>图十二：Composite模式的一种变体&nbsp;<BR><BR>虽然将Composite从Component类层次中分离出来，但并没有损害Composite模式的内涵。这样做不一定就会比上面的那个要好，各有不同的应用，不过有时候用这样的方法来处理子树要容易些，概念上也更为清晰。<BR><BR><BR>下面的代码，给出了一个Composite模式简单的Java实现：&nbsp;<BR><BR>public abstract class Component{<BR>public abstract void operation();&nbsp;<BR>public void add(Component component){};<BR>public void remove(Component component){};&nbsp;<BR>}<BR><BR>import java.util.*;<BR>public class Composite extends Component{<BR>String name;<BR>ArrayList children = new ArrayList();<BR>public Composite(String name){<BR>this.name = name;<BR>}<BR>public void add(Component component){<BR>children.add(component);<BR>}<BR>public void remove(Component component){<BR>children.remove(component);<BR>}<BR>public void operation(){<BR>System.out.println(name);<BR>Iterator iterator = children.iterator();<BR>while(iterator.hasNext()){<BR>Component child = (Component)iterator.next();<BR>child.operation();<BR>}<BR>}<BR>}<BR><BR>public class Leaf extends Component{<BR>String name;<BR>public Leaf(String name){<BR>this.name = name;<BR>}<BR>public void operation(){<BR>System.out.println(name);<BR>}<BR>}<BR><BR><BR><BR><BR><B>Strategy模式&nbsp;<BR></B><BR>Strategy模式主要用来将算法实现从类中分离出来，并封装在一个单独的类中。更简单的说，对象与其行为（behaviour）这本来紧密联系的两部分被解耦，分别放在了两个不同的类中。这使得对同一个行为，可以方便的在任何时候切换不同的实现算法。而通过对策略的封装，为其提供统一的接口，也可以很容易的引入新的策略。<BR><BR><BR><BR>AWT的LayoutManager，是Strategy模式的一个例子。对于GUI而言，每个组件（Component）在容器中（Container）的排放是需要遵循一定的算法的。通常的方法是使用绝对坐标，就像VB，Delphi之类的工具所作的那样，记录每个组件在容器中的位置。这当然会带来一些问题，比如在窗体缩放的时候，就需要手工编码改变组件的大小和位置，以使得原来的比例得以保存。而在AWT中，引入了布局管理器（LayoutManager）的概念，使得布局的方法大大丰富，编码过程也变得简单。<BR><BR><BR><BR>一个容器，比如Applet，Panel等，仅仅记录其包含的组件，而布局管理器中封装了对容器中组件进行布局的算法，具体地说，就是指明容器中组件的位置和尺寸的大小。通过布局管理器，你只需要确定想放置的组件间的相对位置即可，这一方面简化编码，另一方面也有助于实现软件的平台无关性。<BR><BR><BR><IMG height=278 src="http://www.sawin.cn/doc/share/883726.gif" width=554 border=0><BR><BR><BR><BR><BR>图十三：AWT中的容器和布局管理器的关系&nbsp;<BR><BR>每一个容器均有一个布局管理器，当容器需要布置它的组件时，它调用布局管理器的方法布置容器内的组件。LayoutManager2继承于LayoutManager，提供更为细致的布局功能，它可以让布局管理器为组件加上约束条件已确定组件如何被布置。例如，为了确定组件被摆放在边框内的位置，BorderLayout在它的组件上加上方向指示。<BR><BR><BR>特别的，通过实现LayoutManager或者LayoutManager2接口，可以很容易实现自定义的布局策略。<BR><BR>回到模式的话题上来，如果有几个很相似的类，其区别仅仅是在个别行为上的动作不同，这时候就可以考虑使用Strategy模式。这样，通过策略组合，将原来的多个类精简为一个带有多个策略的类。这很符合OO设计的原则：找到变化的部分，并将其封装起来！Strategy模式同样的为子类继承提供了一个好的替代方案，当使用继承机制的时候，行为的改变是静态的，你指能够改变一次--而策略是动态的，可以在任何时候，切换任何次数。更为重要的是，策略对象可以在不同的环境中被不同的对象所共享。以布局管理器为例，虽然每一个容器只有一个布局管理器，但是一个布局管理器可以为多个容器工作。<BR><BR><BR><IMG height=267 src="http://www.sawin.cn/doc/share/885331.gif" width=433 border=0><BR><BR><BR><BR>图十四：Strategy模式的类图&nbsp;<BR><BR>Strategy模式也有一些缺点，比如，应用程序必须知道所有的策略对象，并从中选者其一。而且在策略对象被使用的时候，它和Context对象之间通常是紧耦合的，Context对象必须为策略对象提供与具体算法相关的数据或者其它的东西，而这些数据的传递可能并不能够风装载抽象地策略类中，因为并不是所有的算法都会需要这些数据的。另外，因为策略对象通常由应用程序所创建，Context对象并不能够控制Strategy的生命期，而在概念上，这个策略应该从属于Context对象，其生命期不应该超出Context的范围对象。<BR><BR><BR>通常的，Strategy很容易和Bridge模式相混淆。确实，他们有着很相近的结构，但是，他们却是为解决不同的问题而设计的。Strategy模式注重于算法的封装，而Bridge模式注重于分离抽象和实现，为一个抽象体系提供不同的实现。<BR><BR><BR><B>Iterator 模式&nbsp;<BR></B><BR>Iterator模式用来规格化对某一数据结构的遍历接口。<BR><BR><BR>JDK中在Collection Framework中引入了Iterator接口，提供对一个Collection的遍历。每一个Collection类中都定义有从Collection接口中继承而来的iterator()方法，来得到一个Iterator对象，我们称之为遍历器，Iterator接口很简单：<BR><BR>hasNext()：用来判断在遍历器中是否还有下一个元素。<BR><BR>next()：返回遍历器中的下一个元素。<BR><BR>remove()：在被遍历的Collection类中删除最后被返回的那个对象。<BR><BR>我们就以最为常用的Vector为例，看看在Collection Framework中，Iterator模式是如何被实现的。在此之前，我们需要先了解一些Vector和Collection&nbsp;<BR>Framework的结构。<BR><BR>Collection接口作为这个Framework的基础，被所有其它的集合类所继承或者实现。对Collection接口，有一个基本的实现是抽象类AbstractCollection，它实现了大部分与具体数据结构无关的操作。比如判断一个对象是否存在于这个集合类中的contains()方法：&nbsp;<BR><BR>public boolean contains(Object o) {<BR>Iterator e = iterator();<BR>if (o==null) {<BR>while (e.hasNext())<BR>if (e.next()==null)<BR>return true;<BR>} else {<BR>while (e.hasNext())<BR>if (o.equals(e.next()))<BR>return true;<BR>}<BR>return false;<BR>}<BR><BR>而这其中调用的iterator()方法是一个抽象方法，有赖于具体的数据结构的实现。但是对于这个containers()方法而言，并不需要知道具体的Iterator实现，而只需要知道它所提供的接口，能够完成某类任务既可，这就是抽象类中抽象方法的作用。其它的在AbstractCollection中实现的非抽象方法，大部分都是依赖于抽象方法iterator()方法所提供的Iterator接口来实现的。这种设计方法是引入抽象类的一个关键所在，值得仔细领悟。<BR><BR><BR>List接口继承Collection接口，提供对列表集合类的抽象；对应的AbstractList类继承AbstractCollection，并实现了List接口，作为List的一个抽象基类。它对其中非抽象方法的实现，也大抵上与AbstractCollection相同，这儿不再赘叙。<BR><BR><BR><BR>而对应于Collection的Iterator，List有其自己的ListIterator，ListIterator继承于Iterator，并添加了一些专用于List遍历的方法：<BR><BR>boolean hasPrevious()：判断在列表中当前元素之前是否存在有元素。<BR><BR>Object previous()：返回列表中当前元素之前的元素。<BR><BR>int nextIndex()：<BR><BR>int previousIndex()：<BR><BR>void set(Object o)：<BR><BR>void add(Object o)：<BR><BR>ListIterator针对List，提供了更为强劲的功能接口。在AbstractList中，实现了具体的iterator()方法和listIterator()方法，我们来看看这两个方法是如何实现的：&nbsp;<BR><BR><BR>public Iterator iterator() {<BR>return new Itr(); //Itr是一个内部类<BR>}<BR>private class Itr implements Iterator {<BR>int cursor = 0;//Iterator的计数器，指示当前调用next()方法时会被返回的元素的位置<BR>int lastRet = -1;//指示刚刚通过next()或者previous()方法被返回的元素的位置，-1<BR>//表示刚刚调用的是remove()方法删除了一个元素。<BR><BR>//modCount是定义在AbstractList中的字段，指示列表被修改的次数。Iterator用//这个值来检查其包装的列表是否被其他方法所非法修改。<BR>int expectedModCount = modCount;<BR><BR>public boolean hasNext() {<BR>return cursor != size();<BR>}<BR>public Object next() {<BR>try {<BR>//get方法仍然是一个抽象方法，依赖于具体的子类实现<BR>Object next = get(cursor);<BR>//检查列表是否被不正确的修改<BR>checkForComodification();<BR>lastRet = cursor++;<BR>return next;<BR>} catch(IndexOutOfBoundsException e) {<BR>checkForComodification();<BR>throw new NoSuchElementException();<BR>}<BR>}<BR>public void remove() {<BR>if (lastRet == -1)<BR>throw new IllegalStateException();<BR>checkForComodification();<BR>try {<BR>//同样remove(int)也依赖于具体的子类实现<BR>AbstractList.this.remove(lastRet);<BR>if (lastRet &lt; cursor)<BR>cursor--;<BR>lastRet = -1;<BR>expectedModCount = modCount;<BR>} catch(IndexOutOfBoundsException e) {<BR>throw new ConcurrentModificationException();<BR>}<BR>}<BR>final void checkForComodification() {<BR>if (modCount != expectedModCount)<BR>throw new ConcurrentModificationException();<BR>}<BR>}<BR><BR><BR>这儿的设计技巧和上面一样，都是使用抽象方法来实现一个具体的操作。抽象方法作为最后被实现的内容，依赖于具体的子类。抽象类看起来很像是一个介于接口和子类之间的一个东西。<BR><BR>从设计上来讲，有人建议所有的类都应该定义成接口的形式，这当然有其道理，但多少有些极端。当你需要最大的灵活性的时候，应该使用接口，而抽象类却能够提供一些缺省的操作，最大限度的统一子类。抽象类在许多应用框架（Application&nbsp;<BR>Framework）中有着很重要的作用。例如，在一个框架中，可以用抽象类来实现一些缺省的服务比如消息处理等等。这些抽象类能够让你很容易并且自然的把自己的应用嵌入到框架中去。而对于依赖于每个应用具体实现的方法，可以通过定义抽象方法来引入到框架中。&nbsp;<BR><BR><BR>其实在老版本的JDK中也有类似的概念，被称为Enumeration。Iterator其实与Enmeration功能上很相似，只是多了删除的功能。用Iterator不过是在名字上变得更为贴切一些。模式的另外一个很重要的功用，就是能够形成一种交流的语言（或者说文化）。有时候，你说Enumeration大家都不明白，说Iterator就都明白了。<BR><BR><BR><B>小结：&nbsp;<BR></B><BR>这部分介绍了三个模式：Composite，Strategy和Iterator。Composite是一个结构性的模式，用来协调整体和局部的关系，使之能够被统一的安排在一个树形的结构中，并简化了编程。Strategy模式与Bridge模式在结构上很相似，但是与Bridge不同在于，它是一个行为模式，更侧重于结构的语义以及算法的实现。它使得程序能够在不同的算法之间自由方便的作出选择，并能够在运行时切换到其他的算法，很大程度上增加了程序的灵活性。Iterator模式提供统一的接口操作来实现对一个数据结构的遍历，使得当数据结构的内部算法发生改变时，客户代码不需要任何的变化，只需要改变相应的Iterator实现，就可以无缝的集成在原来的程序中。<BR><BR><BR><B>参考资源：&nbsp;<BR></B><BR>设计模式：可复用面向对象软件的基础 机械工业出版社&nbsp;<BR><BR>Java2类库增补版 机械工业出版社&nbsp;<BR><BR>Java2图形设计：AWT卷 机械工业出版社&nbsp;<BR><BR>可视化面向对象建模技术 北京航天航空工业大学出版社&nbsp;<BR><BR>DK1.3源代码&nbsp;<BR><BR>UML用户指南 机械工业出版社&nbsp;<BR><BR><BR><BR>关于作者&nbsp;<BR><BR>刘武东：武汉大学计算机学院2001级研究生。研究方向：可重用组件技术，设计模式。</FONT></P></DIV>
<P>　</P></FONT></FONT>]]></description>
</item><item>
<title><![CDATA[2006-07-25面试之Swing线程安全]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=yug001&amp;id=16924</link>
<author>yug001</author>
<pubDate>2006/7/25 22:04:33</pubDate>
<description><![CDATA[<P>呵呵，今天面试，被人问的快糊了，自认为自己对Swing很了解，对方问了二个关于Swing方面的问题。</P>
<P>1、Swing在线程是安全的吗？我回答：不是，但不知道为什么。查询后知道swing的大部分方法在线程上不是安全的，只</P>
<P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD align=middle bgColor=#f7fbfe colSpan=2 height=40><SPAN style="FONT-SIZE: 16pt; COLOR: #05006c"><B>线程与Swing</B></SPAN></TD></TR>
<TR>
<TD class="table2 style3" align=middle bgColor=#f7fbfe colSpan=2 height=20><A href="http://www.51cto.com/" target=_blank><FONT color=#000000>http://www.51cto.com</FONT></A> 2005-10-10 16:48 作者:Hans Muller，Kathy Walrath 出处:java.sun.com
<SCRIPT language=javascript src="/php/count.php?view=yes&amp;artID=6788"></SCRIPT>
 </TD></TR>
<TR>
<TD align=middle bgColor=#f7fbfe colSpan=2 height=10></TD></TR>
<TR>
<TD align=left bgColor=#f7fbfe colSpan=2 height=0>
<TABLE class=shortmsg cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=shortmsg_td>【导读】本文讨论线程和Swing组件。目的不仅是为了帮助你以线程安全的方式使用Swing API，而且解释了我们为什么会选择现在这样的线程方案。</TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD align=middle bgColor=#f7fbfe colSpan=2 height=10></TD></TR>
<TR>
<TD vAlign=top align=left width="98%" bgColor=#f7fbfe height=142><SPAN class=top11>
<P>
<P>本文包括以下内容：<BR><BR>单线程规则：Swing线程在同一时刻仅能被一个线程所访问。一般来说，这个线程是事件派发线程（event-dispatching thread）。<BR><BR>规则的例外：有些操作保证是线程安全的。<BR><BR>事件分发：如果你需要从事件处理（event-handling）或绘制代码以外的地方访问UI，那么你可以使用SwingUtilities类的invokeLater()或invokeAndWait()方法。<BR><BR>创建线程：如果你需要创建一个线程??比如用来处理一些耗费大量计算能力或受I/O能力限制的工作??你可以使用一个线程工具类如SwingWorker或Timer。<BR><BR>为什么我们这样实现Swing：我们用一些关于Swing的线程安全的背景资料来结束这篇文章。</P>
<P>Swing的规则是：<BR><BR>一旦Swing组件被具现化（realized），所有可能影响或依赖于组件状态的代码都应该在事件派发线程中执行。<BR><BR>这个规则可能听起来有点吓人，但对许多简单的程序来说，你用不着为线程问题操心。在我们深入如何撰写Swing代码之前，让我们先来定义两个术语：具现化（realized）和事件派发线程（event-dispatching thread）。<BR><BR>具现化的意思是组建的paint()方法已经或可能会被调用。一个作为顶级窗口的Swing组件当调用以下方法时将被具现化：setVisible(true)、show()或（可能令你惊奇）pack()。当一个窗口被具现化，它包含的所有组件都被具现化。另一个具现化一个组件的方法是将它放入到一个已经具现化的容器中。稍后你会看到一些对组件具现化的例子。<BR><BR>事件派发线程是执行绘制和事件处理的线程。例如，paint()和actionPerformed()方法会自动在事件派发线程中执行。另一个将代码放到事件派发线程中执行的方法是使用SwingUtilities类的invokeLater()方法。<BR><BR>所有可能影响一个已具现化的Swing组件的代码都必须在事件派发线程中执行。但这个规则有一些例外：<BR><BR>有些方法是线程安全的：在Swing API的文档中，线程安全的方法用以下文字标记：<BR><BR>This method is thread safe, although most Swing methods are not. <BR><BR>（这个方法是线程安全的，尽管大多数Swing方法都不是。）<BR><BR>一个应用程序的GUI常常可以在主线程中构建和显示：下面的典型代码是安全的，只要没有（Swing或其他）组件被具现化：<BR><BR>public class MyApplication {public static void main(String[] args) { JFrame f = new JFrame("Labels"); // 在这里将各组件 // 加入到主框架…… f.pack(); f.show(); // 不要再做任何GUI工作…… } }<BR><BR>上面所示的代码全部在“main”线程中运行。对f.pack()的调用使得JFrame以下的组件都被具现化。这意味着，f.show()调用是不安全的且应该在事件派发线程中执行。尽管如此，只要程序还没有一个看得到的GUI，JFrame或它的里面的组件就几乎不可能在f.show()返回前收到一个paint()调用。因为在f.show()调用之后不再有任何GUI代码，于是所有GUI工作都从主线程转到了事件派发线程，因此前面所讨论的代码实际上是线程安全的。<BR><BR>一个applet的GUI可以在init()方法中构造和显示：现有的浏览器都不会在一个applet的init()和start()方法被调用前绘制它。因而，在一个applet的init()方法中构造GUI是安全的，只要你不对applet中的对象调用show()或setVisible(true)方法。<BR><BR>要顺便一提的是，如果applet中使用了Swing组件，就必须实现为JApplet的子类。并且，组件应该添加到的JApplet内容窗格（content pane）中，而不要直接添加到JApplet。对任何applet，你都不应该在init()或start()方法中执行费时的初始化操作；而应该启动一个线程来执行费时的任务。<BR><BR>下述JComponent方法是安全的，可以从任何线程调用：repaint()、revalidate()、和invalidate()。repaint()和revalidate()方法为事件派发线程对请求排队，并分别调用paint()和validate()方法。invalidate()方法只在需要确认时标记一个组件和它的所有直接祖先。<BR><BR>监听者列表可以由任何线程修改：调用addListenerTypeListener()和removeListenerTypeListener()方法总是安全的。对监听者列表的添加/删除操作不会对进行中的事件派发有任何影响。<BR><BR>注意：revalidate()和旧的validate()方法之间的重要区别是，revalidate()会缓存请求并组合成一次validate()调用。这和repaint()缓存并组合绘制请求类似。<BR><BR>大多数初始化后的GUI工作自然地发生在事件派发线程。一旦GUI成为可见，大多数程序都是由事件驱动的，如按钮动作或鼠标点击，这些总是在事件派发线程中处理的。<BR><BR>不过，总有些程序需要在GUI成为可见后执行一些非事件驱动的GUI工作。比如：<BR><BR>在成为可用前需要进行长时间初始化操作的程序：这类程序通常应该在初始化期间就显示出GUI，然后更新或改变GUI。初始化过程不应该在事件派发线程中进行；否则，重绘组件和事件派发会停止。尽管如此，在初始化之后，GUI的更新/改变还是应该在事件派发线程中进行，理由是线程安全。<BR><BR>必须响应非AWT事件来更新GUI的程序：例如，想象一个服务器程序从可能运行在其他机器上的程序得到请求。这些请求可能在任何时刻到达，并且会引起在一些可能未知的线程中对服务器的方法调用。这个方法调用怎样更新GUI呢？在事件派发线程中执行GUI更新代码。<BR><BR>SwingUtilities类提供了两个方法来帮助你在事件派发线程中执行代码：<BR><BR>invokeLater()：要求在事件派发线程中执行某些代码。这个方法会立即返回，不会等待代码执行完毕。<BR><BR>invokeAndWait()：行为与invokeLater()类似，除了这个方法会等待代码执行完毕。一般地，你可以用invokeLater()来代替这个方法。<BR><BR>下面是一些使用这几个API的例子。请同时参阅《The Java Tutorial》中的“BINGO example”，尤其是以下几个类：CardWindow、ControlPane、Player和OverallStatusPane。<BR><BR>使用invokeLater()方法<BR><BR>你可以从任何线程调用invokeLater()方法以请求事件派发线程运行特定代码。你必须把要运行的代码放到一个Runnable对象的run()方法中，并将此Runnable对象设为invokeLater()的参数。invokeLater()方法会立即返回，不等待事件派发线程执行指定代码。这是一个使用invokeLater()方法的例子：<BR><BR>Runnable doWorkRunnable = new Runnable() { public void run() { doWork(); }};SwingUtilities.invokeLater(doWorkRunnable);<BR><BR>使用invokeAndWait()方法<BR><BR>invokeAndWait()方法和invokeLater()方法很相似，除了invokeAndWait()方法会等事件派发线程执行了指定代码才返回。在可能的情况下，你应该尽量用invokeLater()来代替invokeAndWait()。如果你真的要使用invokeAndWait()，请确保调用invokeAndWait()的线程不会在调用期间持有任何其他线程可能需要的锁。<BR><BR>这是一个使用invokeAndWait()的例子：<BR><BR>void showHelloThereDialog() throws Exception { Runnable showModalDialog = new Runnable() { public void run() { JOptionPane.showMessageDialog( myMainFrame, "Hello There"); } }; SwingUtilities.invokeAndWait (showModalDialog);}<BR><BR>类似地，假设一个线程需要对GUI的状态进行存取，比如文本域的内容，它的代码可能类似这样：<BR><BR>void printTextField() throws Exception { final String[] myStrings = new String[2]; Runnable getTextFieldText = new Runnable() { public void run() { myStrings[0] = textField0.getText(); myStrings[1] = textField1.getText(); } }; SwingUtilities.invokeAndWait (getTextFieldText); System.out.println(myStrings[0] + " " + myStrings[1]);}<BR><BR>如果你能避免使用线程，最好这样做。线程可能难于使用，并使得程序的debug更困难。一般来说，对于严格意义下的GUI工作，线程是不必要的，比如对组件属性的更新。<BR><BR>不管怎么说，有时候线程是必要的。下列情况是使用线程的一些典型情况：<BR><BR>执行一项费时的任务而不必将事件派发线程锁定。例子包括执行大量计算的情况，会导致大量类被装载的情况（如初始化），和为网络或磁盘I/O而阻塞的情况。<BR><BR>重复地执行一项操作，通常在两次操作间间隔一个预定的时间周期。<BR><BR>要等待来自客户的消息。<BR><BR>你可以使用两个类来帮助你实现线程：<BR><BR>SwingWorker：创建一个后台线程来执行费时的操作。<BR><BR>Timer：创建一个线程来执行或多次执行某些代码，在两次执行间间隔用户定义的延迟。<BR><BR>使用SwingWorker类<BR><BR>SwingWorker类在SwingWorker.java中实现，这个类并不包含在Java的任何发行版中，所以你必须单独下载它。<BR><BR>SwingWorker类做了所有实现一个后台线程所需的肮脏工作。虽然许多程序都不需要后台线程，后台线程在执行费时的操作时仍然是很有用的，它能提高程序的性能观感。<BR><BR>SwingWorker&acute;s get() method. Here&acute;s an example of using SwingWorker:<BR><BR>要使用SwingWorker类，你首先要实现它的一个子类。在子类中，你必须实现construct()方法还包含你的长时间操作。当你实例化SwingWorker的子类时，SwingWorker创建一个线程但并不启动它。你要调用你的SwingWorker对象的start()方法来启动线程，然后start()方法会调用你的construct()方法。当你需要construct()方法返回的对象时，可以调用SwingWorker类的get()方法。这是一个使用SwingWorker类的例子：<BR><BR>...// 在main方法中： final SwingWorker worker = new SwingWorker() { public Object construct() { return new expensiveDialogComponent(); } }; worker.start();...// 在动作事件处理方法中: JOptionPane.showMessageDialog (f, worker.get());<BR><BR>当程序的main()方法调用start()方法，SwingWorker启动一个新的线程来实例化ExpensiveDialogComponent。main()方法还构造了由一个窗口和一个按钮组成的GUI。<BR><BR>当用户点击按钮，程序将阻塞，如果必要，阻塞到ExpensiveDialogComponent创建完成。然后程序显示一个包含ExpensiveDialogComponent的模式对话框。你可以在MyApplication.java找到整个程序。<BR><BR>使用Timer类<BR><BR>Timer类通过一个ActionListener来执行或多次执行一项操作。你创建定时器的时候可以指定操作执行的频率，并且你可以指定定时器的动作事件的监听者（action listener）。启动定时器后，动作监听者的actionPerformed()方法会被（多次）调用来执行操作。<BR><BR>定时器动作监听者（action listener）定义的actionPerformed()方法将在事件派发线程中调用。这意味着你不必在其中使用invokeLater()方法。<BR><BR>这是一个使用Timer类来实现动画循环的例子：<BR><BR>public class AnimatorApplicationTimer extends JFrame implements ActionListener { ...//在这里定义实例变量 Timer timer; public AnimatorApplicationTimer(...) { ... // 创建一个定时器来 // 来调用此对象action handler。 timer = new Timer(delay, this); timer.setInitialDelay(0); timer.setCoalesce(true); ... } public void startAnimation() { if (frozen) { // 什么都不做。应用户要求 // 停止变换图像。 } else { // 启动（或重启动）动画！ timer.start(); } } public void stopAnimation() { // 停止动画线程。 timer.stop(); } public void actionPerformed (ActionEvent e) { // 进到下一帧动画。 frameNumber++; // 显示。 repaint(); } ...}<BR><BR>在一个线程中执行所有的用户界面代码有这样一些优点：<BR><BR>组件开发者不必对线程编程有深入的理解：像ViewPoint和Trestle这类工具包中的所有组件都必须完全支持多线程访问，使得扩展非常困难，尤其对不精通线程编程的开发者来说。最近的一些工具包如SubArctic和IFC，都采用和Swing类似的设计。<BR><BR>事件以可预知的次序派发：invokeLater()排队的runnable对象从鼠标和键盘事件、定时器事件、绘制请求的同一个队列派发。在一些组件完全支持多线程访问的工具包中，组件的改变被变化无常的线程调度程序穿插到事件处理过程中。这使得全面测试变得困难甚至不可能。<BR><BR>更低的代价：尝试小心锁住临界区的工具包要花费实足的时间和空间在锁的管理上。每当工具包中调用某个可能在客户代码中实现的方法时（如public类中的任何public和protected方法），工具包都要保存它的状态并释放所有锁，以便客户代码能在必要时获得锁。当控制权交回到工具包，工具包又必须重新抓住它的锁并恢复状态。所有应用程序都不得不负担这一代价，即使大多数应用程序并不需要对GUI的并发访问。<BR><BR>这是的SubArctic Java Toolkit的作者对在工具包中支持多线程访问的问题的描述：我们的基本信条是，当设计和建造多线程应用程序，尤其是那些包括GUI组件的应用程序时，必须保证极端小心。线程的使用可能会很有欺骗性。在许多情况下，它们表现得能够极好的简化编成，使得设计“专注于单一任务的简单自治实体”成为可能。在一些情况下它们的确简化了设计和编码。然而，在几乎所有的情况下，它们都使得调试、测试和维护的困难大大增加甚至成为不可能。无论大多数程序员所受的训练、他们的经验和实践，还是我们用来帮助自己的工具，都不是能够用来对付非决定论的。例如，全面测试（这总是困难的）在bug依赖于时间时是几乎不可能的。尤其对于Java来说，一个程序要运行在许多不同类型的机器的操作系统平台上，并且每个程序都必须在抢先和非抢先式调度下都能正常工作。</P>
<P>由于这些固有的困难，我们力劝你三思是否绝对有使用线程的必要。尽管如此，有些情况下使用线程是必要的（或者是被其他软件包强加的），所以subArctic提供了一个线程安全的访问机制。本章讨论了这一机制和怎样在一个独立线程中安全地操作交互树。</P>
<P>他们所说的线程安全机制非常类似于SwingUtilities类提供的invokeLater()和invokeAndWait()方法。</P>
<P>作者：Hans Muller，Kathy Walrath <BR><BR>翻译：郭晓刚<BR><BR>原文来自：java.sun.com<BR></P></SPAN></TD></TR></TBODY></TABLE></P>]]></description>
</item>
</channel>
</rss>