Class.forName & ClassLoader.loadClass 比较

4,191 阅读2分钟

1 前言

类的生命周期,分为加载,连接(验证,准备,解析),初始化,使用,卸载这几个过程。

Class.forNameClassLoader.loadClass都会执行加载过程,将指定的类加载到内存中供使用。

但加载后,在默认情况下:Class.forName初始化ClassLoader.loadClass在加载后什么都不会做。

2 Class.forName

java.lang.Class中有两个forName方法,参数不同,都是调用了private方法forName0

private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class<?> caller) throws ClassNotFoundException;

参数

  • name:类的全名
  • initialize:是否初始化
  • loader:使用的类加载器
  • caller:调用者的类

返回:使用loader加载的全名为name的类

  • forName(String className)
  • forName(String name, boolean initialize, ClassLoader loader)

2.1 仅指定className

public static Class<?> forName(String className)
                throws ClassNotFoundException {
        // 获取调用者的类
        Class<?> caller = Reflection.getCallerClass();
        // 使用调用者的调用者的ClassLoader加载,加载后对类进行初始化。
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

2.2 同时指定ClassLoader

    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
             //有SecurityManager时,才需要获取调用者的类。
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        // 指定类全名,是否初始化,ClassLoader,调用者的类 调用forName0
        return forName0(name, initialize, loader, caller);
    }

3 ClassLoader.loadClass

参数

  • name:类的全名
  • resolve:加载后时候进行生命周期中的连接部分

返回:使用当前ClassLoader加载的全名为name的类

过程

  • 1 调用findLoadedClass(name)检查类是否已经加载,如果没有加载过,则继续;
  • 2 parent存在,根据双亲委派模型,调用parent.loadClass(name, false),优先从parent中执行loadClass(但不进行连接);
  • 3 parent不存在,则调用findBootstrapClassOrNull(name)判断是否在Bootstrap Classloader中加载过;
  • 4 如果类仍未找到,则执行findClass查找类,findClass由ClassLoader的子类实现,如java.net.URLClassLoader
  • 5 最后,根据resolve参数,确定是否执行连接。
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 检查类是否已经加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 双亲委派,优先从parent中执行loadClass,但执行时,不进行连接。
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                    // name是否在Bootstrap Classloader中加载过,是的话则返回该类。
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    
                    // 执行findClass查找类,findClass由ClassLoader的子类实现。
                    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;
        }
    }

ClassLoader中还有一个不带resolve的loadClass方法,在类加载完后不执行连接:

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

4 总结

Class.forName和ClassLoader.loadClass都会执行加载过程。不同的是,在类加载后:

  • Class.forName:默认执行初始化,但可以指定;
  • ClassLoader.loadClass:默认不做任何事(不连接,不初始化),但可以指定是否连接