这里所谓的可扩展应用,是指这样的编程语言或者系统,它可以在不修改现有系统整体或任意一部分功能的情况下,扩展自身的功能。对于传统的编程语言,比如Cobol、C、C++,如果要为它们的应用增加新功能,程序员必须重新编辑、编译和发布程序,因为这些语言都是静态链接的语言,不具备动态添加功能的机制(也就是说,生成执行代码的时候,所有的程序代码必须事先准备妥当)。Java应用的可扩展能力突破了这些限制。
Java应用的可扩展功能包含了允许动态地定义新的数据类型以及允许用户插入自己的程序例程的能力。这一切是如何实现的呢?下面我们通过实例来了解具体的实现过程。
普通应用的扩展
在Java中,扩展性源于继承,具体手段或者是扩展一个类,或者是实现一个接口。Java接口的主要用途就在于此。(Java接口定义了一组方法,但不包含实现。实现某个接口的类必须实现该接口定义的所有方法,因而也就遵循某种确定的行为模式。)
为什么说这个功能对于普通程序来说也很重要呢?如果一个程序是可以动态扩展的,用户就不必再为了添加新功能而去修改源程序。这就避免了搞乱原有的代码,使得用户能够专注于自己的那一部分代码。此外,程序不必为了引入新功能而重新启动,这对于那些需要不间断运行的程序来说无疑是一个福音。
为进一步了解Java程序的动态扩展技术,我们来看一个例子。一家快速增长的保险公司想要用计算机管理它的报价系统。这家公司现有两个产品:人寿保险(lifecare)和医疗保险(medicare)。根据保额、期限、客户年龄和保险产品的不同,月保险费用的计算方法也不同。系统应该能够在不修改原有代码的情况下,引入保险公司推出的其他产品。为现有产品设计的类模型如图一所示。
当一个客户试图获取某种保险产品的报价时,系统创建一个对应该产品类型的对象,调用该对象的calculatePremium()方法,根据指定产品的计算方法计算出保险费用。系统利用一个XML文件(或属性文件)描述现有产品信息,比如保险产品的名称和相应的类名称。XML文件如Listing 1所示。
【Listing 1:描述产品信息的XML文件】
系统从XML文件读取指定产品的类的全称,动态地创建对象。然后,系统调用calculatePremium(),根据指定的保额、期限和客户年龄,按照特定产品的计算逻辑计算出精确的保险费。
现在我们来看看系统如何动态地装入对象。在把类装入内存和创建特定产品类型的对象时,系统用到了Java类库java.lang.Class。Class类的实例或者代表着Java应用中的一个类,或者代表着一个接口。在后台,Java虚拟机(JVM)常常利用Class类操作Java类;然而,用户程序也同样可以通过Class类的实例操作Java类。请参见Listing 2的Class类摘要。
【Listing 2:Class类概要】
public final class java.lang.Class extends
java.lang.Object
{
public static Class forName(String className)
public static Class forName(
String name, boolean initialize, ClassLoader loader)
public Class[] getClasses()
public ClassLoader getClassLoader()
public Class getComponentType()
public Constructor getConstructor(Class[] parameterTypes)
public Constructor[] getConstructors()
public Class[] getDeclaredClasses()
public Constructor getDeclaredConstructor(
Class[] parameterTypes)
public Constructor[] getDeclaredConstructors()
public Field getDeclaredField(String name)
public Field[] getDeclaredFields()
public Method getDeclaredMethod(String name, Class[],
parameterTypes)
public Method[] getDeclaredMethods()
public Class getDeclaringClass()
public Field getField(String name)
public Field[] getFields()
public Class[] getInterfaces()
public Method getMethod(
String name, Class[] parameterTypes)
public Method[] getMethods()
public int getModifiers()
public String getName()
public Package getPackage()
public ProtectionDomain getProtectionDomain()
public URL getResource(String name)
public InputStream getResourceAsStream(String name)
public Object[] getSigners()
public Class getSuperclass()
public boolean isArray()
public boolean isAssignableFrom(Class cls)
public boolean isInstance(Object obj)
public boolean isInterface()
public boolean isPrimitive()
public Object newInstance()
public String toString()
}
在这里,我们感兴趣的主要是forName()方法和newInstance()方法。静态方法forName()返回和指定类名字关联的Class对象。它通过类装入器把类装入到执行程序。类名字参数可以是classpath中存在的任意一个类。如果不能找到指定类,则forName()方法抛出ClassNotFoundException异常。newInstance()方法为Class对象代表的类新建一个实例。newInstance()方法也利用类的不带参数的构造函数创建新对象,因此该类必须有一个不带参数的构造函数。如果newInstance()方法由于任何原因不能实例化一个类,它将抛出InstantiationException异常;如果不能访问该类或它的构造函数,则抛出IllegalAccessException异常。Listing 3显示了Product接口和它的实现:MediCare,LifeCare。
【Listing 3:Product接口及其实现】
// Product.java
// Product接口
package com.test.dynamic;
public interface Product
{
public float calculatePremium(
float face-value,int term, int age);
}
// MediCare.java
// Product的一个实现
package com.test.dynamic;
public class MediCare implements Product
{
public float calculatePremium(
float face-value, int term , int age)
{
float premium;
// 计算保险费
// ......
return premium;
}
}
// LifeCare.java
// Product的一个实现
package com.test.dynamic;
public class LifeCare implements Product
{
public float calculatePremium(
float face-value, int term , int age)
{
float premium;
// 计算保险费(不同的保险产品,计算方法不同)
// .......
return premium;
}
}
当用户询问报价时,报价系统根据保险产品的名字装入并实例化产品类。下面我们用两个方法实现装入和调用代码。GetProductFromName根据指定的产品名字生成合适的产品对象,makeQuote()方法计算保险费。Listing 4显示了它们的代码:
【Listing 4:生成产品对象,计算保险费】
public Object GetProductFromName(String productName)
{
// 待装入对象的类名称
String className = null;
// 利用XML分析库org.xml.sax,
// 从XML文件获取产品的相应类名称
// ......
// 假定类名字对应正确的产品,例如
// "Com.test.dynamic.MediCare"
try
{
Object o = null;
o = Class.forName(className).newInstance();
}
//catch(ClassNotFoundException e){}
//catch(InstantiationException e){}
//catch(IllegalAccessException e){}
catch( Exception e )
{
e.printStackTrace();
}
return o;
}
// makeQuote方法无返回值
public void makeQuote()
{
String productName;
float faceValue , premium ;
int faceValue, age;
// 从应用的用户界面获取所有参数,包括
// 产品名称、期限、保额、年龄
// (有效的产品可以