java类的加载过程?
- 加载【class 信息加载到jvm】
- 验证【验证class信息的正确性】
- 准备【类或接口创建静态字段并将这些字段初始化为其默认值】
- 解析【解析是从运行时常量池中的符号引->直接引用1. 方法调用2. 静态属性引用】
- 初始化【类或接口的初始化包括执行类或接口的初始化方法】
- 使用
- 卸载
什么是双亲委派?
当一个类加载器收到类加载请求时,它首先将请求委托给其父类加载器处理。只有当父类加载器无法加载该类时,子类加载器才会尝试自行加载。这种机制按照类加载器的层级关系,逐层进行委派,旨在保证类加载的有序性和安全性
类加载器有哪些及加载顺序?
- bootstrap class loader【JAVA_HOME/lib】
- extention class loader【JAVA_HOME/jre/lib/ext】
- application class loader【JAVA_HOME/calsspath】
- custom class loader【加载自定义目录】
如何打破双亲委派
- 自定义类加载器,重写loadClass,重写findClass并不会打破,但是可以把具体的加载逻辑重写在findClass方法中
import java.net.URL;
import java.net.URLClassLoader;
public class CustomClassLoader extends URLClassLoader {
public CustomClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 自定义加载逻辑,这里可以根据自己的需求加载特定的类
if (name.startsWith("com.example.special")) {
return findClass(name);
}
return super.loadClass(name);
}
public static void main(String[] args) throws Exception {
// 创建自定义类加载器,并指定父类加载器为系统类加载器
URL[] urls = new URL[]{new URL("file:/path/to/jar/file.jar")};
CustomClassLoader customClassLoader = new CustomClassLoader(urls, ClassLoader.getSystemClassLoader());
// 使用自定义类加载器加载特定的类
Class<?> specialClass = customClassLoader.loadClass("com.example.special.SpecialClass");
Object specialObject = specialClass.newInstance();
// 调用特定类的方法
specialClass.getMethod("specialMethod").invoke(specialObject);
}
}
- 使用线程上下文类加载器【Thread.setContextClassLoader(xxloader)】如果创建线程时还未设置,他将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。
public class Main {
public static void main(String[] args) {
// 创建自定义类加载器,并指定父类加载器为系统类加载器
CustomClassLoader customClassLoader = new CustomClassLoader();
// 创建一个新的线程,并在其中设置线程上下文类加载器为自定义类加载器
Thread thread = new Thread(() -> {
Thread.currentThread().setContextClassLoader(customClassLoader);
// 在新线程中使用线程上下文类加载器加载特定类
try {
Class<?> specialClass = Class.forName("com.example.special.SpecialClass");
Object specialObject = specialClass.newInstance();
specialClass.getMethod("specialMethod").invoke(specialObject);
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
});
// 启动新线程
thread.start();
}
}
class CustomClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 自定义加载逻辑,这里可以根据自己的需求加载特定的类
if (name.startsWith("com.example.special")) {
return findClass(name);
}
return super.loadClass(name);
}
}
哪些场景下需要自定义加载器?
- 动态加载类:有些类在程序运行时才能确定需要加载,而不是在编译时就确定好的。自定义类加载器可以实现根据需要动态加载这些类。
- 热部署:在应用程序运行时更新或替换某些类文件,以实现热部署功能。这种情况下,自定义类加载器可以加载新的类版本而不必重启整个应用程序。
- 加载非标准位置的类:有时需要从非标准的位置(比如数据库、网络或特定的文件系统)加载类,自定义类加载器可以实现这种功能。