java的类加载及ClassLoader

转自:java类的加载机制

JVM加载类的逻辑过程

三大阶段:加载、连接、初始化
连接阶段:验证、准备、解析

加载

根据类的 全路径限定名称 ,找到类的 二进制字节流文件。
根据类的 二进制字节流文件 ,生成类的 运行时数据结构 ,存储在方法区内。
生成类的 java.lang.Class对象,存储在jvm 堆中。

连接·验证

类的二进制字节流文件格式验证:是否符合.class文件格式的规范,如主次版本号、常量类型等。
元数据验证、字节码验证、符号引用验证

连接·准备

为类的静态变量分配内存,并将其初始化为默认值(0, null, false)。
例外:对于定义为 final static 的静态变量,此时就会被赋值为代码中指定的值。
如 private final static int a=3; 准备阶段完成后,a的值为3。

连接·解析

把类中的符号引用转换为直接引用。
将 :类、类字段、类方法、接口、接口方法、方法类型、方法句柄、和调用点限定符,以上7种符号引用,
替换为 : 直接指向目标的指针、相对偏移量、或者一个间接定位到目标的句柄。
如:将 class MyClass {} 中的 MyClass 替换为 MyClass 这个类的java.lang.Class实例对象在堆中的实际地址?

初始化(主动使用类的时候,才会进行初始化)

对静态变量赋值。
JVM可能根据预判,自动对未使用到的类进行加载、连接,但是不会进行初始化。
只有当主动使用类时,才会对类进行初始化。

主动使用类test.User的6种情况

new User();
User.staticVar;
User.staticMethod();
Class.forName(“test.User”);
初始化子类时,首先会初始化父类。
JVM启动时,被标明为启动类的类。

类初始化机制

当某个类被主动使用时,要对其进行初始化

  • 先初始化其父类
  • 如果类自身还未被加载,则对其加载和连接
  • 最后顺序执行类中的初始化语句,即:对static变量赋值,执行static代码块等

代码层面的类加载

Class.forName(“test.User”); // 加载、连接、初始化
ClassLoader.loadClass(“test.User”); // 只加载、连接,不初始化

类加载器的运行机制

  • 全盘负责
    当一个类加载器负责加载某Class时,该类所依赖和引用的其它所有Class也将由此类加载器负责加载,除非显式地使用另一个类加载器来载入
  • 父类委托
    先委托父类加载器对类进行加载,只有当父类加载器无法加载该类时,才在自己的类路径中加载该类
  • 缓存机制
    会将所有加载过的Class缓存,当使用某Class时,先从缓存中寻找该Class。只有当缓存中不存在该Class时,才会加载Class的二进制字节码文件,生成Class对象,并缓存。
    因此修改了Class后,必须重启JVM,程序的修改才会生效。

Jdk自带的类加载器

  • Bootstrap ClassLoader:
    负责加载jre\lib下,或-Xbootclasspath参数指定的路径下的.jar、java.*等类文件(如java.lang.String)
  • ExtClassLoader:
    负责加载jre\lib\ext下,或java.ext.dirs系统变量指定的路径下的所有.jar、javax.*等类文件(如javax.servlet)
    开发者可以直接使用该类加载器
  • AppClassLoader:
    负责加载用户类路径(ClassPath)所指定的类
    开发者可以直接使用该类加载器

自定义类加载器

一般只重写findClass()方法,用于实现网络方式获取Class的二进制字节流。示例:

package com.neo.classloader;

import java.io.*;

public class MyClassLoader extends ClassLoader {

    private String root;

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] loadClassData(String className) {
        String fileName = root + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
        try {
            InputStream ins = new FileInputStream(fileName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = 0;
            while ((length = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getRoot() {
        return root;
    }

    public void setRoot(String root) {
        this.root = root;
    }

    public static void main(String[] args)  {

        MyClassLoader classLoader = new MyClassLoader();
        classLoader.setRoot("E:\\temp");

        Class<?> testClass = null;
        try {
            testClass = classLoader.loadClass("com.neo.classloader.Test2");
            Object object = testClass.newInstance();
            System.out.println(object.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/wenxindiaolong061/article/details/89485830