关于ClassLoader与动态扩展

我们要使用一个类,这个类首先要被装载,然后进行连接,初始化等步骤。一个类的装载是由类装载器(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();
		}
	}
}





上面是些简单的例子,希望对大家有点帮助。。如果大牛路过看到什么错误 了,还望不吝赐教。。。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值