posts - 97,  comments - 93,  trackbacks - 0
 
/**
    *@ the titlt  about a Random example about choose 7 from 33
    *@ the auther Nicky (EN) QuQiang(CH)
    *@ the date   2006.9.1
**/



/** the rules

//一等奖:选中6个正选号及特别号;

//二等奖:选中5个正选号及特别号;

//三等奖:选中5个正选号;

//四等奖:选中4个正选号及特别号;

//五等奖:选中4个正选号或选中3个正选号及特别号;

//六等奖:选中3个正选号。

**/

import java.util.*;

public class NotSameRandoml{
     private static String transform;
     private static String match="00";
     private static int special;


  //产生彩票主逻辑函数
   private static void Nicky(int[] guess){
      Random r = new Random();     //构造伪随机生成器
      //某些映射实现可明确保证其顺序,如 TreeMap 类;某些映射实现则不保证顺序,如 HashMap 类
      Map map = new TreeMap();   //Map 接口的实现
      int n = 0;
      int nt = 1;
      String[] temps=new String[7];
      
      while(true){
          n = r.nextInt(33)+1; //产生1~33的随机数
          //if( map.get(new Integer(n))!=null){
          //   nt = ((Integer)map.get(new Integer(n))).intValue();  
          //}
          //避免了产生的随机数字重复
          if(map.containsValue(n)){
            continue;
          }
          map.put(new Integer(nt),new Integer(n));//将指定的值与此映射中的指定键相关联
          if(map.size()==7){
             break;
          }
          nt++;
      }
      
      Iterator it = map.keySet().iterator(); //返回此映射中包含的键的 set 视图
      for(int i=0;it.hasNext();i++){
       Object o = it.next();
        // 为了更符合现实中33选7,数字为01。。。2位
       int temp=((Integer)map.get(o)).intValue();
       if(temp>=1&&temp<10){
           transform="0"+Integer.toString(temp);
           match=match+" "+transform;
           temps[i]=transform;
           if(((Integer)o).intValue()==7){
                   special=temp;
                   System.out.println(""+transform+"为产生的特别中奖中奖号码");
               }else  
                 System.out.println(""+transform+"为产生的第"+((Integer)o).intValue()+"个中奖号码");
        }else{
        temps[i]=Integer.toString(temp);
        match=match+" "+temps[i];
        if(((Integer)o).intValue()==7){
           System.out.println(""+transform+"为产生的特别中奖中奖号码");
        }else
        System.out.println(""+temp+"为产生的第"+((Integer)o).intValue()+"个中奖号码");
       }
      }
      String creat=match.substring(3);
      System.out.println("所产生的中奖号码串为:"+creat);
      //System.out.println("对产生的中奖号码顺序排序为:"+creats);
      Sort(temps);
      check(map,guess);
   }
   
   //实现排序,也可以调用方法,但是却必须要解决Void问题
    private static void Sort(String[] temps) {
         for(int i=0;i<temps.length;i++){
          for(int j=i+1;j<temps.length;j++){
         if(Integer.parseInt(temps[i])>Integer.parseInt(temps[j])){
            String k;
            k=temps[i];temps[i]=temps[j];temps[j]=k;
         }
        }
      }
      System.out.println("对产生的中奖号码顺序排序为:");
      for(int i=0;i<temps.length;i++){
          System.out.print(temps[i]+" ");
      }
      System.out.println("\n");
    }
   
   
  //输出结果类别  
   private static void check(Map map ,int[] guess){
        int flag=0;
         for(int i=0;i<guess.length-1;i++){
            if(map.containsValue(guess[i])){
                    flag++;
                }
         }
         if(guess[guess.length-1]==special){
            flag=flag+10;
         }
         switch(flag){
              case 16: System.out.println("恭喜您中一等奖");break;
              case 15: System.out.println("恭喜您中二等奖");break;
              case  5: System.out.println("恭喜您中三等奖");break;
              case 14: System.out.println("恭喜您中四等奖");break;
              case 13: System.out.println("恭喜您中五等奖");break;
              case  4: System.out.println("恭喜您中五等奖");break;
              case  3: System.out.println("恭喜您中六等奖");break;
              default: System.out.println("谢谢参与,祝您下次中奖");
         }
   }
   
   
   
  //说明
 
   private static void usage(){
      System.out.println("Usage:java Randomol program [the number you guess for the lucky nums.]");
      System.out.println("\t And the nums. you must typed 7,else you will be cancel by the game rules");
      System.out.println("\t The first 6 nums is your basic nums.,the last one is your special num.");
      System.exit(0);
   }
   
   
  //主函数
   public static void main(String []args){
        if(args.length==0||args.length>7){
              usage();
            }//带入参数
            int[] guess=new int[7];
            for(int i=0;i<args.length;i++){
               guess[i]=Integer.parseInt(args[i]);
            }
            //判断所输入的号码是否相同
        List <Integer> ls= new ArrayList<Integer>();
        for(int i=0;i<guess.length;i++){
           if(ls.contains(guess[i])){
              System.out.println("您所买的号码不可以相同");
              System.exit(0);
           }else  ls.add(guess[i]);
        }
        Nicky(guess);      
        System.exit(0);
   }
}
posted @ 2006-10-12 18:45 wqwqwqwqwq 阅读(439) | 评论 (0)编辑 收藏
呵呵 不知道写的如何。但是总感觉,在UltraEdit下写,还是很爽的,或许是因为学校办公室的电脑内存太小的缘故吧。
 1 /**
 2  * @the author:Nicky(EN) QuQiang(CH)
 3  * @the data :2006.8.27
 4  **/
 5 // 当然本算法最好编制成两到3个文件
 6 
 7 public class HalfSearch{
 8     private static int lengthValue;
 9     private static byte  flag=0;
10     private static int SearchValue=0;
11    
12     //定义返回数组类型的方法  此方法给出的public权限
13     public static int[] Sort(int[] b){
14         for(int i=0;i<b.length;i++){
15             for(int j=i+1;j<b.length;j++){
16                 int temp;
17                 if(b[i]>b[j]){
18                     temp=b[i];b[i]=b[j];b[j]=temp;
19                 }
20             }
21         }
22         System.out.println("\u6309\u5e8f\u6392\u5217\u4e3a:");  //对应汉字为     按序排列为:
23         for(int i=0;i<b.length;i++){
24           System.out.println(b[i]);
25         }                            //冒泡排序法排序  或直接使用JDK提供的sort()函数进行排列
26         return b;
27     }
28 
29     //折半查找
30     private static int Half(int[] a){
31         int m=a.length,k,n=1;
32         System.out.println("\u5f53\u524d\u5171\u8f93\u5165"+m+"\u4e2a\u6570\u7b26"); //对应汉字为  "当前共输入" 个数符
33         int index=0;
34         if(m==1){
35            if(a[0]==SearchValue){
36              flag=1;
37              index=1;
38            }
39            else{
40               flag=0;
41               index=1;
42            }
43         }             //考虑到用户除输入查找数符外,只输入一个数符,即n=m=1
44         for(int i=1;;i++){
45           if(n<m){
46              index+=1;
47              k=(n+m)/2;
48              if(a[k-1]>SearchValue){
49                  m=k-1;      //此时定义最大值为当前中间值的前一个值  //第 次查找未成功,当前值大于SearchValue
50                  System.out.println("\u7b2c"+index+"\u6b21\u67e5\u627e\u672a\u6210\u529f\uff0c \u5f53\u524d\u503c\u5927\u4e8e"+SearchValue);//否则将9改为变量
51              }else if(a[k-1]<SearchValue){
52                  n=k+1;          //未找到,则在后半区间进行查找  //小于
53                  System.out.println("\u7b2c"+index+"\u6b21\u67e5\u627e\u672a\u6210\u529f\uff0c \u5f53\u524d\u503c\u5c0f\u4e8e"+SearchValue);//否则将9改为变量
54              }else{
55                  System.out.println("\u67e5\u627e\u6210\u529f");  //查找成功
56                  flag=1;
57                  break;
58              }
59            }else break;
60          }
61         return index;
62     }                      //也可以定义一个用户需输入的查找值
63    
64     //使用说明
65    private static void usage(){
66       System.out.println("Usage:java HalfSearch program [the number you search] [the array list you give]");
67       System.out.println("\t [the number you search] the only number you want to use it to test this program");
68       System.out.println("\t [the Array list you give] A array list may be it contains it may be not");
69       System.exit(0);
70    }
71       
72     //主函数的功能  传入查找数组
73     public static void main(String[] args){
74         if(args.length==0||args.length==1){
75             usage();
76             System.exit(0);
77         }
78         SearchValue=Integer.parseInt(args[0]);
79         lengthValue=args.length;
80         int index=0;
81         int[] TransArray=new int[args.length-1];
82         for(int i=1;i<args.length;i++){
83             TransArray[i-1]=Integer.parseInt(args[i]);
84             System.out.println(TransArray[i-1]);
85         }
86         index=HalfSearch.Half(HalfSearch.Sort(TransArray));
87         //输出查询结果
88         switch(flag){
89              //在本次折半查找中共经过  次查找,未成功   成功
90             case 0: System.out.println("\u5728\u672c\u6b21\u6298\u534a\u67e5\u627e\u4e2d\u5171\u7ecf\u8fc7"+index+"\u6b21\u67e5\u627e,\u672a\u6210\u529f");break;
91             case 1: System.out.println("\u5728\u672c\u6b21\u6298\u534a\u67e5\u627e\u4e2d\u5171\u7ecf\u8fc7"+index+"\u6b21\u67e5\u627e,\u6210\u529f");break;
92         }
93         System.exit(0);
94     }
95 }


