在我的博客: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。
以上就是关于类加载器的介绍!