参考文献3 指出有两个方法可以解决这个问题。一是被装载的模块是虚拟机的主类库加载器已经加载的某个类库的派生类库(subclass),一是被加载的模块实现某个已经被系统虚拟机的主类库加载器加载的接口(interface)。 在浏览器中通常都使用了第一种方法,譬如说所有的applet都是java.applet.Applet的派生类库,因此在所有的applet源代码中都有类似于public class MyClass extends Applet 的声明。在这里我们采用参考文献3 中介绍的第二种方法,也就是被加载的新模块实现某个预先设计好的接口。
声明接口UpdatableModule如下: public interface UpdatableModule { void start(String RunTimeParam) }
由于这个接口在设计时刻已经存在,它可以被虚拟机的主类库加载器和将要被加载的新功能模块所调用。新功能模块所需要做的,只是实现这个接口中的方法,例如: public class NewModule_1 implements UpdatableModule { void start(String RunTimeParam) { System.out.PRintln("This is new module 1."); } } public class NewModule_2 implements UpdatableModule { void start(String RunTimeParam) { System.out.println("This is new module 2."); } }
在运行时刻,主程序需要从外部获得新的功能模块名,利用用户自定义的类库加载器加载新的功能模块,生成一个新的功能模块对象,然后通过事先定义好的接口调用新的功能模块中的方法。例如: public class Test { public static void main(String[] args) { SimpleClassLoader scl = new SimpleClassLoader(); String RunTimeModule; Object o; Class c; RunTimeModule = args[0]; c = scl.loadClass(RunTimeModule); o = c.newInstance(); ((UpdatableModule) o).start("No parameter needed."); } }
示范程序 下面我们介绍一个简单的数据采集与处理程序。该程序采集当前的系统时间并按照一定的格式输出到标准输出设备,其中的数据处理模块(即数据输出模块)可以在运行时刻被更新。该程序包括如下功能模块: DataBuffer---------数据缓冲区 DataCollector------数据采集模块 DataProcessor------数据处理模块接口 PrintData_1--------数据输出模块,实现数据处理模块DataProcessor的接口 PrintData_2--------数据输出模块,实现数据处理模块DataProcessor的接口 TestGUI------------测试图形界面 数据缓冲区DataBuffer存放数据采集模块DataCollector 所采集的数据,它提供了更新数据和查询数据的方法。 public class DataBuffer { private String data;
// 更新数据的方法
public synchronized void UpdateData(String s) { data = s; notifyAll(); }