java 除了在运行时连接类型之外,还可以在运行时动态决定连接哪一种类型。java的体系结构允许动态扩展java程序,过程包括运行时决定所使用的类型,装载它们,使用它们。
一、动态扩展的方法
通过传递类型的名称,使用java.lang.class的forName()方法。
通过用户自定义的类装载器的loadClass()方法,用户自定义的类装载器可以从java.class.ClassLoader的任何子类创建。
二、动态扩展的例子:
浏览器启动的时候,不知道将要从网络上装载的class的文件,当遇到applet的网页时才知道每个applet所需的类和接口的名字。
三、程序:
1、使用java.lang.class的forName()方法:
public class TestClass {
public void hello() {
System.out.println("Hello,World!");
}
}
public class TestForName {
/**
*
* @param args
*/
public static void main(String args[]) {
if (args.length == 0) {
System.out.println("please input the class name!");
return;
}
for (int i = 0; i < args.length; i++) {
try {
Class> c = Class.forName(args[i]);
Object obj = c.newInstance();
TestClass tc = (TestClass) obj;
tc.hello();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
程序解析:
forName()接受的字符串参数是类型的全限定名称,如果成功装载了类型,或者之前已经被成功装载过,会返回代表类型的Class的实例,如果不成功,将会抛出ClassNotFoundException异常。
forName()试图确认所需的类型被装载到当前命名空间中,这个当前的命名空间就是类型所属的定义类装载器的命名空间。如果没有明确地在命令行或者环境变量中指定一个类的路径,系统类装载器会在当前目录中寻找所需的类型。
2、使用自定义的类装载器的loadClass()方法
方法:
程序:
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @author hansen
*
*/
public class TestClassLoader extends ClassLoader {
private String basePath;
/**
* constructor
*
* @param basePath
*/
public TestClassLoader(String basePath) {
this.basePath = basePath;
}
/**
* constructor
*
* @param parent
* @param basePath
*/
public TestClassLoader(ClassLoader parent, String basePath) {
super(parent);
this.basePath = basePath;
}
/*
* (non-Javadoc)
*
* @see java.lang.ClassLoader#findClass(java.lang.String)
*/
@Override
protected Class> findClass(String className)
throws ClassNotFoundException {
byte[] classData = getTypeFromBasePath(className);
if (null == classData) {
throw new ClassNotFoundException();
}
return defineClass(className, classData, 0, classData.length);
}
/**
* get the class type from the base path
*
* @param typeName
* @return
*/
private byte[] getTypeFromBasePath(String typeName) {
FileInputStream in = null;
String fileName = basePath + File.separatorChar
+ typeName.replace('.', File.separatorChar) + ".class";
try {
in = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BufferedInputStream input = new BufferedInputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
int c = input.read();
while (c != -1) {
out.write(c);
c = input.read();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return out.toByteArray();
}
}
解析:
findClass的工作方式:
1、接受需要装载的类型的全限定名称作为唯一的参数。首先试图查找或者生生成一个字节数组,内容是java class文件格式,文件格式定义了所需要装载的类型。
2、如果findClass()无法确定或者生成字节数组,会抛出ClassNotFoundException()异常并中止。否则findClass()调用defineClass(),把所需的类型名称、字节数组和一个可选的指定了这个类型所属的受保护域的ProtectionDomain对象作为参数。
3、如果defineClass()返回了一个代表这个类型的Class实例,findClass()简单地吧同一个Class实例返回给它的调用者。否则,defineClass()抛出某些异常并中止findClass()也抛出同样的异常并中止。