2005年03月31日

 jad文件的错误代码,分享

jad ( Java Application Discriptor ) 文件是J2ME的一个重要的组成部分,在我们发布J2ME程序的时候,jad文件经常报出各种错误,如:

      com.sun.kvem.midletsuite.InvalidJadException: Reason = 13

其中的错误原因是1-52的代码,很让人费解。我在网上找到了这些代码的解释,和大家分享如下:

public static final int JAD_SERVER_NOT_FOUND = 1;
public static final int JAD_NOT_FOUND = 2;
public static final int MISSING_PROVIDER_CERT = 4;
public static final int CORRUPT_PROVIDER_CERT = 5;
public static final int UNKNOWN_CA = 6;
public static final int INVALID_PROVIDER_CERT = 7;
public static final int CORRUPT_SIGNATURE = 8;
public static final int INVALID_SIGNATURE = 9;
public static final int UNSUPPORTED_CERT = 10;
public static final int EXPIRED_PROVIDER_CERT = 11;
public static final int EXPIRED_CA_KEY = 12;
public static final int MISSING_SUITE_NAME = 13;
public static final int MISSING_VENDOR = 14;
public static final int MISSING_VERSION = 15;
public static final int INVALID_VERSION = 16;
public static final int OLD_VERSION = 17;
public static final int MISSING_JAR_URL = 18;
public static final int JAR_SERVER_NOT_FOUND = 19;
public static final int JAR_NOT_FOUND = 20;
public static final int MISSING_JAR_SIZE = 21;
public static final int SUITE_NAME_MISMATCH = 25;
public static final int VERSION_MISMATCH = 26;
public static final int VENDOR_MISMATCH = 27;
public static final int INVALID_KEY = 28;
public static final int INVALID_VALUE = 29;
public static final int INSUFFICIENT_STORAGE = 30;
public static final int JAR_SIZE_MISMATCH = 31;
public static final int NEW_VERSION = 32;
public static final int UNAUTHORIZED = 33;
public static final int JAD_MOVED = 34;
public static final int CANNOT_AUTH = 35;
public static final int CORRUPT_JAR = 36;
public static final int INVALID_JAD_TYPE = 37;
public static final int INVALID_JAR_TYPE = 38;
public static final int ALREADY_INSTALLED = 39;
public static final int DEVICE_INCOMPATIBLE = 40;
public static final int MISSING_CONFIGURATION = 41;
public static final int MISSING_PROFILE = 42;
public static final int INVALID_JAD_URL = 43;
public static final int INVALID_JAR_URL = 44;
public static final int PUSH_DUP_FAILURE = 45;
public static final int PUSH_FORMAT_FAILURE = 46;
public static final int PUSH_PROTO_FAILURE = 47;
public static final int PUSH_CLASS_FAILURE = 48;
public static final int AUTHORIZATION_FAILURE = 49;
public static final int ATTRIBUTE_MISMATCH = 50;
public static final int PROXY_AUTH = 51;
public static final int TRUSTED_OVERWRITE_FAILURE = 52;

有了这些代码的解释,我们就很容易知道错误的原因了。

     另外,大家知道,有的手机在安装jar文件的时候,不需要jad文件。这是因为手机厂商在操作系统中内置了对jar文件的解析功能。就如同手机自动生成jad文件,然后使用这个jad文件安装jar文件一样。

2005年03月26日

NokiaS40S60开发平台1.0已知问题(翻译)

作者:陈跃峰

出自:http://blog.csdn.net/mailbomb

 

1、  Nokia3300不支MMA(声音处理)类库。

2、  Image.getGraphics()方法在不同的软件版本中工作不同,该方法无法在新版本的76503650N-Gage中正常工作。即这些机器中无法实现双缓冲技术。

3、  Nokia76503650N-Gage,无法控制背景灯和震动。

4、  同时播放声音在S60模拟器上可以运行,但是真机不支持。

5、  7650不支持WMA(短信息API)

6、  7210SDK1.0softkey1softkey2也产生leftright按键代码。

7、  S60中堆内存不同。一般S60设备有2M,而3600只有1.6M

8、  记录集枚举问题。在S40SDK和真机中,使用RecordStore.deleteRecord(id)以后,枚举工作不正常。

9、  Nokia6230 MIDP Concept SDK beta0.2不支持CLDC1.1

10、              NUL字符(0×00)TextBox中有问题。S401.0 SDK和真机中都存在该问题。

11、              如果MIDlet在后台运行时,MIDI声音不停止。S601.0中存在该问题。

12、              当正在运行的MIDlet被系统Screen隐含调用的类选择显示屏幕时有问题。在S60 1.0中存在该问题。

13、              S40界面风格中的字体大小不一致。在S40 1.0SDK和真机,以及7250i310061086200中。

14、              Item.getLable()方法在ChoiceGroup中无法返回正确的label

15、              Nokia MIDP 1.0中,DeviceControl.setLights(0,0)没有关闭键区灯。屏幕等关闭,而键区等依然亮着。涉及具有翻页键盘的S40 1.0设备。

16、              S60 1.0设备中,Date对象无法返回当前时间。

 

详细文档参看:

       http://www.forum.nokia.com/ndsCookieBuilder?fileParamID=3895

IBM developerWorks search results
1900多篇IBM专家在eclipse方面的技术文章
    http://www-106.ibm.com/search/searchResults.jsp?searchType=1&searchSite=dW&query=eclipse&searchScope=dW

在工作台外使用Eclipse GUI
Using the Eclipse GUI outside the Eclipse Workbench
    http://www-106.ibm.com/developerworks/java/library/os-ecgui1/

Eclipse平台下进行C/C++开发
C/C++ development with the Eclipse Platform
    http://www-128.ibm.com/developerworks/opensource/library/os-ecc/index.html

创建本机、跨平台 GUI 应用程序
    http://www-900.ibm.com/developerWorks/cn/linux/guitoolkit/j-nativegui/index.shtml
创建本机的跨平台 GUI 应用程序(重述)
    http://www-900.ibm.com/developerworks/cn/java/j-nativegui2/

将基于 Swing 的开发工具插入 Eclipse 中
    http://www-900.ibm.com/developerWorks/cn/java/os-swing/index.shtml
Responding to resource changes in the Eclipse workspace
    http://www.eclipse.org/articles/Article-Resource-deltas/resource-deltas.html

在 Eclipse Workbench 之外使用 Eclipse GUI,第 1 部分:独立使用 JFace 和 SWT
    http://www-900.ibm.com/developerWorks/cn/java/os-ecgui1/
在 Eclipse Workbench 之外使用 Eclipse GUI,第 2 部分:使用 JFace 图像注册表
    http://www-900.ibm.com/developerWorks/cn/linux/opensource/os-ecgui2/index.shtml
在 Eclipse Workbench 之外使用 Eclipse GUI,第 3 部分:添加操作、菜单和工具栏
    http://www-900.ibm.com/developerWorks/cn/linux/opensource/os-ecgui3/index.shtml

JFace 开发向导
    http://www-900.ibm.com/developerWorks/cn/linux/opensource/os-ecjfw/index.shtml
Creating JFace Wizards
    http://www.eclipse.org/articles/Article-JFace%20Wizards/wizardArticle.html

开发 Eclipse 插件
    http://www-900.ibm.com/developerWorks/cn/java/os-ecplug/index.shtml
