Java类加载过程和双亲委派原则

一、ClassLoader的继承关系(简图)

二、Java的类加载过程

Java中类加载主要分为以下5个步骤:

  • 加载:通过一个类的完全限定名查找此类字节码文件,并利用字节码文件创建一个Class对象
  • 验证:确保Class文件的字节流中包含信息符合当前虚拟机要求;
    • 文件格式验证;
    • 字节码验证;
    • 元数据验证;
    • 符号引用验证;
  • 准备:为类变量分配内存并初始化(分配到方法区)。不包含final类型的类变量,因为final在编译时就会分配了;
  • 解析:主要将常量池中的符号引用替换为直接引用的过程;
  • 初始化:类加载最后阶段,若该类具有父类,则对其进行初始化;
    1. 父类静态成员和stastic代码块;
    2. 子类静态成员和stastic代码块;
    3. 父类成员变量和非stastic代码块;
    4. 父类构造函数;
    5. 子类成员变量和非stastic代码块;
    6. 子类构造函数

三、双亲委派概念

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

四、这样做有什么优点呢?

  1. 很明显,这样可以少写很多代码,哈哈哈。因为这样搞得话基本上就在ClassLoader类里边定义加载的方法就行了,其他子类都调用父类的方法。
  2. 可以避免重复加载:后边会说到,加载的时候会先读缓存,缓存没有才会去加载,所以都交给ClassLoader去加载的话,就没有重复加载的问题了;如果从网络传过来一个java.lang.Integer,那么类型相同直接读缓存了,这样你就无法在外部篡改Java的核心类库
  3. 非常安全:如果自定义一个java.lang.XXX,按照一般逻辑肯定是给ClassLoader加载了,ClassLoader一看java.lang包里边没有这个类,就让子类自己实例化了 ,但是子类没有访问java.lang包的权利,就达到了不允许加载的效果;

五、双亲委派实现

实现很简单,就是只在ClassLoader中定义加载方法load(String name,boolean resolve),看下源码:

// 1.使用protected严格限制为父子类使用
    protected Class loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        // 2.加锁,防止了多个classLoader同时请求加载同一个类
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // 3.首先,检查该类是否已经加载,说白了就是读缓存
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 4.这里需要解释一下,ClassLoader是没有父类的,那么为了符合双亲委派原则,他在这里做了一下判断
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        // 5.一个native方法,应该是从内存中读取之类的
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                // 如果仍然没有找到,就执行findClass()
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
 
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

如果都没有找到,就会执行findClass(),但是ClassLoader类并没有实现这个方法,而是交给子类去实现,比如URLClassLoader。这就实现了双亲委派的最后一句话,父类没有找到的时候,才会自己去寻找

所以如果我们自定义ClassLoader的时候,一般可能需要重写findClass()方法,来处理父类找不到的问题。

看一下这个方法:

protected Class findClass(final String name)
            throws ClassNotFoundException
    {
        final Class result;
        try {
            result = AccessController.doPrivileged(
                    new PrivilegedExceptionAction>() {
                        public Class run() throws ClassNotFoundException {
                            String path = name.replace('.', '/').concat(".class");
                            Resource res = ucp.getResource(path, false);
                            // 如果资源找到了,就调用defineClass()生成Class对象,并返回
                            // 如果没有,返回null
                            if (res != null) {
                                try {
                                    return defineClass(name, res);
                                } catch (IOException e) {
                                    throw new ClassNotFoundException(name, e);
                                }
                            } else {
                                return null;
                            }
                        }
                    }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        // 为空就抛异常
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }

太深的没有深究,大家可以参考这篇博客:https://www.cnblogs.com/mybatis/p/9396135.html

如果有错误的,欢迎指正

你可能感兴趣的:(Java设计思想,java)