目录
- 概述
- 类加载器
- 执行顺序
- 加载时机与过程
- 类加载的四个时机
- 一个类的一生
- 类加载途径
- 自定义类加载器
- 工作准备
- 编写自定义加载器
- 结果
- 结束
概述
类加载器
jvm 的类加载是通过 ClassLoader 及其子类来完成的。
有以下类加载器
注意: bootstrap 引导程序
根据上图总结如下
- 启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录或通过 -Xbootclasspath 参数指定路径中的且被虚拟机认可(rt.jar) 的类库。
- 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录或者通过 java.ext.dirs 系统变量指定路径中的类库。
- 应用程序类加载器(Application ClassLoader):负责加载用户路径 classpath 上的类库 (自己编写的程序在此加载)
- 自定义类加载器(User ClassLoader):加载应用之外的类文件 (例如:JRebel 热部署)
执行顺序
来看一下图:
- 检查顺序是自底向上: 加载过程中会先检查类是否被已加载,从 Custom 到 BootStrap 逐层检查,只要
某个类加载器
已加载就视为此类已加载
,**好处:**保证此类所有 ClassLoader 只加载一次。 - 加载的顺序是自顶向下: 也就是由上层来逐层尝试加载此类。
加载时机与过程
类加载的四个时机
public class Student{
private static int age;
public static void method(){
}
}
// Student.age
// Student.method();
// new Student();
Class t = Class.forName("java.lang.Thread")
- 遇到 new、getStatic、putStatic、invokeStatic 四条指令时
- 使用 java.lang.reflect 包方法时,对类进行反射调用
- 初始化一个类时,发现其父类还没初始化,要先初始化其父类
- 当虚拟机启动时,用户需要指定一个主类 main,需要先将主类加载
一个类的一生
当一个 .java 文件被编译成 class 文件
类的生命周,而非对象的生命周期,看下图
类加载主要做了三件事:
- 1.根据
类全限定名称
,二进制字节流加载 class 文件 - 2.字节流静态数据 ,进入方法区 (永久代、元空间)
- 3.创建字节码 Class 对象
类加载途径
加载途径总结如下:
- 1.jar/war
- 2.jsp生成的 class
- 3.数据库中的二进制字节流
- 4.网络中的二进制字节流
- 5.动态代理生成的二进制字节流
自定义类加载器
工作准备
准备好需要加载的类,代码很简单
package com.fun.demo;
public class Test {
public void say() {
System.out.println("hello jvm");
}
}
编译完成后放置如下路径: /Users/hyl/Desktop/jk/jvm/lib/com/fun/demo
编写自定义加载器
自定义类加载器 CustomClassLoader 继承 ClassLoader ;重写 findClass()方法,在findClass方法中调用 defineClass() 方法
自定义加载器如下,代码还是很简单的。
package com.fun.classloader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class CustomClassLoader extends ClassLoader {
private String rootPath;
public CustomClassLoader(String rootPath) {
this.rootPath = rootPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// name 传进来的全类名称
String absolutePath = rootPath + "/" + name.replace(".", "/") + ".class";
try {
FileInputStream in = new FileInputStream(absolutePath);
byte[] classByte = in.readAllBytes();
return defineClass(null, classByte, 0, classByte.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException exception) {
exception.printStackTrace();
}
return null;
}
}
测试代码如下:
package com.fun.classloader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
public class Test {
//public static void main(String[] args) {
// String tmp = "com.fun.demo.Test";
// System.out.println(tmp.replace(".", "/"));
//}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
CustomClassLoader loader = new CustomClassLoader("/Users/hyl/Desktop/jk/jvm/lib");
Class<?> clz = loader.loadClass("com.fun.demo.Test");
if (Objects.nonNull(clz)) {
Object o = clz.newInstance();
Method method = clz.getMethod("say", null);
method.invoke(o, null);
}
}
}
结果
运行 main 方法,得到的效果如下:
结束
至此,jvm 类加载系统就结束了,如有疑问,欢迎评论区留言。