驾驭 Eclipse 功能部件
    http://www-900.ibm.com/developerWorks/cn/linux/opensource/os-ecfeat/index.shtml
扩展 Eclipse 的 Java 开发工具
    http://www-900.ibm.com/developerWorks/cn/linux/opensource/os-ecjdt/index.shtml

j2EE基础概念(总结)

Java基础方面:

1,作用域public,protected,private,以及不写时的区别

2,ArrayList和Vector的区别,HashMap和Hashtable的区别

3,char型变量能不能定义为一个中文?为什么?

4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么?

5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?

6,内部类的实现方式?

7,垃圾回收机制,如何优化程序?

8,float型float f=3.4是否正确?

Jsp方面

1,jsp有哪些内置对象?作用分别是什么?

2,jsp有哪些动作?作用分别是什么?

3,include的两种实现方式的区别?

4,两种跳转方式分别是什么?有什么区别?

Servlet方面

1,说一说Servlet的生命周期?

2,Servlet版本间(忘了问的是哪两个版本了)的不同?

Jdbc,Jdo方面

1,可能会让你写一段Jdbc连Oracle的程序.

2,Class.forName的作用?为什么要用?

3,Jdo是什么?

Xml方面

1,xml有哪些解析技术?区别是什么?

2,你在项目中用到了xml技术的哪些方面?如何实现的?

3,用jdom解析xml文件时如何解决中文问题?如何解析?

EJB方面

1,EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别?

MVC方面

1,MVC的各个部分都有那些技术来实现?如何实现?

设计模式方面:

1,开发中都用到了那些设计模式?用在什么场合?

JavaScript方面

      1,如何校验数字型?

CORBA

      1,CORBA是什么?用途是什么?


谁来做出解答阿!
————————————————————-
回答一部分。
1,作用域public,protected,private,以及不写时的区别
public 在其他的包中的类也可以引用,protected只限于同一个包内的类,private只有
自己可以使用。不写的时候和protected一样。
2,ArrayList和Vector的区别,HashMap和Hashtable的区别
ArrayList需要预先定义大小,Vector不用。HashMap和Hashtable的默认初始化容量
(default initial capacity)不同 HashMap是16,Hashtable为11。
3,char型变量能不能定义为一个中文?为什么?
可以定义。因为中文也是16bit的。
4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么?
查看jdk文档。
5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
这个具体的去看。
6,内部类的实现方式?
内部类”是在另一个类的内部声明的类。从Java 1.1开始,你可在一个类中声明另一个
类,这与声明字段和方法非常相似。
7,垃圾回收机制,如何优化程序?
在变量不使用的时候将其赋值为null。
8,float型float f=3.4是否正确?
不行。类型不匹配。改为float f=3.4f。

Servlet方面

1,说一说Servlet的生命周期?
Servlet的生命周期是当服务器装载运行servlets,接收来自客户端的多个请求并且返回
数据给客户端,然后再删除移开servlets的时间。
2,Servlet版本间(忘了问的是哪两个版本了)的不同?
我个人认为这个问题没有什么实际意义。
Jdbc,Jdo方面

1,可能会让你写一段Jdbc连Oracle的程序.
有通式。Connection conn = null;
        String driver = “oracle.jdbc.driver.OracleDriver”;
        String url = “jdbc:oracle:thin:@xxx:1521:xxx”;
        String user = “xxx”;
        String password = “xxx”;
        try
        {
            Class.forName(“oracle.jdbc.driver.OracleDriver”);
            conn = DriverManager.getConnection(url, user, password);
        }
        catch (ClassNotFoundException e)
        {
            System.err.print(“ClassNotFoundException: Load jdbc-driver failure!”);
            System.err.println(e.getMessage());
        }
        catch (SQLException e)
        {
            e.printStackTrace();
            try
            {
                if (conn != null)
                {
                    conn.close();
                    conn = null;
                }
            }
            catch (SQLException se)
            {
            }
        }
2,Class.forName的作用?为什么要用?
返回一个指定名称的class对象。用它是为了load你指定名称的class。
3,Jdo是什么?
全称Java Data Objects。提供了在事务处理数据库中Java对象模型的明显的持久性,直
接支持了Java类的实例,应用程序不用处理任何其它的数据模型。

Xml方面

1,xml有哪些解析技术?区别是什么?
1) CSS是Cascading Style Sheet的缩写,即“层叠样式表”,在1997年W3C颁布HTML4标准
的同时也公布了有关样式单的第一个标准CSS1。
2)XSL(eXtensible Style Language,可扩展的样式语言)是最强大和灵活的样式语
言,是特别为应用XML而设计的,它完全遵循XML规则,进一步完善了XML本身。
3)Data-Island。还有几个,但是主要是第二个,其他的我认为不是主流。
2,你在项目中用到了xml技术的哪些方面?如何实现的?
xslt,fo,html解析,xml数据封装和解析。使用第三方软件包实现。
3,用jdom解析xml文件时如何解决中文问题?如何解析?
没用过,不太清楚。我使用的castor。(jbuilder9中有)

JavaScript方面

      1,如何校验数字型?
isNaN(parseFloat(xxx))如果为true,则不是数字。

做点力所能及的补充:
  (1)Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而
ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比
Vector好。
  (2)当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而
ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
  (3)Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一
个实现。
  (4)性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而
HashMap的不是。
(5)只有HashMap可以让你将空值作为一个表的条目的key或value

1,xml有哪些解析技术?区别是什么?

有两种:DOM和SAX.

DOM:可以得到一个包含文档中所有元素的树结构.
SAX:在文档的不同治点产生事件,应用程序可以决定如何处理这些事件以从解析器中得到信息.

Java基础方面:(未写的是已经有人回答或我自己也未知或不确定)

4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么?
  :::
  我所知道的有两种实现方法:
    一种是将方法同步:public synchronized methodName(…){….}
    另一种是将对象同步(此对象不能是null值): synchronized (object) { ….. }

5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
  继承时候类的执行顺序是:
    父类中被static关键字定义的部分是按所定义的顺序而最先被初始化的;
    父类构造函数(按调用顺序);

    子类中被static关键字定义的部分是按所定义的顺序而最先被初始化的;
    子类构造函数(按调用顺序);
    其他则按方法的调用顺序.

6,内部类的实现方式?
  public class XXXXX{ // 只有一个public类

    …
 
    class YYYYY{ // 可以有的访问类型是[ protected | private | 无 ]
      …
    }

  }

8,float型float f=3.4是否正确?
  我也不知道是否正确.
  我一般是这样:float f=3.4F; 或 float f=3.4f;// 后面跟一个英文大写或小写字母F

JavaScript方面

      1,如何校验数字型?
        var v = document.all.Telephone.value;
        if(isNan(v)){
          alert(“全是数字”);
        }

yangtaylor的解释有一半以上是错误的,大家不要作标准答案背哦
noisysilence的解释很不错,说明你已经是高手了,不过说明一点,Vector的精妙设置是
在于第二个参数的设置,其余的都基本正确。

关于“作用域public,protected,private,以及不写时的区别”的说明—
前三个常用,就不用说了,关于不写时的情况我来说明一下:
JAVA的作用域其实有5种,除了上面的3种外还有:private protected,default
public————–不说了
protected———–除了所有的子类可访问外,同包的非子类也可以访问
private————-也不说了
private protected—只有子类可以访问(这才是我们理解意义上的protected)
default————-只有同包的类可以访问,即使是子类但不同包仍不能访问

