Overview概述
Java GUI 工具包一直是一个倍受争议的话题。同样的争论也发生在其他编程语言如Smalltalk。实际上每个平台无关的语言都存在着这样的争论。Java作为当前最受广泛使用的编程语言而尤为突出。
这场争论在支持模拟组件(如widgets和control,在下文中也称之为仿造组件)和支持本地组件(在下文中也称之为原生组件)的人们之间展开,于是Java开发者形成了两个不同的阵营,提倡使用模拟组件的Swing,和提倡使用原生组件的SWT。
历史
Internet上有许多围绕这一争论的故事。你可能已经听说过它们中的大多数了,其中之一有助于让你理清头绪,让我们就从这里开始,Amy Fowler是Swing阵营的一个倡导者。
回到上个世纪90年代,曾几何时有3家庞大的Smalltalk公司——IBM、Parc-Place和 Digitalk。在90年代初期3家公司的市场份额大致相等,生活是美好的。Parc-Place采用仿窗口部件(emulated widgets)的设计(即Swing的设计),IBM和Digitalk则采用原生窗口部件(native widgets)。后来IBM压倒了另外两家,因此他们打算合并成一家,假设叫做Parc-Place Digitalk。随后当他们试图将他们的产品融合到一个叫做Jigsaw的计划中时爆发了一场大战,计划由于政治原因失败了(开发人员实际上已经能让它运转起来),就因为原生和仿造两派的死战。
Amy赢得了精神上的胜利,不过IBM赢得了他们所有的生意,因为这两家公司在一整年里除了吵架什么都没做。当尘埃落定之后PPD(Parc-Place Digitalk当时已改名为Objectshare,跟Windscale改名为Sellafield的原因相同——让人们淡忘之前发生的灾难)的股票价格从60美元掉到了低于1美元1股。他们因为伪报收入被NASDAQ摘牌,从此消失。
当时,AWT已经出现了。SUN当时已经建立了一套基本的可移植控件类,这些类映射到不同操作系统上的原生窗口组件(native widget),当时的AWT还满是漏洞,远不能称为可靠,还需要SUN的coder们去修补。然后Amy被雇佣了,她承诺通过轻量级方案解决所有窗口组件的问题,以此说服SUN管理层让她当了GUI开发部门的头头。随后Amy雇佣了所有她过去在Parc-Place的旧朋友,让他们来开发Swing。
在IBM,VisualAge for Java最初是用Smalltalk(用的是原生窗口组件)写的,当将这些工具向Java代码库迁移时,他们需要一套窗口组件。IBM这边的开发人员都是原来搞Smalltalk的那一批人,他们对管理层要求用Swing来构建WebSphere Studio工具都非常不情愿。“Swing是个可怕的充满缺陷的怪兽“。因此开始了一个新的项目,把他们的Smalltalk原生窗口组件移植到Java上去。这个工具集后来被成为SWT,S开始是Simple的缩写,不过后来变成了Standard的缩写。这个项目获得了成功,被运用在发布的 VisualAge Micro Edition产品中。他们当时发现在Swing读事件队列的时候用了一种可能留下内存漏洞的方式,而不得不采用他们自己的查询 Windows事件队列的循环,以纠正这个错误。这促成了他们关于SWT和 AWT/Swing不能共存的决定。他们把这个工具包放到了Eclipse中,这是一个来自于早期Visual Age的工具平台。
你应该已经从上述的故事中对三者的历史有了大概的了解,尤其是SWT。现在你也许会觉得,IBM创建SWT的理由是合理的而Swing应该沿用SWT采用的方式。这样的观点是片面的,当你深入了解到Java的本质之后,你会发现其实并不像你想象的那么简单。
先决条件
什么才是Java本质的,影响到工具集设计的特征呢?或者说,什么才是Java GUI工具集设计的先决条件呢?
答案来自于Sun对Java的承诺之一:write once, run anywhere(一次编写,随处运行)。这是Java不同于其他语言的优势所在。在Java被创建之前,软件的跨平台性能是开发者,特别是那些希望对多平台提供支持的开发者的梦魇。在当今的生活中Internet的使用已经相当的普遍了,在世界不同角落的人们在不同的平台上工作着。软件提供商为不同的操作系统提供支持是再平凡不过的事情。Java的write-once-run-anywhere(WORA)承诺显然减轻了开发者的负担,极大地提高了软件开发的生产力。
然而编写跨平台的应用程序,你必须使用支持平台无关性的标准库。这些标准库包括语言支持,公共用途,网络,I/O和GUI工具集等。所以当Sun开始设计GUI工具集的时候,首要任务就是考虑一个设计良好的平台无关的API。AWT和Swing都被小心地设计以保证平台兼容性。SWT则相反,它在设计之初并不以扩展性为原则,它为一个专有的IDE Visual Age for Java而设计,Windows作为这个IDE的首选运行环境拥有很高的优先级考量。SWT API类似于WIndows,通常它并不如Swing的扩展性好,尽管Steve Northover,SWT之父,辩称SWT是平台无关的,你可以很容易地发现许多Windows API的痕迹。
区别
GUI应用程序是软件的一种主要类型,所以Java的GUI库应该是标准化并被集成到JRE平台中的。然而不同的操作系统有不同的GUi风格和组件集。有一些组件在所以平台上有相似的观感。这些共有组件如按钮,标签,文本域,单选框等被称为标准组件。不同的GUI工具集提供了不同的组件集。GUI工具集总是遵循不同的原则来选择组件类型和特征以实现。考察一个工具集,有两个不同的要素:组件类型和组件特征。
Terms
首先让我图解两个数学概念:最大公约数和最小公倍数。如下图,三个集合代表不同的操作系统。相交的部分是最大公约数,合并的部分是最小公倍数。
现在让我们来考察Java GUI工具集AWT,SWT和Swing的组件类型和特征
AWT
AWT组件集遵循最大公约数原则,即AWT只拥有所有平台上都存在的组件的公有集合。所以你在AWT中无法获取如表或树等高级组件,因为它们在某些平台上不支持。AWT的组件特征同样遵循这一原则。它只提高平台上公有的特征。例如AWT按钮不能附着图片,因为在Motif平台上,按钮是不支持图片的。
由于它低劣的组件集和特征,AWT无法吸引开发者。它是Sun不推荐使用的,只是为了确保向下兼容和支持Swing。
SWT
SWT最初的目标之一是为了提供比AWT更为丰富的组件集。它遵循最小公倍数原则以提供一个各个平台上包含的组件的并集。思路是如果一个组件在某个平台上包含,那么SWT就会包装它并用java代码和JNI来调用它。如果一个组件在某一平台上不存在,它就会用继承并绘制Composite的方式来模拟组件。一个SWT Composite类似于AWT的Canvas。以这种方式,SWT提供了较AWT更为丰富的组件集。值得指出的是SWT的JNI封装不同于AWT,它的模拟也不同于Swing。
在组件特征方面,SWT类似于AWT。它遵循最小公倍数原则。在早期的SWT版本中,SWT按钮因为和AWT同样的原因不支持附着图片。在之后的版本中,许多缺失的特征采用模拟的方式补全。但仍有许多特征无法采用纯粹的模拟实现。SWT将组件的控制交给本地操作系统。它难以扩展。只有例如图形装饰等特征可以借助模拟绘制来自定义实现。所以严格意义上将,SWT组件的组件集和特征因其难于扩展而不如Swing来得丰富。
Swing
Swing是三者中最强大和灵活的。在组件类型上,它遵循最大公约数原则。由于Swing可以控制自身GUI系统的全部并有很好的可扩展和灵活性,它几乎可以创建所有你想象得到的组件。唯一的限制是它的AWT容器。在Swing中你还不能跨平台地实现真正的透明化和不规则矩形窗口,因为Swing依赖于AWT顶层容器例如Applet, Window, Frame and Dialog等。除此之外,Swing几乎实现了所有平台上的标准组件。
在组件特征上,Swing遵循最小公倍数原则。它拥有所有平台上可提供的组件特征。不仅如此,你还可以继承已有的Swing组件并添加新的特性。
上面比较主要是在API级别上的。让我们将比较的焦点转移到实现细节上。Swing和SWT/AWT的区别是Swing是纯Java实现,而SWT和AWT是Java和JNI的混合。当然,它们的目标都是相同的,提供一个跨平台的APIs。然而为了达到这一点,SWT和AWT不得不牺牲一些组件和特性以提供一个通用的APIs。
AWT
一个AWT组件通常是一个包含了对等体接口类型引用的组件类。这个引用指向本地对等体实现。举java.awt.Label为例,它的对等体接口是LabelPeer。LabelPeer是平台无关的。在不同平台上,AWT提供不同的对等体类来实现LabelPeer。在Windows上,对等体类是WlabelPeer,它调用JNI来实现label的功能。这些JNI方法用C或C++编写。它们关联一个本地的label,真正的行为都在这里发生。作为整体,AWT组件由AWT组件类和AWT对等体提供了一个全局公用的API给应用程序使用。一个组件类和它的对等体接口是平台无关的。底层的对等体类和JNI代码是平台相关的。
SWT
SWT也使用JNI的方法论来实现。但细节不同于AWT。SWT的拥护者听到人们拿SWT和AWT相提并论可是会很生气的,Steve Northover,SWT之父,就曾为此抱怨过。
没错,它们是不同的。让我们深究SWT的代码。在SWT中,各个平台上唯一相同的部分是组件的接口,是类和方法的定义签名。所有的底层代码都是平台差异的。SWT为每个平台提供了OS类。这个类用JNI封装了许多本地APIs。SWT组件类通过把这些JNI方法黏合在一起提供一个有意义的功能。
例如,在Windows上,文本域的选择是由一个系统调用处理的。这个系统调用在Windows的OS类中作为一个本地方法实现。所以在Windows平台的Text的setSelection方法中只用到了一个JNI调用。
然而,在motif上,文本域的选择包含两个本地调用。SWT就在motif的OS类中实现了两个调用。所以在motif上组件类需要作两次调用来实现文本的选择。
现在你应该能看出SWT和AWT的最大不同了,它们使用了不同的对等体编程方式来消除平台差异。SWT用java代码或有JNI实现的java对等体来黏合系统调用。而AWT把代码包含在对等体中,使情况复杂化了,我个人觉得SWT的方法更加明智。
SWING
到了Swing这里,一切就变得清晰和直接了。除了顶层容器,Swing的实现不依赖于具体平台。它掌管了所有的控制和资源。Swing所需要的是事件输入来驱动系统,以及承接自顶层AWT容器的图形处理,字体和颜色。普通的Swing组件可以看作是AWT容器的一块逻辑区域。它们并没有注册对等体。所有添加到同一顶层容器的Swing组件共享它的AWT对等体以获取系统资源,如字体,图形处理等。Swing将组件自己的数据结构存储在JVM的空间中。它完全由自己管理画图处理,事件分发和组件布局。
由于AWT和SWT都持有对本地组件的引用,它们必须以正确的方式释放这些引用以避免内存泄露和JVM崩溃。AWT将绝大多数资源管理任务交给系统,将开发者从单调乏味的资源管理中解救出来。然而这使得AWT的实现复杂化了。一旦它实现了,开发者很少有机会犯错误并使他们的程序崩溃。
SWT用的是另一种方法。大体上,SWT让开发者自己来管理资源。它的一条著名的规则是:谁创建,谁释放。因此开发者必须谨慎地显式调用dispose方法释放每一个由他创建的组件和资源。这简化了SWT的实现模型,但把开发者摆在了因错误编码而易于造成程序崩溃这一风险之上。
模拟方式的区别
Swing和SWT在它们的实现上都使用了模拟。SWT只模拟平台上缺失的组件。区别是SWT的模拟更像是AWT的Canvas实现的模拟。SWT的Composite类有它自己在操作系统中相应的对等体。它从自己的对等体中获得所有它所需要的资源如图形处理的对象,字体和颜色等。它直接从操作系统获取所有的事件并进行处理。然而,Swing组件在操作系统中没有相应的对等体。它只是一块顶层容器中的逻辑区域,实际上它从顶层容器的对等体中借用资源。Swing的事件并不是底层系统产生的事件。它们实际是由顶层容器处理AWT事件所产生的伪事件。我们会在稍后的事件部分中详细介绍它。
图形层结构
另一个不同之处是Swing组件的z-order系统是来自于AWT组件的。如上所述,Swing组件与顶层AWT容器共享一个对等体。因此,Swing组件也和顶层容器有相同的z-order。SWT和AWT组件都有不同于顶层容器的z-order,通常是高于顶层容器。故而如果AWT组件和Swing组件混合在一起的话,Swing组件将可能被AWT组件遮住。当操作系统开始更新UI的时候,顶层容器和Swing组件总是先于AWT组件绘制。当它们完成绘制,AWT组件会覆盖Swing可能绘制过的地方。因此不提倡Swing和AWT组件的混用。如果有一个浮动的Swing组件如菜单,AWT组件很可能遮盖菜单。
布局管理器
并不是三者中的所有部分都是不同的。布局管理器是一个例外。开发GUI应用程序,当容器改变大小的时候,组件需要重定位或改变大小。在传统的编程语言中,这依靠监听大小改变的事件来实现。相应的片段散落在源代码的各个角落降低了程序的可读性。Java引入了将布局代码封装的思路,称之为布局管理器。当布局管理器对象被设置到一个容器中,它自动处理大小改变的事件。当大小改变时,管理器的布局方法被调用以重定位子组件或调整它们的形状。
AWT,SWT和Swing都以这样的方式来组织,而都有它们各种独特的布局管理器。由于AWT和Swing拥有一个共同的超类java.awt.Component,它们的布局管理器可以交替地使用。
Look And Feel观感
包括SWT和AWT在内的本地工具集并不支持Look And Feel机制。它们将组件捆绑在操作系统上,有其优势和劣势。其中的一个劣势是它们不支持可插拔的Look And Feel。将绘制处理交由操作系统完成剥夺了它们实现自定义组件Look And Feel的能力,也就使得它们无法提供这种机制。Look And Feel机制越来越成为GUI工具集中不可缺少的一部分。
Swing拥有很好的Look And Feel支持。你甚至可以动态地改变Swing应用程序的Look And Feel,鉴于AWT和SWT将组件控制完全交给操作系统处理,这是它们所无法超越的任务。我曾经听很多人抱怨过Sun在Swing上的设计。他们觉得Swing为什么不像SWT那样沿用AWT的思路呢?事实上,Look And Feel机制正是Swing走到这个方向上的原因之一。如果Swing遵循的是包装已有的组件并模拟不存在的组件的路线,那它就无法提供Look And Feel机制。因为提供Look And Feel机制是本地策略所无法完成的任务。
Graphics and Fonts图形和字体
Swing作为一个仿生系统,它的图形工具集较之AWT和SWT强大许多。Swing基于其自身系统中的两个基础组件群:Java 2D和AWT。Java 2D在Java中是强大的类库,它为高级图像处理,颜色管理,图形绘制和填充,坐标系变换和字体生成提供丰富的特性。相较之下,AWT和AWT仅对这些特性提供有限访问,它们是相对原始和低级的。
JavaBeans Specification Conformity JavaBeans规范一致性
Swing和AWT在设计之初就秉承了JavaBeans规范,它们的组件类与JavaBeans规范一致。然而SWT并没有很好的遵循这一规范。例如,在SWT的组件类中没有无参的构造器。每个组件都必须至少拥有一个单参数的构造器。这个参数就是父组件的引用。这意味着无论何时组件被创建,它都直接被添加到一棵组件树中。一个组件无法脱离于已注册的本地对等体而存在。这样,SWT就能让由编程者创建的组件在display的dispose方法被调用的时候自动被释放。
More on Resource Management更多在资源管理方面的内容
SWT的组件构造器策略可以排除某些内存泄露的可能性。AWT在资源管理方面也有类似的问题。但它采用了不同的方式解决。当AWT组件被创建的时候,相应的对等体并不会立即被创建。即便它被添加到一棵组件树,而如果这棵树还不可见,那么对等体仍不会被创建。只有当顶层容器被设为可见,这些对等体才会被创建。创建对等体的方法通常在addNotify中,它们通常递归地调用父组件的addNotify直到整棵组件树上的对等体都被创建了。当顶层容器由dispose方法销毁的时候,一个对应的方法removeNotify将会被递归地调用以释放这些对等体。这样,AWT在不由开发者介入的情况下管理了它的资源。
Event System事件系统
一个事件要求特定的动作被执行,它被作为消息由外界或系统自身发送给GUI系统。这些事件包括来自计算机设备如鼠标键盘和网络端口的I/O中断,以及GUI系统的逻辑事件触发,比如一个按钮的ActionEvent事件。
Single-Threaded vs Multiple-Threaded 单线程 vs 多线程
事件分发遵循两种不同的模型。单线程分发模型和多线程分发模型。
在单线程分发模型中,一个事件从队列中抽出并在同一个线程中被立即处理。事件处理后,紧跟着的下一个事件再被抽出并继续下一轮的循环。在多线程分发模型中,从队列中获取事件的线程启动另一个被称作任务线程的线程,并把事件交给它处理。而获取事件的线程并不等待处理线程的结束。它简单的获取下一个线程并分发它。
事件处理通常涉及应用程序的数据变化。而且这些数据经常是组件需要显示的。多线程分发很容易产生同步问题,它产生多个可能互相干扰的事件处理线程。在一个稳定的GUI系统中,组件应该能够保持视图与模型间的同步。由于同步问题的出现,多线程模型要求开发者拥有更多并发编程的经验。而对于普通编程人员,造成同步错误是很容易的。因此许多GUI系统并不使用这一模型。
单线程模型通过强制事件序列化地被处理提供了实际上的同步。AWT,SWT和Swing 都采用了这一模型来分发事件。但单线程模型也会有它自己的问题。其中之一就是线程专注。既然所有的事件都在一个线程中被分发,如果其中的一个事件的处理费时过久,将会拖延下一个事件的抽取和执行。如果有一个PAINT事件被延后,那么在屏幕上就会呈现为无法响应。这经常使用户感觉到软件很慢。许多这样的低效程序是由于开发者的经验不足造成的。他们的做法是将耗时任务填充到监听器方法中。由于这种错误的编程方式在Swing中大量被使用而尤为突出,这也是它慢而丑陋的坏名声的由来之一。实际上,如果你懂得使用线程,Swing应用程序可以表现出很高的响应度。
Thread Safety线程安全
上述问题的解决方案是启动一个单独的工作者线程来完成耗时处理,这样就能把事件分发线程释放处理以继续运作。如多线程模型中所做的那样,然而这同样会引入在多线程模型中出现的同步问题。许多GUI工具集都有自己先天性的机制来解决这一问题,例如,在组件树上的组合锁。开发者不需要为如何安全编程而操心。这样的工具集被成为线程安全的。AWT就是其中之一。
然而,由于不必要的同步使得建立这样的GUI系统过于负责并造成了额外的开销。因此,Swing和SWT被设计为非线程安全的,这意味着开发者必须谨慎地实现他们的多线程任务。SWT和Swing在运行时线程安全行为上有一个小小的区别。SWT总是检查改变组件的操作是否在事件分发线程上执行。这样,开发者就能够发现同步问题。而Swing不这样,这是Swing的一个不知之初,这其实并不难实现。
Event Dispatching Thread事件分发线程
AWT读取操作系统中的基本事件并在java代码中处理它们。AWT事件分发线程被命名为?AWT-{OS}-Thread。这个线程由AWT系统隐式启动。当AWT应用程序启动时,这个线程在背后启动,在操作系统上抽取和移除事件。当诸如按钮动作这样的逻辑事件被触发时,它传递给注册在按钮上的操作监听器。开发者也能为AWT组件编写鼠标,键盘和绘制事件的事件监听器。这通常通过扩展AWT Canvas组件来完成。这一组件支持了所有已提供的事件,而且你可以通过重写事件处理方法,实现一个自定义的组件。
然而,在Swing中,这是一个不同的线程。Swing把AWT事件作为自身事件系统的一个输入。它获取AWT事件并做一些初始化处理,产生一个对应的Swing事件并把它放到自己的事件队列上。Swing也有自己的事件分发线程,通常命名为EventQueue-0。这个线程从事件队列中抽取Swing事件,就如同AWT从操作系统中抽取那样。然后它把事件分发给目标组件。通常事件首先被分发给组件的顶层容器,然后由顶层容器的dispatch方法处理,它可能被再分发或重定向到一个Swing组件,在那里继续由自己的监听器进行处理。
例如,当一个鼠标移过一个JButton或JFrame时,一个指向JFrame的AWT事件在AWT线程上触发。AWT线程检查它是否需要作更多的处理,然后把它包装成一个Swing的MouseEvent对象并把它添加到EventQueue队列中。当获得MouseEvent事件后,EventQueue-0抽取这个事件并判断出目标Swing组件。这里,这个组件是JButton。然后它产生了一个包含相对坐标位置和事件源的新的MouseEvent重定向到这个JButton上,然后调用这个JButton的dispatch以继续分发。JButton的dispatch过滤事件给特定的方法最终实现由鼠标监听器在该点上的分发的点击。
SWT更类似于AWT。唯一的区别是它要求开发者显式地书写事件循环代码。然而底层的实现细节是不同于AWT的。看看SWT的读取和分发事件代码,它会让你想起MFC的代码风格。
Event Listener事件监听器
AWT,SWT和Swing都有相似的事件监听器模型。它们都使用观察者模式,组件和监听器的连接方式是把监听器添加到组件上,这组成了一个对象网络的模型。当事件被触发并分发给组件,组件调用它的监听器以处理事件。一个监听器也可以继续分发事件给后续的处理,或产生一个新的事件并把它广播到网络中的其他节点上。基本上有两种不同的广播事件方式。一种是同步调用监听器。另一种是异步地将事件发送回队列,它将在新一轮的事件分发中被分发出去。
除了直接发送给队列的方式,Swing还有一些其他的分发异步事件的方法。其中之一是调用SwingUtilities 或EventQueue 的invokeLater,这一方法包装一个Runnable对象到事件中并把它发送给事件队列。这确保了Runnable的run方法能在事件分发线程上执行,保证了线程安全。实际上,Swing的Timer好SwingWorker基于这一机制实现。SWT也有相应的发送异步事件的方式。它的invokeLater的对应方法是display.asyncExec,它以一个Runnable对象作为参数。
我从一个技术层面给出了他们优劣势上的一个清单,以结束本文。
AWT
AWT是Sun不推荐使用的工具集。然而它在许多非桌面环境如移动或嵌入式设备中有着自己的优势。
更少的内存。它对运行在有限环境中的GUI程序的开发,是合适的。
1.更少的启动事件。由于AWT组件是本地由操作系统实现的。绝大多数的二进制代码已经在如系统启动的时候被预装载了,这降低了它的启动事件。
2.更好的响应。由于本地组件由操作系统渲染。
3.从java 1.x时代就为JRE支持的标准GUI工具集,你不用单独安装它,你不用担心平台差异的问题。
4.成熟稳定的。它能够正常工作并很少使你的程序崩溃。
然而,事物都有它们不好的一面。让我们来例数它吧。
1. 更少的组件类型。表和树这些重要的组件缺失了。它们是桌面应用程序中普遍使用的。
2.缺乏丰富的组件特征。按钮不支持图片附着。这很明显是它遵循的设计原则造成的。
3.不支持Look And Feel。AWT被设计为使用本地组件。因此,它依赖系统来提供Look And Feel支持。如果目标系统并不支持这一特性,那么AWT将无法改变它的Look And Feel。
4.无扩展性。AWT的组件是本地组件。JVM中的AWT类实例实际只是包含本地组件的引用。唯一的扩展点是AWT的Canvas组件,你可以从零开始创建自定义组件。然而无法继承和重用一个已有的AWT组件。
SWT
SWT有如下优势:
1.丰富的组件类型。SWT提供了种类繁多的组件,从基础组件如按钮和标签到高级的表格和树。
2.相对的丰富组件特性。尽管SWT也遵循最大公倍数原则,它采用模拟的方式重新设计了对更多组件特性的支持。所以同AWT相比,它有着相对丰富的组件特性。
3.更快的响应时间。基于和AWT同样的原因,SWT组件包装了本地组件,由操作系统实现渲染。操作系统通常对渲染处理做了优化,保存GUI二进制代码为标准库,减少了内存的使用,提高了响应性能。
4.更少的内存消耗。既然操作系统为本地组件提供了优化,这一点就容易理解了。
不足之处:
1.不在JRE的标准库中。因此你必须将它和你的程序捆绑在一起,并为你所要支持的每个操作系统创建单独的安装程序。
2.不够成熟和稳定。SWT因其设计上的一些缺陷,如资源管理,Windows友好等,被认为是不稳定的。它可以在Windows上表现得很好,但在其他操作系统上,它经常是不稳定且容易崩溃的。这很大程度上是因为它把资源管理交给开发者来处理,而并不是所有的开发人员能够正确地处理这些。
3.在非Windows平台下的性能不高。如同第2点提到的,SWT被设计为与Windows API相协调的,这导致了在非Windows平台上的性能问题,糟糕的UI感官,不稳定甚至内存泄露。
4.无Look And Feel 支持。和AWT同样的原因。
5.不可扩展,和AWT同样的原因。在SWT中你可以通过有限的方式扩展一个组件,例如,监听PAINT事件并添加自定义绘图到组件上,但鉴于你只能控制绘制处理的一部分,这是十分有限的。你也只能在操作系统绘制完组件后补充,这并不能很好支持自定义。许多应用程序在自定义行为上有很高的要求。
SWING
Swing是三者中最强大的GUI工具集。它和另外两者相比同样有自己的优劣势。
1、丰富的组件类型。Swing提供了非常广泛的标准组件。这些组件和SWT一样丰富。基于它良好的可扩展性,除了标准组件,Swing还提供了大量的第三方组件。许多商业或开源的Swing组件库在开发多年后都已经可以方便地获取了。
2、丰富的组件特性。Swing不仅包含了所有平台上的特性,它还支持根据程序所运行的平台来添加额外特性。Swing组件特性遵循特定原则,易于扩展,因此能够提供较SWT和AWT更多的功能。
3、好的组件API模型支持。Swing遵循MVC模式,这是一种非常成功的设计模式。它的API成熟并设计良好。经过多年的演化,Swing组件APIs变得越来越强大,灵活和可扩展。它的API设计被认为是最成功的GUI API之一。较之SWT和AWT更面向对象,也更灵活而可扩展。
4、出色的Look And Feel支持。MVC设计模型允许Swing分离组件视图和它的数据模型。它有高级的UI委托来将UI渲染委托给UI类。这些类被注册到一个展现特定的Look And Feel的对象上。已经有上百个Look And Feel 可以提高各种各样的GUI风格。你甚至可以基于其他人的成果编写组件的Look And Feel 。
5、标准的GUI库。Swing和AWT一样是JRE中的标准库。因此,你不用单独地将它们随你的应用程序一起分发。它们是平台无关的,所以你不用担心平台兼容性。
6、成熟稳定。Swing已经开发出来7年之久了。在Java5之后它变得越来越成熟稳定。由于它是纯Java实现的,不会有SWT的兼容性问题。Swing在每个平台上都有相同的性能,不会有明显的性能差异。
7、可扩展和灵活性。Swing完全在Java空间中实现。它可以控制它所需要的一起。Swing基于MVC的结构使得它可以发挥Java作为一门面向对象语言的优势。它提供了许多扩展组件的方法。让我们来列举一下:
A.继承已有组件;
B.靠复合组件的方式扩展。
C.从零开始使用JComponent编写自定义组件;
D.使用渲染器和编辑器机制扩展复制的Swing组件,如JList,JComboBox,JTable,JTree等;
E.基于已有Look And Feel 或从零开始创建新的Look And Feel;
F.使用LayeredPane,GlassPane或拖放机制开发高级的组件,例如浮动的固定组件,自定义弹出窗口,自定义菜单等。
8、总体上良好的性能。Swing的速度是其为人诟病的一点。然而随着JRE的开发,Swing的性能如今已经有了很大的提高。特别是Java5之后,Swing的总体速度能够接近本地小控件系统。
一个GUI的速度总是从两个方面被衡量:响应时间和数据反馈时间。
响应事件指从事件任务出现到组件更新UI的这段时间。例如按下一个按钮,拖动一个组件,改变一个多标签面板等。在这个方面本地组件总能比模拟组件有更好的响应。AWT和SWT通常比Swing表现出更好的响应时间。然而事实并非总是如此。这取决于操作系统。如果本地组件没有被良好的实现,那结果就是相反的。例如Windows开发了不错的GUI库,而Linux平台通常差得较远。SWT可能在Windows上表现得比Swing好,而在Linux上表现得比Swing差。也就是说,AWT/SWT的性能决定于底层平台。随着JRE的开发,Swing的响应性能能够随着JVM的优化,更好的实现方式,以及图形硬件加速而得到长足的改进。在Windows上,Java6的Swing可以媲美SWT的性能。在非Windows环境中,Swing可以表现出更好的响应时间。
数据输送时间是指用于将应用程序数据传递给UI组件所需要的时间。例如,一个学生管理系统要求从数据库中装载学生信息并在一个表格中显示出来。花费在从内存到表格组件的数据传递时间被称为数据输送时间.在这个方面,Swing通常比其他二者的性能更好。或许当数据量不大的情况下并不明显。但当海量的数据被输送给表格的时候,这一点就显而易见了。为了理解这一点,提醒你注意JVM和本地操作系统是两个分离的运行时环境。由于JNI的调用在两个环境中跨越式地发生,通常比一个普通的Java调用花费更长的时间。通常这包含两个处理。一个是Java数据结构转换为本地数据结构,另一个是方法返回时的本地数据结构转换为Java对象。其他的性能开销暂时忽略不计。当一个大范围数组的数据从本地组件中输送过来,大量反复的JNI调用将极大地拖垮性能。
Swing的另一个优势是它有许多的组件模型以提高输送的性能。例如TableModel被映射为两个维度上的数组。这样,在Swing组件中甚至不需要进行数据方式的转换。Swing直接将应用程序数据显示在屏幕上,节省了在数据转换上所花费的事件。
Swing也有不足之处:
比AWT和SWT更多的内存消耗。Swing自己实现了所有组件。因此,它在运行时装载了大量的类。一些其他的问题来源于小的可变对象的创建,如Rectangle,Point,这些对象基于同步的考虑通常不可重用。Java在堆上创建所以对象。小的对象通常导致了额外的堆空间消耗。许多小的对象较之大对象更难以有效地被垃圾回收。因此,Swing应用程序通常无法及时回收大而小的对象。这种情况的普遍就会导致性能下降。
更多的启动时间。现在JVM已经快得多了。许多人甚至扬言它可以媲美C++的实现。但多数的Java应用程序还是看上去很慢。实际上,Java性能的很多问题来源于类装载机制。这是一个I/O操作,故而能够明显地降低Java应用程序的速度。也许这是每个动态链接系统中都要面对的问题吧。Swing通常包含了上千个Swing类的使用,在Swing应用程序可以显示它的主窗口之前,它比AWT或SWT装载了多得多的类。这严重降低了Swing的启动时间。这种问题也许会相对好一点如果Swing的类是以共享系统库的方式预加载的。
上述的比较总的来说是技术上的总结。相对其他方面的因素也会影响你对一个工具集的选择。例如,文档,支持,学习曲线和社区等,但既然我们关注的是技术层面,就不在这里讲的太多了。