posted @ 2006-08-27 17:02 wqwqwqwqwq 阅读(632) | 评论 (0)编辑 收藏
前几天参加了SUN在大连的暑期免费培训,主要是介绍了Netbeans集成开发环境和jdk1.5、1.6的一些新的特性。原来很少使用netbeans 来作程序,即使它是sun的产品。主要是因为4.1以前的版本过于garbage.但是参加完这次培训之后,对其很有信心。这次培训之后,我对sun的了解更加进了一步。这次培训的老师是李涛老师和叶亮老师,这两位老师我还是很佩服的。虽然sun的中国研究院,没有微软的那么大的名气,但是我感觉这个团体还是一个很令人敬佩和挑战创新的团体。我们无法想象没有IDE的java编程,虽然在原来的文章中我更鼓励初学者使用文本编辑工具。
    对于J2EE的框架理念我感觉这次没有进行深入的介绍,或与也是我感觉比较遗憾的一点了。
下面给大家介绍一点JDK1.5种新引入的泛型:


以下代码摘自java.util包的List接口和Iterator接口的定义:

public interface List<E> {
  void add(E x);
  Iterator<E> iterator();
}
public interface Iterator<E> {
  E next();
  boolean hasNext();
}

类型参数

与尖括号有关的一些东西是JDK 5引入的新东西, 它们是List和Iterator接口的形式的类型参数(简称类型形参)声明.
而在对泛型声明List进行调用时(例如: List<Integer>), 所有出现的类型形参(如
E)的地方, 都会被实际的类型参数(简称类型实参, 如 Integer)所替换掉.

虽然与C++中的模板机制在形式上很想像, 但必需注意, Java中的泛型声明决不会在调用时被展成多份副本: 不论是在源码级, 二进制级, 还是在磁盘或内存中, 都不会被展开!

泛型声明只会也只需编译一次, 并生成一个类文件(class文件), 这一点跟普通的类或接口完全一样.

类型参数其实跟方法或构造器中所用的通常参数相类似. 一个方法中可以声明它用以处理的形式的值参数, 相似地, 泛型声明也有其形式的类型参数; 当方法被调用时, 实际参数会替换形式参数, 然后执行方法体, 同样, 当泛型声明被调用时, 实际的类型参数会替换掉形式的类型参数.

关于命名约定的备注: 推荐使用精炼而简明(如, 单个字符)的方式为形式的类型参数命名. 最好避免使用小写字符, 以便与普通的类或接口的参数相区分开来. 许多宣传品类型使用 E 表示其元素的类型形参.




先看以下两行代码是否合法:
List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2
第一行没问题, 关键在第二行代码, 大多数人会认为, 一个String的List自然更是一个Object的List, 因此, 第2行没问题.

好, 接着看以下代码:
lo.add(new Object()); // 3
String s = ls.get(0); // 4: 试图将一个Object赋给一个String!

可见, 通过别名lo, 我们能对ls, 一个String的列表, 进行数据操作(特别是插入一个Object), 从而导致ls不仅仅是容纳了String对象! 这是Java编译器不容许的! 编译时, 第2行会报告一个编译错误的.

通常, 若Foo是Bar的一个子类型(子类或子接口), G是某个泛型声明, 则G<Foo>并不是G<Bar>的一个子类型.

这一点往往是最难以理解的, 因为它和通常的直观相左. 在直观的理解中, 我们实际上假定了集合是不会变动的, 但java语言中则非如此.




假定要输出一个集合中的所有元素. 以下分别是旧版本及新版本(JDK 1.5)中的写法:

void printCollection(Collection c) {
  Iterator i = c.iterator();
  for( k = 0; k < c.size(); k++) {
    System.out.println( i.next() );
}}

void printCollection(Collection<Object> c) {
  for(Object e : c) {
    System.out.println(e);
}}

问题在于, 新版本反而不如旧版本更有用些. 因为旧版本能使用各种类型的集合作为参数, 但新版本则只能使用Collection<Object>. 而正如上节看到的, Collection<Object>并不是其它各种集合的超类型(父类型).

所有集合的超类型应该写作: Collection<?>, 读作: collection of unknown(未知集合), 即一个集合, 其元素类型可以与任何类型相匹配. 因此称这种类型为通配类型.

正确实现上述旧版本的代码可以这么写:
void printCollection(Collection<?> c) {
  for(Object e : c) {
    System.out.println(e);
}}