我也是看了很多参考书才最终得到的结果,在此献给大家了。

ArrayList和Vector的区别上面已说,我说说它们会带来得影响吧。
同为对象集合,ArrayList可由编译器检查而Vector则不会,所以如果用Vector返回对象
集合,编译器是无法查错得,只有在运行时才能才能发现。例如:
********采用Vector*************
server side:
public Vector getCustomDataSet(int num)
{
   Vector v = new Vector();
   for(int i=0; i<num; i++)
   {
      CustomData customData = new CustomData(i);
      v.add(customData)  //此处加入自定义的数据结构CustomData
   }
   return v;
}
client side:
 
  Vector v = getCustomDataSet(5);
  for(int i=0; i<v.size(); i++)
  {
     OtherData data = (OtherData)v.get(i);//此处取出的是customData却转换为
OtherData,编译器通过  
  }

********采用ArrayList*************
server side:
public CustomData[] getCustomDataSet(int num)
{
   CustomData[] customData = new CustomData[5];
   for(int i=0; i<num; i++)
   {
      customData[i] = new CustomData(i);
   }
   return customData;
}
client side:
 
  CustomData[] datas = getCustomDataSet(5);
  for(int i=0; i<datas.length; i++)
  {
     OtherData data = datas[i];//编译不通过,类型不匹配
  }


下边是以前我找的关于Hashtable和HashMap的不同之处。


Hashtable和HashMap
Hashtable和HashMap类有三个重要的不同之处。第一个不同主要是历史原因。Hashtable
是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。
也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,虽
然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,
但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静
态的 synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的
对象来返回。这个对象的方法可以让你同步访问潜在的 HashMap。这么做的结果就是当你
不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而
且同步增加了很多处理费用。
第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中
只有一条记录可以是一个空的 key,但任意数量的条目可以是空的value。这就是说,如
果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返
回 null。如果有必要,用containKey()方法来区别这两种情况。
一些资料建议,当需要同步时,用Hashtable,反之用 HashMap。但是,因为在需要时,
HashMap可以被同步,HashMap的功能比Hashtable的功能更多,而且它不是基于一个陈旧
的类的,所以有人认为,在各种情况下,HashMap都优先于Hashtable。

XML 的编程接口:  DOM    SAX   JDOM   JAXP

文档对象模型(通常称为 DOM)为 XML 文档的已解析版本定义了一组接口。解析器读入
整个文档,然后构建一个驻留内存的树结构,然后您的代码就可以使用 DOM 接口来操作
这个树结构。您可以遍历树以了解原始文档包含了什么,您可以删除树的几个部分,还可
以重新排列树和添加新的分支
DOM 提供了一组丰富的功能,您可以用这些功能来解释和操作 XML 文档,但使用它们是
有代价的。
DOM 构建整个文档驻留内存的树。如果文档很大,就会要求有极大的内存。
DOM 创建表示原始文档中每个东西的对象,包括元素、文本、属性和空格。如果您只需关
注原始文档的一小部分,那么创建那些永远不被使用的对象是极其浪费的。
DOM 解析器必须在您的代码取得控制权之前读取整个文档。对于非常大的文档,这会引起
显著的延迟


为了解决 DOM 问题,XML-DEV 参与者们(由 David Megginson 领导)创建了 SAX 接
口。SAX 的几个特征解决了 DOM 的问题:

SAX 解析器向您的代码发送事件。当解析器发现元素开始、元素结束、文本、文档的开始
或结束等时,它会告诉您。您可以决定什么事件对您重要,而且可以决定要创建什么类型
的数据结构以保存来自这些事件的数据。如果您没有显式地保存来自某个事件的数据,它
就被丢弃。
SAX 解析器根本不创建任何对象,它只是将事件传递给您的应用程序。如果希望基于那些
事件创建对象,这将由您来完成。
SAX 解析器在解析开始的时候就开始发送事件。当解析器发现文档开始、元素开始和文本
等时,代码会收到一个事件。您的应用程序可以立即开始生成结果;您不必一直等到整个
文档被解析完毕。更妙的是,如果您只查找文档中某些内容,代码一旦找到所要找的东西
就可以抛出一个异常。该异常会停止 SAX 解析器,然后代码用它找到的数据做它需要做
的任何事。
SAX 解析器也有些问题引人关注:

SAX 事件是无状态的。当 SAX 解析器在 XML 文档中发现文本时,它就向您的代码发送一
个事件。该事件仅仅给您发现的文本;它不告诉您什么元素包含那个文本。如果您想知道
这一点,则必须自己编写状态管理代码。
SAX 事件不是持久的。如果应用程序需要一个数据结构来对 XML 文档建模,则必须自己
编写那样的代码。如果您需要从 SAX 事件访问数据,并且没有把那个数据存储在代码
中,那么您不得不再次解析该文档。

JDOM 是基于 Java 技术的开放源码项目,它试图遵循 80/20 规则:用 DOM 和 SAX 20%
的功能来满足 80% 的用户需求。JDOM 使用 SAX 和 DOM 解析器,因此它是作为一组相对
较小的 Java 类被实现的。

JDOM 的主要特性是它极大地减少了您必须编写的代码数量。尽管本篇介绍性教程并不深
入讨论编程主题,但 JDOM 应用程序的长度通常是 DOM 应用程序的三分之一,大约是
SAX 应用程序的一半。(当然,坚持使用 DOM 的纯粹主义者会建议说:从长远来看,学
习和使用 DOM 终会有所回报)。JDOM 并不做所有的事,但对于大多数您要做的解析,它
可能正好适合您。

尽管 DOM、SAX 和 JDOM 为大多数常见任务提供了标准接口,但仍有些事情是它们不能解
决的。例如,在 Java 程序中创建 DOMParser 对象的过程因 DOM 解析器的不同而不同。
为了修正这个问题,Sun 发布了 JAXP(用于 XML 解析的 Java API,Java API for XML
Parsing)。该 API 为使用 DOM、SAX 和 XSLT 处理 XML 文档提供了公共接口。

JAXP 提供的诸如 DocumentBuilderFactory 和 DocumentBuilder 之类的接口为不同的解
析器提供了一个标准接口。还有一些方法可以允许您控制底层的解析器是否可以识别名称
空间以及是否使用 DTD 或模式来验证 XML 文档。
为了确定哪种接口适合您,您需要理解所有接口的设计要点,而且需要理解应用程序用您
将要处理的 XML 文档来做什么。考虑下面的问题将有助于您找到正确的方法。

