文章目录
一、概述
1.类加载的分类
2.类加载器的必要性
3.命名空间
4.类加载机制的基本特征
二、复习:类的加载器分类
1.引导类加载器
2.扩展类加载器
3.系统类加载器
4.用户自定义类加载器
三、测试不同的类加载器
四、ClassLoader源码解析
1.ClassLoader的主要方法
- loadClass源码解析(满足双亲委派机制的主要逻辑就是在loadClass方法中实现的)
假设测试代码:ClassLoader.getSystemClassLoader.loadClass("com.atguigu.java.User")
涉及到对如下方法的使用
protected Class<?> loadClass(String name, boolean resolve)//resolve:true-加载class的同时进行解析操作
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {//同步操作,保证只能加载一次
// 首先,在缓存中判断是否已经加载同名的类
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//获取当前类加载器的父类加载器
if (parent != null) {
//如果存在父类加载器,则调用父类加载器进行类的加载
c = parent.loadClass(name, false);
} else {//parent为null:父类加载器是引导类加载器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {//当前类的加载器的父类加载器未加载此类 or 当前类的加载器未加载此类
//调用当前ClassLoad的findClass()
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {//是否进行解析操作
resolveClass(c);
}
return c;
}
}
2.SecureClassLoader与URLClassLoader
3.Class.forName()与ClassLoader.loadClass()
五、双亲委派机制
1.定义与本质
2.优势与劣势
3.破坏双亲委派机制
-
破坏双亲委派机制1
-
破坏双亲委派机制2
-
破坏双亲委派机制3
4.热替换的实现
代码实现热替换案例:
1、先自定类加载器(下面有代码案例)
2、用死循环来模拟在一直运行的程序,每次循环都创建了一个新的类加载器
public class LoopRun {
public static void main(String[] args) throws Exception {
while (true){
MyClassLoader myClassLoader = new MyClassLoader("D:\\代码包\\JVMDemo\\chapter08\\src\\com\\atguigu\\java\\");
Class clazz = myClassLoader.findClass("Demo1.class");
Object demo = clazz.newInstance();
Method method = clazz.getMethod("hot");
method.invoke(demo);
Thread.sleep(5000);
}
}
}
六、沙箱安全机制
七、自定义类的加载器
1.实现方式
/**
* 自定义ClassLoader
*
* @author xzt
* @create 2021-01-19 15:28
*/
public class MyClassLoader extends ClassLoader {
private String byteCodePath;//字节码文件的路径
public MyClassLoader(String path) {
this.byteCodePath = path;
}
public MyClassLoader(ClassLoader parent, String byteCodePath) {
super(parent);
this.byteCodePath = byteCodePath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//获取字节码文件的完整路径
String fileName = "";
if (name.contains(".class")) {
fileName = byteCodePath + name;
} else {
fileName = byteCodePath + name + ".class";
}
BufferedInputStream bis = null;
ByteArrayOutputStream baos = null;
int len = -1;
try {
//获取一个输入流
bis = new BufferedInputStream(new FileInputStream(fileName));
//获取一个字节数组输出流
baos = new ByteArrayOutputStream();
//具体读入数据并写出数据的过程
byte[] data = new byte[1024];
while ((len = bis.read(data)) != -1) {
baos.write(data, 0, len);
}
//获取内存中完整的字节数据
byte[] byteCode = baos.toByteArray();
//调用defineClass将字节数组的数据转换为Class的实例
Class clazz = defineClass(null, byteCode,0, byteCode.length);
return clazz;
} catch (Exception e) {
e.printStackTrace();
}finally {
if (baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}