这时, 可以用任意类型的集合来调用此方法. 注意在方法体中, 仍然从 c 中读入元素并赋给了Object, 这是没有错误的, 因此不论类型实参是何种集合, 它的元素都是object. 然而, 如果任意给它增加一个object则是不安全的:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时的错误

由于我们不知道c的元素类型是什么, 所以不能给它增加一个object. 方法add()接受一个类型E的参数, 而E与集合的元素类型相同. 当类型实参是?时, 它表示未知的类型, 我们传递给add的参数必须是这个未知类型的子类型. 不幸的是, 既然类型未知, 也就无法决定其子类型, 于是什么也不能作为其参数. 唯一的例外是null, 因为null是所有类型的一个成员.

另一方面, 如果给了一个List<?>, 我们可以调用get()方法并使用其返回的元素. 虽然返回的元素类型是未知类型, 但它总归是一个object, 因此将get()返回的元素赋给一个Object类型的变量, 或将其传递给一个可接受Object的参数都是安全的.

posted @ 2006-08-05 14:36 wqwqwqwqwq 阅读(197) | 评论 (0)编辑 收藏

只对5以上的版本有效,官方网站上有的,不过很多人没看到罢了

Spicing Up Your NetBeans IDE 5.0 With Substance

 

 

This tech tip shows you how you can spice up your NetBeans IDE 5.0's look and feel with color themes, custom button-shapes and watermarks (wallpapers) using Kirill's Substance plug-in. To use Substance with IDE 4.1 or any other Swing-based application, refer to Spicing Up Your Swing GUI With Substance.

IDE with beyonce watermark

Preparations

  • The Substance Look and Feel NBM works with JDK 5.0+ in NetBeans IDE 5.0 (development version).
  • If you want to integrate a watermark into your Swing application, you need a suitable (wide) wallpaper image file. Download this aquamarine JPG from digitalblasphemy as a sample.
  • For some features, you need to know how to access your operating system's commandline

To install the substance-netbeans plugin into NetBeans IDE 5.0,

  1. download substance-netbeans.nbm from Kirill's project page and save the NBM file to your home directory. For this tutorial, I am using version 2.1_02.
  2. To plug the module in, open NetBeans IDE, and choose Tools > Update Center from the menu. In the Update Wizard window, check the box next to Install Manually Downloaded Modules (.nbm files), and then click the Next button.
  3. In the next screen, click the Add Button and select the substance-netbeans NBM file you just downloaded, then click OK. The NBM file shows up in the list of modules to install. Click the Next button.
  4. Make sure the NBM is visible in the field labelled Include to Install, then click the Next button. If a license agreement comes up, click Accept and then Next to proceed.
  5. A screen with the download progress bar appears. Again click the Next button.
  6. Check the Include box next to the line saying "NetBeans-Substance". If you are asked whether you really want to install an unsigned module, click Yes to proceed.
  7. Click the Finish button. If you are told that the IDE must be restarted, check the box saying Restart the IDE and then click the OK button.
  8. After the IDE has restarted, look into the View menu: If you see four new menu items, Button Shapes, Colors, Themes, and Watermarks, the plugin was installed successfully.

http://www.netbeans.org/kb/50/substance-look-and-feel.html#creating

按着步骤作就行了,先要下载一个.nbm的插件。
posted @ 2006-07-22 17:24 wqwqwqwqwq 阅读(559) | 评论 (0)编辑 收藏

在Java中,我们只要利用BigInteger类,可以完成同样功能;这里也测试了异常以及Dialog的产生。

开发环境:Windows Server 2003 Standard Edition SP1, J2SDK 1.5.0_06, Eclipse 3.1.2
源代码如下:

//Factorial.java
import java.math.BigInteger;
import javax.swing.*;


/**
 * 计算任意正整数的阶乘
 * 
 *
 */
public class Factorial {
 public static void main(String[] args) {
  BigInteger x = BigInteger.valueOf(1); //存储结果
  int num = 1; //待计算的整数
  String s = null;
  boolean correct = false;
  do {
   try {
    s = JOptionPane.showInputDialog(null, "请输入要计算的数(正整数):");
    if (s == null)
     break;
    else {
     num = Integer.parseInt(s);
     if (num < 0)
      throw new IllegalArgumentException();
     else correct = true;
    }  
   } catch (NumberFormatException e) {
    JOptionPane.showMessageDialog(null, "数据格式错误!");
    continue;
   } catch (IllegalArgumentException e) {
    JOptionPane.showMessageDialog(null,"请输入一个正整数!");
    continue;
   }
   break;
  } while (true);
  if (correct == true) {
   for (int i = 1; i <= num; i++)
    x = x.multiply(BigInteger.valueOf(i));
   JTextArea textArea = new JTextArea(x.toString(), 5, 30);
   textArea.setEditable(false);
   textArea.setLineWrap(true);
   Object[] object = {num + " ! : ",new JScrollPane(textArea)};
   JDialog dialog = new JOptionPane(object).createDialog(null,"阶乘的结果");
   dialog.setVisible(true);
  }
  System.exit(0);
 }
}

 

运行结果如下:

posted @ 2006-07-22 17:21 wqwqwqwqwq 阅读(2049) | 评论 (2)编辑 收藏

    通常我们学习一门语言的时候,第一个写的程序是输出"Hello,World!",C/C++/Java中的入口都是main方法。实际上,在Java中,即便没有main方法,我们也可以输出Hello,程序如下:

/**
 * @(#)Hello.java
 * 没有main方法,输出Hello,World!
 * 本程序请直接用javac编译,java解释运行
 * 经测试,如果在Eclipse中试图运行,默认情况下,会启动失败
 *
 * @version J2SDK 1.4.2_10-b03
 */
public class Hello {
 static {
  System.out.println("Hello,World!");
  System.exit(0);  //!如果缺少这一句,会出现运行期异常
 }
}

    假使我们为Hello类增加一个main方法,虽然它也是static的,但是静态初始化块会在main之前被执行。

