优秀的编程知识分享平台

网站首页 > 技术文章 正文

面试官:Class.forName和loadClass的区别

nanyue 2024-09-06 20:25:34 技术文章 6 ℃

Class.forName 和 ClassLoader.loadClass 都可以用来反射加载一个类,主要有2种区别:

使用的类加载器不一样

Class.forName 最终是使用调用方的 ClassLoader 来加载类,在代码中使用 Reflection.getCallerClass() 方法获取类加载器来加载类

 public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                // 使用调用方的 ClassLoad 加载类
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }

ClassLoader.loadClass 遵循类加载的双亲委派模型类加载类,在代码中使用 ClassLoader parent 加载类

// The parent class loader for delegation
if (parent != null) {
    c = parent.loadClass(name, false);
}

双亲委托模型:当一个类加载器接收到一个类加载的任务时,不会立即展开加载,而是将加载任务委托给它的父类加载器去执行,每一层的类都采用相同的方式,直至委托给最顶层的启动类加载器为止。如果父类加载器无法加载委托给它的类,便将类的加载任务退回给下一级类加载器去执行加载。

类的装载过程不一样

类的装载过程为:

                      链接
                       |
           --------------------------
加载 ----> | 验证 ----> 准备 ----> 解析| ----> 初始化 ----> 使用 ----> 卸载
           --------------------------

Class.forName 会装载类到初始化阶段,因此会执行类中的静态方法块,调用 forName0 时传入的 initialize 为 true

public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        // initialize = true
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

ClassLoader.loadClass 会装载类到加载节点,不会执行链接操作,因此不会执行类中的静态方法块,在代码中调用 loadClass 时传入的 resolve 为 false,表示不链接类

if (parent != null) {
    // resolve = false
    c = parent.loadClass(name, false);
}

如:

class Test {
    static {
        System.out.print("loaded");
    }   
}

使用 Class.forName 加载 Test 类会输出 "loaded",而使用 ClassLoader.loadClass 加载则不会输出 "loaded"

最近发表
标签列表