前言
作用:实现类加载的功能,确定被加载类 在 Java虚拟机中 的 唯一性。
类加载器的类型数量分别从 Java虚拟机 & Java开发者的角度来看,如下图:
1. 作用 负责加载以下类:
- 存放在
\lib
目录中的类 - 被
-Xbootclasspath
参数所指定路径中、并且是被虚拟机识别的类库
仅按文件名识别,如:rt.jar,名字不符合的类库即使放在lib目录中也不会被加载
2. 特别注意
- 启动类加载器 无法 被
Java
程序直接引用 - 用户在编写自定义类加载器时,若需把 加载请求 委派 给 引导类加载器,直接使用
null
代替即可,如java.lang.ClassLoader.getClassLoader()
方法所示:
@CallerSensitive
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}
二、扩展类加载器(Extension ClassLoader)
1. 作用 负责加载以下类:
\lib\ext
目录中的类库- 被
java.ext.dirs
系统变量所指定的路径中的所有类库
2. 特别注意
- 由
sum.misc.Launcher$ExtClassLoader
类实现 - 开发者可以直接使用扩展类加载器
1. 作用 负责加载 用户类路径(ClassPath)上所指定的类库
2. 特别注意
- 也称为系统类加载器,因为该类加载器是ClassLoader中的getSystemClassLoader()方法的返回值
- 由sum.misc.Launcher$AppClassLoader类实现
- 开发者可以直接使用该类加载器
- 若开发者 没 自定义类加载器,程序默认使用该类加载器
在 Java 虚拟机中,各种类加载器 配合使用 的 模型(关系)是 双亲委派模型
四、双亲委派模型1. 模型说明 2. 工作流程
双亲委派模型的工作流程代码实现在 java.lang.ClassLoader
的 loadClass()
中,具体如下:
@Override
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class c = findLoadedClass(name);
// 检查需要加载的类是否已经被加载过
if (c == null) {
try {
// 若没有加载,则调用父加载器的loadClass()方法
if (parent != null) {
c = parent.loadClass(name, false);
}else{
// 若父类加载器为空,则默认使用启动类加载器作为父加载器
c=findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 若父类加载器加载失败会抛出ClassNotFoundException,
//说明父类加载器无法完成加载请求
}
if(c==null){
// 在父类加载器无法加载时
// 再调用本身的findClass方法进行类加载
c=findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}
若一个类加载器收到了类加载请求。 1、把 该类加载请求 委派给 父类加载器去完成,而不会自己去加载该类; 2、只有当 父类加载器 反馈 自己无法完成该加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会自己去加载
3. 优点 Java类随着它的类加载器一起具备了一种带优先级的层次关系
1. 如:类 java.lang.Object(存放在rt.jar中)在加载过程中,无论哪一个类加载器要加载这个类,
最终需委派给模型顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。
2. 若没有使用双亲委派模型(即由各个类加载器自行去加载)、用户编写了一个java.lang.Object的类(放在ClassPath中),
那系统中将出现多个不同的Object类,Java体系中最基础的行为就无法保证
五、自定义类加载器
主要是通过继承自 ClassLoader 类 从而自定义一个类加载器
步骤1:自定义类加载器 MyClassLoader.java
// 继承自ClassLoader类
public class MyClassLoader extends ClassLoader {
// 类加载器的名称
private String name;
// 类存放的路径
private String classpath = "E:/";
MyClassLoader(String name) {
this.name = name;
}
MyClassLoader(ClassLoader parent, String name) {
super(parent);
this.name = name;
}
@Override
public Class findClass(String name) {
byte[] data = loadClassData(name);
return this.defineClass(name, data, 0, data.length);
}
public byte[] loadClassData(String name) {
try {
name = name.replace(".", "//");
System.out.println(name);
FileInputStream is = new FileInputStream(new File(classpath + name
+ ".class"));
byte[] data = new byte[is.available()];
is.read(data);
is.close();
return data;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
步骤2:定义待加载的类
public class TestObject {
public void print() {
System.out.println("hello DiyClassLoader");
}
}
步骤3:定义测试类
public class Test {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
MyClassLoader cl = new MyClassLoader("myClassLoader");
// 步骤1:创建自定义类加载器对象
Class clazz = cl.loadClass("com.carson.TestObject");
// 步骤2:加载定义的测试类:myClassLoader类
TestObject test= (TestObject) clazz.newInstance();
// 步骤3:获得该类的对象
test.print();
// 输出
}
}
// 输出结果
hello DiyClassLoader