posted @ 2006-07-22 17:19 wqwqwqwqwq 阅读(526) | 评论 (0)编辑 收藏
一直以来都对AI(人工智能)颇感兴趣,可惜都是些外层的皮毛,离入门的境界还远得很。最近在看《程序员》杂志2003年的合订本,突然发现了一个技术专题,就是Robocode,忙到网上下载了一个,初次尝试,感觉很有趣,特此推荐。

    一、Robocode简介:Robocode是一位IBM的工程师Mat Nelson用Java语言所创造的机器人战斗仿真引擎。Robocode不是一个完整游戏,它是个半成品,你所做的就是为你的机器人坦克编写智能程序, 让它能够移动、进攻、防御、躲避、开火。只用几十行代码,就能立刻创造出一个简单但完整机器人,你可以立即将它装入Robocode 引擎中,再从Robocode 自带的那些水平不一的示例机器人中选取一个进行一番对战,还可以在网上下载由其他程序员编写的水平更高的机器人,与它们比试一下,看看自己的水平到底如 何。
    开发Robocode,也是一个极佳的学习Java 语言的过程。随着你的机器人的”智力”水平的提高,你的编程能力也就跟着水涨船高了。


    二、如果您想了解更多的细节,请查看如下的资料:

    1.有关Robocode的详细资料,请查看如下的pdf文档,内含Robocode的详细介绍、Robocode安装、高水平机器人的代码分析、高级瞄 准策略、Robocode内核分析等7篇文章,帮助你入门,全部资料来自《程序员》2003年合订本配套光盘,请点击这里:http://www.loujing.com/mywork/java/Robocode/RobocodeBrief.pdf,(首先请确保你计算机内安装了pdf文档阅读器,可自Adobe的网站自由下载,http://www.chinese-s.adobe.com/products/acrobat/readstep2.html)。

    2.如果您需要了解Robocode更详细的信息,可参看如下网站:
    Robocode在IBM的官方网站为:http://www.alphaworks.ibm.com/tech/robocode;另外,现在Robocode项目已经终止,成为开源项目,您可以从如下站点下载其源代码:http://robocode.sourceforge.net/

    3.您可以自我的网站下载Robocode的1.0.6版本,下载地址为:http://www.loujing.com/mywork/java/Robocode/Robocode.rar,下载解压后双击其中的install.bat即可安装。当然,请首先确保您的机器里安装了J2SE SDK(Java软件开发包),如果您不知道如何设置Java运行环境,请参考本人的另一篇文章:ShowArticle.asp?ArticleID=31

    4.如果您是在Eclipse里进行Robocode的开发,您可以参考我的这篇文章http://www.loujing.com/Article/ShowArticle.asp?ArticleID=33

    希望您在游戏中也能不断提高自己的Java编程水平,新年快乐!
posted @ 2006-07-22 17:18 wqwqwqwqwq 阅读(1716) | 评论 (1)编辑 收藏

/**
 * 程序运行当年的日历,程序运行当日以*号表示
 */

import java.util.*;

public class CalendarTest {
 public static void main(String[] args) {
  GregorianCalendar d = new GregorianCalendar();
  int year = d.get(Calendar.YEAR);
  int month = d.get(Calendar.MONTH);
  int today = d.get(Calendar.DAY_OF_YEAR);
  d.set(d.get(Calendar.YEAR),0,1);                  //设置为当年1月1日(0表示1月)
  do {
   System.out.println(d.get(Calendar.MONTH) + 1 + "月");
   System.out.println("Sun Mon Tue Wed Thu Fri Sat"); //输出月表头
   month = d.get(Calendar.MONTH);                //取得月份    
   int weekday = d.get(Calendar.DAY_OF_WEEK);    //获得当月1号,在一周中是星期几
   for(int i = Calendar.SUNDAY; i < weekday; i++)//控制当月1号的起始位置
    System.out.print("    ");
   do {
    weekday = d.get(Calendar.DAY_OF_WEEK);
    if (d.get(Calendar.DAY_OF_MONTH) < 10)    //如果日期小于10,多输出一个空格,以便对齐
     System.out.print(" ");
    System.out.print(d.get(Calendar.DAY_OF_MONTH));//输出日期
    if (today == d.get(Calendar.DAY_OF_YEAR)) //如果是当日,则输出一个*号
     System.out.print("* ");
    else
     System.out.print("  ");
    if (weekday == Calendar.SATURDAY)         //到达周六,则换行
     System.out.println();
    d.add(Calendar.DAY_OF_YEAR,1);            //日期累加
   } while (d.get(Calendar.MONTH) == month);     //如果还是当月,继续循环
   System.out.println("\n");
  } while (d.get(Calendar.YEAR) == year);           //如果还是当年,继续循环
 }
}
posted @ 2006-07-22 17:17 wqwqwqwqwq 阅读(346) | 评论 (0)编辑 收藏
动机:
充分利用java阵营众多的类库

工具:

IKVM――把java bytecode 转换成IL程序,并提供大部分J2SE 1.4类的.net实现(IKVM.GNU.Classpath.dll)

winrar――提取jar,打包jar

Java IDE(可选)――阅读源代码,浏览类之间的关系,我用的是eclipse

反编译工具(可选)――没源代码时用,主要也是浏览类与类之间的关系,java反编译我用的是DJ Java Decompiler,.net用...
posted @ 2006-05-24 09:15 wqwqwqwqwq 阅读(434) | 评论 (0)编辑 收藏
SWT
Java语言的声望和它在桌面应用程序(GUI程序)所取得的成就显然极不相符,至今仍然很少能看到非常成功Java桌面程序。虽然有JBuilder, Netbean,JProbe等大型软件作为代表,但这仍不能证明Java的GUI程序是成功的:它们的外观总是和同一操作系统平台下的其它软件显得格格 不入。对机器配置的需求也似乎永无止境,这使得它们只能被一些总是拥有当前最高性能PC的程序员们所容忍,或是那些不在乎金钱和时间的专业用户所接受。对 绝大多数计算机使用者来说,AWT或SWING代表着怪异的界面和无法接受的速度。Standard Widget Toolkit(SWT)或许是Java这一噩梦的终结者,广大Java程序员终于可以开发出高效率的GUI程序,它们拥有标准的外观,几乎没有人能看出 你的程序是用Java写出来的,更为重要的是,这些程序是跨平台的。

SWT本身仅仅是Eclipse组织为了开发Eclipse IDE环境所编写的一组底层图形界面 API。或许是无心插柳,或是有意为之,至今为止,SWT无论是在性能和外观上,都超越了SUN公司提供的AWT和SWING。目前Eclipse IDE已经开发到了2.1版本,SWT已经十分稳定。这里指的稳定应该包含两层意思:

一是指性能上的稳定,其中的关键是源于SWT的设计理念。SWT最大化了操作系统的图形构件API,就是说只要操作系统提供了相应图形的构件,那么 SWT只是简单应用JNI技术调用它们,只有那些操作系统中不提供的构件,SWT才自己去做一个模拟的实现。可以看出SWT的性能上的稳定大多时候取决于 相应操作系统图形构件的稳定性。

另一个稳定是指SWT API包中的类、方法的名称和结构已经少有改变,程序员不用担心由于Eclipse组织开发进度很快(Eclipse IDE每天都会有一个Nightly版本的发布),而导致自己的程序代码变化过大。从一个版本的SWT更新至另一版本,通常只需要简单将SWT包换掉就可 以了。

第一个SWT程序

下面让我们开始一个SWT程序。(注意:以下的例子和说明主要针对Windows平台,其它的操作系统应该大同小异)。首先要在Eclipse安装文 件中找到SWT包,Eclipse组织并不提供单独的SWT包下载,必须下载完整的Eclipse开发环境才能得到SWT包。SWT是作为Eclipse 开发环境的一个插件形式存在,可以在${你的eclipse安装路径}\plugins路径下的众多子目录下去搜索SWT.JAR文件,在找到的JAR文 件中包含了SWT全部的Java类文件。因为SWT应用了JNI技术,因此同时也要找到相对应的JNI本地化库文件,由于版本和操作平台的不同,本地化库 文件的名称会有些差别,比如SWT-WIN32-2116.DLL是Window平台下Eclipse Build 2116的动态库,而在Unix平台相应版本的库文件的扩展名应该是.so,等等。注意的是,Eclipse是一个开放源代码的项目,因此你也可以在这些 目录中找到SWT的源代码,相信这会对开发很有帮助。下面是一段打开空窗口的代码(只有main方法)。