要用 Java 编写应用程序吗?JAXP 使用 DOM、SAX 和 JDOM;如果您用 Java 编写代码,
那么您应使用 JAXP 将您的代码与各种解析器实现的细节隔离。
应用程序将如何部署?如果您的应用程序将要作为 Java applet 部署,那么您会希望使
要下载的代码数量最小,别忘了 SAX 解析器比 DOM 解析器小。还要知道使用 JDOM 时,
除了 SAX 或 DOM 解析器之外还要求编写少量的代码。
一旦解析了 XML 文档,还需要多次访问那些数据吗?如果您需要回过头来访问 XML 文件
的已解析版本,DOM 可能是正确的选择。而 SAX 事件被触发时,如果您以后需要它,则
由您(开发人员)自己决定以某种方式保存它。如果您需要访问不曾保存的事件,则必须
再次解析该文件。而 DOM 自动保存所有的数据。
只需要 XML 源文件的少量内容吗?如果您只需要 XML 源文件的少量内容,那么 SAX 可
能是正确的选择。SAX 不会为源文件中的每个东西创建对象;您要确定什么是重要的。使
用 SAX,您要检查每个事件以了解它是否与您的需要有关,然后相应地处理它。更妙的
是,一旦找到您正在寻找的东西,您的代码就会抛出一个异常来完全停止 SAX 解析器。
您正在一台内存很少的机器上工作吗?若是的话,不管您可能考虑到的其它因素是什么,
SAX 是您的最佳选择。

问题一:我声明了什么!

String s = “Hello world!”;

许多人都做过这样的事情,但是,我们到底声明了什么?回答通常是:一个String,内容是“Hello world!”。这样模糊的回答通常是概念不清的根源。如果要准确的回答,一半的人大概会回答错误。
这个语句声明的是一个指向对象的引用,名为“s”,可以指向类型为String的任何对象,目前指向”Hello world!”这个String类型的对象。这就是真正发生的事情。我们并没有声明一个String对象,我们只是声明了一个只能指向String对象的引用变量。所以,如果在刚才那句语句后面,如果再运行一句:

String string = s;

我们是声明了另外一个只能指向String对象的引用,名为string,并没有第二个对象产生,string还是指向原来那个对象,也就是,和s指向同一个对象。

问题二:”==”和equals方法究竟有什么区别?

==操作符专门用来比较变量的值是否相等。比较好理解的一点是:
int a=10;
int b=10;
则a==b将是true。
但不好理解的地方是:
String a=new String(“foo”);
String b=new String(“foo”);
则a==b将返回false。

根据前一帖说过,对象变量其实是一个引用,它们的值是指向对象所在的内存地址,而不是对象本身。a和b都使用了new操作符,意味着将在内存中产生两个内容为”foo”的字符串,既然是“两个”,它们自然位于不同的内存地址。a和b的值其实是两个不同的内存地址的值,所以使用”==”操作符,结果会是false。诚然,a和b所指的对象,它们的内容都是”foo”,应该是“相等”,但是==操作符并不涉及到对象内容的比较。
对象内容的比较,正是equals方法做的事。

看一下Object对象的equals方法是如何实现的:
boolean equals(Object o){

return this==o;

}
Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法,那你的类使用equals和使用==会得到同样的结果。同样也可以看出,Object的equals方法没有达到equals方法应该达到的目标:比较两个对象内容是否相等。因为答案应该由类的创建者决定,所以Object把这个任务留给了类的创建者。

看一下一个极端的类:
Class Monster{
private String content;

boolean equals(Object another){ return true;}

}
我覆盖了equals方法。这个实现会导致无论Monster实例内容如何,它们之间的比较永远返回true。

所以当你是用equals方法判断对象的内容是否相等,请不要想当然。因为可能你认为相等,而这个类的作者不这样认为,而类的equals方法的实现是由他掌握的。如果你需要使用equals方法,或者使用任何基于散列码的集合(HashSet,HashMap,HashTable),请察看一下java doc以确认这个类的equals逻辑是如何实现的。

问题三:String到底变了没有?

没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。请看下列代码:

String s = “Hello”;
s = s + ” world!”;

s所指向的对象是否改变了呢?从本系列第一篇的结论很容易导出这个结论。我们来看看发生了什么事情。在这段代码中,s原先指向一个String对象,内容是”Hello”,然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为”Hello world!”,原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {
private String s;

public Demo {
s = “Initial Value”;
}

}
而非
s = new String(“Initial Value”);
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即StringBuffer。

问题四:final关键字到底修饰了什么?

final使得被修饰的变量”不变”,但是由于对象型变量的本质是“引用”,使得“不变”也有了两种含义:引用本身的不变,和引用指向的对象不变。

引用本身的不变:
final StringBuffer a=new StringBuffer(“immutable”);
final StringBuffer b=new StringBuffer(“not immutable”);
a=b;//编译期错误

引用指向的对象不变:
final StringBuffer a=new StringBuffer(“immutable”);
a.append(” broken!”); //编译通过

可见,final只对引用的“值”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的“值”相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。

理解final问题有很重要的含义。许多程序漏洞都基于此—-final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它“永远不变”。其实那是徒劳的。

问题五:到底要怎么样初始化!

本问题讨论变量的初始化,所以先来看一下Java中有哪些种类的变量。
1. 类的属性,或者叫值域
2. 方法里的局部变量
3. 方法的参数

对于第一种变量,Java虚拟机会自动进行初始化。如果给出了初始值,则初始化为该初始值。如果没有给出,则把它初始化为该类型变量的默认初始值。

int类型变量默认初始值为0
float类型变量默认初始值为0.0f
double类型变量默认初始值为0.0
boolean类型变量默认初始值为false
char类型变量默认初始值为0(ASCII码)
long类型变量默认初始值为0
所有对象引用类型变量默认初始值为null,即不指向任何对象。注意数组本身也是对象,所以没有初始化的数组引用在自动初始化后其值也是null。

对于两种不同的类属性,static属性与instance属性,初始化的时机是不同的。instance属性在创建实例的时候初始化,static属性在类加载,也就是第一次用到这个类的时候初始化,对于后来的实例的创建,不再次进行初始化。这个问题会在以后的系列中进行详细讨论。

对于第二种变量,必须明确地进行初始化。如果再没有初始化之前就试图使用它,编译器会抗议。如果初始化的语句在try块中或if块中,也必须要让它在第一次使用前一定能够得到赋值。也就是说,把初始化语句放在只有if块的条件判断语句中编译器也会抗议,因为执行的时候可能不符合if后面的判断条件,如此一来初始化语句就不会被执行了,这就违反了局部变量使用前必须初始化的规定。但如果在else块中也有初始化语句,就可以通过编译,因为无论如何,总有至少一条初始化语句会被执行,不会发生使用前未被初始化的事情。对于try-catch也是一样,如果只有在try块里才有初始化语句,编译部通过。如果在catch或finally里也有,则可以通过编译。总之,要保证局部变量在使用之前一定被初始化了。所以,一个好的做法是在声明他们的时候就初始化他们,如果不知道要出事化成什么值好,就用上面的默认值吧!

其实第三种变量和第二种本质上是一样的,都是方法中的局部变量。只不过作为参数,肯定是被初始化过的,传入的值就是初始值,所以不需要初始化。

问题六:instanceof是什么东东?

instanceof是Java的一个二元操作符,和==,>,<是同一类东东。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。举个例子:

String s = “I AM an Object!”;
boolean isObject = s instanceof Object;

我们声明了一个String对象引用,指向一个String对象,然后用instancof来测试它所指向的对象是否是Object类的一个实例,显然,这是真的,所以返回true,也就是isObject的值为True。
instanceof有一些用处。比如我们写了一个处理账单的系统,其中有这样三个类:

