这里所谓的可扩展应用是指这样的编程语言或者系统它可以在不修改现有系统整体或任意一部分功能的情况下扩展自身的功能对于传统的编程语言比如CobolCC++如果要为它们的应用增加新功能程序员必须重新编辑编译和发布程序因为这些语言都是静态链接的语言不具备动态添加功能的机制(也就是说生成执行代码的时候所有的程序代码必须事先准备妥当)Java应用的可扩展能力突破了这些限制
Java应用的可扩展功能包含了允许动态地定义新的数据类型以及允许用户插入自己的程序例程的能力这一切是如何实现的呢?下面我们通过实例来了解具体的实现过程
普通应用的扩展
在Java中扩展性源于继承具体手段或者是扩展一个类或者是实现一个接口Java接口的主要用途就在于此(Java接口定义了一组方法但不包含实现实现某个接口的类必须实现该接口定义的所有方法因而也就遵循某种确定的行为模式)
为什么说这个功能对于普通程序来说也很重要呢?如果一个程序是可以动态扩展的用户就不必再为了添加新功能而去修改源程序这就避免了搞乱原有的代码使得用户能够专注于自己的那一部分代码此外程序不必为了引入新功能而重新启动这对于那些需要不间断运行的程序来说无疑是一个福音
为进一步了解Java程序的动态扩展技术我们来看一个例子一家快速增长的保险公司想要用计算机管理它的报价系统这家公司现有两个产品人寿保险(lifecare)和医疗保险(medicare)根据保额期限客户年龄和保险产品的不同月保险费用的计算方法也不同系统应该能够在不修改原有代码的情况下引入保险公司推出的其他产品为现有产品设计的类模型如图一所示
当一个客户试图获取某种保险产品的报价时系统创建一个对应该产品类型的对象调用该对象的calculatePremium()方法根据指定产品的计算方法计算出保险费用系统利用一个XML文件(或属性文件)描述现有产品信息比如保险产品的名称和相应的类名称XML文件如Listing 所示
【Listing 描述产品信息的XML文件】
系统从XML文件读取指定产品的类的全称动态地创建对象然后系统调用calculatePremium()根据指定的保额期限和客户年龄按照特定产品的计算逻辑计算出精确的保险费
现在我们来看看系统如何动态地装入对象在把类装入内存和创建特定产品类型的对象时系统用到了Java类库javalangClassClass类的实例或者代表着Java应用中的一个类或者代表着一个接口在后台Java虚拟机(JVM)常常利用Class类操作Java类然而用户程序也同样可以通过Class类的实例操作Java类请参见Listing 的Class类摘要
【Listing Class类概要】
public final class javalangClass extends
javalangObject
{
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 显示了Product接口和它的实现MediCareLifeCare
【Listing Product接口及其实现】
// Productjava
// Product接口
package comtestdynamic;
public interface Product
{
public float calculatePremium(
float facevalueint term int age);
}
// MediCarejava
// Product的一个实现
package comtestdynamic;
public class MediCare implements Product
{
public float calculatePremium(
float facevalue int term int age)
{
float premium;
// 计算保险费
//
return premium;
}
}
// LifeCarejava
// Product的一个实现
package comtestdynamic;
public class LifeCare implements Product
{
public float calculatePremium(
float facevalue int term int age)
{
float premium;
// 计算保险费(不同的保险产品计算方法不同)
//
return premium;
}
}
当用户询问报价时报价系统根据保险产品的名字装入并实例化产品类下面我们用两个方法实现装入和调用代码GetProductFromName根据指定的产品名字生成合适的产品对象makeQuote()方法计算保险费Listing 显示了它们的代码
【Listing 生成产品对象计算保险费】
public Object GetProductFromName(String productName)
{
// 待装入对象的类名称
String className = null;
// 利用XML分析库orgxmlsax
// 从XML文件获取产品的相应类名称
//
// 假定类名字对应正确的产品例如
// ComtestdynamicMediCare
try
{
Object o = null;
o = ClassforName(className)newInstance();
}
//catch(ClassNotFoundException e){}
//catch(InstantiationException e){}
//catch(IllegalAccessException e){}
catch( Exception e )
{
eprintStackTrace();
}
return o;
}
// makeQuote方法无返回值
public void makeQuote()
{
String productName;
float faceValue premium ;
int faceValue age;
// 从应用的用户界面获取所有参数包括
// 产品名称期限保额年龄
// (有效的产品可以