我们要使用一个类,这个类首先要被装载,然后进行连接,初始化等步骤。一个类的装载是由类装载器(ClassLoader)完成 的。
Java中类装载器大体分为两种,一种是启动类装载器(Bootstrap ClassLoader),这种类装载器是jvm实现的一部分,它不是ClassLoader类的子类; 另一种是用户自定义的类装载器,这类类装载器都是ClassLoader类的子类,Java API中提供了几个自定义类装载器的实现:ExtClassLoader——用来装载标准扩展库中的类,AppClassLoader——一般用来装载我们定义的类。
这里简单的说一下类装载器的双亲委派模型。
除了启动类装载器,每一个类装载器都 有一个parent,ExtClassLoader的parent是Bootstrap ClassLoader,AppClassLoader的parent是ExtClassLoader,像我们自定义的ClassLoader,如果不明确指定parent,其parent都 默认是AppClassLoader。可以通过调用ClassLoader实例的getParent()方法来获取其parent,如果其双亲 是Bootstrap ClassLoader,这个方法将返回null。当一个类装载器被请求装载一个类型时,它首先请求其parent来装载这个类型,其parent再向上请求直到Bootstrap ClassLoader,如果其parent能够装载这个类开则返回代表这个类型的Class实例,如果不能此 ClassLoader尝试装载,如果成功返回代表这个类型的Class实例,否则抛出异常。
在Java术语中,要求某个类装载器去装载一个类型,但是却返回了其他类装载器装载的类型,这种装载器被称为是那个类型的初始类装载器,而实际定义那个类型的类装载器称为该类型的定义类装载器。
下面我们通过一个简的例子来看一下 我们平时定义的类和API中的类都 是通过哪些ClassLoader来装载的。
ClassLoaderDemo.java
package kevin;
import java.util.List;
public class ClassLoaderDemo {
public static void main(String[] args) {
Class thisCls = ClassLoaderDemo.class;
System.out
.println("the class loader of this class with main method is:"
+ thisCls.getClassLoader());
Class sysCls = System.class;
System.out.println("the loader of the System class is: "
+ sysCls.getClassLoader());
Class listCls = List.class;
System.out
.println("the loader of the List class in package java.util is:"
+ listCls.getClassLoader());
/*
* 以上三句的打印结果如下:
* the class loader of this class with main methodis:sun.misc.Launcher$AppClassLoader@19821f
* the loader of the System class is: null
* the loader of the List class in package java.util is:null
* 由此可见我们自定义的类是默认是由AppClassLoader装载的,而API中的类是由BootStrap
* ClassLoader,即启动类装载 器装载的
*/
}
}
Java允许动态扩展程序,这个过程包括运行时决定所使用的类型,装载它们,使用它们。动态扩展一般可以通过两种方法来实现,第一种是使用自定义的类装载器的loadClass()方法,另一种就是使用Class.forName()方法。
下面我们首先通过自定义类装载器的方法来动态扩展java程序。
Hello.java
package kevin;
public class Hello {
public void sayHello(){
System.out.println("Hello,free world...");
}
}
HelloClassLoader.java
package kevin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 自定义一个类装载器
*/
public class HelloClassLoader extends ClassLoader {
/**
* 类所在的目录路径
*/
private String basePath;
public HelloClassLoader(String basePath) {
this.basePath = basePath;
}
public HelloClassLoader(ClassLoader parent, String basePath) {
super(parent);
this.basePath = basePath;
}
/**
* 一般我们要重写这个方法,然后将代表这个class文件的byte数组数据传递给defineClass方法,它会将这个数组
* 转换在一个代表此类的Class实例返回
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
byte[] data = getClassData(name);
if (data == null) {
throw new ClassNotFoundException();
}
return defineClass(name, data, 0, data.length);
}
/**
* 获取类文件的字节数据
* @param name 类的全限定名
* @return 包含类数据的byte型数组
*/
public byte[] getClassData(String name) {
String filePath = basePath + name.replace(".", File.separator);
byte[] buf = null;
try {
FileInputStream in = new FileInputStream(filePath);
buf = new byte[in.available()];
in.read(buf);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return buf;
}
}
CustomLoaderDemo.java
package kevin;
public class CustomLoaderDemo {
public static void main(String[] args) {
//创建自定义的ClassLoader的实例
HelloClassLoader loader = new HelloClassLoader("");
System.out.println(loader.getParent());
try {
//用自定义的类装载器来装载类,这是动态扩展的一种途径
Class cls = loader.loadClass("kevin.Hello");
System.out.println(cls.getClassLoader());
Hello hello = (Hello) cls.newInstance();
hello.sayHello();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
虽然我们请求让自定义的类装载器HelloClassLoader来装载Hello.class,但是该类 的定义类装载器却是AppClassLoader,这个可以通过上面说过的双亲委派模型来解释。
下面是一个forName()的例子。使用forName()可以保证在这个方法返回之前 ,被装载的类型会被连接和初始化,而loadClass()不能对此做出承诺。
EasyHello.java
package kevin; public class EasyHello { public static void main(String[] args) { // 动态扩展的另一种途径是用Class类的静态方法forName() try { Class cls = Class.forName("kevin.Hello"); System.out.println(cls.getClassLoader()); Hello hello = (Hello) cls.newInstance(); hello.sayHello(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
上面是些简单的例子,希望对大家有点帮助。。如果大牛路过看到什么错误 了,还望不吝赐教。。。