public class Bill {//省略细节}
public class PhoneBill extends Bill {//省略细节}
public class GasBill extends Bill {//省略细节}

在处理程序里有一个方法,接受一个Bill类型的对象,计算金额。假设两种账单计算方法不同,而传入的Bill对象可能是两种中的任何一种,所以要用instanceof来判断:

public double calculate(Bill bill) {
if (bill instanceof PhoneBill) {
//计算电话账单
}
if (bill instanceof GasBill) {
//计算燃气账单
}

}
这样就可以用一个方法处理两种子类。

然而,这种做法通常被认为是没有好好利用面向对象中的多态性。其实上面的功能要求用方法重载完全可以实现,这是面向对象变成应有的做法,避免回到结构化编程模式。只要提供两个名字和返回值都相同,接受参数类型不同的方法就可以了:

public double calculate(PhoneBill bill) {
//计算电话账单
}

public double calculate(GasBill bill) {
//计算燃气账单
}

所以,使用instanceof在绝大多数情况下并不是推荐的做法,应当好好利用多态。

2005年03月21日

To summarize what you’ve seen so far, your first and most efficient choice to hold a group of objects should be an array, and you’re forced into this choice if you want to hold a group of primitives. In the remainder of this chapter we’ll look at the more general case, when you don’t know at the time you’re writing the program how many objects you’re going to need, or if you need a more sophisticated way to store your objects. Java provides a library of container classes to solve this problem, the basic types of which are List, Set, and Map. You can solve a surprising number of problems by using these tools.

Among their other characteristics—Set, for example, holds only one object of each value, and Map is an associative array that lets you associate any object with any other object—the Java container classes will automatically resize themselves. So, unlike arrays, you can put in any number of objects and you don’t need to worry about how big to make the container while you’re writing the program.

使用 Eclipse 插件来编辑、编译和调试应用程序

级别:入门

David Gallardo (david@gallardo.org)
软件顾问
2004 年 01 月

本文为您提供关于 Eclipse 平台的概述,包括其起源和体系结构。本文首先简要讨论 Eclipse 的开放源代码性质及其对多种编程语言的支持,然后通过一个简单的程序例子展示 Java 开发环境。本文还将考查以插件扩展形式可用的一些软件开发工具,并展示一个用于 UML 建模的插件扩展。

Eclipse 是什么?
Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括 Java 开发工具(Java Development Tools,JDT)。

虽然大多数用户很乐于将 Eclipse 当作 Java IDE 来使用,但 Eclipse 的目标不仅限于此。Eclipse 还包括插件开发环境(Plug-in Development Environment,PDE),这个组件主要针对希望扩展 Eclipse 的软件开发人员,因为它允许他们构建与 Eclipse 环境无缝集成的工具。由于 Eclipse 中的每样东西都是插件,对于给 Eclipse 提供插件,以及给用户提供一致和统一的集成开发环境而言,所有工具开发人员都具有同等的发挥场所。

这种平等和一致性并不仅限于 Java 开发工具。尽管 Eclipse 是使用 Java 语言开发的,但它的用途并不限于 Java 语言;例如,支持诸如 C/C++、COBOL 和 Eiffel 等编程语言的插件已经可用,或预计会推出。Eclipse 框架还可用来作为与软件开发无关的其他应用程序类型的基础,比如内容管理系统。

基于 Eclipse 的应用程序的突出例子是 IBM 的 WebSphere Studio Workbench,它构成了 IBM Java 开发工具系列的基础。例如,WebSphere Studio Application Developer 添加了对 JSP、servlet、EJB、XML、Web 服务和数据库访问的支持。

Eclipse 是开放源代码的软件
开放源代码软件是这样一种软件,它们在发布时附带了旨在确保将某些权利授予用户的许可证。当然,最明显的权利就是源代码必须可用,以便用户能自由地修改和再分发该软件。这种用户权利的保护是通过一种称为 copyleft 的策略来完成的:软件许可证主张版权保护,除非明确授予用户这样的权利,否则用户不得分发该软件。copyleft 还要求同一许可证涵盖任何被再分发的软件。这实际上倒置了版权的目的——使用版权来授予用户权利,而不是为软件的开发者保留版权——copyleft 经常被描述为“保留所有版权”。

曾经四处蔓延的对开放源代码软件的许多恐惧、担忧和疑虑,都与某些 copyleft 许可证的所谓“病毒”性质有关——如果使用开放源代码软件作为您开发的程序的一部分,您将失去自己的知识产权,因为该许可证将“传染”您开发的专有部分。换句话说,该许可证可能要求与开放源代码软件一起打包的所有软件,都必须在相同的许可证之下发布。虽然这对最著名的 copyleft 许可证(即 GNU 通用公共许可证,例如 Linux 就是在该许可证之下发布的)来说可能是事实,当时还有其他许可证在商业化和社区考虑之间提供了较好的平衡。

开放源代码计划(Open Software Initiative)是一家非营利机构,它明确定义了开放源代码的含义及满足其标准的认证许可证。Eclipse 是在 OSI 认可的通用公共许可证(CPL)1.0 版之下被授予许可证的,CPL“旨在促进程序的商业化使用……”(欲获得指向通用公共许可证 1.0 版完整文本的链接,请参阅本文稍后的 参考资料)。

为 Eclipse 创建插件或将 Eclipse 用作软件开发应用程序基础的开发人员,需要发布他们在 CPL 下使用或修改的任何 Eclipse 代码,但是他们可以自由决定自己添加的代码的许可证授予方式。与出自 Eclipse 的软件一起打包的专有代码不需要作为开放源代码来授予许可证,该源代码也不需要提供给用户。

尽管大多数开发人员不会使用 Eclipse 来开发插件,或创建基于 Eclipse 的新产品,但是 Eclipse 的开放源代码性质所意味的,并不只是它使得 Eclipse 免费可用(尽管便于商业化的许可证意味着插件可能要花钱)。开放源代码鼓励创新,并激励开发人员(甚至是商业开发人员)为公共开放源代码库贡献代码。对此存在许多原因,不过最本质的原因或许是为这个项目作贡献的开发人员越多,这个项目就会变得对每个人都越宝贵。随着这个项目变得更加有用,更多的开发人员将会使用它,并围绕它形成一个社区,就像那些围绕 Apache 和 Linux 形成的社区一样。

Eclipse 是什么机构?
Eclipse.org 协会管理和指导 Eclipse 正在进行中的开发。在据说 IBM 花了 4000 万美元开发 Eclipse,并把它作为一个开放源代码项目发布之后,Eclipse.org 协会吸收了许多软件工具提供商,包括 Borland、Merant、Rational、RedHat、SuSE、TogetherSoft 和 QNX。从那以后还有其他公司相继加入,包括 Hewlett Packard、Fujitsu、Sybase。这些公司分别向理事会派了一名代表,这个理事会负责确定 Eclipse 项目的方向和范围。

在最高层,项目管理委员会(Project Management Committee,PMC)管理着 Eclipse 项目。这个项目被划分为多个子项目,每个子项目都有一名负责人。大型子项目又被划分为组,每个组也有一名负责人。目前,这其中的大多数管理角色都由最初开发 Eclipse 的 IBM 子公司 Object Technology International (OTI)的人担任,但是作为一个开放源代码的项目,它欢迎任何人的参与。任何特定部门的职责是通过该部门对项目的贡献来争取的。

现在我们已经考察了 Eclipse 背后的一些理论、历史和管理,下面让我们考察该产品本身。

Eclipse 工作台
在第一次打开 Eclipse 时,首先看到的是下面的欢迎屏幕:

图 1. Eclipse 工作台
Eclipse 工作台

Eclipse 工作台由几个称为 视图(view) 的窗格组成,比如左上角的 Navigator 视图。窗格的集合称为 透视图(perspective)。默认的透视图是 Resource 透视图,它是一个基本的通用视图集,用于管理项目以及查看和编辑项目中的文件。

Navigator 视图 允许您创建、选择和删除项目。Navigator 右侧的窗格是 编辑器区域。取决于 Navigator 中选定的文档类型,一个适当的编辑器窗口将在这里打开。如果 Eclipse 没有注册用于某特定文档类型(例如,Windows 系统上的 .doc 文件)的适当编辑器,Eclipse 将设法使用外部编辑器来打开该文档。

Navigator 下面的 Outline 视图 在编辑器中显示文档的大纲;这个大纲的准确性取决于编辑器和文档的类型;对于 Java 源文件,该大纲将显示所有已声明的类、属性和方法。

Tasks 视图 收集关于您正在操作的项目的信息;这可以是 Eclipse 生成的信息,比如编译错误,也可以是您手动添加的任务。

该工作台的大多数其他特性,比如菜单和工具栏,都应该和其他那些熟悉的应用程序类似。一个便利的特性就是不同透视图的快捷方式工具栏,它显示在屏幕的左端;这些特性随上下文和历史的不同而有显著差别。Eclipse 还附带了一个健壮的帮助系统,其中包括 Eclipse 工作台以及所包括的插件(比如 Java 开发工具)的用户指南。至少浏览一遍这个帮助系统是值得的,这样可以看到有哪些可用的选项,同时也可更好地理解 Eclipse 的工作流程。

为继续这个短暂的 Eclipse 之旅,我们将在 Navigator 中创建一个项目。右键单击 Navigator 视图,然后选择 New=>Project。当 New Project 对话框出现时,选择左面的 Java。标准 Eclipse 只有一种 Java 项目类型,名为“Java Project”。如果安装了插件来提供 JSP 和 servlet 支持,我们会从这里看到一个用于 Web 应用程序的附加选项。眼下,请选择 Java Project,在提示项目名称时输入“Hello”,然后按 Finish。

接下来,我们将检查一下 Java 透视图。取决于您喜欢的屏幕管理方式,您可以通过选择 Window=>Open Perspective=>Java 来改变当前窗口中的透视图,也可以通过选择 Window=>New Window,然后再选择这个新的透视图,从而打开一个新的窗口。

正如您可能预期的那样,Java 透视图包含一组更适合于 Java 开发的视图。其中之一就是左上角的视图,它是一个包含各种 Java 包、类、jar 和其他文件的层次结构。这个视图称为 Package Explorer。还要注意主菜单已经展开了——并且出现了两个新的菜单项:Source 和 Refactor。

Java 开发环境(JDE)
为试验一下 Java 开发环境,我们将创建并运行一个“Hello, world”应用程序。使用 Java 透视图,右键单击“Hello”项目,选择 New=>Class,如图 2 所示。在随后出现的对话框中,键入“Hello”作为类名称。在“Which method stubs would you like to create?”下面,选中“public static void main(String[] args)”复选框,然后按 Finish。

图 2. 在 Java 透视图中创建新类
新类

这样将在编辑器区域创建一个包含 Hello 类和空的 main() 方法的 .java 文件,如图 3 所示。然后向该方法添加如下代码(注意其中 i 的声明是有意省略了的):

图 3. Java 编辑器中的 Hello 类
Hello 类

您会在键入时注意到 Eclipse 编辑器的一些特性,包括语法检查和代码自动完成。在 2.1 版(我曾下载 M2 版来试用过)中,当您键入开括号或双引号时,Eclipse 会自动提供配对的符号,并将光标置于符号对之内。

在其他情况下,您可以通过按 Ctrl-Space 来调用代码自动完成功能。代码自动完成提供了上下文敏感的建议列表,您可通过键盘或鼠标来从列表中选择。这些建议可以是针对某个特定对象的方法列表,也可以是基于不同的关键字(比如 forwhile)来展开的代码片断。

语法检查依赖增量编译。每当您保存代码,它就在后台接受编译和语法检查。默认情况下,语法错误将以红色下划线显示,一个带白 “X” 的红点将出现在左边沿。其他错误在编辑器的左边沿通过灯泡状的图标来指示;这些就是编辑器或许能为您修复的问题——即所谓的Quick Fix(快速修复)特性。

上面的代码例子在 for 语句后面有一个灯泡状图标,因为 i 的声明被省略了。双击该图标将调出建议的修复列表。在此例中,它将提供创建一个类字段 i、一个局部变量 i 或一个方法参数 i 的建议;单击其中的每一个建议都会显示将要生成的代码。图 4 显示了该建议列表和建议创建一个局部变量之后生成的代码。

图 4. Quick Fix 建议
灯泡状帮助

双击该建议就会把建议代码插入到代码中的恰当位置。

一旦代码无错误地编译完成,您就能够从 Eclipse 菜单上选择 Run 来执行该程序(注意这里不存在单独的编译步骤,因为编译是在您保存代码时进行的。如果代码没有语法错误,它就可以运行了)。这时会出现一个具有适当默认设置的 Launch Configurations 对话框;请按右上角的 Run 按钮。一个新的选项卡式窗格将出现在下面的窗格(控制台)中,其中显示了程序的输出,如图 5 所示。

图 5. 程序的输出
Hello 输出

也可以在 Java 调试器中运行程序。首先双击编辑器视图左端的灰色边沿,从而在调用 System.out.println() 之后的 main() System.out.println() 中设置一个断点。一个蓝色的点将会出现在那里。然后从 Run 菜单上选择 Debug。正如上面描述的,这时会出现一个 Launch Configurations 对话框。请选择 Run。透视图将自动切换到 Debug 透视图,其中具有许多有趣的新视图,如图 6 所示:

图 6. Debug 透视图
调试输出

首先,请注意该透视图左上角的 Debug 视图。这个视图显示调用堆栈,并且标题栏中有一个工具栏,它允许您控制程序的执行,包括继续、挂起或终止程序、跟踪下一个语句、单步执行下一个语句,或者从方法返回。

右上角的窗格包含许多选项卡式的视图,包括 Variables、Breakpoints、Expressions 和 Display。这里我单击了 Variables 视图,以便我们能够看到 i 的当前值。

可以通过上下文敏感的帮助,获得关于这些视图的更多信息:单击视图的标题,然后按 F1。

附加插件
除了像 JDT 这样用于编辑、编译和调试应用程序的插件外,还有些可用的插件支持从建模、生成自动化、单元测试、性能测试、版本控制到配置管理的完整开发过程。

Eclipse 标准地附带了配合 CVS 使用的插件,CVS 是用于源代码控制的开放源代码并发版本系统(Concurrent Versions System)。Team 插件连接到 CVS 服务器,允许开发团队的成员操作一组源代码文件,却不会相互覆盖其他人的更改。这里不打算进一步探讨如何从 Eclipse 内部进行源代码控制,因为这需要安装 CVS 服务器,不过支持开发团队而不只是独立的开发,这是 Eclipse 的一个重要的必备特性。

已经可用或已宣布要推出的一些第三方插件包括:

版本控制和配置管理

