Java ClassLoader 类加载器

    xiaoxiao2021-03-25  69

    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。       加载 就是指将class文件读入内存,并为之创建一个Class对象。 任何类被使用时系统都会建立一个Class对象。        连接             验证 是否有正确的内部结构,并和其他类协调一致             准备 负责为类的静态成员分配内存,并设置默认初始化值(静态成员,类的创建而创建)            解析 将类的二进制数据中的符号引用替换为直接引用       初始化 就是我们以前讲过的初始化步骤

    加载器

    1. Bootstrap ClassLoader

    负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

    2. Extension ClassLoader

    负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

    3. App ClassLoader

    负责记载classpath中指定的jar包及目录中class

    4. Custom ClassLoader

    属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

    加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

    类的初始化

        类什么时候才被初始化:

    1)创建类的实例,也就是new一个对象

    2)访问某个类或接口的静态变量,或者对该静态变量赋值

    3)调用类的静态方法

    4)反射(Class.forName("com.lyj.load"))

    5)初始化一个类的子类(会首先初始化子类的父类)

    6)JVM启动时标明的启动类,即文件名和类名相同的那个类

             只有这6中情况才会导致类的类的初始化。

    自定义加载器

    public class CustomClassLoader extends ClassLoader { // 要加载的类 private String name; // 设置父加载器和需要加载的类 public CustomClassLoader(ClassLoader parent , String name) { super(parent); if(name == null || name.length() <= 0) throw new NullPointerException(); this.name = name; } // 重写加载器 @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> clazz = null; /**如果是我们想要热加载的类则调用我们重写的findClass方法来加载**/ if(this.name.equals(name) && !"java".equals(name)){ // 调用系统加载class文件 clazz = findLoadedClass(name); if(clazz == null) // 调用自定义的 clazz = findClass(name); // 类的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析统称为连接 //如果类已连接过,resolveClass方法会直接返回 if(resolve) resolveClass(clazz); return clazz; } // 调用父加载器 return super.loadClass(name , resolve); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // 获得实际存储class文件位置 String fileName = c2f(name); // 读取class文件 byte[] bytes = f2b(fileName); // 根据class文件生成Class类对象 return defineClass(name, bytes, 0, bytes.length); } // 类名转为文件名 private String c2f(String name){ /**编译后的class文件存放的目录**/ String baseDir = "F:\\classes"; name = name.replace("." , File.separator); name = baseDir + name + ".class"; return name; } // 读取文件byte数组 private byte[] f2b(String fileName){ RandomAccessFile file = null; FileChannel channel = null; byte[] bytes = null; try { /**随机存取文件对象,只读取模式**/ file = new RandomAccessFile(fileName , "r"); /**NIO文件通道**/ channel = file.getChannel(); /**NIO字节缓冲**/ ByteBuffer buffer = ByteBuffer.allocate(1024); int size = (int) channel.size(); bytes = new byte[size]; int index = 0; /**从NIO文件通道读取数据**/ while (channel.read(buffer) > 0){ /**字节缓冲从写模式转为读取模式**/ buffer.flip(); while (buffer.hasRemaining()){ bytes[index] = buffer.get(); ++index; } /**字节缓冲的readerIndex、writerIndex置零**/ buffer.clear(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if (channel != null) { try { channel.close(); } catch (IOException e) { e.printStackTrace(); } } if (file != null) { try { file.close(); } catch (IOException e) { e.printStackTrace(); } } } return bytes; } // 加载 public Class<?> loadClass(){ try { // 调用系统loadClass(name,false) return loadClass(name); } catch (ClassNotFoundException e) { e.printStackTrace(); return null; } } }

    public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException { String name = "top.xfdtm.test.Person"; CustomClassLoader loader = new CustomClassLoader(Thread.currentThread() .getContextClassLoader(), name); Class<?> clazz = loader.loadClass(); Object obj = clazz.newInstance(); }

    转载请注明原文地址: https://ju.6miu.com/read-33695.html

    最新回复(0)