import com.e2one.example;
public class OpenShell{
public static void main(String [] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.open();
// 开始事件处理循环,直到用户关闭窗口
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}

确信在CLASSPATH中包括了SWT.JAR文件,先用Javac编译例子程序。编译无错后可运行java -Djava.library.path=${你的SWT本地库文件所在路径} com.e2one.example.OpenShell,比如SWT-WIN32-2116.DLL件所在的路径是C:\swtlib,运行的命令应该 是java -Djava.library.path=c:\swtlib com.e2one.example.OpenShell。成功运行后,系统会打开了一个空的窗口。

剖析SWT API

下面再让我们进一步分析SWT API的组成。所有的SWT类都用org.eclipse.swt做为包的前缀,下面为了简化说明,我们用*号代表前缀org.eclipse.swt, 比如*.widgets包,代表的是org.eclipse.swt.widgets包。

我们最常用的图形构件基本都被包括在*.widgets包中,比如Button,Combo,Text,Label,Sash,Table等等。其中 两个最重要的构件当数Shell和Composite。Shell相当于应用程序的主窗口框架,上面的例子代码中就是应用Shell构件打开一个空窗口。 Composite相当于SWING中的Panel对象,充当着构件容器的角色,当我们想在一个窗口中加入一些构件时,最好到使用Composite作为 其它构件的容器,然后再去*.layout包找出一种合适的布局方式。SWT对构件的布局也采用了SWING或AWT中Layout和Layout Data结合的方式,在*.layout包中可以找到四种Layout和与它们相对应的布局结构对象(Layout Data)。在*.custom包中,包含了对一些基本图形构件的扩展,比如其中的CLabel,就是对标准Label构件的扩展,上面可以同时加入文字 和图片,也可以加边框。StyledText是Text构件的扩展,它提供了丰富的文本功能,比如对某段文字的背景色、前景色或字体的设置。在 *.custom包中也可找到一个新的StackLayout布局方式。

SWT对用户操作的响应,比如鼠标或键盘事件,也是采用了AWT和SWING中的Observer模式,在*.event包中可以找到事件监听的 Listener接口和相应的事件对象,例如常用的鼠标事件监听接口MouseListener,MouseMoveListener和 MouseTrackListener,及对应的事件对象MouseEvent。

*.graphics包中可以找到针对图片、光标、字体或绘图的API。比如可通过Image类调用系统中不同类型的图片文件。通过GC类实现对图片、构件或显示器的绘图功能。

对不同平台,Eclipse还开发了一些富有针对性的API。例如,在Windows平台,可以通过*.ole.win32包很容易的调用ole控件,这使Java程序内嵌IE浏览器或Word、Excel等程序成为可能!

更复杂的程序

下面让我们展示一个比上面例子更加复杂一些的程序。这个程序拥有一个文本框和一个按键,当用户点击按键的时候,文本框显示一句欢迎信息。

为了文本框和按键有比较合理的大小和布局,这里采用了GradLayout布局方式。这种布局是SWT中最常用也是最强大的布局方式,几乎所有的格式 都可能通过GradLayout去达到。下面的程序也涉及到了如何应用系统资源(Color),以及如何释放系统资源。

private void initShell(Shell shell) {
//为Shell设置布局对象
GridLayout gShellLay = new GridLayout();
shell.setLayout(gShellLay);
//构造一个Composite构件作为文本框和按键的容器
Composite panel = new Composite(shell,SWT.NONE);
//为Panel指定一个布局结构对象。
这里让Panel尽可能的占满Shell,
也就是全部应用程序窗口的空间。
GridData gPanelData = new GridData(GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
panel.setLayoutData(gPanelData);
//为Panel也设置一个布局对象。文本框和按键将按这个布局对象来显示。
GridLayout gPanelLay = new GridLayout();
panel.setLayout(gPanelLay);
//为Panel生成一个背景色
final Color bkColor = new Color(Display.getCurrent(),200,0,200);
panel.setBackground(bkColor);
//生成文本框
final Text text = new Text(panel,SWT.MULTI|SWT.WRAP);
//为文本框指定一个布局结构对象,
这里让文本框尽可能的占满Panel的空间。
GridData gTextData = new GridData (GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
text.setLayoutData(gTextData);
//生成按键
Button butt = new Button(panel,SWT.PUSH);
butt.setText("Push");
//为按键指定鼠标事件
butt.addMouseListener(new MouseAdapter(){
public void mouseDown(MouseEvent e){
//当用户点击按键的时候,显示信息
text.setText("Hello SWT");
}
});
//当主窗口关闭时,会触发DisposeListener。这里用来释放Panel的背景色。
shell.addDisposeListener(new DisposeListener(){
public void widgetDisposed(DisposeEvent e) {
bkColor.dispose();
}
});
}

把这段代码中的方法initShell()加入到第一个打开空窗口的例子中,得到的是一段能成功运行的完整GUI应用程序。运行方法可参考第一个例子。  

系统资源的管理

在一个图形化的操作系统中开发程序,都要调用系统中的资源,如图片、字体、颜色等。通常这些资源都是有限的,程序员务必非常小心的使用这些资源:当不再使用它们时,就请尽快释放,不然操作系统迟早会油尽灯枯,不得不重新启动,更严重的会导致系统崩溃。
SWT是用Java开发的,Java语言本身的一大优势就是JVM的"垃圾回收机制",程序员通常不用理会变量的释放,内存的回收等问题。那么对SWT而言,系统资源的操作是不是也是如此?答案是一个坏消息,一个好消息。

坏消息是SWT并没采用JVM的垃圾回收机制去处理操作系统的资源回收问题,一个关键的因素是因为JVM的垃圾回收机制是不可控的,也就是说程序员不 能知道,也不可能做到在某一时刻让JVM回收资源!这对系统资源的处理是致命的,试想你的程序希望在一个循环语句中去查看数万张图片,常规的处理方式是每 次调入一张,查看,然后就立即释放该图片资源,而后在循环调入下一张图片,这对操作系统而言,任何时刻程序占用的仅仅是一张图片的资源。但如果这个过程完 全交给JVM去处理,也许会是在循环语句结束后,JVM才会去释放图片资源,其结果可能是你的程序还没有运行结束,操作系统已经宕掉。

但下面的好消息也许会让这个坏消息变得无关紧要。对于SWT,只需了解两条简单的"黄金"法则就可以放心的使用系统资源!之所以称为黄金法则,一是因 为少,只有两条,二是因为它们出奇的简单。第一条是"谁占用,谁释放",第二条是"父构件被销毁,子构件也同时被销毁"。第一条原则是一个无任何例外的原 则,只要程序调用了系统资源类的构造函数,程序就应该关心在某一时刻要释放这个系统资源。比如调用了

Font font = new Font (display, "Courier", 10, SWT.NORMAL);

那么就应该在不在需要这个Font的时候调用

font.dispose();

对于第二个原则,是指如果程序调用某一构件的dispose()方法,那么所有这个构件的子构件也会被自动调用dispose()方法而销毁。通常这里指的子构件与父构件的关系是在调用构件的构造函数时形成的。比如,

Shell shell = new Shell();
Composite parent = new Composite(shell,SWT.NULL);
Composite child = new Composite(parent,SWT.NULL);

其中parent的父构件是shell,而shell则是程序的主窗口,所以没有相应的父构件,同时parent又包括了child子构件。如果调用 shell.dispose()方法,应用第二条法则,那么parent和child构件的dispose()方法也会被SWT API自动调用,它们也随之销毁。

线程问题

在任何操作平台的GUI系统中,对构件或一些图形API的访问操作都要被严格同步并串行化。例如,在一个图形界面中的按键构件可被设成可用状态 (enable)或禁用状态(disable),正常的处理方式是,用户对按键状态设置操作都要被放入到GUI系统的事件处理队列中(这意味着访问操作被 串行化),然后依次处理(这意味着访问操作被同步)。想象当按键可用状态的设置函数还没有执行结束的时候,程序就希望再设置该按键为禁用状态,势必会引起 冲突。实际上,这种操作在任何GUI系统都会触发异常。

Java语言本身就提供了多线程机制,这种机制对GUI编程来说是不利的,它不能保证图形构件操作的同步与串行化。SWT采用了一种简单而直接的方式 去适应本地GUI系统对线程的要求:在SWT中,通常存在一个被称为"用户线程"的唯一线程,只有在这个线程中才能调用对构件或某些图形API的访问操 作。如果在非用户线程中程序直接调用这些访问操作,那么SWTExcepiton异常会被抛出。但是SWT也在*.widget.Display类中提供 了两个方法可以间接的在非用户线程的进行图形构件的访问操作,这是通过的syncExec(Runnable)和asyncExec(Runnable) 这两个方法去实现的。例如:

//此时程序运行在一个非用户线程中,并且希望在构件panel上加入一个按键。

Display.getCurrent().asyncExec(new Runnable() {
public void run() {
Button butt = new Button(panel,SWT.PUSH);
butt.setText("Push");
}
});

方法syncExec()和asyncExec()的区别在于前者要在指定的线程执行结束后才返回,而后者则无论指定的线程是否执行都会立即返回到当前线程。

SWT的扩展:JFace

JFace与SWT的关系好比Microsoft的MFC与SDK的关系,JFace是基于SWT开发,其API比SWT更加易于使用,但功能却没SWT来的直接。比如下面的代码应用JFace中的MessageDialog打开一个警告对话框:

MessageDialog.openWarning(parent,"Warning","Warning message");

如果只用SWT完成以上功能,语句不会少于30行!

JFace原本是为更加方便的使用SWT而编写的一组API,其主要目的是为了开发Eclipse IDE环境,而不是为了应用到其它的独立应用程序。因此在Eclipse 2.1版本之前,很难将JFace API完整的从Eclipse的内核API中剥离出来,总是要多多少少导入一些非JFace以外的Eclipse核心代码类或接口才能得到一个没有任何编 译错误的JFace开发包。但目前Eclipse组织似乎已经逐渐意识到了JFace在开发独立应用程序起到的重要作用,在正在开发的2.1版本中, JFace也开始变成了和SWT一样的完整独立的开发包,只是这个开发包还在变动中(笔者写本文时,应用的Eclipse2.1M3版本)。JFace开 发包的包前缀是以org.eclipse.jface开头。JAR包和源代码也和SWT一样,也在${你的eclipse安装路径}\plugins路径 下去找。

对开发人员来说,在开发一个图形构件的时候,比较好的方式是先到JFace包去找一找,看是不是有更简洁的实现方法,如果没有再用SWT包去自己实 现。比如JFace为对话框提供了很好的支持,除了各种类型的对话框(比如上面用的MessageDialog,或是带有Title栏的对话框),如要实 现一个自定义的对话框也最好从JFace中的Dialog类继承,而不是从SWT中的*.widget.Dialog继承。

应用JFace中的Preference包中的类很容易为自己的软件做出一个很专业的配置对话框。对于Tree、Table等图形构件,它们在显示的 同时也要和数据关联,例如Table中显示的数据,在JFace中的View包中为此类构件提供了Model-View方式的编程方法,这种方法使显示与 数据分开,更加利于开发与维护。JFace中提供最多的功能就是对文本内容的处理。可以在org.eclipse.jface.text.*包中找到数十 个与文本处理相关类。

与应用程序更近一步

Java程序通常是以class文件的方式发布的,运行class需要JRE或JDK的支持。这又是Java GUI程序的另一个致命的弱点,想象对一个面向广大用户的应用程序来说,无论你的程序功能有多简单,或是你的代码十分的精简,你都不得不让用户去下载一个 7、8M的JRE,那是多么令人沮丧的一件事。而且对程序员来说,Class通常意味着源代码的暴露,反编译的工具让那些居心叵测的人轻易得到你的源代 码。虽然有很多对Class的加密方法,但那总是以牺牲性能为代价的。好在我们还有其它的方式可用:把Class编译成exe文件!

通过SWT开发包,简单、跨平台、可靠等这些Java语言本身所具有的优点正渐渐融合到图形界面的应用程序开发中去。因此,我相信Java语言的另一扇成功之门正在逐渐打开。Java语言的声望和它在桌面应用程序(GUI程序)所取得的成就显然极不相符,至今仍然很少能看到非常成功Java桌面程序。虽然有JBuilder, Netbean,JProbe等大型软件作为代表,但这仍不能证明Java的GUI程序是成功的:它们的外观总是和同一操作系统平台下的其它软件显得格格 不入。对机器配置的需求也似乎永无止境,这使得它们只能被一些总是拥有当前最高性能PC的程序员们所容忍,或是那些不在乎金钱和时间的专业用户所接受。对 绝大多数计算机使用者来说,AWT或SWING代表着怪异的界面和无法接受的速度。Standard Widget Toolkit(SWT)或许是Java这一噩梦的终结者,广大Java程序员终于可以开发出高效率的GUI程序,它们拥有标准的外观,几乎没有人能看出 你的程序是用Java写出来的,更为重要的是,这些程序是跨平台的。

SWT本身仅仅是Eclipse组织为了开发Eclipse IDE环境所编写的一组底层图形界面 API。或许是无心插柳,或是有意为之,至今为止,SWT无论是在性能和外观上,都超越了SUN公司提供的AWT和SWING。目前Eclipse IDE已经开发到了2.1版本,SWT已经十分稳定。这里指的稳定应该包含两层意思:

一是指性能上的稳定,其中的关键是源于SWT的设计理念。SWT最大化了操作系统的图形构件API,就是说只要操作系统提供了相应图形的构件,那么 SWT只是简单应用JNI技术调用它们,只有那些操作系统中不提供的构件,SWT才自己去做一个模拟的实现。可以看出SWT的性能上的稳定大多时候取决于 相应操作系统图形构件的稳定性。

另一个稳定是指SWT API包中的类、方法的名称和结构已经少有改变,程序员不用担心由于Eclipse组织开发进度很快(Eclipse IDE每天都会有一个Nightly版本的发布),而导致自己的程序代码变化过大。从一个版本的SWT更新至另一版本,通常只需要简单将SWT包换掉就可 以了。

第一个SWT程序

下面让我们开始一个SWT程序。(注意:以下的例子和说明主要针对Windows平台,其它的操作系统应该大同小异)。首先要在Eclipse安装文 件中找到SWT包,Eclipse组织并不提供单独的SWT包下载,必须下载完整的Eclipse开发环境才能得到SWT包。SWT是作为Eclipse 开发环境的一个插件形式存在,可以在${你的eclipse安装路径}\plugins路径下的众多子目录下去搜索SWT.JAR文件,在找到的JAR文 件中包含了SWT全部的Java类文件。因为SWT应用了JNI技术,因此同时也要找到相对应的JNI本地化库文件,由于版本和操作平台的不同,本地化库 文件的名称会有些差别,比如SWT-WIN32-2116.DLL是Window平台下Eclipse Build 2116的动态库,而在Unix平台相应版本的库文件的扩展名应该是.so,等等。注意的是,Eclipse是一个开放源代码的项目,因此你也可以在这些 目录中找到SWT的源代码,相信这会对开发很有帮助。下面是一段打开空窗口的代码(只有main方法)。

import com.e2one.example;
public class OpenShell{
public static void main(String [] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.open();
// 开始事件处理循环,直到用户关闭窗口
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}

确信在CLASSPATH中包括了SWT.JAR文件,先用Javac编译例子程序。编译无错后可运行java -Djava.library.path=${你的SWT本地库文件所在路径} com.e2one.example.OpenShell,比如SWT-WIN32-2116.DLL件所在的路径是C:\swtlib,运行的命令应该 是java -Djava.library.path=c:\swtlib com.e2one.example.OpenShell。成功运行后,系统会打开了一个空的窗口。

剖析SWT API

下面再让我们进一步分析SWT API的组成。所有的SWT类都用org.eclipse.swt做为包的前缀,下面为了简化说明,我们用*号代表前缀org.eclipse.swt, 比如*.widgets包,代表的是org.eclipse.swt.widgets包。

我们最常用的图形构件基本都被包括在*.widgets包中,比如Button,Combo,Text,Label,Sash,Table等等。其中 两个最重要的构件当数Shell和Composite。Shell相当于应用程序的主窗口框架,上面的例子代码中就是应用Shell构件打开一个空窗口。 Composite相当于SWING中的Panel对象,充当着构件容器的角色,当我们想在一个窗口中加入一些构件时,最好到使用Composite作为 其它构件的容器,然后再去*.layout包找出一种合适的布局方式。SWT对构件的布局也采用了SWING或AWT中Layout和Layout Data结合的方式,在*.layout包中可以找到四种Layout和与它们相对应的布局结构对象(Layout Data)。在*.custom包中,包含了对一些基本图形构件的扩展,比如其中的CLabel,就是对标准Label构件的扩展,上面可以同时加入文字 和图片,也可以加边框。StyledText是Text构件的扩展,它提供了丰富的文本功能,比如对某段文字的背景色、前景色或字体的设置。在 *.custom包中也可找到一个新的StackLayout布局方式。

SWT对用户操作的响应,比如鼠标或键盘事件,也是采用了AWT和SWING中的Observer模式,在*.event包中可以找到事件监听的 Listener接口和相应的事件对象,例如常用的鼠标事件监听接口MouseListener,MouseMoveListener和 MouseTrackListener,及对应的事件对象MouseEvent。

*.graphics包中可以找到针对图片、光标、字体或绘图的API。比如可通过Image类调用系统中不同类型的图片文件。通过GC类实现对图片、构件或显示器的绘图功能。

对不同平台,Eclipse还开发了一些富有针对性的API。例如,在Windows平台,可以通过*.ole.win32包很容易的调用ole控件,这使Java程序内嵌IE浏览器或Word、Excel等程序成为可能!

更复杂的程序

下面让我们展示一个比上面例子更加复杂一些的程序。这个程序拥有一个文本框和一个按键,当用户点击按键的时候,文本框显示一句欢迎信息。

为了文本框和按键有比较合理的大小和布局,这里采用了GradLayout布局方式。这种布局是SWT中最常用也是最强大的布局方式,几乎所有的格式 都可能通过GradLayout去达到。下面的程序也涉及到了如何应用系统资源(Color),以及如何释放系统资源。

private void initShell(Shell shell) {
//为Shell设置布局对象
GridLayout gShellLay = new GridLayout();
shell.setLayout(gShellLay);
//构造一个Composite构件作为文本框和按键的容器
Composite panel = new Composite(shell,SWT.NONE);
//为Panel指定一个布局结构对象。
这里让Panel尽可能的占满Shell,
也就是全部应用程序窗口的空间。
GridData gPanelData = new GridData(GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
panel.setLayoutData(gPanelData);
//为Panel也设置一个布局对象。文本框和按键将按这个布局对象来显示。
GridLayout gPanelLay = new GridLayout();
panel.setLayout(gPanelLay);
//为Panel生成一个背景色
final Color bkColor = new Color(Display.getCurrent(),200,0,200);
panel.setBackground(bkColor);
//生成文本框
final Text text = new Text(panel,SWT.MULTI|SWT.WRAP);
//为文本框指定一个布局结构对象,
这里让文本框尽可能的占满Panel的空间。
GridData gTextData = new GridData (GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
text.setLayoutData(gTextData);
//生成按键
Button butt = new Button(panel,SWT.PUSH);
butt.setText("Push");
//为按键指定鼠标事件
butt.addMouseListener(new MouseAdapter(){
public void mouseDown(MouseEvent e){
//当用户点击按键的时候,显示信息
text.setText("Hello SWT");
}
});
//当主窗口关闭时,会触发DisposeListener。这里用来释放Panel的背景色。
shell.addDisposeListener(new DisposeListener(){
public void widgetDisposed(DisposeEvent e) {
bkColor.dispose();
}
});
}

把这段代码中的方法initShell()加入到第一个打开空窗口的例子中,得到的是一段能成功运行的完整GUI应用程序。运行方法可参考第一个例子。  

系统资源的管理

在一个图形化的操作系统中开发程序,都要调用系统中的资源,如图片、字体、颜色等。通常这些资源都是有限的,程序员务必非常小心的使用这些资源:当不再使用它们时,就请尽快释放,不然操作系统迟早会油尽灯枯,不得不重新启动,更严重的会导致系统崩溃。
SWT是用Java开发的,Java语言本身的一大优势就是JVM的"垃圾回收机制",程序员通常不用理会变量的释放,内存的回收等问题。那么对SWT而言,系统资源的操作是不是也是如此?答案是一个坏消息,一个好消息。

坏消息是SWT并没采用JVM的垃圾回收机制去处理操作系统的资源回收问题,一个关键的因素是因为JVM的垃圾回收机制是不可控的,也就是说程序员不 能知道,也不可能做到在某一时刻让JVM回收资源!这对系统资源的处理是致命的,试想你的程序希望在一个循环语句中去查看数万张图片,常规的处理方式是每 次调入一张,查看,然后就立即释放该图片资源,而后在循环调入下一张图片,这对操作系统而言,任何时刻程序占用的仅仅是一张图片的资源。但如果这个过程完 全交给JVM去处理,也许会是在循环语句结束后,JVM才会去释放图片资源,其结果可能是你的程序还没有运行结束,操作系统已经宕掉。

但下面的好消息也许会让这个坏消息变得无关紧要。对于SWT,只需了解两条简单的"黄金"法则就可以放心的使用系统资源!之所以称为黄金法则,一是因 为少,只有两条,二是因为它们出奇的简单。第一条是"谁占用,谁释放",第二条是"父构件被销毁,子构件也同时被销毁"。第一条原则是一个无任何例外的原 则,只要程序调用了系统资源类的构造函数,程序就应该关心在某一时刻要释放这个系统资源。比如调用了

Font font = new Font (display, "Courier", 10, SWT.NORMAL);

那么就应该在不在需要这个Font的时候调用

font.dispose();

对于第二个原则,是指如果程序调用某一构件的dispose()方法,那么所有这个构件的子构件也会被自动调用dispose()方法而销毁。通常这里指的子构件与父构件的关系是在调用构件的构造函数时形成的。比如,

Shell shell = new Shell();
Composite parent = new Composite(shell,SWT.NULL);
Composite child = new Composite(parent,SWT.NULL);

其中parent的父构件是shell,而shell则是程序的主窗口,所以没有相应的父构件,同时parent又包括了child子构件。如果调用 shell.dispose()方法,应用第二条法则,那么parent和child构件的dispose()方法也会被SWT API自动调用,它们也随之销毁。

线程问题

在任何操作平台的GUI系统中,对构件或一些图形API的访问操作都要被严格同步并串行化。例如,在一个图形界面中的按键构件可被设成可用状态 (enable)或禁用状态(disable),正常的处理方式是,用户对按键状态设置操作都要被放入到GUI系统的事件处理队列中(这意味着访问操作被 串行化),然后依次处理(这意味着访问操作被同步)。想象当按键可用状态的设置函数还没有执行结束的时候,程序就希望再设置该按键为禁用状态,势必会引起 冲突。实际上,这种操作在任何GUI系统都会触发异常。

Java语言本身就提供了多线程机制,这种机制对GUI编程来说是不利的,它不能保证图形构件操作的同步与串行化。SWT采用了一种简单而直接的方式 去适应本地GUI系统对线程的要求:在SWT中,通常存在一个被称为"用户线程"的唯一线程,只有在这个线程中才能调用对构件或某些图形API的访问操 作。如果在非用户线程中程序直接调用这些访问操作,那么SWTExcepiton异常会被抛出。但是SWT也在*.widget.Display类中提供 了两个方法可以间接的在非用户线程的进行图形构件的访问操作,这是通过的syncExec(Runnable)和asyncExec(Runnable) 这两个方法去实现的。例如:

//此时程序运行在一个非用户线程中,并且希望在构件panel上加入一个按键。

Display.getCurrent().asyncExec(new Runnable() {
public void run() {
Button butt = new Button(panel,SWT.PUSH);
butt.setText("Push");
}
});

方法syncExec()和asyncExec()的区别在于前者要在指定的线程执行结束后才返回,而后者则无论指定的线程是否执行都会立即返回到当前线程。

SWT的扩展:JFace

JFace与SWT的关系好比Microsoft的MFC与SDK的关系,JFace是基于SWT开发,其API比SWT更加易于使用,但功能却没SWT来的直接。比如下面的代码应用JFace中的MessageDialog打开一个警告对话框:

MessageDialog.openWarning(parent,"Warning","Warning message");

如果只用SWT完成以上功能,语句不会少于30行!

JFace原本是为更加方便的使用SWT而编写的一组API,其主要目的是为了开发Eclipse IDE环境,而不是为了应用到其它的独立应用程序。因此在Eclipse 2.1版本之前,很难将JFace API完整的从Eclipse的内核API中剥离出来,总是要多多少少导入一些非JFace以外的Eclipse核心代码类或接口才能得到一个没有任何编 译错误的JFace开发包。但目前Eclipse组织似乎已经逐渐意识到了JFace在开发独立应用程序起到的重要作用,在正在开发的2.1版本中, JFace也开始变成了和SWT一样的完整独立的开发包,只是这个开发包还在变动中(笔者写本文时,应用的Eclipse2.1M3版本)。JFace开 发包的包前缀是以org.eclipse.jface开头。JAR包和源代码也和SWT一样,也在${你的eclipse安装路径}\plugins路径 下去找。

对开发人员来说,在开发一个图形构件的时候,比较好的方式是先到JFace包去找一找,看是不是有更简洁的实现方法,如果没有再用SWT包去自己实 现。比如JFace为对话框提供了很好的支持,除了各种类型的对话框(比如上面用的MessageDialog,或是带有Title栏的对话框),如要实 现一个自定义的对话框也最好从JFace中的Dialog类继承,而不是从SWT中的*.widget.Dialog继承。

应用JFace中的Preference包中的类很容易为自己的软件做出一个很专业的配置对话框。对于Tree、Table等图形构件,它们在显示的 同时也要和数据关联,例如Table中显示的数据,在JFace中的View包中为此类构件提供了Model-View方式的编程方法,这种方法使显示与 数据分开,更加利于开发与维护。JFace中提供最多的功能就是对文本内容的处理。可以在org.eclipse.jface.text.*包中找到数十 个与文本处理相关类。

与应用程序更近一步

Java程序通常是以class文件的方式发布的,运行class需要JRE或JDK的支持。这又是Java GUI程序的另一个致命的弱点,想象对一个面向广大用户的应用程序来说,无论你的程序功能有多简单,或是你的代码十分的精简,你都不得不让用户去下载一个 7、8M的JRE,那是多么令人沮丧的一件事。而且对程序员来说,Class通常意味着源代码的暴露,反编译的工具让那些居心叵测的人轻易得到你的源代 码。虽然有很多对Class的加密方法,但那总是以牺牲性能为代价的。好在我们还有其它的方式可用:把Class编译成exe文件!

通过SWT开发包,简单、跨平台、可靠等这些Java语言本身所具有的优点正渐渐融合到图形界面的应用程序开发中去。因此,我相信Java语言的另一扇成功之门正在逐渐打开。
posted @ 2006-05-20 18:36 wqwqwqwqwq 阅读(462) | 评论 (0)编辑 收藏
仅列出标题
共10页: First 上一页 2 3 4 5 6 7 8 9 10 下一页 
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678




常用链接

留言簿(10)

随笔分类(95)

随笔档案(97)

文章档案(10)

相册

J2ME技术网站

java技术相关

mess

搜索

  •  

最新评论

阅读排行榜

校园梦网网络电话,中国最优秀的网络电话