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()也拋出同樣的異常並中止。