类加载

95 阅读3分钟
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);
    }
}

哪些场景下需要自定义加载器?
  • 动态加载类:有些类在程序运行时才能确定需要加载,而不是在编译时就确定好的。自定义类加载器可以实现根据需要动态加载这些类。
  • 热部署:在应用程序运行时更新或替换某些类文件,以实现热部署功能。这种情况下,自定义类加载器可以加载新的类版本而不必重启整个应用程序。
  • 加载非标准位置的类:有时需要从非标准的位置(比如数据库、网络或特定的文件系统)加载类,自定义类加载器可以实现这种功能。