类加载过程
  • 加载:在磁盘通过IO读入字节码文件,
  • 验证:验证字节码的正确性
  • 准备:给类的静态变量分配内存,同时根据类型赋予初始默认值。
  • 解析:将符号引用替换为直接引用。
  • 初始化:给类的静态变量初始化为指定的值,执行静态代码块。
  • 注意:final修改的类常量,在编译的时候就会赋值。实例的常量,是在创建对象的时候才赋
类加载器分类
  • 引导类加载器(BootStrap ClassLoader):负载加载JRE/lib目录下的核心类库。(它比较特殊,底层是由C++实现的)
  • 扩展类加载器(Extension ClassLoader):负载加载JRE/lib/ext目录下jar包中的类。
  • 应用类加载器(Application ClassLoader):负载加载ClassPath路径下的自己写的类,也是系统的默认类加载器。
  • 自定义类加载器:负载加载自定义路径下的类。
  • 注意:ExtClassLoader和AppClassLoader是Launcher的静态内部类,在启动类Launcher初始化的过程这个,同时会实例化ExtClassLoader和AppClassLoader。
  public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                } catch (InstantiationException var6) {
                } catch (ClassNotFoundException var7) {
                } catch (ClassCastException var8) {
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

双亲委派机制

  • 系统默认的类加载器是应用类加载器,一个类要加载,应用类加载器会判断之前有没有加载过当前类,如果没有加载过,就会向上委托,交给扩展类加载器来进行加载。
  • 扩展类加载器加载当前类时,也会进行判断有没有加载过当前类,如果没有就继续向上委托,交给引导类加载器来进行加载。
  • 引导类加载会先进行判断有没有加载过当前类,如果没有,就会从指定加载的类目录中去找当前类,如果找到,就由引导类加载器加载该类;如果没有找到,就说明加载失败,就向下交给扩展类加载器去加载该类。
  • 扩展类加载器也会从指定加载的类目录中去找当前类,如果找到,就由扩展类加载器加载该类;如果没有找到,说明也加载失败,继续向下交给应用类加载器去加载该类。
  • 应用类加载器会从ClassPath中去找当前类,如果找到,就有应用类加载器加载该类。
为什么要设计双亲委派机制?
  • 避免类的重复加载:父加载器已经加载的类,子加载器无需加载,保证被加载类的唯一性。
  • 沙箱安全机制:防止核心类库被恶意篡改。
思考:为什么类加载要从下向上委托,而不是直接从上向下委托?

因为被加载的类,绝大部分都是ClassPath路径下我们自己编写的类,采用从下向上委托,只有第一次加载需要走一次完成的委托流程,重复加载的时候应用类加载就可以直接加载;如果,采用从上向下委托的流程,我们编写的类每次加载都要走一遍委托的流程,整体来看,还是自下向上委托设计更合理。

如何自定义类加载器?

自定义类加载器只需要直接继承java.lang.ClassLoader,该类有两个核心方法

  • loadClass(String name, boolean resolve),这个方法实现了双亲委派机制。
  • findClass(String name),默认是空实现,自定义类加载器重写findClass即可。
如何打破双亲委派机制?

在自定义类加载中提高过,java.lang.ClassLoader中有两个核心方法,loadClass方法中实现了双亲委派机制,如果想要打破双亲委派机制,我们只需要自己重新loadClass方法,自定义类加载逻辑即可。


筱火渔
1 声望0 粉丝