ClassLoader类加载器

在我的博客:Java之反射中讲到取得具体类的Class类对象的三种方式:1.调用Object类的getClass()方法 2.具体类名.class 3.调用Class类的类方法forName()。而通过Class类中提供的forName()方法取得对应的Class类对象的过程是先进行类加载从而取得对应的Class类对象,此时类加载的方式是通过ClassPath配置的路径进行类的加载。那如果出现所需要的类的加载路径在网络或不是ClassPath配置的路径下的文件,这时候就需要自身实现类加载器,通过你想加载的类的路径去加载该类。这也是ClassLoader类的作用。

通过上述的解释知:ClassLoader类的主要作用是实现类加载器,使得类的加载路径不再是默认路径(即ClassPath配置的路径),还可以是网络或其他路径。

1.ClassLoader类加载器

先来看看两个方法:(1)Class类的一个方法getClassLoader(),该方法的功能是取得当前类的类加载器;(2)ClassLoader类的一个方法getParent(),该方法用于取得当前类加载器的父类。

Class类getClassLoader()方法的源代码如下:

public ClassLoader getClassLoader() 

ClassLoader类的getParent()方法的源代码如下:

    public final ClassLoader getParent() 

下面调用上述的两个方法:

package www.bit.java.reflect;
//自定义类
class Apple{
	
}
public class Test {
	public static void main(String[] args) throws Exception {
		Class<?> cls=Class.forName("www.bit.java.reflect.Apple");
		ClassLoader classLoader=cls.getClassLoader();
		System.out.println(classLoader);
		System.out.println(classLoader.getParent());
		System.out.println(classLoader.getParent().getParent());
	}
}

运行结果如下:

sun.misc.Launcher$AppClassLoader@6d06d69c
sun.misc.Launcher$ExtClassLoader@70dea4e
null

解释如下:可以看出自定义类Apple的类加载器为AppClassLoader类,而AppClassLoader的父类为ExtClassLoader类,ExtClassLoader的父类为null。为什么呢?因为在为自定义类加载器进行类加载时,默认的类加载器为AppClassLoader类,而AppleClassLoader类加载器的父类为ExtClassLoader类,ExtClassLoader类加载器的父类原本应该为Bootstrap,但由于Bootstrap无法被java程序直接使用,故返回null。

以上解释知道了三种类加载器:Bootstrap、ExtClassLoader、AppClassLoader,其实还有一种类加载器是自定义类加载器,用户根据自身要求定义的类加载器。下面具体解释这些类加载器。

(1)Bootstrap:启动类加载器,使用C++实现,是虚拟机(JVM)自身的一部分。该类加载器加载的目录为

所安装软件(如安装Eclipse软件)的路径目录/jre/bin,Bootstrap类加载器不能被Java程序直接使用。其他三种类加载器均是由Java实现的,都继承于ClassLoader类。

(2)ExtClassLoader:扩展类加载器,该类加载器加载的目录为所安装软件(如安装Eclipse软件)的路径目录/jre/bin/ext,该类加载器是由Java实现的,故可以直接被Java程序使用。

(3)AppClassLoader:应用程序类加载器,该类加载器加载的类都是用户自定义的类。该类加载器的加载路径为用户自身配置的ClassPath的路径。

(4)自定义类加载器:这需要用户自己去定义类加载器,去加载想要加载的路径下的类。下面讲如何自定义类加载器。

2.自定义类加载器

首先将一个类文件不再放入默认路径,而是放在其他路径如:C:\Users\Administrator\Desktop\Hello.class,该路径在自定义类加载器加载时使用。

其次自定义类加载器的类需要继承于ClassLoader类,在该类中调用defineClass()方法。下面看看ClassLoader类提供的defineClass()方法的使用:

    protected final Class<?> defineClass(String name, byte[] b, int off, int len)

(1)返回类型为Class类的对象

(2)第一个参数name接收需要加载的类的类名称

(3)第二个参数b接收的是类文件的内容

(4)第三个参数和第四个参数表示读取数组b的字节范围

下面进行代码演示:

package www.bit.java.reflect;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;


//自定义类继承ClassLoader类
class MyClassLoader extends ClassLoader{
	/**
	 * 定义一个方法,用于实现自定义类加载器的功能
	 * @param className接收类名称
	 * @param classData接收类文件内容
	 * @param 第三、四参数表示使用classData的字节范围
	 * @return 返回Class类对象
	 * @throws IOException 
	 */
	public Class<?> loadDate(String className) throws IOException{
		//定义一个字符数组,用于保存类文件中的内容
		byte[] classData=this.loadClassData();
		return super.defineClass(className,classData,0,classData.length);
	}
	/**
	 * 通过指定文件路径进行类文件的读取,即进行二进制文件的读取
	 * @return 返回保存类文件的内容的字符数组
	 * @throws IOException 
	 */
	public byte[] loadClassData() throws IOException {
		InputStream input=new FileInputStream("C:\\Users\\Administrator\\Desktop\\Hello.class");
		//取得所有字节内容,放在内存中
		ByteArrayOutputStream arrayOutputStream=new ByteArrayOutputStream();
		//读取到缓冲区
		byte[] data=new byte[20];
		int temp=0;
		while((temp=input.read(data))!=-1)
		{
			arrayOutputStream.write(data,0,temp);
		}
		byte[] result=arrayOutputStream.toByteArray();
		input.close();
		arrayOutputStream.close();
		return result;
	}
}
public class Test {
	public static void main(String[] args) throws Exception {
		Class<?> cls=new MyClassLoader().loadDate("Hello");
		System.out.println(cls.getClassLoader());
	}
}

运行结果如下:

www.bit.java.reflect.MyClassLoader@5c647e05

此时,你会发现类加载器变为自定义的类加载器MyClassLoader而不是默认的类加载器AppClassLoader。

以上就是关于类加载器的介绍!

猜你喜欢

转载自blog.csdn.net/tongxuexie/article/details/80354621