  • CVS
  • Merant PVCS
  • Rational ClearCase

UML 建模

  • OMONDO EclipseUML
  • Rational XDE (代替 Rose)
  • Together WebSphere Studio Edition

图形

  • Batik SVG
  • Macromedia Flash

Web 开发、HTML、XML

  • Macromedia Dreamweaver
  • XMLBuddy

应用服务器集成

  • Sysdeo Tomcat launcher

欲了解可用插件的更完整列表,请参阅 参考资料 中的链接。

例子:一个用于 UML 建模的插件
要查看插件的例子,以及查看它是如何与 Eclipse 集成的,请下载流行的 OMONDO EclipseUML(参阅 参考资料 中的链接);您需要注册,不过该插件是免费的。这个插件依赖 GEF,即 Graphical Editor Framework,这是另一个 Eclipse 插件。GEF 是 Tools 子项目的一部分。要下载 GEF,请转到 Eclipse Web 站点(参阅 参考资料),选择“downloads”,然后单击“Tools PMC downloads page”链接。注意您需要下载 OMONDO 推荐的 GEF 版本(针对 OMONDO 1.0.2 的是 GEF 2.0 版)。

下载之后,插件的安装通常是通过解压缩下载文件,并将其内容复制到 Eclipse 插件目录来完成的。在此例中,GEF 需要解压缩到 Eclipse目录(它将自动从该目录进入插件目录)。为安全起见,您可能想将它解压缩到某个临时目录,再相应地从那里复制相关目录。如果 Eclipse 正在运行,您需要停止它然后再重新启动它,这样它才能识别新安装的插件。

一旦 EclipseUML(以及 GEF)安装完成,您就能够像创建一个 Java 类文件一样创建一个类图。在 Java 透视图中,右键单击 Package Explorer 中的“Hello”项目,然后从弹出菜单上选择 New=>Other。New 对话框的左边窗格中将会有一个用于 UML 的新选项。EclipseUML 的免费版本仅支持类图,因此右侧的惟一选项是 UML Class Diagram。请选择 UML Class Diagram,然后为该类图键入一个名称,比如“Hello”:

图 7. Class Diagram 编辑器
类图

编辑器区域中将会出现一个图形编辑器,它带有用于绘制类图的画布。您可以通过两种方式创建类图:通过将 Java 文件从 Package Explorer 拖放到类图上,从而对现有代码进行逆向工程;或者使用空白类图上面工具栏中可用的绘制工具。要试验第一种方法,请创建一个名为 Person 的新类(使用 File=>New=>Class),然后赋予它下面列出的两个私有属性:


/** Person.java
 * @author david
 */
public class Person {
private String name;
private Address address;

/**
 * Returns the address.
 * @return Address
 */
public Address getAddress() {
         return address;
}

/**
 * Returns the name.
 * @return String
 */
public String getName() {
        return name;
}

/**
 * Sets the address.
 * @param address The address to set
 */
public void setAddress(Address address) {
        this.address = address;
}

/**
 * Sets the name.
 * @param name The name to set
 */
public void setName(String name) {
       this.name = name;
}

}

(应该承认,我仅键入了针对 name 和 address 的行。getter 和 setter 方法是通过 Eclipse 自动生成的,即右键单击源代码,然后从弹出菜单上选择 Source=>Generate Getter and Setter 。)

请保存并关闭 Person.java Hello.ucd。

图 8. Person 类图
Person 类图

要从 UML 创建 Java 类,请单击类图窗口顶部工具栏上的“New class”按钮,即左起第三个按钮,然后单击类图。当 New 类向导打开时,请键入 Adress 作为类名称,然后按 Finish。

您可以右键单击类名称并选择 New=>Attribute,从而给类添加属性。在 New 属性对话框中,请输入属性名称、类型和可见性。然后右键单击类名称并选择 New=>Method 来添加方法。

当您更改类图时,图下面的 Source Editor 窗口将反映所做的更改。最后,您可以单击 Association 按钮(左起第五个),绘制一条从 Person 类指向 Address 类的线段,从而绘制这两个类之间的关系图。这样会调出另外一个对话框,您可以在其中输入关联属性(请参考 EclipseUML 帮助,以了解关于必需信息的更多内容)。完成后的图应该类似如下:

图 9. 关联
关联

这个 UML 插件展示了 Eclipse 插件的几个典型特点。首先,它展示了工具之间的紧密集成。表面上绝对无法看出有多个组件在工作;与 Eclipse 平台和 JDT 的集成是无缝的。例如,当 Person 类被创建时,它显示语法错误是因为它的一个属性 Address 没有定义。一旦 Address 类在 UML 图中创建完成,这些组件就会分开显示出来。

另一个特点是 EclipseUML 利用其他插件提供的功能的能力——在此例中是 GEF 插件,它提供用于开发可视化编辑器的工具。

还有另一个特点涉及 EclipseUML 插件使用多层次功能来分发的方式。支持类图的基本插件是免费的,但是更成熟的版本要付费才能使用。

Eclipse 平台体系结构
Eclipse 平台是一个具有一组强大服务的框架,这些服务支持插件,比如 JDT 和插件开发环境(PDE)。它由几个主要的部分构成:平台运行库、工作区、工作台、团队支持和帮助。

图 10. Eclipse 平台体系结构
Eclipse 平台体系结构

平台
平台运行库是内核,它在启动时检查已安装了哪些插件,并创建关于它们的注册表信息。为降低启动时间和资源使用,它在实际需要任何插件时才加载该插件。除了内核外,其他每样东西都是作为插件来实现的。

工作区
工作区是负责管理用户资源的插件。这包括用户创建的项目、那些项目中的文件,以及文件变更和其他资源。工作区还负责通知其他插件关于资源变更的信息,比如文件创建、删除或更改。

工作台
工作台为 Eclipse 提供用户界面。它是使用标准窗口工具包(SWT)和一个更高级的 API(JFace)来构建的;SWT 是 Java 的 Swing/AWT GUI API 的非标准替代者,JFace 则建立在 SWT 基础上,提供用户界面组件。

SWT 已被证明是 Eclipse 最具争议的部分。SWT 比 Swing 或 SWT 更紧密地映射到底层操作系统的本机图形功能,这不仅使得 SWT 更快速,而且使得 Java 程序具有更像本机应用程序的外观和感觉。使用这个新的 GUI API可能会限制 Eclipse 工作台的可移植性,不过针对大多数流行操作系统的 SWT 移植版本已经可用。

Eclipse 对 SWT 的使用只会影响 Eclipse 自身的可移植性——使用 Eclipse 构建的任何 Java 应用程序都不会受到影响,除非它们使用 SWT 而不是使用 Swing/AWT。

团队支持
团队支持组件负责提供版本控制和配置管理支持。它根据需要添加视图,以允许用户与所使用的任何版本控制系统(如果有的话)交互。大多数插件都不需要与团队支持组件交互,除非它们提供版本控制服务。

帮助
帮助组件具有与 Eclipse 平台本身相当的可扩展能力。与插件向 Eclipse 添加功能相同,帮助提供一个附加的导航结构,允许工具以 HTML 文件的形式添加文档。

Eclipse 的前景
围绕 Eclipse 的开发正处于关键阶段。主要软件工具提供商都参与进来了,并且开放源代码 Eclipse 插件项目的数量正在与日俱增。

可移植、可扩展、开放源代码的框架并不是个新思想(您会想起 Emacs),但是由于它成熟、健壮和优雅的设计,Eclipse 带来了全新的动力。IBM 价值 4000 万美元的世界级软件在开放源代码领域的发布,给业界带来了久违的震撼。

参考资料

关于作者
David Gallardo 是一名独立软件顾问和作家,他的专长是软件国际化、Java Web 应用程序和数据库开发。他成为专业软件工程师已经有 15 年了,他拥有许多操作系统、编程语言和网络协议的经验。他最近在一家 B2B 电子商务公司 TradeAccess, Inc.从事先进的数据库和国际化开发。在这之前,他是 Lotus Development Corporation 国际产品开发部的高级工程师,负责开发为 Lotus 产品(包括 Domino)提供 Unicode 和国际化语言支持的跨平台库。可以通过
david@gallardo.org 与 David 联系。
2005年03月19日

RTTI allows you to discover type information from an anonymous base-class reference. Thus, it’s ripe for misuse by the novice, since it might make sense before polymorphic method calls do. For many people coming from a procedural background, it’s difficult not to organize their programs into sets of switch statements. They could accomplish this with RTTI and thus lose the important value of polymorphism in code development and maintenance. The intent of Java is that you use polymorphic method calls throughout your code, and you use RTTI only when you must.

However, using polymorphic method calls as they are intended requires that you have control of the base-class definition, because at some point in the extension of your program you might discover that the base class doesn’t include the method you need. If the base class comes from a library or is otherwise controlled by someone else, one solution to the problem is RTTI: you can inherit a new type and add your extra method. Elsewhere in the code you can detect your particular type and call that special method. This doesn’t destroy the polymorphism and extensibility of the program, because adding a new type will not require you to hunt for switch statements in your program. However, when you add new code in your main body that requires your new feature, you must use RTTI to detect your particular type.

Putting a feature in a base class might mean that, for the benefit of one particular class, all of the other classes derived from that base require some meaningless stub of a method. This makes the interface less clear and annoys those who must override abstract methods when they derive from that base class. For example, consider a class hierarchy representing musical instruments. Suppose you wanted to clear the spit valves of all the appropriate instruments in your orchestra. One option is to put a clearSpitValve( ) method in the base class Instrument, but this is confusing because it implies that Percussion and Electronic instruments also have spit valves. RTTI provides a much more reasonable solution in this case because you can place the method in the specific class (Wind in this case), where it’s appropriate. However, a more appropriate solution is to put a prepareInstrument( ) method in the base class, but you might not see this when you’re first solving the problem and could mistakenly assume that you must use RTTI.

Finally, RTTI will sometimes solve efficiency problems. Suppose your code nicely uses polymorphism, but it turns out that one of your objects reacts to this general purpose code in a horribly inefficient way. You can pick out that type using RTTI and write case-specific code to improve the efficiency. Be wary, however, of programming for efficiency too soon. It’s a seductive trap. It’s best to get the program working first, then decide if it’s running fast enough, and only then should you attack efficiency issues—with a profiler (see Chapter 15).

RTTI allows you to discover type information from an anonymous base-class reference. Thus, it’s ripe for misuse by the novice, since it might make sense before polymorphic method calls do. For many people coming from a procedural background, it’s difficult not to organize their programs into sets of switch statements. They could accomplish this with RTTI and thus lose the important value of polymorphism in code development and maintenance. The intent of Java is that you use polymorphic method calls throughout your code, and you use RTTI only when you must.

However, using polymorphic method calls as they are intended requires that you have control of the base-class definition, because at some point in the extension of your program you might discover that the base class doesn’t include the method you need. If the base class comes from a library or is otherwise controlled by someone else, one solution to the problem is RTTI: you can inherit a new type and add your extra method. Elsewhere in the code you can detect your particular type and call that special method. This doesn’t destroy the polymorphism and extensibility of the program, because adding a new type will not require you to hunt for switch statements in your program. However, when you add new code in your main body that requires your new feature, you must use RTTI to detect your particular type.

Putting a feature in a base class might mean that, for the benefit of one particular class, all of the other classes derived from that base require some meaningless stub of a method. This makes the interface less clear and annoys those who must override abstract methods when they derive from that base class. For example, consider a class hierarchy representing musical instruments. Suppose you wanted to clear the spit valves of all the appropriate instruments in your orchestra. One option is to put a clearSpitValve( ) method in the base class Instrument, but this is confusing because it implies that Percussion and Electronic instruments also have spit valves. RTTI provides a much more reasonable solution in this case because you can place the method in the specific class (Wind in this case), where it’s appropriate. However, a more appropriate solution is to put a prepareInstrument( ) method in the base class, but you might not see this when you’re first solving the problem and could mistakenly assume that you must use RTTI.

Finally, RTTI will sometimes solve efficiency problems. Suppose your code nicely uses polymorphism, but it turns out that one of your objects reacts to this general purpose code in a horribly inefficient way. You can pick out that type using RTTI and write case-specific code to improve the efficiency. Be wary, however, of programming for efficiency too soon. It’s a seductive trap. It’s best to get the program working first, then decide if it’s running fast enough, and only then should you attack efficiency issues—with a profiler (see Chapter 15).

2005年03月17日

The class Class (described previously in this chapter) supports the concept of reflection, and there’s an additional library, java.lang.reflect, with classes Field, Method, and Constructor (each of which implement the Member interface). Objects of these types are created by the JVM at run time to represent the corresponding member in the unknown class. You can then use the Constructors to create new objects, the get( ) and set( ) methods to read and modify the fields associated with Field objects, and the invoke( ) method to call a method associated with a Method object. In addition, you can call the convenience methods getFields( ), getMethods( ), getConstructors( ), etc., to return arrays of the objects representing the fields, methods, and constructors. (You can find out more by looking up the class Class in the JDK documentation.) Thus, the class information for anonymous objects can be completely determined at run time, and nothing need be known at compile time.

It’s important to realize that there’s nothing magic about reflection. When you’re using reflection to interact with an object of an unknown type, the JVM will simply look at the object and see that it belongs to a particular class (just like ordinary RTTI), but then, before it can do anything else, the Class object must be loaded. Thus, the .class file for that particular type must still be available to the JVM, either on the local machine or across the network. So the true difference between RTTI and reflection is that with RTTI, the compiler opens and examines the .class file at compile time. Put another way, you can call all the methods of an object in the “normal” way. With reflection, the .class file is unavailable at compile time; it is opened and examined by the run-time environment.

It’s important to realize that there’s nothing magic about reflection. When you’re using reflection to interact with an object of an unknown type, the JVM will simply look at the object and see that it belongs to a particular class (just like ordinary RTTI), but then, before it can do anything else, the Class object must be loaded. Thus, the .class file for that particular type must still be available to the JVM, either on the local machine or across the network. So the true difference between RTTI and reflection is that with RTTI, the compiler opens and examines the .class file at compile time. Put another way, you can call all the methods of an object in the “normal” way. With reflection, the .class file is unavailable at compile time; it is opened and examined by the run-time environment.